diff --git a/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py b/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py index a272a24180d..9998a0745b7 100644 --- a/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py +++ b/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py @@ -16,15 +16,20 @@ from typing import Literal, Protocol, cast from agent_framework import ( + AgentResponseUpdate, ChatOptions, + CheckpointID, + CheckpointStorage, Content, ContextProvider, FileCheckpointStorage, HistoryProvider, + InMemoryCheckpointStorage, Message, RawAgent, SupportsAgentRun, WorkflowAgent, + WorkflowCheckpoint, ) from agent_framework.exceptions import AgentFrameworkException from azure.ai.agentserver.responses import ( @@ -287,6 +292,156 @@ def _checkpoint_storage_for_context(root: str, context_id: str, *, user_id: str ) +class _ContextAwareCheckpointStorage: + """A :class:`CheckpointStorage` decorator that runs request-scoped post-save logic. + + The workflow saves checkpoints internally with no post-save hook, so we wrap + the storage handed to ``run()``: :meth:`save` persists to the inner storage + then publishes the exact reference for response-level persistence. Every + other operation delegates unchanged to the inner storage. + + Created **per request** so checkpoint references are sent to the current + response handler while the wrapped inner storage stays cached across turns. + """ + + def __init__( + self, + inner: CheckpointStorage, + publisher: _WorkflowCheckpointPublisher, + context_id: str, + reference_kind: Literal["base", "current"], + ) -> None: + self._inner = inner + self._publisher = publisher + self._context_id = context_id + self._reference_kind: Literal["base", "current"] = reference_kind + + async def save(self, checkpoint: WorkflowCheckpoint) -> CheckpointID: + checkpoint_id = await self._inner.save(checkpoint) + self._publisher.publish(checkpoint_id, self._context_id, self._reference_kind) + return checkpoint_id + + async def load(self, checkpoint_id: CheckpointID) -> WorkflowCheckpoint: + return await self._inner.load(checkpoint_id) + + async def list_checkpoints(self, *, workflow_name: str) -> list[WorkflowCheckpoint]: + return await self._inner.list_checkpoints(workflow_name=workflow_name) + + async def delete(self, checkpoint_id: CheckpointID) -> bool: + return await self._inner.delete(checkpoint_id) + + async def get_latest(self, *, workflow_name: str) -> WorkflowCheckpoint | None: + return await self._inner.get_latest(workflow_name=workflow_name) + + async def list_checkpoint_ids(self, *, workflow_name: str) -> list[CheckpointID]: + return await self._inner.list_checkpoint_ids(workflow_name=workflow_name) + + +@dataclass(frozen=True) +class _WorkflowCheckpointReference: + """A response-persisted pointer to an exact workflow checkpoint.""" + + checkpoint_id: CheckpointID + context_id: str + kind: Literal["base", "current"] + + def to_dict(self) -> dict[str, str]: + return { + "checkpoint_id": self.checkpoint_id, + "context_id": self.context_id, + "kind": self.kind, + } + + @classmethod + def from_value(cls, value: Any) -> _WorkflowCheckpointReference | None: + if not isinstance(value, Mapping): + return None + value_mapping = cast(Mapping[str, Any], value) + checkpoint_id = value_mapping.get("checkpoint_id") + context_id = value_mapping.get("context_id") + kind = value_mapping.get("kind") + if not isinstance(checkpoint_id, str) or not isinstance(context_id, str) or kind not in {"base", "current"}: + return None + return cls(checkpoint_id=checkpoint_id, context_id=context_id, kind=cast(Literal["base", "current"], kind)) + + +class _WorkflowCheckpointPublisher: + """Publish workflow checkpoint references for response-level persistence.""" + + CHECKPOINT_REFERENCE_KEY = "workflow_checkpoint" + LEGACY_CHECKPOINT_ID_KEY = "last_checkpoint_id" + + def __init__(self, response_event_stream: ResponseEventStream) -> None: + self._response_event_stream = response_event_stream + self._pending: asyncio.Queue[_WorkflowCheckpointReference | None] = asyncio.Queue() + + def seed(self, reference: _WorkflowCheckpointReference | None) -> None: + metadata = self._response_event_stream.internal_metadata + if reference is None: + metadata.pop(self.CHECKPOINT_REFERENCE_KEY, None) + metadata.pop(self.LEGACY_CHECKPOINT_ID_KEY, None) + return + metadata[self.CHECKPOINT_REFERENCE_KEY] = reference.to_dict() + metadata[self.LEGACY_CHECKPOINT_ID_KEY] = reference.checkpoint_id + + def get_persisted_reference( + self, + *, + fallback_context_id: str | None, + ) -> _WorkflowCheckpointReference | None: + metadata = self._response_event_stream.internal_metadata + reference = _WorkflowCheckpointReference.from_value(metadata.get(self.CHECKPOINT_REFERENCE_KEY)) + if reference is not None: + return reference + + legacy_checkpoint_id = metadata.get(self.LEGACY_CHECKPOINT_ID_KEY) + if isinstance(legacy_checkpoint_id, str) and fallback_context_id is not None: + return _WorkflowCheckpointReference( + checkpoint_id=legacy_checkpoint_id, + context_id=fallback_context_id, + kind="current", + ) + return None + + def publish( + self, + checkpoint_id: CheckpointID, + context_id: str, + kind: Literal["base", "current"], + ) -> None: + self._pending.put_nowait( + _WorkflowCheckpointReference( + checkpoint_id=checkpoint_id, + context_id=context_id, + kind=kind, + ) + ) + + async def next_pending(self) -> _WorkflowCheckpointReference | None: + return await self._pending.get() + + def checkpoint_event(self, reference: _WorkflowCheckpointReference) -> Any: + # Apply the immutable queued reference immediately before snapshotting so + # a later workflow save cannot alter an earlier response checkpoint. + self.seed(reference) + return self._response_event_stream.checkpoint() + + def finish_run(self) -> None: + # The sentinel is ordered after every reference published by this run. + self._pending.put_nowait(None) + + +@dataclass(frozen=True) +class _WorkflowCheckpointExecution: + """Checkpoint storages and recovery mode for one hosted workflow request.""" + + restore_storage: CheckpointStorage | None + restore_checkpoint_id: CheckpointID | None + write_storage: CheckpointStorage + resume_current_turn: bool + publisher: _WorkflowCheckpointPublisher + + def _approval_storage_path_for_user(base_path: str, user_id: str) -> str: """Return the per-user approval storage file path under the base directory. @@ -352,12 +507,18 @@ def consent_url_from_error(exc: BaseException) -> list[ConsentError] | None: consent_errors: list[ConsentError] = [] error_message_start = inner_exception.error.message.find("{") if error_message_start == -1: - logger.warning("Consent error message does not contain JSON: %s", inner_exception.error.message) + logger.warning( + "Consent error message does not contain JSON: %s", + inner_exception.error.message, + ) return None consent_details_json = inner_exception.error.message[error_message_start:] consent_details = json.loads(consent_details_json) if "errors" not in consent_details or not isinstance(consent_details["errors"], list): - logger.warning("Consent error message JSON does not contain 'errors' list: %s", consent_details_json) + logger.warning( + "Consent error message JSON does not contain 'errors' list: %s", + consent_details_json, + ) return None for error in consent_details["errors"]: if ( @@ -370,13 +531,24 @@ def consent_url_from_error(exc: BaseException) -> list[ConsentError] | None: ): consent_url = error["error"]["message"] # type: ignore if isinstance(consent_url, str): - consent_errors.append(ConsentError(name=error.get("name", "Unknown"), consent_url=consent_url)) # type: ignore + consent_errors.append( + ConsentError( + name=error.get("name", "Unknown"), # type: ignore + consent_url=consent_url, + ) + ) else: - logger.warning("Consent URL in error message is not a valid URL: %s", consent_url) # type: ignore + logger.warning( + "Consent URL in error message is not a valid URL: %s", + consent_url, # type: ignore + ) if consent_errors: return consent_errors except json.JSONDecodeError: - logger.warning("Failed to parse consent details JSON: %s", inner_exception.error.message) + logger.warning( + "Failed to parse consent details JSON: %s", + inner_exception.error.message, + ) return None @@ -415,6 +587,7 @@ def __init__( 2. The agent must not have any context providers that maintain context in memory, because the hosting environment may get deactivated between requests, and any in-memory context would be lost. + 3. Resiliency (resilient_background=True) is ONLY supported for workflows. """ super().__init__(prefix=prefix, options=options, store=store, **kwargs) @@ -431,25 +604,37 @@ def __init__( provider.source_id, ) + resilient_background = bool(options and options.resilient_background) + self._resilient_background = resilient_background + self._is_workflow_agent = False - self._checkpoint_storage_path = None + self._checkpoint_storage_path: str | None = None if isinstance(agent, WorkflowAgent): if agent.workflow._runner_context.has_checkpointing(): # pyright: ignore[reportPrivateUsage] raise RuntimeError( "There should not be a checkpoint storage already present in the workflow agent. " "The hosting infrastructure will manage checkpoints instead." ) - self._checkpoint_storage_path = ( - self.CHECKPOINT_STORAGE_PATH - if self.config.is_hosted - else os.path.join(os.getcwd(), self.CHECKPOINT_STORAGE_PATH.lstrip("/")) - ) self._is_workflow_agent = True + elif resilient_background: + logger.warning( + "Resilient background mode is enabled for a non-workflow agent. " + "Crash recovery is not supported for non-workflow agents." + ) + + # Durable (file-based) storage is used when hosted, or when a workflow + # agent opts into resilient background execution -- crash recovery needs + # checkpoints and approval requests to survive a process restart. + # Otherwise local development keeps everything in memory. + use_file_storage = self.config.is_hosted or (resilient_background and self._is_workflow_agent) + if self._is_workflow_agent and use_file_storage: + self._checkpoint_storage_path = self._resolve_storage_path(self.CHECKPOINT_STORAGE_PATH) self._agent = agent + self._approval_storage_path = self._resolve_storage_path(self.FUNCTION_APPROVAL_STORAGE_PATH) self._approval_storage = ( - FileBasedFunctionApprovalStorage(self.FUNCTION_APPROVAL_STORAGE_PATH) - if self.config.is_hosted + FileBasedFunctionApprovalStorage(self._approval_storage_path) + if use_file_storage else InMemoryFunctionApprovalStorage() ) # Per-user (multi-tenant) approval stores. Hosted file-based approval @@ -460,6 +645,12 @@ def __init__( # (in-memory) dev and protocol v1 (no user id) keep the single shared # ``self._approval_storage``. self._approval_storages_by_user: dict[str, ApprovalStorage] = {} + # Per-context (and per-user) in-memory checkpoint stores used for local + # (non-hosted) development. Hosted deployments persist checkpoints to disk + # via FileCheckpointStorage instead. Instances are cached by + # (user_id, context_id) so a workflow's state survives across turns within + # the same process, matching the persistence the file layout provides. + self._checkpoint_storages_by_key: dict[tuple[str | None, str], CheckpointStorage] = {} # Lazy agent lifecycle: the agent (and any MCP tools it owns) is entered on # the first request rather than at server startup, so that authentication # failures during MCP connect can be surfaced to the client as an @@ -497,6 +688,15 @@ async def _cleanup_agent(self) -> None: self._agent_stack = None await stack.aclose() + def _resolve_storage_path(self, mount_path: str) -> str: + """Resolve a durable storage mount path for the current environment. + + Hosted deployments re-root storage under the session's persistent home + directory. Local resilient runs use the current working directory. + """ + root = Path.home() if self.config.is_hosted else Path.cwd() + return str(root / mount_path.lstrip("/\\")) + def _approval_storage_for_user(self, user_id: str | None) -> ApprovalStorage: """Return the approval storage scoped to ``user_id`` when applicable. @@ -515,7 +715,7 @@ def _approval_storage_for_user(self, user_id: str | None) -> ApprovalStorage: storage = self._approval_storages_by_user.get(user_id) if storage is None: storage = FileBasedFunctionApprovalStorage( - _approval_storage_path_for_user(self.FUNCTION_APPROVAL_STORAGE_PATH, user_id) + _approval_storage_path_for_user(self._approval_storage_path, user_id) ) self._approval_storages_by_user[user_id] = storage return storage @@ -525,7 +725,7 @@ async def _handle_response( request: CreateResponse, context: ResponseContext, cancellation_signal: asyncio.Event, - ) -> AsyncIterable[ResponseStreamEvent | dict[str, Any]]: + ) -> AsyncIterable[ResponseStreamEvent]: """Handle the creation of a response.""" # Fail fast if the service is on protocol v1.0.0 if self.config.is_hosted and context.platform_context.call_id is None: @@ -535,192 +735,224 @@ async def _handle_response( "downgrade the `agent-framework-foundry-hosting` package to `1.0.0a260625` or before to use 1.0.0." ) - if self._is_workflow_agent: - # Workflow agents are handled differently because they require checkpoint restoration - return self._handle_inner_workflow(request, context) - return self._handle_inner_agent(request, context) - - async def _handle_inner_agent( - self, - request: CreateResponse, - context: ResponseContext, - ) -> AsyncIterable[ResponseStreamEvent | dict[str, Any]]: - """Handle the creation of a response for a regular (non-workflow) agent.""" - response_event_stream = ResponseEventStream(response_id=context.response_id, model=request.model) - yield response_event_stream.emit_created() - yield response_event_stream.emit_in_progress() - - # Track the current active output item builder for streaming; - # lazily created on matching content, closed when a different type arrives. - tracker: _OutputItemTracker | None = None + # Common per-request setup shared by the workflow and non-workflow paths: + # create the response stream and the streaming output-item tracker, emit + # the opening lifecycle events, and convert any exception raised while + # producing the response into a terminal ``response.failed`` event (which + # also drains the tracker so the SSE stream stays well-formed). + response_event_stream = self._create_response_event_stream(request, context) + tracker = _OutputItemTracker(response_event_stream) + created_yielded = False + in_progress_yielded = False try: - user_id = context.platform_context.user_id_key - approval_storage = self._approval_storage_for_user(user_id) - input_items = await context.get_input_items() - input_messages = await _items_to_messages(input_items, approval_storage=approval_storage) - - history = await context.get_history() - run_kwargs: dict[str, Any] = { - "messages": [ - *(await _output_items_to_messages(history, approval_storage=approval_storage)), - *input_messages, - ] - } - is_streaming_request = request.stream is not None and request.stream is True - - chat_options, are_options_set = _to_chat_options(request) + workflow_checkpoint_execution: _WorkflowCheckpointExecution | None = None + if self._is_workflow_agent: + # Seed the workflow baseline before response.created is yielded so + # recovery never observes an unreferenced conversation directory. + workflow_checkpoint_execution = await self._prepare_workflow_checkpoint_execution( + request, + context, + response_event_stream, + ) - if are_options_set and not isinstance(self._agent, RawAgent): - logger.warning("Agent doesn't support runtime options. They will be ignored.") + created_event = response_event_stream.emit_created() + created_yielded = True + yield created_event + in_progress_event = response_event_stream.emit_in_progress() + in_progress_yielded = True + yield in_progress_event + + if workflow_checkpoint_execution is not None: + if not context.is_recovery and workflow_checkpoint_execution.restore_checkpoint_id is not None: + # ``response.created`` strips framework-internal metadata + # from its wire payload. Explicitly checkpoint the live + # response before executing the new turn so a crash before + # its first superstep can restore the prior durable state. + yield cast(ResponseStreamEvent, response_event_stream.checkpoint()) + inner = self._handle_inner_workflow( + request, + context, + response_event_stream, + tracker, + cancellation_signal, + workflow_checkpoint_execution, + ) else: - run_kwargs["options"] = chat_options - - # Lazy-enter the agent (and any MCP tools it owns). The MCP client wraps gateway - # consent failures (and other connection-time errors) in AgentFrameworkException; if - # one of those is a consent error we surface the consent link to the client through - # the already-opened response stream instead of failing the request. Other exception - # types fall through to the outer handler below and become ``response.failed``. - try: - await self._ensure_agent_ready() - except AgentFrameworkException as ex: - consent_errors = consent_url_from_error(ex) - if consent_errors is None: - raise - for consent_error in consent_errors: - logger.warning("Consent URL for tool '%s': %s", consent_error.name, consent_error.consent_url) - oauth_item = OAuthConsentRequestOutputItem( - id=IdGenerator.new_id("oacr"), - consent_link=consent_error.consent_url, - server_label=consent_error.name, + if context.is_recovery: + logger.warning( + "Recovery mode is not supported for non-workflow agents. " + "The agent will restart from the original input." ) - builder = response_event_stream.add_output_item(oauth_item.id) - yield builder.emit_added(oauth_item) - yield builder.emit_done(oauth_item) - yield response_event_stream.emit_completed() - return - - tracker = _OutputItemTracker(response_event_stream) if is_streaming_request else None + inner = self._handle_inner_agent(request, context, response_event_stream, tracker, cancellation_signal) - if not is_streaming_request: - # Run the agent in non-streaming mode - response = await self._agent.run(stream=False, **run_kwargs) # type: ignore[reportUnknownMemberType] + async for event in inner: + yield event - async for item in _to_outputs_for_messages( - response_event_stream, - response.messages, - approval_storage=approval_storage, - ): - yield item - else: - if tracker is None: # pragma: no cover - defensive, set above - raise RuntimeError("Streaming tracker was not initialized.") - # Run the agent in streaming mode - async for update in self._agent.run(stream=True, **run_kwargs): # type: ignore[reportUnknownMemberType] - for content in update.contents: - for event in tracker.handle(content): - yield event - if tracker.needs_async: - async for item in _to_outputs( - response_event_stream, - content, - approval_storage=approval_storage, - ): - yield item - tracker.needs_async = False + # Stop only between complete agent updates. Interrupting the event + # sequence for one update can leave an output builder half-open. + for event in tracker.close(): + yield event + if ( + context.shutdown.is_set() + and self._resilient_background + and context.mode_flags.background + and context.mode_flags.store + ): + await context.exit_for_recovery() + if cancellation_signal.is_set(): + logger.debug( + "Workflow response handler exiting early: %s", + "client cancelled" if context.client_cancelled else "steering pressure", + ) + yield response_event_stream.emit_completed() + return - # Close any remaining active builder - for event in tracker.close(): - yield event yield response_event_stream.emit_completed() except Exception as ex: - logger.exception("Failed to produce response for agent") + if not created_yielded: + yield response_event_stream.emit_created() + if not in_progress_yielded: + yield response_event_stream.emit_in_progress() + logger.exception("Failed to produce response") for event in self._emit_failure(response_event_stream, tracker, ex): yield event - async def _handle_inner_workflow( + @staticmethod + def _create_response_event_stream(request: CreateResponse, context: ResponseContext) -> ResponseEventStream: + """Create a response stream seeded from recovery state when available.""" + if context.is_recovery: + persisted_response = context.persisted_response + if persisted_response is not None: + return ResponseEventStream(response=persisted_response, response_id=context.response_id) + return ResponseEventStream(response_id=context.response_id, model=request.model) + + async def _handle_inner_agent( self, request: CreateResponse, context: ResponseContext, - ) -> AsyncIterable[ResponseStreamEvent | dict[str, Any]]: - """Handle the creation of a response for a workflow agent.""" - response_event_stream = ResponseEventStream(response_id=context.response_id, model=request.model) - yield response_event_stream.emit_created() - yield response_event_stream.emit_in_progress() + response_event_stream: ResponseEventStream, + tracker: _OutputItemTracker, + cancellation_signal: asyncio.Event, + ) -> AsyncIterable[ResponseStreamEvent]: + """Handle the creation of a response for a regular (non-workflow) agent. - # Track the current active output item builder for streaming; - # lazily created on matching content, closed when a different type arrives. - tracker: _OutputItemTracker | None = None + The response stream, tracker, and opening lifecycle events are produced + by :meth:`_handle_response`, which also converts any raised exception + into a terminal ``response.failed`` event (draining the tracker so the + SSE stream stays well-formed). + """ + # We always run the agent in streaming mode. Foundry Hosted Agent + # handles streaming vs. non-streaming at the platform level. + user_id = context.platform_context.user_id_key + approval_storage = self._approval_storage_for_user(user_id) + input_items = await context.get_input_items() + input_messages = await _items_to_messages(input_items, approval_storage=approval_storage) + + history = await context.get_history() + run_kwargs: dict[str, Any] = { + "messages": [ + *(await _output_items_to_messages(history, approval_storage=approval_storage)), + *input_messages, + ] + } + + chat_options, are_options_set = _to_chat_options(request) + + if are_options_set and not isinstance(self._agent, RawAgent): + logger.warning("Agent doesn't support runtime options. They will be ignored.") + else: + run_kwargs["options"] = chat_options + # Lazy-enter the agent (and any MCP tools it owns). The MCP client wraps gateway + # consent failures (and other connection-time errors) in AgentFrameworkException; if + # one of those is a consent error we surface the consent link to the client through + # the already-opened response stream instead of failing the request. Other exception + # types fall through to the outer handler and become ``response.failed``. try: - user_id = context.platform_context.user_id_key - approval_storage = self._approval_storage_for_user(user_id) + await self._ensure_agent_ready() + except AgentFrameworkException as ex: + consent_errors = consent_url_from_error(ex) + if consent_errors is None: + raise + for consent_error in consent_errors: + logger.warning( + "Consent URL for tool '%s': %s", + consent_error.name, + consent_error.consent_url, + ) + oauth_item = OAuthConsentRequestOutputItem( + id=IdGenerator.new_id("oacr"), + consent_link=consent_error.consent_url, + server_label=consent_error.name, + ) + builder = response_event_stream.add_output_item(oauth_item.id) + yield builder.emit_added(oauth_item) + yield builder.emit_done(oauth_item) + return + + async for update in self._agent.run(stream=True, **run_kwargs): # type: ignore[reportUnknownMemberType] + for content in update.contents: + for event in tracker.handle(content): + yield event + if tracker.needs_async: + async for item in _to_outputs( + response_event_stream, + content, + approval_storage=approval_storage, + call_id_override=tracker.take_async_call_id_override(), + ): + yield item + tracker.needs_async = False + if cancellation_signal.is_set() or context.shutdown.is_set(): + return + + async def _handle_inner_workflow( + self, + request: CreateResponse, + context: ResponseContext, + response_event_stream: ResponseEventStream, + tracker: _OutputItemTracker, + cancellation_signal: asyncio.Event, + checkpoint_execution: _WorkflowCheckpointExecution, + ) -> AsyncIterable[ResponseStreamEvent]: + """Handle the creation of a response for a workflow agent. + + The response stream, tracker, and opening lifecycle events are produced + by :meth:`_handle_response`, which also converts any raised exception + into a terminal ``response.failed`` event (draining the tracker so the + SSE stream stays well-formed). + """ + # The following should never happen due to the checks above. + # This is for type safety and defensive programming. + if not isinstance(self._agent, WorkflowAgent): + raise RuntimeError("Agent is not a workflow agent.") + + user_id = context.platform_context.user_id_key + approval_storage = self._approval_storage_for_user(user_id) + + # Workflow agents are not async context managers in any built-in path, + # but call _ensure_agent_ready for symmetry with the regular path so + # any future async resources owned by the workflow are entered here. + await self._ensure_agent_ready() + + # Select the workflow run to drive this turn. Recovery resumes from + # the last persisted checkpoint; a normal turn optionally restores a + # prior checkpoint first (consumed internally) and then delivers the + # new user input. Both paths feed the same event-processing loop below. + if context.is_recovery and checkpoint_execution.resume_current_turn: + run_stream = self._resume_workflow_with_updates( + context.response_id, + checkpoint_execution, + ) + else: input_items = await context.get_input_items() input_messages = await _items_to_messages(input_items, approval_storage=approval_storage) - is_streaming_request = request.stream is not None and request.stream is True _, are_options_set = _to_chat_options(request) if are_options_set: logger.warning("Workflow agent doesn't support runtime options. They will be ignored.") - if request.previous_response_id is not None and context.conversation_id is not None: - raise RuntimeError("Previous response ID cannot be used in conjunction with conversation ID.") - context_id = request.previous_response_id or context.conversation_id - - # The following should never happen due to the checks above. - # This is for type safety and defensive programming. - if self._checkpoint_storage_path is None: - raise RuntimeError("Checkpoint storage path is not configured for workflow agent.") - if not isinstance(self._agent, WorkflowAgent): - raise RuntimeError("Agent is not a workflow agent.") - - # Workflow agents are not async context managers in any built-in path, - # but call _ensure_agent_ready for symmetry with the regular path so - # any future async resources owned by the workflow are entered here. - await self._ensure_agent_ready() - - # Per-user checkpoint isolation for multi-tenant hosting (container - # protocol v2): the per-user partition key computed above - # (``x-agent-user-id``) scopes every checkpoint directory for this turn, - # so one tenant can never restore or observe another tenant's workflow - # state -- even with a guessed or forged context id. The key is stable - # per user across turns, so multi-turn continuity is preserved. Absent - # (``None``)/empty in local development or protocol v1, where the - # unscoped single-tenant layout is used. - - # Determine the latest checkpoint (if any) so we can resume the - # workflow's prior state for this turn. The directory is keyed by - # the inbound context id (conversation_id when set, otherwise - # previous_response_id). Multi-turn declarative workflows need the - # workflow's internal state (e.g. Conversation.messages, - # intermediate Local.* variables) to survive across user turns; - # the only place that state lives is the workflow checkpoint, so - # on every turn we restore the latest checkpoint and feed the new - # input back into the start executor as a continuation rather than - # a fresh run. - latest_checkpoint_id: str | None = None - restore_storage: FileCheckpointStorage | None = None - if context_id is not None: - restore_storage = _checkpoint_storage_for_context( - self._checkpoint_storage_path, context_id, user_id=user_id - ) - latest_checkpoint = await restore_storage.get_latest(workflow_name=self._agent.workflow.name) - if latest_checkpoint is not None: - latest_checkpoint_id = latest_checkpoint.checkpoint_id - - # Storage that will receive checkpoints written during this turn. - # When the caller chains with previous_response_id, the next turn - # will reference the current response_id as its previous_response_id, - # so new checkpoints must land under the current response_id (or the - # conversation_id when set). When conversation_id is set, this - # matches restore_storage; when only previous_response_id was - # supplied, restore_storage points at the *prior* response's - # directory and write_storage points at the *current* response's. - write_context_id = context.conversation_id or context.response_id - write_storage = _checkpoint_storage_for_context( - self._checkpoint_storage_path, write_context_id, user_id=user_id - ) - # Multi-turn pattern: when we have a prior checkpoint, restore it # first (drive the workflow back to idle with prior state intact), # then make a separate call that delivers the new user input. This @@ -737,81 +969,352 @@ async def _handle_inner_workflow( # ``run(input_messages, ...)`` call may contain ``function_call_output`` # items (carried as FunctionResult/FunctionApprovalResponse content) # that fulfill them via :meth:`WorkflowAgent._process_pending_requests`. - if latest_checkpoint_id is not None: - if is_streaming_request: - async for _ in self._agent.run( - stream=True, - checkpoint_id=latest_checkpoint_id, - checkpoint_storage=restore_storage, - ): - pass - else: - await self._agent.run( - stream=False, - checkpoint_id=latest_checkpoint_id, - checkpoint_storage=restore_storage, - ) - - if not is_streaming_request: - # Run the agent in non-streaming mode with the new user input. - response = await self._agent.run( - input_messages, - stream=False, - checkpoint_storage=write_storage, + if checkpoint_execution.restore_checkpoint_id is not None: + restore_stream = self._agent.run( + stream=True, + checkpoint_id=checkpoint_execution.restore_checkpoint_id, + checkpoint_storage=checkpoint_execution.restore_storage, ) - - async for item in _to_outputs_for_messages( + async for event in self._drive_workflow_stream( + restore_stream, + checkpoint_execution.publisher, response_event_stream, - response.messages, - approval_storage=approval_storage, + tracker, + approval_storage, + cancellation_signal, + context.shutdown, + forward_updates=False, ): - yield item - - await self._delete_not_latest_checkpoints(write_storage, self._agent.workflow.name) - yield response_event_stream.emit_completed() - return - - tracker = _OutputItemTracker(response_event_stream) + yield event + if cancellation_signal.is_set() or context.shutdown.is_set(): + return - # Run the workflow agent in streaming mode with the new user input. - async for update in self._agent.run( + run_stream = self._agent.run( input_messages, stream=True, - checkpoint_storage=write_storage, + checkpoint_storage=checkpoint_execution.write_storage, + ) + + async for event in self._drive_workflow_stream( + run_stream, + checkpoint_execution.publisher, + response_event_stream, + tracker, + approval_storage, + cancellation_signal, + context.shutdown, + ): + yield event + + async def _resume_workflow_with_updates( + self, + response_id: str, + checkpoint_execution: _WorkflowCheckpointExecution, + ) -> AsyncIterable[AgentResponseUpdate]: + """Resume current-turn workflow work without discarding its output events. + + ``WorkflowAgent.run(checkpoint_id=...)`` consumes restored workflow + events internally. Recovery must forward those events because they can + contain response output produced after the last persisted snapshot. + """ + if not isinstance(self._agent, WorkflowAgent): + raise RuntimeError("Agent is not a workflow agent.") + if checkpoint_execution.restore_checkpoint_id is None or checkpoint_execution.restore_storage is None: + raise RuntimeError("Current-turn recovery requires an exact workflow checkpoint reference.") + + async for workflow_event in self._agent.workflow.run( + stream=True, + checkpoint_id=checkpoint_execution.restore_checkpoint_id, + checkpoint_storage=checkpoint_execution.restore_storage, + ): + for update in self._agent._convert_workflow_event_to_agent_response_updates( # pyright: ignore[reportPrivateUsage] + response_id, + workflow_event, ): - for content in update.contents: - for event in tracker.handle(content): + yield update + + async def _drive_workflow_stream( + self, + run_stream: AsyncIterable[AgentResponseUpdate], + publisher: _WorkflowCheckpointPublisher, + response_event_stream: ResponseEventStream, + tracker: _OutputItemTracker, + approval_storage: ApprovalStorage, + cancellation_signal: asyncio.Event, + shutdown_signal: asyncio.Event, + *, + forward_updates: bool = True, + ) -> AsyncIterable[ResponseStreamEvent]: + """Drive workflow updates and publish queued checkpoint references.""" + iterator = run_stream.__aiter__() + + async def _next_update() -> AgentResponseUpdate: + return await anext(iterator) + + update_task: asyncio.Task[AgentResponseUpdate] | None = asyncio.create_task(_next_update()) + checkpoint_task = asyncio.create_task(publisher.next_pending()) + + try: + while True: + wait_tasks: set[asyncio.Task[Any]] = {cast(asyncio.Task[Any], checkpoint_task)} + if update_task is not None: + wait_tasks.add(cast(asyncio.Task[Any], update_task)) + done, _ = await asyncio.wait( + wait_tasks, + return_when=asyncio.FIRST_COMPLETED, + ) + + # Checkpoints are published before a later workflow update can be + # consumed, preserving the workflow stream's save ordering. + if checkpoint_task in done: + reference = checkpoint_task.result() + if reference is None: + break + for event in tracker.close(): yield event - if tracker.needs_async: - async for item in _to_outputs( - response_event_stream, content, approval_storage=approval_storage - ): - yield item - tracker.needs_async = False - - # Close any remaining active builder - for event in tracker.close(): - yield event + yield cast(ResponseStreamEvent, publisher.checkpoint_event(reference)) + checkpoint_task = asyncio.create_task(publisher.next_pending()) + continue - await self._delete_not_latest_checkpoints(write_storage, self._agent.workflow.name) - yield response_event_stream.emit_completed() - except Exception as ex: - logger.exception("Failed to produce response for workflow agent") - for event in self._emit_failure(response_event_stream, tracker, ex): - yield event + try: + update = cast(asyncio.Task[AgentResponseUpdate], update_task).result() + except StopAsyncIteration: + update_task = None + # Drain every checkpoint published by the completed workflow + # before allowing the response to emit its terminal event. + publisher.finish_run() + continue + + if forward_updates: + for content in update.contents: + for event in tracker.handle(content): + yield event + if tracker.needs_async: + async for item in _to_outputs( + response_event_stream, + content, + approval_storage=approval_storage, + call_id_override=tracker.take_async_call_id_override(), + ): + yield item + tracker.needs_async = False + if cancellation_signal.is_set() or shutdown_signal.is_set(): + return + + update_task = asyncio.create_task(_next_update()) + finally: + pending_tasks = [task for task in (update_task, checkpoint_task) if task is not None and not task.done()] + for task in pending_tasks: + task.cancel() + for task in pending_tasks: + with suppress(asyncio.CancelledError): + await task + + def _get_checkpoint_storage( + self, + context_id: str, + user_id: str | None, + publisher: _WorkflowCheckpointPublisher, + *, + reference_kind: Literal["base", "current"], + ) -> CheckpointStorage: + """Return the checkpoint storage for ``context_id``, file-based when durable else in-memory. + + Mirrors the approval-storage strategy: a durable checkpoint path is + configured when hosted, or when a workflow agent opts into resilient + background execution; in that case checkpoints persist to disk + (partitioned per user and per context id via + :class:`FileCheckpointStorage`). Otherwise local development keeps them + in memory, cached by ``(user_id, context_id)`` so a workflow's state + survives across turns within the same process. + + The resolved storage is wrapped in :class:`_ContextAwareCheckpointStorage` + so request-scoped post-save logic (tied to ``response_event_stream``) runs + after every checkpoint save. + + Raises: + RuntimeError: If ``context_id`` / ``user_id`` is not a safe single path segment. + """ + if self._checkpoint_storage_path is not None: + inner: CheckpointStorage = _checkpoint_storage_for_context( + self._checkpoint_storage_path, context_id, user_id=user_id + ) + else: + key = (user_id, context_id) + cached = self._checkpoint_storages_by_key.get(key) + if cached is None: + cached = InMemoryCheckpointStorage() + self._checkpoint_storages_by_key[key] = cached + inner = cached + return _ContextAwareCheckpointStorage(inner, publisher, context_id, reference_kind) + + async def _prepare_workflow_checkpoint_execution( + self, + request: CreateResponse, + context: ResponseContext, + response_event_stream: ResponseEventStream, + ) -> _WorkflowCheckpointExecution: + """Prepare exact restore/write checkpoint state before response creation. + + Per-user checkpoint isolation for multi-tenant hosting (container + protocol v2): the per-user partition key (``x-agent-user-id``) scopes + every checkpoint directory for this turn, so one tenant can never + restore or observe another tenant's workflow state -- even with a + guessed or forged context id. The key is stable per user across turns, + so multi-turn continuity is preserved. Absent (``None``)/empty in local + development or protocol v1, where the unscoped single-tenant layout is + used. + + The restore storage determines the latest checkpoint (if any) so we can + resume the workflow's prior state for this turn. Its directory is keyed + by the inbound context id (``conversation_id`` when set, otherwise + ``previous_response_id``). Multi-turn declarative workflows need the + workflow's internal state (e.g. Conversation.messages, intermediate + Local.* variables) to survive across user turns; the only place that + state lives is the workflow checkpoint, so on every turn we restore the + latest checkpoint and feed the new input back into the start executor as + a continuation rather than a fresh run. + + The write storage receives checkpoints written during this turn. When + the caller chains with ``previous_response_id``, the next turn will + reference the current ``response_id`` as its ``previous_response_id``, so + new checkpoints must land under the current ``response_id`` (or the + ``conversation_id`` when set). When ``conversation_id`` is set, this + matches the restore storage; when only ``previous_response_id`` was + supplied, the restore storage points at the *prior* response's directory + and the write storage points at the *current* response's. + + The initial response snapshot is seeded with the prior checkpoint + reference before ``response.created`` is emitted. Recovery then restores + only the exact reference carried by the persisted response, never a + potentially newer orphan returned by ``get_latest()``. + """ + if not isinstance(self._agent, WorkflowAgent): + raise RuntimeError("Agent is not a workflow agent.") + + if request.previous_response_id is not None and context.conversation_id is not None: + raise RuntimeError("Previous response ID cannot be used in conjunction with conversation ID.") + + user_id = context.platform_context.user_id_key + # A conversation keeps one stable checkpoint context. A response-linked + # chain reads the prior response's context but writes this turn under + # the current response so the next turn can reference it. + prior_context_id = request.previous_response_id or context.conversation_id + write_context_id = context.conversation_id or context.response_id + publisher = _WorkflowCheckpointPublisher(response_event_stream) + + persisted_reference: _WorkflowCheckpointReference | None = None + if context.is_recovery: + # Crash recovery must use the checkpoint named by the persisted + # response snapshot, not whichever checkpoint happens to be newest. + persisted_reference = publisher.get_persisted_reference( + fallback_context_id=write_context_id, + ) + + restore_checkpoint_id: CheckpointID | None = None + restore_storage: CheckpointStorage | None = None + resume_current_turn = False + + if persisted_reference is not None: + # Recovery has an exact durable boundary. "current" resumes work + # already started by this request; "base" restores prior state and + # allows the request input to be delivered again. + restore_checkpoint_id = persisted_reference.checkpoint_id + restore_storage = self._get_checkpoint_storage( + persisted_reference.context_id, + user_id, + publisher, + reference_kind=persisted_reference.kind, + ) + resume_current_turn = persisted_reference.kind == "current" + elif not context.is_recovery and prior_context_id is not None: + # Normal multi-turn request: restore the prior turn's durable head, + # then run the new input as a continuation of that workflow. + restore_storage = self._get_checkpoint_storage( + prior_context_id, + user_id, + publisher, + reference_kind="base", + ) + latest_checkpoint = await restore_storage.get_latest(workflow_name=self._agent.workflow.name) + if latest_checkpoint is not None: + restore_checkpoint_id = latest_checkpoint.checkpoint_id + persisted_reference = _WorkflowCheckpointReference( + checkpoint_id=latest_checkpoint.checkpoint_id, + context_id=prior_context_id, + kind="base", + ) + await self._delete_checkpoints_except( + restore_storage, + self._agent.workflow.name, + latest_checkpoint.checkpoint_id, + ) + elif context.is_recovery and request.previous_response_id is not None: + # Legacy recovery: older response snapshots did not carry a + # structured baseline reference, so recover the prior turn's latest + # checkpoint. This cannot select a current-turn orphan because those + # checkpoints live under the current response id. A conversation's + # shared directory cannot safely distinguish a baseline from an orphan. + restore_storage = self._get_checkpoint_storage( + request.previous_response_id, + user_id, + publisher, + reference_kind="base", + ) + latest_checkpoint = await restore_storage.get_latest(workflow_name=self._agent.workflow.name) + if latest_checkpoint is not None: + restore_checkpoint_id = latest_checkpoint.checkpoint_id + persisted_reference = _WorkflowCheckpointReference( + checkpoint_id=latest_checkpoint.checkpoint_id, + context_id=request.previous_response_id, + kind="base", + ) + + if not context.is_recovery: + # Persist the selected prior boundary in the initial response + # snapshot, covering a crash before this turn writes a checkpoint. + publisher.seed(persisted_reference) + + # Every checkpoint created after this request starts is a current-turn + # boundary and must be written where this response can recover it. + write_storage = self._get_checkpoint_storage( + write_context_id, + user_id, + publisher, + reference_kind="current", + ) + return _WorkflowCheckpointExecution( + restore_storage=restore_storage, + restore_checkpoint_id=restore_checkpoint_id, + write_storage=write_storage, + resume_current_turn=resume_current_turn, + publisher=publisher, + ) + + @staticmethod + async def _delete_checkpoints_except( + checkpoint_storage: CheckpointStorage, + workflow_name: str, + checkpoint_id: CheckpointID, + ) -> None: + """Prune a prior turn only after a later request selects its durable head.""" + for candidate in await checkpoint_storage.list_checkpoints(workflow_name=workflow_name): + if candidate.checkpoint_id != checkpoint_id: + await checkpoint_storage.delete(candidate.checkpoint_id) @staticmethod - async def _delete_not_latest_checkpoints(checkpoint_storage: FileCheckpointStorage, workflow_name: str) -> None: - """Delete all checkpoints except the latest one. + def _drain_tracker(tracker: _OutputItemTracker | None) -> Generator[ResponseStreamEvent]: + """Drain any in-progress streaming output item so the SSE stream stays well-formed. - We only need the last checkpoint for each invocation. + Any error raised while closing the tracker is logged and otherwise + ignored so that the original failure is always what the client sees. """ - latest_checkpoint = await checkpoint_storage.get_latest(workflow_name=workflow_name) - if latest_checkpoint is not None: - all_checkpoints = await checkpoint_storage.list_checkpoints(workflow_name=workflow_name) - for checkpoint in all_checkpoints: - if checkpoint.checkpoint_id != latest_checkpoint.checkpoint_id: - await checkpoint_storage.delete(checkpoint.checkpoint_id) + if tracker is None: + return + try: + yield from tracker.close() + except Exception: + logger.exception("Error while closing streaming tracker after failure") @staticmethod def _emit_failure( @@ -824,15 +1327,9 @@ def _emit_failure( Drains any in-progress streaming output item first so the resulting SSE stream stays well-formed, then emits ``response.failed`` carrying the exception's message (falling back to the exception type name when - ``str(ex)`` is empty). Any error raised while draining the tracker is - logged and otherwise ignored so that the original failure is always - what the client sees. + ``str(ex)`` is empty). """ - if tracker is not None: - try: - yield from tracker.close() - except Exception: - logger.exception("Error while closing streaming tracker after failure") + yield from ResponsesHostServer._drain_tracker(tracker) message = str(ex) or type(ex).__name__ yield response_event_stream.emit_failed(message=message) @@ -850,6 +1347,7 @@ class _OutputItemTracker: """ _DELTA_TYPES = frozenset({"text", "text_reasoning", "function_call", "mcp_server_tool_call"}) + _MCP_ITEM_IDS_METADATA_KEY = "mcp_item_ids" def __init__(self, stream: ResponseEventStream) -> None: self._stream = stream @@ -862,8 +1360,19 @@ def __init__(self, stream: ResponseEventStream) -> None: self._text_content: TextContentBuilder | None = None self._reasoning_item: OutputItemReasoningItemBuilder | None = None self._summary_part: ReasoningSummaryPartBuilder | None = None + self._reasoning_protected_data: str | None = None self._fc_builder: OutputItemFunctionCallBuilder | None = None self._mcp_builder: OutputItemMcpCallBuilder | None = None + self._mcp_call_id: str | None = None # call_id of the in-progress MCP call for result matching + persisted_mcp_item_ids = stream.internal_metadata.get(self._MCP_ITEM_IDS_METADATA_KEY) + self._mcp_item_ids: dict[str, str] = {} + if isinstance(persisted_mcp_item_ids, Mapping): + persisted_mapping: Mapping[Any, Any] = persisted_mcp_item_ids # pyright: ignore[reportUnknownVariableType] + for call_id, item_id in persisted_mapping.items(): + if isinstance(call_id, str) and isinstance(item_id, str): + self._mcp_item_ids[call_id] = item_id + self._sync_mcp_item_ids_metadata() + self._async_call_id_override: str | None = None self.needs_async = False def handle(self, content: Content) -> Generator[ResponseStreamEvent]: @@ -880,13 +1389,16 @@ def handle(self, content: Content) -> Generator[ResponseStreamEvent]: if self._text_content is not None: yield self._text_content.emit_delta(content.text) - elif content.type == "text_reasoning": + elif content.type == "text_reasoning" and (content.text is not None or content.protected_data is not None): if self._active_type != "text_reasoning": yield from self._close() - yield from self._open_reasoning() - self._accumulated.append(content.text or "") - if self._summary_part is not None: - yield self._summary_part.emit_text_delta(content.text or "") + yield from self._open_reasoning(content) + if content.protected_data is not None: + self._reasoning_protected_data = content.protected_data + if content.text is not None: + self._accumulated.append(content.text) + if self._summary_part is not None: + yield self._summary_part.emit_text_delta(content.text) elif content.type == "function_call" and content.call_id is not None: if self._active_type != "function_call" or self._active_id != content.call_id: @@ -912,27 +1424,39 @@ def handle(self, content: Content) -> Generator[ResponseStreamEvent]: and self._active_type == "mcp_server_tool_call" and self._mcp_builder is not None and content.call_id is not None - and content.call_id == self._mcp_builder.item_id + and content.call_id == self._mcp_call_id ): accumulated = "".join(self._accumulated) yield self._mcp_builder.emit_arguments_done(accumulated) yield self._mcp_builder.emit_completed() - yield self._mcp_builder.emit_done(output=_stringify_mcp_output(content.output)) + yield self._mcp_builder.emit_done() + self._async_call_id_override = self._take_mcp_item_id(content.call_id) self._mcp_builder = None + self._mcp_call_id = None self._active_type = None self._active_id = None self._accumulated.clear() - self.needs_async = False + # In b8 the mcp_call done event no longer carries output inline. + # Signal the async path to emit a separate custom_tool_call_output item. + self.needs_async = True return else: yield from self._close() + if content.type == "mcp_server_tool_result" and content.call_id is not None: + self._async_call_id_override = self._take_mcp_item_id(content.call_id) self.needs_async = True def close(self) -> Generator[ResponseStreamEvent]: """Close any remaining active builder.""" yield from self._close() + def take_async_call_id_override(self) -> str | None: + """Return and clear the persisted call ID for the pending async output.""" + call_id = self._async_call_id_override + self._async_call_id_override = None + return call_id + # -- Private open/close helpers -- def _open_message(self) -> Generator[ResponseStreamEvent]: @@ -943,12 +1467,16 @@ def _open_message(self) -> Generator[ResponseStreamEvent]: yield self._message_item.emit_added() yield self._text_content.emit_added() - def _open_reasoning(self) -> Generator[ResponseStreamEvent]: + def _open_reasoning(self, content: Content) -> Generator[ResponseStreamEvent]: self._reasoning_item = self._stream.add_output_item_reasoning_item() self._summary_part = self._reasoning_item.add_summary_part() + self._reasoning_protected_data = content.protected_data self._active_type = "text_reasoning" self._active_id = None - yield self._reasoning_item.emit_added() + added = self._reasoning_item.emit_added() + if self._reasoning_protected_data is not None: + _set_reasoning_encrypted_content(self._stream, added, self._reasoning_protected_data) + yield added yield self._summary_part.emit_added() def _open_function_call(self, content: Content) -> Generator[ResponseStreamEvent]: @@ -964,8 +1492,11 @@ def _open_mcp_call(self, content: Content) -> Generator[ResponseStreamEvent]: self._mcp_builder = self._stream.add_output_item_mcp_call( server_label=content.server_name or "default", name=content.tool_name or "", - item_id=content.call_id, ) + self._mcp_call_id = content.call_id + if content.call_id is not None: + self._mcp_item_ids[content.call_id] = self._mcp_builder.item_id + self._sync_mcp_item_ids_metadata() self._active_type = "mcp_server_tool_call" self._active_id = content.call_id or f"{content.server_name or 'default'}::{content.tool_name}" yield self._mcp_builder.emit_added() @@ -983,9 +1514,13 @@ def _close(self) -> Generator[ResponseStreamEvent]: elif self._active_type == "text_reasoning" and self._summary_part and self._reasoning_item: yield self._summary_part.emit_text_done(accumulated) yield self._summary_part.emit_done() - yield self._reasoning_item.emit_done() + done = self._reasoning_item.emit_done() + if self._reasoning_protected_data is not None: + _set_reasoning_encrypted_content(self._stream, done, self._reasoning_protected_data) + yield done self._summary_part = None self._reasoning_item = None + self._reasoning_protected_data = None elif self._active_type == "function_call" and self._fc_builder: yield self._fc_builder.emit_arguments_done(accumulated) @@ -997,11 +1532,23 @@ def _close(self) -> Generator[ResponseStreamEvent]: yield self._mcp_builder.emit_completed() yield self._mcp_builder.emit_done() self._mcp_builder = None + self._mcp_call_id = None self._active_type = None self._active_id = None self._accumulated.clear() + def _take_mcp_item_id(self, call_id: str) -> str | None: + item_id = self._mcp_item_ids.pop(call_id, None) + self._sync_mcp_item_ids_metadata() + return item_id + + def _sync_mcp_item_ids_metadata(self) -> None: + if self._mcp_item_ids: + self._stream.internal_metadata[self._MCP_ITEM_IDS_METADATA_KEY] = dict(self._mcp_item_ids) + else: + self._stream.internal_metadata.pop(self._MCP_ITEM_IDS_METADATA_KEY, None) + # endregion @@ -1082,12 +1629,16 @@ async def _item_to_message(item: Item, *, approval_storage: ApprovalStorage | No msg = cast(ItemMessage, item) if isinstance(msg.content, str): return Message(role=msg.role, contents=[Content.from_text(msg.content)]) - return Message(role=msg.role, contents=[_convert_message_content(part) for part in msg.content]) + return Message( + role=msg.role, + contents=[_convert_message_content(part) for part in msg.content], + ) if item.type == "output_message": output_msg = cast(ItemOutputMessage, item) return Message( - role=output_msg.role, contents=[_convert_output_message_content(part) for part in output_msg.content] + role=output_msg.role, + contents=[_convert_output_message_content(part) for part in output_msg.content], ) if item.type == "function_call": @@ -1099,6 +1650,7 @@ async def _item_to_message(item: Item, *, approval_storage: ApprovalStorage | No fc.call_id, fc.name, arguments=fc.arguments, + informational_only=True, ) ], ) @@ -1115,10 +1667,18 @@ async def _item_to_message(item: Item, *, approval_storage: ApprovalStorage | No reasoning = cast(ItemReasoningItem, item) reason_contents: list[Content] = [] if reasoning.summary: - for summary in reasoning.summary: - reason_contents.append(Content.from_text_reasoning(id=reasoning.id, text=summary.text)) - else: - reason_contents.append(Content.from_text_reasoning(id=reasoning.id)) + for index, summary in enumerate(reasoning.summary): + reason_contents.append( + Content.from_text_reasoning( + id=reasoning.id, + text=summary.text, + protected_data=reasoning.encrypted_content if index == 0 else None, + ) + ) + elif reasoning.encrypted_content is not None: + reason_contents.append( + Content.from_text_reasoning(id=reasoning.id, text=None, protected_data=reasoning.encrypted_content) + ) return Message(role="assistant", contents=reason_contents) if item.type == "mcp_call": @@ -1374,12 +1934,16 @@ async def _output_item_to_message(item: OutputItem, *, approval_storage: Approva if item.type == "output_message": output_msg = cast(OutputItemOutputMessage, item) return Message( - role=output_msg.role, contents=[_convert_output_message_content(part) for part in output_msg.content] + role=output_msg.role, + contents=[_convert_output_message_content(part) for part in output_msg.content], ) if item.type == "message": msg = cast(OutputItemMessage, item) - return Message(role=msg.role, contents=[_convert_message_content(part) for part in msg.content]) + return Message( + role=msg.role, + contents=[_convert_message_content(part) for part in msg.content], + ) if item.type == "function_call": fc = cast(OutputItemFunctionToolCall, item) @@ -1390,6 +1954,7 @@ async def _output_item_to_message(item: OutputItem, *, approval_storage: Approva fc.call_id, fc.name, arguments=fc.arguments, + informational_only=True, ) ], ) @@ -1406,10 +1971,18 @@ async def _output_item_to_message(item: OutputItem, *, approval_storage: Approva reasoning = cast(OutputItemReasoningItem, item) contents: list[Content] = [] if reasoning.summary: - for summary in reasoning.summary: - contents.append(Content.from_text_reasoning(id=reasoning.id, text=summary.text)) - else: - contents.append(Content.from_text_reasoning(id=reasoning.id)) + for index, summary in enumerate(reasoning.summary): + contents.append( + Content.from_text_reasoning( + id=reasoning.id, + text=summary.text, + protected_data=reasoning.encrypted_content if index == 0 else None, + ) + ) + elif reasoning.encrypted_content is not None: + contents.append( + Content.from_text_reasoning(id=reasoning.id, text=None, protected_data=reasoning.encrypted_content) + ) return Message(role="assistant", contents=contents) if item.type == "mcp_call": @@ -1768,11 +2341,28 @@ def _arguments_to_str(arguments: Any | None) -> str: return json.dumps(arguments, default=_argument_json_default) +def _set_reasoning_encrypted_content( + stream: ResponseEventStream, + event: ResponseStreamEvent, + encrypted_content: str, +) -> None: + """Attach encrypted reasoning state to both the event and persisted response item.""" + item = getattr(event, "item", None) + if item is None or item.type != "reasoning": + raise RuntimeError("Expected a reasoning output item event.") + item.encrypted_content = encrypted_content + for output_item in reversed(stream.response.output): + if output_item.type == "reasoning" and output_item.id == item.id: + output_item.encrypted_content = encrypted_content + break + + async def _to_outputs( stream: ResponseEventStream, content: Content, *, approval_storage: ApprovalStorage | None = None, + call_id_override: str | None = None, ) -> AsyncIterator[ResponseStreamEvent]: """Converts a Content object to an async sequence of ResponseStreamEvent objects. @@ -1780,6 +2370,7 @@ async def _to_outputs( stream: The ResponseEventStream to use for building events. content: The Content to convert. approval_storage: An optional ApprovalStorage instance to use for saving and loading function approval requests. + call_id_override: The persisted item ID to use for a tool result when it differs from the framework call ID. Yields: ResponseStreamEvent: The converted event objects. @@ -1790,9 +2381,18 @@ async def _to_outputs( if content.type == "text" and content.text is not None: async for event in stream.aoutput_item_message(content.text): yield event - elif content.type == "text_reasoning": - async for event in stream.aoutput_item_reasoning_item(content.text or ""): + elif content.type == "text_reasoning" and (content.text is not None or content.protected_data is not None): + item = stream.add_output_item_reasoning_item() + added = item.emit_added() + if content.protected_data is not None: + _set_reasoning_encrypted_content(stream, added, content.protected_data) + yield added + for event in item.summary_part(content.text or ""): yield event + done = item.emit_done() + if content.protected_data is not None: + _set_reasoning_encrypted_content(stream, done, content.protected_data) + yield done elif content.type == "function_call": async for event in stream.aoutput_item_function_call( content.name, # type: ignore[arg-type] @@ -1813,7 +2413,6 @@ async def _to_outputs( mcp_call = stream.add_output_item_mcp_call( server_label=content.server_name or "default", name=content.tool_name or "", - item_id=content.call_id, ) yield mcp_call.emit_added() async for event in mcp_call.aarguments(_arguments_to_str(content.arguments)): @@ -1821,14 +2420,11 @@ async def _to_outputs( yield mcp_call.emit_completed() yield mcp_call.emit_done() elif content.type == "mcp_server_tool_result": - output = ( - content.output - if isinstance(content.output, str) - else str(content.output) - if content.output is not None - else "" - ) - async for event in stream.aoutput_item_custom_tool_call_output(content.call_id or "", output): + output = _stringify_mcp_output(content.output) + async for event in stream.aoutput_item_custom_tool_call_output( + call_id_override or content.call_id or "", + output, + ): yield event elif content.type == "shell_tool_call": action = FunctionShellAction(commands=content.commands or [], timeout_ms=0, max_output_length=0) @@ -1911,68 +2507,4 @@ def _stringify_mcp_output(output: Any) -> str: return str(output) -def _emit_completed_mcp_call( - stream: ResponseEventStream, - call_content: Content, - *, - arguments: str, - output: str, -) -> Generator[ResponseStreamEvent]: - """Emit a single completed MCP call item carrying both arguments and output.""" - mcp_call = stream.add_output_item_mcp_call( - server_label=call_content.server_name or "default", - name=call_content.tool_name or "", - item_id=call_content.call_id, - ) - yield mcp_call.emit_added() - yield mcp_call.emit_arguments_done(arguments) - yield mcp_call.emit_completed() - yield mcp_call.emit_done(output=output) - - -async def _to_outputs_for_messages( - stream: ResponseEventStream, - messages: Sequence[Message], - *, - approval_storage: ApprovalStorage | None = None, -) -> AsyncIterator[ResponseStreamEvent]: - """Convert messages to output events with hosted-MCP call/result coalescing. - - Parse once in message/content order and emit either: - - a single canonical completed ``mcp_call`` when adjacent hosted MCP - call/result content are encountered, or - - standard output items for all other content types. - """ - pending_mcp_call: Content | None = None - - for message in messages: - for content in message.contents: - if pending_mcp_call is not None: - if content.type == "mcp_server_tool_result" and content.call_id == pending_mcp_call.call_id: - for event in _emit_completed_mcp_call( - stream, - pending_mcp_call, - arguments=_arguments_to_str(pending_mcp_call.arguments), - output=_stringify_mcp_output(content.output), - ): - yield event - pending_mcp_call = None - continue - - async for event in _to_outputs(stream, pending_mcp_call, approval_storage=approval_storage): - yield event - pending_mcp_call = None - - if content.type == "mcp_server_tool_call" and content.call_id: - pending_mcp_call = content - continue - - async for event in _to_outputs(stream, content, approval_storage=approval_storage): - yield event - - if pending_mcp_call is not None: - async for event in _to_outputs(stream, pending_mcp_call, approval_storage=approval_storage): - yield event - - # endregion diff --git a/python/packages/foundry_hosting/tests/test_responses.py b/python/packages/foundry_hosting/tests/test_responses.py index 498caa65c5e..b0b41d8d83d 100644 --- a/python/packages/foundry_hosting/tests/test_responses.py +++ b/python/packages/foundry_hosting/tests/test_responses.py @@ -10,11 +10,12 @@ from __future__ import annotations +import asyncio import json import uuid from collections.abc import AsyncIterator, Awaitable, Callable, Sequence from dataclasses import dataclass -from typing import Literal, overload +from typing import Literal, cast, overload from unittest.mock import AsyncMock, MagicMock, patch import httpx @@ -27,6 +28,7 @@ Content, FileCheckpointStorage, HistoryProvider, + InMemoryCheckpointStorage, Message, RawAgent, ResponseStream, @@ -40,7 +42,8 @@ WorkflowMessage, executor, ) -from azure.ai.agentserver.responses import InMemoryResponseProvider +from azure.ai.agentserver.responses import InMemoryResponseProvider, ResponseContext, ResponseEventStream +from azure.ai.agentserver.responses.streaming._checkpoint import ResponseCheckpointEvent from mcp import McpError from mcp.types import ErrorData from typing_extensions import Any @@ -52,8 +55,12 @@ ConsentError, FileBasedFunctionApprovalStorage, # pyright: ignore[reportPrivateUsage] InMemoryFunctionApprovalStorage, # pyright: ignore[reportPrivateUsage] + _ContextAwareCheckpointStorage, # pyright: ignore[reportPrivateUsage] _item_to_message, # pyright: ignore[reportPrivateUsage] _output_item_to_message, # pyright: ignore[reportPrivateUsage] + _OutputItemTracker, # pyright: ignore[reportPrivateUsage] + _WorkflowCheckpointExecution, # pyright: ignore[reportPrivateUsage] + _WorkflowCheckpointPublisher, # pyright: ignore[reportPrivateUsage] consent_url_from_error, ) @@ -77,9 +84,8 @@ def _make_function_approval_request_content( def _make_agent( + stream_updates: list[AgentResponseUpdate], *, - response: AgentResponse | None = None, - stream_updates: list[AgentResponseUpdate] | None = None, raw_agent: bool = True, ) -> MagicMock: """Create a mock agent implementing SupportsAgentRun.""" @@ -89,25 +95,16 @@ def _make_agent( agent.description = "A mock agent for testing" agent.context_providers = [] - if response is not None: - - async def run_non_streaming(*args: Any, **kwargs: Any) -> AgentResponse: - return response - - agent.run = AsyncMock(side_effect=run_non_streaming) - - if stream_updates is not None: - - async def _stream_gen() -> AsyncIterator[AgentResponseUpdate]: - for update in stream_updates: - yield update + async def _stream_gen() -> AsyncIterator[AgentResponseUpdate]: + for update in stream_updates: + yield update - def run_streaming(*args: Any, **kwargs: Any) -> Any: - if kwargs.get("stream"): - return ResponseStream(_stream_gen()) # type: ignore - raise NotImplementedError("Only streaming is configured on this mock") + def run_streaming(*args: Any, **kwargs: Any) -> Any: + if kwargs.get("stream"): + return ResponseStream(_stream_gen()) # type: ignore + raise NotImplementedError("Only streaming is configured on this mock") - agent.run = MagicMock(side_effect=run_streaming) + agent.run = MagicMock(side_effect=run_streaming) return agent @@ -181,9 +178,9 @@ def _sse_event_types(events: list[dict[str, Any]]) -> list[str]: class TestResponsesHostServerInit: def test_init_basic(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) assert server is not None @@ -206,25 +203,661 @@ async def save_messages( pass hp = _LoadMessagesHistoryProvider(source_id="test", load_messages=True) - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) agent.context_providers = [hp] + with pytest.raises(RuntimeError, match="history provider"): ResponsesHostServer(agent) + def test_init_does_not_auto_enable_resilient_background(self) -> None: + """resilient_background is never auto-enabled — it requires explicit opt-in.""" + from azure.ai.agentserver.responses import ResponsesServerOptions + + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) + + created_options: list[ResponsesServerOptions] = [] + original_init = ResponsesServerOptions.__init__ + + def capture(self: ResponsesServerOptions, **kwargs: Any) -> None: + original_init(self, **kwargs) + created_options.append(self) + + with patch.object(ResponsesServerOptions, "__init__", capture): + _make_server(agent) + + # No ResponsesServerOptions should have been created with resilient_background=True. + assert not any(o.resilient_background for o in created_options), ( + "resilient_background should not be auto-enabled; it requires explicit opt-in." + ) + + def test_init_respects_explicit_options(self) -> None: + """Explicit options are used as-is without modification.""" + from azure.ai.agentserver.responses import ResponsesServerOptions + + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) + + explicit_options = ResponsesServerOptions(resilient_background=False) + server = ResponsesHostServer(agent, options=explicit_options, store=InMemoryResponseProvider()) + assert server is not None + + async def test_hosted_storage_persists_across_server_instances(self, tmp_path: Any) -> None: + from agent_framework_foundry_hosting._responses import ( # pyright: ignore[reportPrivateUsage] + _checkpoint_storage_for_context, + ) + + home = tmp_path / "home" + hosted_config = MagicMock(is_hosted=True, sse_keepalive_interval=0) + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + agent.workflow.name = "wf" + agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) + + with ( + patch("azure.ai.agentserver.core._config.AgentConfig.from_env", return_value=hosted_config), + patch("agent_framework_foundry_hosting._responses.Path.home", return_value=home), + ): + first_server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + checkpoint_root = first_server._checkpoint_storage_path # pyright: ignore[reportPrivateUsage] + assert checkpoint_root == str(home / ".checkpoints") + assert first_server._approval_storage._storage_path == str( # pyright: ignore[reportPrivateUsage] + home / ".function_approvals" / "approval_requests.json" + ) + assert first_server._approval_storage_for_user( # pyright: ignore[reportPrivateUsage] + "user-A" + )._storage_path == str(home / ".function_approvals" / "user-A" / "approval_requests.json") # type: ignore[attr-defined] + + checkpoint = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + first_storage = _checkpoint_storage_for_context(checkpoint_root, "conversation", user_id="user-A") + await first_storage.save(checkpoint) + + approval = _make_function_approval_request_content(request_id="apr_persisted") + await first_server._approval_storage.save_approval_request( # pyright: ignore[reportPrivateUsage] + "apr_persisted", approval + ) + + replacement_server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + replacement_root = replacement_server._checkpoint_storage_path # pyright: ignore[reportPrivateUsage] + replacement_storage = _checkpoint_storage_for_context(replacement_root, "conversation", user_id="user-A") + + restored_checkpoint = await replacement_storage.load(checkpoint.checkpoint_id) + restored_approval = await replacement_server._approval_storage.load_approval_request( # pyright: ignore[reportPrivateUsage] + "apr_persisted" + ) + + assert restored_checkpoint.checkpoint_id == checkpoint.checkpoint_id + assert restored_approval.id == "apr_persisted" # type: ignore[attr-defined] + + def test_init_warns_when_resilient_background_is_used_with_non_workflow_agent( + self, caplog: pytest.LogCaptureFixture + ) -> None: + from azure.ai.agentserver.responses import ResponsesServerOptions + + agent = _make_agent(stream_updates=[]) + + with caplog.at_level("WARNING", logger="agent_framework_foundry_hosting._responses"): + ResponsesHostServer( + agent, + options=ResponsesServerOptions(resilient_background=True), + store=MagicMock(), + ) + + assert "Crash recovery is not supported for non-workflow agents." in caplog.text + + def test_init_steerable_conversations_creates_options_when_store_supplied(self) -> None: + """steerable_conversations=True creates options even when an explicit store is supplied.""" + from azure.ai.agentserver.responses import ResponsesServerOptions + + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) + + captured_options: list[ResponsesServerOptions] = [] + original_init = ResponsesServerOptions.__init__ + + def capture_options(self: ResponsesServerOptions, **kwargs: Any) -> None: + original_init(self, **kwargs) + captured_options.append(self) + + with patch.object(ResponsesServerOptions, "__init__", capture_options): + ResponsesHostServer( + agent, + options=ResponsesServerOptions(steerable_conversations=True), + store=InMemoryResponseProvider(), + ) + + assert any(o.steerable_conversations for o in captured_options), ( + "Expected at least one ResponsesServerOptions with steerable_conversations=True" + ) + # endregion +class TestDurableResponseStreamSeeding: + async def test_delete_checkpoints_except_keeps_selected_checkpoint(self) -> None: + storage = InMemoryCheckpointStorage() + retained = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + deleted = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + await storage.save(retained) + await storage.save(deleted) + + await ResponsesHostServer._delete_checkpoints_except( # pyright: ignore[reportPrivateUsage] + storage, + "wf", + retained.checkpoint_id, + ) + + checkpoints = await storage.list_checkpoints(workflow_name="wf") + assert [checkpoint.checkpoint_id for checkpoint in checkpoints] == [retained.checkpoint_id] + + async def test_workflow_checkpoint_save_publishes_without_waiting_for_response_checkpoint(self) -> None: + stream = ResponseEventStream(response_id="resp_sync", model="test-model") + publisher = _WorkflowCheckpointPublisher(stream) + storage = _ContextAwareCheckpointStorage( + InMemoryCheckpointStorage(), + publisher, + "resp_sync", + "current", + ) + checkpoint = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + + checkpoint_id = await asyncio.wait_for(storage.save(checkpoint), timeout=1) + reference = await asyncio.wait_for(publisher.next_pending(), timeout=1) + + assert checkpoint_id == checkpoint.checkpoint_id + assert reference is not None + assert "workflow_checkpoint" not in stream.internal_metadata + event = publisher.checkpoint_event(reference) + assert isinstance(event, ResponseCheckpointEvent) + assert stream.internal_metadata["workflow_checkpoint"] == { + "checkpoint_id": checkpoint.checkpoint_id, + "context_id": "resp_sync", + "kind": "current", + } + + async def test_current_turn_recovery_forwards_resumed_workflow_outputs(self) -> None: + workflow_event = MagicMock() + update = AgentResponseUpdate(contents=[Content.from_text("resumed output")], role="assistant") + + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + + async def _workflow_events() -> AsyncIterator[Any]: + yield workflow_event + + agent.workflow.run = MagicMock(return_value=_workflow_events()) + agent._convert_workflow_event_to_agent_response_updates = MagicMock(return_value=[update]) + server = object.__new__(ResponsesHostServer) + server._agent = agent # pyright: ignore[reportPrivateUsage] + stream = ResponseEventStream(response_id="resp_recovery", model="test-model") + publisher = _WorkflowCheckpointPublisher(stream) + storage = _ContextAwareCheckpointStorage( + InMemoryCheckpointStorage(), + publisher, + "resp_recovery", + "current", + ) + execution = _WorkflowCheckpointExecution( + restore_storage=storage, + restore_checkpoint_id="checkpoint-current", + write_storage=storage, + resume_current_turn=True, + publisher=publisher, + ) + + updates = [ + item + async for item in server._resume_workflow_with_updates( # pyright: ignore[reportPrivateUsage] + "resp_recovery", + execution, + ) + ] + + assert updates == [update] + agent.workflow.run.assert_called_once_with( + stream=True, + checkpoint_id="checkpoint-current", + checkpoint_storage=storage, + ) + agent._convert_workflow_event_to_agent_response_updates.assert_called_once_with( + "resp_recovery", + workflow_event, + ) + + async def test_checkpoint_finalizes_output_and_preserves_mcp_result_identity(self) -> None: + stream = ResponseEventStream(response_id="resp_atomic", model="test-model") + stream.emit_created() + stream.emit_in_progress() + publisher = _WorkflowCheckpointPublisher(stream) + tracker = _OutputItemTracker(stream) + server = object.__new__(ResponsesHostServer) + + async def _updates() -> AsyncIterator[AgentResponseUpdate]: + yield AgentResponseUpdate(contents=[Content.from_text("before checkpoint")], role="assistant") + publisher.publish("checkpoint-text", "resp_atomic", "current") + yield AgentResponseUpdate( + contents=[ + Content.from_mcp_server_tool_call( + call_id="framework-call", + tool_name="search", + server_name="server", + arguments='{"q":"test"}', + ) + ], + role="assistant", + ) + publisher.publish("checkpoint-mcp", "resp_atomic", "current") + + events = [ + event + async for event in server._drive_workflow_stream( # pyright: ignore[reportPrivateUsage] + _updates(), + publisher, + stream, + tracker, + InMemoryFunctionApprovalStorage(), + asyncio.Event(), + asyncio.Event(), + ) + ] + + checkpoint_indexes = [index for index, event in enumerate(events) if isinstance(event, ResponseCheckpointEvent)] + assert len(checkpoint_indexes) == 2 + assert all(events[index - 1].type == "response.output_item.done" for index in checkpoint_indexes) + assert [events[index - 1].item.type for index in checkpoint_indexes] == ["message", "mcp_call"] # type: ignore[union-attr] + + mcp_call_id = next( + item.id + for item in events[checkpoint_indexes[1]].response.output # type: ignore[union-attr] + if item.type == "mcp_call" + ) + recovered_stream = ResponseEventStream( + response=events[checkpoint_indexes[1]].response, # type: ignore[union-attr] + response_id="resp_atomic", + ) + recovered_tracker = _OutputItemTracker(recovered_stream) + result = Content.from_mcp_server_tool_result( + call_id="framework-call", + output=[Content.from_text("result")], + ) + assert list(recovered_tracker.handle(result)) == [] + assert recovered_tracker.needs_async is True + assert recovered_tracker.take_async_call_id_override() == mcp_call_id + + async def test_recovery_uses_exact_persisted_checkpoint_context(self, tmp_path: Any) -> None: + from azure.ai.agentserver.responses.models import CreateResponse + + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + agent.workflow.name = "wf" + agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) + + server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + root = tmp_path / "checkpoints" + root.mkdir() + server._checkpoint_storage_path = str(root) # pyright: ignore[reportPrivateUsage] + + response_id = "resp_current" + stream = ResponseEventStream(response_id=response_id, model="test-model") + stream.internal_metadata["workflow_checkpoint"] = { + "checkpoint_id": "checkpoint-durable", + "context_id": response_id, + "kind": "current", + } + request = CreateResponse(model="test-model", input="hello") + context = ResponseContext(response_id=response_id, mode_flags=MagicMock()) + context.is_recovery = True + + execution = await server._prepare_workflow_checkpoint_execution( # pyright: ignore[reportPrivateUsage] + request, + context, + stream, + ) + + assert execution.resume_current_turn is True + assert execution.restore_checkpoint_id == "checkpoint-durable" + assert execution.restore_storage is not None + restore_storage = cast(_ContextAwareCheckpointStorage, execution.restore_storage) + inner_storage = cast(FileCheckpointStorage, restore_storage._inner) # pyright: ignore[reportPrivateUsage] + assert inner_storage.storage_path == (root / response_id).resolve() + + async def test_recovery_ignores_newer_unsynchronized_checkpoint(self, tmp_path: Any) -> None: + from azure.ai.agentserver.responses.models import CreateResponse + + from agent_framework_foundry_hosting._responses import ( # pyright: ignore[reportPrivateUsage] + _checkpoint_storage_for_context, + ) + + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + agent.workflow.name = "wf" + agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) + + response_id = "resp_current" + root = tmp_path / "checkpoints" + root.mkdir() + storage = _checkpoint_storage_for_context(str(root), response_id) + durable = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + orphan = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + await storage.save(durable) + await storage.save(orphan) + latest = await storage.get_latest(workflow_name="wf") + assert latest is not None + assert latest.checkpoint_id == orphan.checkpoint_id + + server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + server._checkpoint_storage_path = str(root) # pyright: ignore[reportPrivateUsage] + stream = ResponseEventStream(response_id=response_id, model="test-model") + stream.internal_metadata["workflow_checkpoint"] = { + "checkpoint_id": durable.checkpoint_id, + "context_id": response_id, + "kind": "current", + } + request = CreateResponse(model="test-model", input="hello") + context = ResponseContext(response_id=response_id, mode_flags=MagicMock()) + context.is_recovery = True + + execution = await server._prepare_workflow_checkpoint_execution( # pyright: ignore[reportPrivateUsage] + request, + context, + stream, + ) + + assert execution.restore_checkpoint_id == durable.checkpoint_id + + async def test_normal_turn_seeds_base_checkpoint_before_response_created(self, tmp_path: Any) -> None: + from azure.ai.agentserver.responses.models import CreateResponse + + from agent_framework_foundry_hosting._responses import ( # pyright: ignore[reportPrivateUsage] + _checkpoint_storage_for_context, + ) + + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + agent.workflow.name = "wf" + agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) + + previous_response_id = "resp_previous" + response_id = "resp_current" + root = tmp_path / "checkpoints" + root.mkdir() + checkpoint_storage = _checkpoint_storage_for_context(str(root), previous_response_id) + old_checkpoint = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + checkpoint = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + await checkpoint_storage.save(old_checkpoint) + await checkpoint_storage.save(checkpoint) + + server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + server._checkpoint_storage_path = str(root) # pyright: ignore[reportPrivateUsage] + stream = ResponseEventStream(response_id=response_id, model="test-model") + request = CreateResponse(model="test-model", input="hello", previous_response_id=previous_response_id) + context = ResponseContext( + response_id=response_id, + previous_response_id=previous_response_id, + mode_flags=MagicMock(), + ) + + execution = await server._prepare_workflow_checkpoint_execution( # pyright: ignore[reportPrivateUsage] + request, + context, + stream, + ) + + assert execution.resume_current_turn is False + assert execution.restore_checkpoint_id == checkpoint.checkpoint_id + assert stream.internal_metadata["workflow_checkpoint"] == { + "checkpoint_id": checkpoint.checkpoint_id, + "context_id": previous_response_id, + "kind": "base", + } + remaining = await checkpoint_storage.list_checkpoint_ids(workflow_name="wf") + assert remaining == [checkpoint.checkpoint_id] + + async def test_conversation_baseline_is_seeded_before_response_created(self, tmp_path: Any) -> None: + from azure.ai.agentserver.responses.models import CreateResponse + + from agent_framework_foundry_hosting._responses import ( # pyright: ignore[reportPrivateUsage] + _checkpoint_storage_for_context, + ) + + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + agent.workflow.name = "wf" + agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) + + conversation_id = "conv_recovery" + response_id = "resp_recovery" + root = tmp_path / "checkpoints" + root.mkdir() + checkpoint_storage = _checkpoint_storage_for_context(str(root), conversation_id) + checkpoint = WorkflowCheckpoint(workflow_name="wf", graph_signature_hash="hash") + await checkpoint_storage.save(checkpoint) + + server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + server._checkpoint_storage_path = str(root) # pyright: ignore[reportPrivateUsage] + request = CreateResponse(model="test-model", input="hello") + context = ResponseContext( + response_id=response_id, + conversation_id=conversation_id, + mode_flags=MagicMock(), + ) + + prepare = AsyncMock(wraps=server._prepare_workflow_checkpoint_execution) # pyright: ignore[reportPrivateUsage] + with patch.object(server, "_prepare_workflow_checkpoint_execution", new=prepare): + events = server._handle_response(request, context, asyncio.Event()) # pyright: ignore[reportPrivateUsage] + iterator = events.__aiter__() + created = await anext(iterator) + await cast(Any, iterator).aclose() + + assert created.type == "response.created" + prepare.assert_awaited_once() + prepare_args = prepare.await_args + assert prepare_args is not None + prepared_stream = prepare_args.args[2] + expected_reference = { + "checkpoint_id": checkpoint.checkpoint_id, + "context_id": conversation_id, + "kind": "base", + } + assert prepared_stream.internal_metadata["workflow_checkpoint"] == expected_reference + assert cast(Any, created).response.internal_metadata["workflow_checkpoint"] == expected_reference + + async def test_checkpoint_preparation_failure_emits_valid_lifecycle(self) -> None: + from azure.ai.agentserver.responses.models import CreateResponse + + agent = MagicMock(spec=WorkflowAgent) + agent.id = "wf-agent" + agent.name = "wf" + agent.description = "" + agent.context_providers = [] + agent.workflow = MagicMock() + agent.workflow.name = "wf" + agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) + + server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) + request = CreateResponse(model="test-model", input="hello") + context = ResponseContext(response_id="resp_prepare_failure", mode_flags=MagicMock()) + + with patch.object( + server, + "_prepare_workflow_checkpoint_execution", + new=AsyncMock(side_effect=RuntimeError("checkpoint preparation failed")), + ): + events = [ + event + async for event in server._handle_response( # pyright: ignore[reportPrivateUsage] + request, + context, + asyncio.Event(), + ) + ] + + assert [event.type for event in events] == [ + "response.created", + "response.in_progress", + "response.failed", + ] + + async def test_cancellation_signal_emits_completed_for_streaming(self) -> None: + """On steering pressure or client cancel the streaming handler emits response.completed.""" + from azure.ai.agentserver.responses import ResponseContext + from azure.ai.agentserver.responses.models import CreateResponse + + # Agent produces two streaming updates; cancellation fires after the first. + update1 = AgentResponseUpdate(contents=[Content.from_text("chunk 1")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("chunk 2")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) + server = _make_server(agent) + + request = CreateResponse(model="test-model", input="hi", stream=True) + context = ResponseContext(response_id="resp_steer", mode_flags=MagicMock()) + + cancellation_signal = asyncio.Event() + + # Fire cancellation after the first update is processed. + call_count = 0 + + def run_with_cancel(*args: Any, **kwargs: Any) -> Any: + nonlocal call_count + + async def _gen() -> AsyncIterator[AgentResponseUpdate]: + nonlocal call_count + for update in [update1, update2]: + call_count += 1 + if call_count == 1: + # Fire cancellation before yielding the second update. + cancellation_signal.set() + yield update + + if kwargs.get("stream"): + from agent_framework import ResponseStream + + return ResponseStream(_gen()) # type: ignore + raise NotImplementedError + + agent.run = MagicMock(side_effect=run_with_cancel) + + events = [] + with ( + patch.object(ResponseContext, "get_input_items", new=AsyncMock(return_value=[])), + patch.object(ResponseContext, "get_history", new=AsyncMock(return_value=[])), + ): + async for event in server._handle_response(request, context, cancellation_signal): # pyright: ignore[reportPrivateUsage] + events.append(event) + + event_types = [getattr(e, "type", e.get("type") if isinstance(e, dict) else None) for e in events] + # Must have response.created and response.in_progress. + assert "response.created" in event_types + assert "response.in_progress" in event_types + # Must emit response.completed — the framework overrides to cancelled for + # real client cancels; for steering pressure completed is the correct terminal. + assert event_types.count("response.completed") == 1 + + async def test_shutdown_exits_for_recovery_without_terminal_event(self) -> None: + from azure.ai.agentserver.responses._response_context import ResponseExitForRecovery, ResponseModeFlags + from azure.ai.agentserver.responses.models import CreateResponse + + agent = _make_agent( + stream_updates=[AgentResponseUpdate(contents=[Content.from_text("chunk")], role="assistant")] + ) + server = _make_server(agent) + server._resilient_background = True # pyright: ignore[reportPrivateUsage] + request = CreateResponse(model="test-model", input="hi", stream=True) + context = ResponseContext( + response_id="resp_shutdown", + mode_flags=ResponseModeFlags(stream=True, store=True, background=True), + ) + context.shutdown.set() + context.exit_for_recovery = AsyncMock(side_effect=ResponseExitForRecovery()) # type: ignore[method-assign] + events: list[Any] = [] + + with ( + patch.object(ResponseContext, "get_input_items", new=AsyncMock(return_value=[])), + patch.object(ResponseContext, "get_history", new=AsyncMock(return_value=[])), + pytest.raises(ResponseExitForRecovery), + ): + async for event in server._handle_response( # pyright: ignore[reportPrivateUsage] + request, + context, + asyncio.Event(), + ): + events.append(event) + + context.exit_for_recovery.assert_awaited_once() + event_types = [getattr(event, "type", None) for event in events] + assert "response.completed" not in event_types + assert "response.failed" not in event_types + + async def test_non_resilient_shutdown_completes_without_recovery_failure(self) -> None: + from azure.ai.agentserver.responses._response_context import ResponseModeFlags + from azure.ai.agentserver.responses.models import CreateResponse + + agent = _make_agent( + stream_updates=[AgentResponseUpdate(contents=[Content.from_text("chunk")], role="assistant")] + ) + server = _make_server(agent) + request = CreateResponse(model="test-model", input="hi", stream=True) + context = ResponseContext( + response_id="resp_foreground_shutdown", + mode_flags=ResponseModeFlags(stream=True, store=False, background=False), + ) + context.shutdown.set() + events: list[Any] = [] + + with ( + patch.object(ResponseContext, "get_input_items", new=AsyncMock(return_value=[])), + patch.object(ResponseContext, "get_history", new=AsyncMock(return_value=[])), + ): + async for event in server._handle_response( # pyright: ignore[reportPrivateUsage] + request, + context, + asyncio.Event(), + ): + events.append(event) + + event_types = [getattr(event, "type", None) for event in events] + assert "response.completed" in event_types + assert "response.failed" not in event_types + + # region Health Check class TestHealthCheck: async def test_readiness(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) transport = httpx.ASGITransport(app=server) async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: @@ -240,9 +873,9 @@ async def test_readiness(self) -> None: class TestNonStreaming: async def test_basic_text_response(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Hello!")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("Hello")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) resp = await _post(server, input_text="Hi", stream=False) @@ -264,18 +897,13 @@ async def test_basic_text_response(self) -> None: assert text_found, f"Expected 'Hello!' in output, got: {body['output']}" async def test_function_call_and_result(self) -> None: - agent = _make_agent( - response=AgentResponse( - messages=[ - Message( - role="assistant", - contents=[Content.from_function_call("call_1", "get_weather", arguments='{"loc": "NYC"}')], - ), - Message(role="tool", contents=[Content.from_function_result("call_1", result="sunny")]), - Message(role="assistant", contents=[Content.from_text("The weather is sunny!")]), - ] - ) + update1 = AgentResponseUpdate( + contents=[Content.from_function_call("call_1", "get_weather", arguments='{"loc": "NYC"}')], role="assistant" ) + update2 = AgentResponseUpdate(contents=[Content.from_function_result("call_1", result="sunny")], role="tool") + update3 = AgentResponseUpdate(contents=[Content.from_text("The weather")], role="assistant") + update4 = AgentResponseUpdate(contents=[Content.from_text(" is sunny!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2, update3, update4]) server = _make_server(agent) resp = await _post(server, stream=False) @@ -289,33 +917,31 @@ async def test_function_call_and_result(self) -> None: assert "message" in types async def test_hosted_mcp_call_and_result_persist_as_single_mcp_call(self) -> None: - agent = _make_agent( - response=AgentResponse( - messages=[ - Message( - role="assistant", - contents=[ - Content.from_mcp_server_tool_call( - call_id="mcp_abc123", - tool_name="search", - server_name="api_specs", - arguments='{"q": "cats"}', - ) - ], - ), - Message( - role="tool", - contents=[ - Content.from_mcp_server_tool_result( - call_id="mcp_abc123", - output=[Content.from_text(text="found 10 cats")], - ) - ], - ), - Message(role="assistant", contents=[Content.from_text("I found 10 cats!")]), - ] - ) + from azure.ai.agentserver.responses.models import OutputItemCustomToolCallOutput, OutputItemMcpToolCall + + update1 = AgentResponseUpdate( + contents=[ + Content.from_mcp_server_tool_call( + call_id="mcp_abc123", + tool_name="search", + server_name="api_specs", + arguments='{"q": "cats"}', + ) + ], + role="assistant", ) + update2 = AgentResponseUpdate( + contents=[ + Content.from_mcp_server_tool_result( + call_id="mcp_abc123", + output=[Content.from_text(text="found 10 cats")], + ) + ], + role="tool", + ) + update3 = AgentResponseUpdate(contents=[Content.from_text("I found ")], role="assistant") + update4 = AgentResponseUpdate(contents=[Content.from_text("10 cats!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2, update3, update4]) server = _make_server(agent) resp = await _post(server, stream=False) @@ -325,27 +951,36 @@ async def test_hosted_mcp_call_and_result_persist_as_single_mcp_call(self) -> No types = [item["type"] for item in body["output"]] assert "mcp_call" in types - assert "custom_tool_call_output" not in types + # In b8, the MCP call output is emitted as a separate custom_tool_call_output item + # rather than inlined in the mcp_call item's done payload. + assert "custom_tool_call_output" in types mcp_items = [item for item in body["output"] if item["type"] == "mcp_call"] assert len(mcp_items) == 1 - assert mcp_items[0]["id"] == "mcp_abc123" - assert mcp_items[0]["output"] == "found 10 cats" + # In b8, item IDs are auto-generated by the SDK (no longer passed by the adapter). + assert mcp_items[0]["id"].startswith("mcp_") + output_items = [item for item in body["output"] if item["type"] == "custom_tool_call_output"] + assert len(output_items) == 1 + assert output_items[0]["output"] == "found 10 cats" + assert output_items[0]["call_id"] == mcp_items[0]["id"] + + call_message = await _output_item_to_message(OutputItemMcpToolCall(mcp_items[0])) + result_message = await _output_item_to_message(OutputItemCustomToolCallOutput(output_items[0])) + assert call_message.contents[0].call_id == result_message.contents[0].call_id + assert result_message.contents[0].type == "mcp_server_tool_result" async def test_reasoning_content(self) -> None: - agent = _make_agent( - response=AgentResponse( - messages=[ - Message( - role="assistant", - contents=[ - Content.from_text_reasoning(text="Let me think..."), - Content.from_text("The answer is 42"), - ], - ), - ] - ) + update1 = AgentResponseUpdate( + contents=[Content.from_text_reasoning(text="Let me")], + role="assistant", ) + update2 = AgentResponseUpdate( + contents=[Content.from_text_reasoning(text=" think...")], + role="assistant", + ) + update3 = AgentResponseUpdate(contents=[Content.from_text("The answer is")], role="assistant") + update4 = AgentResponseUpdate(contents=[Content.from_text(" 42")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2, update3, update4]) server = _make_server(agent) resp = await _post(server, stream=False) @@ -357,8 +992,25 @@ async def test_reasoning_content(self) -> None: assert "reasoning" in types assert "message" in types + async def test_protected_reasoning_without_text_is_preserved(self) -> None: + agent = _make_agent( + stream_updates=[ + AgentResponseUpdate( + contents=[Content.from_text_reasoning(text=None, protected_data="encrypted-state")], + role="assistant", + ) + ] + ) + server = _make_server(agent) + resp = await _post(server, stream=False) + + assert resp.status_code == 200 + body = resp.json() + reasoning = next(item for item in body["output"] if item["type"] == "reasoning") + assert reasoning["encrypted_content"] == "encrypted-state" + async def test_empty_response(self) -> None: - agent = _make_agent(response=AgentResponse(messages=[])) + agent = _make_agent([]) server = _make_server(agent) resp = await _post(server, stream=False) @@ -367,10 +1019,9 @@ async def test_empty_response(self) -> None: assert body["status"] == "completed" async def test_chat_options_forwarded(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("ok")])]), - raw_agent=True, - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2], raw_agent=True) server = _make_server(agent) resp = await _post( server, @@ -382,9 +1033,9 @@ async def test_chat_options_forwarded(self) -> None: ) assert resp.status_code == 200 - agent.run.assert_awaited_once() + agent.run.assert_called_once() call_kwargs = agent.run.call_args.kwargs - assert call_kwargs["stream"] is False + assert call_kwargs["stream"] is True options = call_kwargs["options"] assert options["temperature"] == 0.5 assert options["top_p"] == 0.9 @@ -731,10 +1382,14 @@ async def test_mcp_tool_call_and_result_streaming_emit_single_completed_mcp_call assert resp.status_code == 200 events = _parse_sse_events(resp.text) done_events = [e for e in events if e["event"] == "response.output_item.done"] - assert len(done_events) == 1 - assert done_events[0]["data"]["item"]["type"] == "mcp_call" - assert done_events[0]["data"]["item"]["id"] == "mcp_abc123" - assert done_events[0]["data"]["item"]["output"] == "found 10 cats" + # In b8, the mcp_call done event and a separate custom_tool_call_output done event are emitted. + mcp_done = [e for e in done_events if e["data"]["item"]["type"] == "mcp_call"] + output_done = [e for e in done_events if e["data"]["item"]["type"] == "custom_tool_call_output"] + assert len(mcp_done) == 1 + # In b8 item IDs are auto-generated by the SDK. + assert mcp_done[0]["data"]["item"]["id"].startswith("mcp_") + assert len(output_done) == 1 + assert output_done[0]["data"]["item"]["output"] == "found 10 cats" # endregion @@ -791,7 +1446,7 @@ async def test_function_call(self) -> None: assert msg.contents[0].type == "function_call" assert msg.contents[0].call_id == "call_1" assert msg.contents[0].name == "get_weather" - assert msg.contents[0].informational_only is False + assert msg.contents[0].informational_only is True async def test_function_call_output(self) -> None: from azure.ai.agentserver.responses.models import FunctionCallOutputItemParam @@ -824,10 +1479,20 @@ async def test_reasoning_no_summary(self) -> None: item = OutputItemReasoningItem({"type": "reasoning", "id": "r-2"}) msg = await _output_item_to_message(item) assert msg.role == "assistant" + assert msg.contents == [] + + async def test_reasoning_with_encrypted_content_only(self) -> None: + from azure.ai.agentserver.responses.models import OutputItemReasoningItem + + item = OutputItemReasoningItem({ + "type": "reasoning", + "id": "r-protected", + "encrypted_content": "encrypted-state", + }) + msg = await _output_item_to_message(item) assert len(msg.contents) == 1 - assert msg.contents[0].type == "text_reasoning" - assert msg.contents[0].id == "r-2" assert msg.contents[0].text is None + assert msg.contents[0].protected_data == "encrypted-state" async def test_mcp_call(self) -> None: from azure.ai.agentserver.responses.models import OutputItemMcpToolCall @@ -1276,7 +1941,7 @@ async def test_function_call(self) -> None: assert msg.contents[0].call_id == "call_1" assert msg.contents[0].name == "get_weather" assert msg.contents[0].arguments == '{"city": "NYC"}' - assert msg.contents[0].informational_only is False + assert msg.contents[0].informational_only is True async def test_function_call_output(self) -> None: from azure.ai.agentserver.responses.models import FunctionCallOutputItemParam @@ -1321,10 +1986,20 @@ async def test_reasoning_no_summary(self) -> None: msg = await _item_to_message(item) assert msg is not None assert msg.role == "assistant" + assert msg.contents == [] + + async def test_reasoning_with_encrypted_content_only(self) -> None: + from azure.ai.agentserver.responses.models import ItemReasoningItem + + item = ItemReasoningItem({ + "type": "reasoning", + "id": "r-protected", + "encrypted_content": "encrypted-state", + }) + msg = await _item_to_message(item) assert len(msg.contents) == 1 - assert msg.contents[0].type == "text_reasoning" - assert msg.contents[0].id == "r-2" assert msg.contents[0].text is None + assert msg.contents[0].protected_data == "encrypted-state" async def test_mcp_call(self) -> None: from azure.ai.agentserver.responses.models import ItemMcpToolCall @@ -1691,7 +2366,7 @@ def _make_multi_response_agent( responses: list[AgentResponse], stream_updates_list: list[list[AgentResponseUpdate]] | None = None, ) -> MagicMock: - """Create a mock agent that returns different responses on successive calls.""" + """Create a streaming-only mock agent that streams a different response on successive calls.""" agent = MagicMock(spec=RawAgent) agent.id = "test-agent" agent.name = "Test Agent" @@ -1700,10 +2375,15 @@ def _make_multi_response_agent( call_index = [0] - async def run_non_streaming(*args: Any, **kwargs: Any) -> AgentResponse: - idx = call_index[0] - call_index[0] += 1 - return responses[idx] + def _updates_for(idx: int) -> list[AgentResponseUpdate]: + # Prefer explicitly provided streaming updates for this turn; otherwise + # derive them from the AgentResponse messages (one update per message). + if stream_updates_list is not None and stream_updates_list[idx]: + return stream_updates_list[idx] + return [ + AgentResponseUpdate(contents=list(message.contents), role=message.role) + for message in responses[idx].messages + ] async def _stream_gen(updates: list[AgentResponseUpdate]) -> AsyncIterator[AgentResponseUpdate]: for update in updates: @@ -1712,20 +2392,11 @@ async def _stream_gen(updates: list[AgentResponseUpdate]) -> AsyncIterator[Agent def run_dispatch(*args: Any, **kwargs: Any) -> Any: idx = call_index[0] call_index[0] += 1 - if kwargs.get("stream") and stream_updates_list is not None: - return ResponseStream(_stream_gen(stream_updates_list[idx])) # type: ignore if not kwargs.get("stream"): - # Need to return a coroutine for non-streaming - async def _ret() -> AgentResponse: - return responses[idx] - - return _ret() - raise NotImplementedError("Streaming not configured for this call index") + raise NotImplementedError("Only streaming is configured on this mock") + return ResponseStream(_stream_gen(_updates_for(idx))) # type: ignore - if stream_updates_list is not None: - agent.run = MagicMock(side_effect=run_dispatch) - else: - agent.run = AsyncMock(side_effect=run_non_streaming) + agent.run = MagicMock(side_effect=run_dispatch) return agent @@ -1735,9 +2406,9 @@ class TestMultiTurnMixedContent: async def test_text_and_image_input_single_turn(self) -> None: """Agent receives a message with text and image content via URL.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("I see a cat!")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("I see")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text(" a cat!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) resp = await _post_json( @@ -1774,9 +2445,9 @@ async def test_text_and_image_input_single_turn(self) -> None: async def test_text_and_file_input_single_turn(self) -> None: """Agent receives a message with text and file content via URL.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("File received")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("File ")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("received")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) resp = await _post_json( @@ -1811,9 +2482,9 @@ async def test_text_and_file_input_single_turn(self) -> None: async def test_text_and_file_data_input_single_turn(self) -> None: """Agent receives a message with text and file content via inline file_data.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("File received")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("File ")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text("received")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) resp = await _post_json( @@ -1852,9 +2523,8 @@ async def test_text_and_file_data_input_single_turn(self) -> None: async def test_text_mime_file_data_decoded(self) -> None: """Agent receives a text/* file_data that is base64-decoded to plain text.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Got it")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("Got it")], role="assistant") + agent = _make_agent(stream_updates=[update1]) server = _make_server(agent) import base64 @@ -1891,9 +2561,8 @@ async def test_text_mime_file_data_decoded(self) -> None: async def test_text_mime_file_data_invalid_base64_falls_through(self) -> None: """Invalid base64 in a text/* file_data falls through to URI passthrough.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Got it")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("Got it")], role="assistant") + agent = _make_agent(stream_updates=[update1]) server = _make_server(agent) resp = await _post_json( @@ -1926,9 +2595,8 @@ async def test_text_mime_file_data_invalid_base64_falls_through(self) -> None: async def test_mixed_text_and_image_input(self) -> None: """Agent receives a single message with both text and image content.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Got it!")])]) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("Got it")], role="assistant") + agent = _make_agent(stream_updates=[update1]) server = _make_server(agent) resp = await _post_json( @@ -1963,11 +2631,9 @@ async def test_mixed_text_and_image_input(self) -> None: async def test_function_call_items_in_input(self) -> None: """Input contains function_call and function_call_output items.""" - agent = _make_agent( - response=AgentResponse( - messages=[Message(role="assistant", contents=[Content.from_text("Weather is sunny!")])] - ) - ) + update1 = AgentResponseUpdate(contents=[Content.from_text("Weather is")], role="assistant") + update2 = AgentResponseUpdate(contents=[Content.from_text(" sunny!")], role="assistant") + agent = _make_agent(stream_updates=[update1, update2]) server = _make_server(agent) resp = await _post_json( @@ -2153,7 +2819,8 @@ async def test_hosted_mcp_call_round_trip_does_not_orphan_function_call_output(s types1 = [item["type"] for item in resp1.json()["output"]] assert "mcp_call" in types1 - assert "custom_tool_call_output" not in types1 + # In b8, MCP output is emitted as a separate custom_tool_call_output item. + assert "custom_tool_call_output" in types1 resp2 = await _post_json( server, @@ -2177,8 +2844,10 @@ async def test_hosted_mcp_call_round_trip_does_not_orphan_function_call_output(s assert len(mcp_call_contents) >= 1 assert len(mcp_result_contents) >= 1 assert all((c.call_id or "") != "mcp_abc123" for c in function_result_contents) - assert any((c.call_id or "") == "mcp_abc123" for c in mcp_call_contents) - assert any((c.call_id or "") == "mcp_abc123" for c in mcp_result_contents) + mcp_call_ids = {c.call_id for c in mcp_call_contents} + mcp_result_ids = {c.call_id for c in mcp_result_contents} + assert None not in mcp_call_ids + assert mcp_call_ids <= mcp_result_ids async def test_multi_turn_reasoning_in_history(self) -> None: """Turn 1 produces reasoning + text, turn 2 sees them in history.""" @@ -2284,9 +2953,7 @@ async def test_multi_turn_with_mixed_content_and_streaming(self) -> None: async def test_text_with_mcp_call_items(self) -> None: """Input contains text message + mcp_call item and the agent processes it.""" agent = _make_agent( - response=AgentResponse( - messages=[Message(role="assistant", contents=[Content.from_text("MCP result received")])] - ) + stream_updates=[AgentResponseUpdate(contents=[Content.from_text("MCP result received")], role="assistant")] ) server = _make_server(agent) @@ -2409,7 +3076,7 @@ async def test_three_turn_conversation_with_mixed_content(self) -> None: async def test_input_with_hosted_file_image(self) -> None: """Input contains an image referenced by file_id (hosted file).""" agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Image analyzed")])]) + stream_updates=[AgentResponseUpdate(contents=[Content.from_text("Image analyzed")], role="assistant")] ) server = _make_server(agent) @@ -2777,7 +3444,7 @@ class TestFunctionApprovalRoundTrip: async def test_non_streaming_emits_mcp_approval_request_and_persists_to_storage(self) -> None: request_content = _make_function_approval_request_content() - agent = _make_agent(response=AgentResponse(messages=[Message(role="assistant", contents=[request_content])])) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[request_content], role="assistant")]) server = _make_server(agent) resp = await _post(server, stream=False) @@ -2917,9 +3584,7 @@ async def test_approval_response_referencing_unknown_id_fails(self) -> None: """Sending an `mcp_approval_response` for a request id that was never persisted must surface as a ``response.failed`` event whose ``error.message`` contains the missing approval request id.""" - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("ok")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("ok")], role="assistant")]) server = _make_server(agent) resp = await _post_json( @@ -3085,12 +3750,20 @@ async def test_handle_inner_workflow_restores_message_role_checkpoint_from_previ agent.workflow = MagicMock() agent.workflow.name = "wf" agent.workflow._runner_context.has_checkpointing = MagicMock(return_value=False) - agent.run = AsyncMock( - side_effect=[ - AgentResponse(messages=[]), - AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("ok")])]), - ] - ) + + async def _restore_stream() -> AsyncIterator[AgentResponseUpdate]: + for _ in (): + yield AgentResponseUpdate(contents=[], role="assistant") + + async def _new_turn_stream() -> AsyncIterator[AgentResponseUpdate]: + yield AgentResponseUpdate(contents=[Content.from_text("ok")], role="assistant") + + run_streams = [_restore_stream(), _new_turn_stream()] + + def run_dispatch(*args: Any, **kwargs: Any) -> Any: + return ResponseStream(run_streams.pop(0)) # type: ignore[arg-type] + + agent.run = MagicMock(side_effect=run_dispatch) server = ResponsesHostServer(agent, store=InMemoryResponseProvider()) server._checkpoint_storage_path = str(root) # pyright: ignore[reportPrivateUsage] @@ -3101,19 +3774,27 @@ async def test_handle_inner_workflow_restores_message_role_checkpoint_from_previ input_item = ItemMessage({"type": "message", "role": "user", "content": "next turn"}) with patch.object(ResponseContext, "get_input_items", new=AsyncMock(return_value=[input_item])): - async for _ in server._handle_inner_workflow(request, context): # pyright: ignore[reportPrivateUsage] - pass + events = [ + event + async for event in server._handle_response( # pyright: ignore[reportPrivateUsage] + request, + context, + asyncio.Event(), + ) + ] assert agent.run.call_count == 2 + assert isinstance(events[2], ResponseCheckpointEvent) + assert cast(Any, events[2].response).internal_metadata["workflow_checkpoint"]["kind"] == "base" restore_call = agent.run.call_args_list[0] assert restore_call.kwargs["checkpoint_id"] == checkpoint.checkpoint_id - assert restore_call.kwargs["checkpoint_storage"].storage_path == (root / previous_response_id).resolve() + assert restore_call.kwargs["checkpoint_storage"]._inner.storage_path == (root / previous_response_id).resolve() new_turn_call = agent.run.call_args_list[1] new_turn_messages = new_turn_call.args[0] assert len(new_turn_messages) == 1 assert new_turn_messages[0].text == "next turn" - assert new_turn_call.kwargs["checkpoint_storage"].storage_path == (root / response_id).resolve() + assert new_turn_call.kwargs["checkpoint_storage"]._inner.storage_path == (root / response_id).resolve() @pytest.mark.parametrize( "bad_id", @@ -3295,7 +3976,7 @@ async def test_handle_inner_workflow_rejects_malicious_context_id( # terminal ``response.failed`` event whose error message names # the rejected context id, so the SSE / non-streaming consumer # observes a well-formed failure rather than a raw exception. - events = [event async for event in server._handle_inner_workflow(request, context)] # pyright: ignore[reportPrivateUsage] + events = [event async for event in server._handle_response(request, context, asyncio.Event())] # pyright: ignore[reportPrivateUsage] after = sorted(p.name for p in tmp_path.iterdir()) failed = [e for e in events if getattr(e, "type", None) == "response.failed"] @@ -3513,9 +4194,7 @@ def test_returns_none_when_message_has_no_json(self) -> None: class TestAgentLifecycle: async def test_agent_entered_lazily_on_first_request(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant")]) server = _make_server(agent) # Construction must not enter the agent. assert agent.__aenter__.await_count == 0 @@ -3524,9 +4203,7 @@ async def test_agent_entered_lazily_on_first_request(self) -> None: assert agent.__aenter__.await_count == 1 async def test_agent_entered_only_once_across_requests(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant")]) server = _make_server(agent) await _post(server, input_text="first", stream=False) @@ -3535,9 +4212,7 @@ async def test_agent_entered_only_once_across_requests(self) -> None: assert agent.__aenter__.await_count == 1 async def test_cleanup_exits_agent_and_allows_reentry(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant")]) server = _make_server(agent) await _post(server, input_text="hello", stream=False) @@ -3556,9 +4231,7 @@ async def test_cleanup_exits_agent_and_allows_reentry(self) -> None: assert agent.__aenter__.await_count == 2 async def test_failed_entry_does_not_cache_stack(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant")]) agent.__aenter__.side_effect = [_make_consent_error(), None] server = _make_server(agent) @@ -3570,9 +4243,7 @@ async def test_failed_entry_does_not_cache_stack(self) -> None: class TestOAuthConsentSurfacing: async def test_non_streaming_consent_error_emits_oauth_output_item(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant")]) agent.__aenter__.side_effect = _make_consent_error("https://consent.example.com/auth") server = _make_server(agent) @@ -3602,6 +4273,7 @@ async def test_streaming_consent_error_emits_oauth_output_item(self) -> None: assert types[0] == "response.created" assert types[1] == "response.in_progress" assert types[-1] == "response.completed" + assert types.count("response.completed") == 1 added = [e for e in events if e["event"] == "response.output_item.added"] oauth_added = [e for e in added if e["data"]["item"]["type"] == "oauth_consent_request"] @@ -3615,9 +4287,7 @@ async def test_streaming_consent_error_emits_oauth_output_item(self) -> None: agent.run.assert_not_called() async def test_non_consent_error_during_entry_propagates(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) + agent = _make_agent(stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hi")], role="assistant")]) agent.__aenter__.side_effect = RuntimeError("boom") server = _make_server(agent) @@ -3635,7 +4305,7 @@ async def test_non_consent_error_during_entry_propagates(self) -> None: async def test_retry_after_consent_succeeds(self) -> None: agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hello!")])]) + stream_updates=[AgentResponseUpdate(contents=[Content.from_text("hello!")], role="assistant")] ) agent.__aenter__.side_effect = [_make_consent_error("https://consent.example.com/auth"), None] server = _make_server(agent) @@ -3655,7 +4325,7 @@ async def test_retry_after_consent_succeeds(self) -> None: assert body2["status"] == "completed" assert any(it["type"] == "message" for it in body2["output"]) assert agent.__aenter__.await_count == 2 - agent.run.assert_awaited_once() + agent.run.assert_called_once() # endregion @@ -3672,14 +4342,22 @@ class TestResponseFailedSurfacing: """ async def test_non_streaming_run_failure_emits_response_failed(self) -> None: - agent = _make_agent( - response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("hi")])]) - ) - - async def _raise(*args: Any, **kwargs: Any) -> AgentResponse: + async def _raise_stream() -> AsyncIterator[AgentResponseUpdate]: + yield AgentResponseUpdate(contents=[Content.from_text("partial ")], role="assistant") raise RuntimeError("non-stream kaboom") - agent.run = AsyncMock(side_effect=_raise) + agent = MagicMock(spec=RawAgent) + agent.id = "test-agent" + agent.name = "Test Agent" + agent.description = "A mock agent for testing" + agent.context_providers = [] + + def run_streaming(*args: Any, **kwargs: Any) -> Any: + if kwargs.get("stream"): + return ResponseStream(_raise_stream()) # type: ignore[arg-type] + raise NotImplementedError("Only streaming is configured on this mock") + + agent.run = MagicMock(side_effect=run_streaming) server = _make_server(agent) resp = await _post(server, input_text="hello", stream=False) @@ -3765,13 +4443,17 @@ async def test_workflow_agent_run_failure_emits_response_failed(self) -> None: """ workflow_agent = _build_text_workflow_agent("ignored") - async def _raise(*args: Any, **kwargs: Any) -> AgentResponse: + async def _raise_stream() -> AsyncIterator[AgentResponseUpdate]: + yield AgentResponseUpdate(contents=[Content.from_text("partial ")], role="assistant") raise RuntimeError("workflow kaboom") - # Patch the public ``run`` to fail. ``_handle_inner_workflow`` only - # invokes the agent once (no checkpoint to restore on a fresh + def _run_stream(*args: Any, **kwargs: Any) -> Any: + return ResponseStream(_raise_stream()) # type: ignore[arg-type] + + # Patch the public ``run`` to stream and then fail. ``_handle_inner_workflow`` + # only invokes the agent once (no checkpoint to restore on a fresh # request), so this is the call that will raise. - with patch.object(workflow_agent, "run", side_effect=_raise): + with patch.object(workflow_agent, "run", new=MagicMock(side_effect=_run_stream)): server = _make_server(workflow_agent) resp = await _post(server, input_text="hello", stream=False) @@ -4066,6 +4748,56 @@ async def test_basic_text_response(self) -> None: ) assert text_found, f"Expected workflow output text in {body['output']}" + async def test_handler_yields_response_checkpoint_for_workflow_supersteps(self) -> None: + from azure.ai.agentserver.responses.models import CreateResponse, ItemMessage + + workflow_agent = _build_text_workflow_agent("checkpointed output") + server = _make_server(workflow_agent) + request = CreateResponse(model="test-model", input="hi") + context = ResponseContext(response_id="resp_checkpoint_events", mode_flags=MagicMock()) + input_item = ItemMessage({"type": "message", "role": "user", "content": "hi"}) + + with patch.object(ResponseContext, "get_input_items", new=AsyncMock(return_value=[input_item])): + events = [ + event + async for event in server._handle_response( # pyright: ignore[reportPrivateUsage] + request, + context, + asyncio.Event(), + ) + ] + + checkpoint_events = [event for event in events if isinstance(event, ResponseCheckpointEvent)] + assert len(checkpoint_events) >= 2 + + async def test_workflow_approval_output_uses_resolved_user_storage(self) -> None: + from azure.ai.agentserver.responses.models import CreateResponse, ItemMessage + + workflow_agent, _ = _build_approval_workflow_agent(approval_request_id="apr_user_scoped") + server = _make_server(workflow_agent) + user_storage = MagicMock() + user_storage.save_approval_request = AsyncMock() + user_storage.load_approval_request = AsyncMock() + request = CreateResponse(model="test-model", input="hi") + context = ResponseContext(response_id="resp_user_scoped", mode_flags=MagicMock()) + context.platform_context.user_id_key = "user-A" + input_item = ItemMessage({"type": "message", "role": "user", "content": "hi"}) + + with ( + patch.object(server, "_approval_storage_for_user", return_value=user_storage), + patch.object(ResponseContext, "get_input_items", new=AsyncMock(return_value=[input_item])), + ): + _ = [ + event + async for event in server._handle_response( # pyright: ignore[reportPrivateUsage] + request, + context, + asyncio.Event(), + ) + ] + + user_storage.save_approval_request.assert_awaited_once() + async def test_basic_text_response_streaming(self) -> None: workflow_agent = _build_text_workflow_agent("hello stream") server = _make_server(workflow_agent) diff --git a/python/pyrightconfig.samples.json b/python/pyrightconfig.samples.json index 3d0c80919ee..41731f9ed31 100644 --- a/python/pyrightconfig.samples.json +++ b/python/pyrightconfig.samples.json @@ -9,7 +9,14 @@ "**/05-end-to-end/**", "**/harness/**", "**/agent_with_foundry_tracing.py", - "**/azure_responses_client_with_foundry.py" + "**/azure_responses_client_with_foundry.py", + "**/monty_code_act.py", + "**/monty_code_interpreter.py", + "**/monty_code_interpreter_manual_wiring.py", + "**/11_monty_codeact/**", + "**/local_shell_with_allowlist.py", + "**/local_shell_with_environment_provider.py", + "**/client_with_local_shell.py" ], "typeCheckingMode": "basic", "reportMissingImports": "error", diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md index 78447f73e46..703cd274f03 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md @@ -38,6 +38,11 @@ To have a multi-turn conversation with the agent, include the previous response curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "How are you?", "previous_response_id": "REPLACE_WITH_PREVIOUS_RESPONSE_ID"}' ``` +## Next steps + +- [13_steering](../13_steering) — steerable conversations: queue a new turn while the previous one is still running +- [15_workflow_resilience](../15_workflow_resilience) — durable workflow recovery across host restarts + ## Deploying the Agent to Foundry To host the agent on Foundry, follow the instructions in the [Deploying the Agent to Foundry](../../README.md#deploying-the-agent-to-foundry) section of the README in the parent directory. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/.env.example b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/.env.example new file mode 100644 index 00000000000..2a38d9c9b81 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/.env.example @@ -0,0 +1,2 @@ +FOUNDRY_PROJECT_ENDPOINT="..." +AZURE_AI_MODEL_DEPLOYMENT_NAME="..." diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/Dockerfile b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/Dockerfile new file mode 100644 index 00000000000..0cc939d9b3a --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY . user_agent/ +WORKDIR /app/user_agent + +RUN if [ -f requirements.txt ]; then \ + pip install -r requirements.txt; \ + else \ + echo "No requirements.txt found"; \ + fi + +EXPOSE 8088 + +CMD ["python", "main.py"] diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/README.md new file mode 100644 index 00000000000..3fd5c5933f3 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/README.md @@ -0,0 +1,65 @@ +# What this sample demonstrates + +How to enable **steerable conversations** with `ResponsesHostServer`. + +When `steerable_conversations=True`, a client can send a new turn to the same +conversation while the previous turn is still running. Instead of receiving +HTTP 409 `conversation_locked`, the new turn is queued and the running handler +receives a cooperative cancellation signal. Once the current turn terminates, +the queued turn runs. + +## How It Works + +1. The server starts with `steerable_conversations=True`. +2. Turn 1 is sent as a foreground streaming request on a shared conversation. +3. Two seconds later, while turn 1 is still streaming, turn 2 arrives on the + same conversation. The server immediately returns `status=queued`. +4. The running handler observes `cancellation_signal.is_set()` (with + `context.client_cancelled=False`, distinguishing steering from a real cancel) + and emits `response.completed` with partial output. +5. Once turn 1 finishes the framework drains the queue and invokes the handler + again for turn 2 (with `context.is_steered_turn=True`). + +See [main.py](main.py) for the server and [client.py](client.py) for the +interactive demo. + +## Running the Sample + +**Terminal 1 — start the server:** + +```bash +uv run python main.py +``` + +**Terminal 2 — run the demo client:** + +```bash +uv run python client.py +``` + +You will see turn 1 streaming, turn 2 arriving as `status=queued`, turn 1 +cutting off, and then turn 2's answer appearing. The server terminal shows +the steering signal and handler cancellation in real time. + +You can also point the client at a deployed instance: + +```bash +uv run python client.py https://your-deployed-agent-url +``` + +## Environment Variables + +Copy `.env.example` to `.env` and fill in your values: + +``` +FOUNDRY_PROJECT_ENDPOINT=https://... +AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o +``` + +Run `az login` before starting the server. + +## Deploying the Agent to Foundry + +To host the agent on Foundry, follow the instructions in the +[Deploying the Agent to Foundry](../../README.md#deploying-the-agent-to-foundry) +section of the README in the parent directory. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/client.py b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/client.py new file mode 100644 index 00000000000..509bca29829 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/client.py @@ -0,0 +1,175 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Steerable conversations demo client. + +Demonstrates steerable_conversations by sending two concurrent turns to a +running ResponsesHostServer. Run this while main.py is running in a second +terminal to see steering in action. + +Usage +----- +Terminal 1 — start the server: + uv run python main.py + +Terminal 2 — run this client: + uv run python client.py [SERVER_URL] + +SERVER_URL defaults to http://localhost:8088. +Set it to your deployed agent URL to test against a hosted instance. + +What you will see +----------------- +Turn 1 starts streaming a long response (counting to 50). Two seconds later, +turn 2 arrives on the same conversation with a different question. Because the +server has steerable_conversations=True, turn 2 is accepted immediately with +status=queued rather than rejected with 409. The server then cancels turn 1's +handler, which emits a partial response.completed event. Once turn 1 finishes, +turn 2 runs and streams its answer. + +Watch the server terminal to see the steering signal, cancellation, and drain +logged in real time. +""" + +from __future__ import annotations + +import asyncio +import json +import os +import sys +from typing import Any + +import httpx +from dotenv import load_dotenv + +load_dotenv() + +_DEFAULT_BASE = "http://localhost:8088" +_MODEL = os.environ.get("AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o") + + +def _parse_sse(chunk: str) -> dict[str, Any] | None: + for line in chunk.splitlines(): + if line.startswith("data: "): + try: + return json.loads(line[6:]) # type: ignore[no-any-return] + except json.JSONDecodeError: + pass + return None + + +async def demo(base: str) -> None: + """Run the steering demonstration against *base* URL.""" + import uuid + + conv_id = f"demo-{uuid.uuid4().hex[:8]}" + turn1_text: list[str] = [] + turn1_terminal: str | None = None + + limits = httpx.Limits(max_keepalive_connections=5, max_connections=5) + async with httpx.AsyncClient(base_url=base, timeout=120, limits=limits) as client: + print(f"\nConnected to {base}") + print(f"Conversation ID: {conv_id}\n") + + # ------------------------------------------------------------------ + # Turn 1: ask the agent to count slowly. Stream the response in + # real-time so we can see the partial output before steering. + # ------------------------------------------------------------------ + async def stream_turn1() -> None: + nonlocal turn1_terminal + print(">>> Turn 1: 'Count from 1 to 50, one number per line.'") + async with client.stream( + "POST", + "/responses", + json={ + "model": _MODEL, + "input": "Count from 1 to 50, one number per line.", + "stream": True, + "store": True, + # "conversation" is the b8 field for a shared conversation ID. + # Both turns must carry the same value to share the same + # multi-turn steerable task. + "conversation": conv_id, + }, + ) as resp: + assert resp.status_code == 200, f"Turn 1 failed: {resp.status_code}" + async for chunk in resp.aiter_text(): + event = _parse_sse(chunk) + if event is None: + continue + et = event.get("type", "") + if et == "response.output_item.delta": + delta = ( + event.get("delta", {}).get("text", "") + or event.get("delta", {}).get("content", "") + or "" + ) + if delta: + print(delta, end="", flush=True) + turn1_text.append(delta) + elif et in ("response.completed", "response.failed", "response.cancelled"): + turn1_terminal = et.split(".")[-1] + break + + # ------------------------------------------------------------------ + # Turn 2: sent 2 s later while turn 1 is still streaming. + # background=True returns status=queued immediately so we don't block. + # ------------------------------------------------------------------ + async def send_turn2() -> dict[str, Any]: + await asyncio.sleep(2) + print("\n\n>>> Turn 2 (sent while turn 1 is still running): 'What is the capital of France?'") + r2 = await client.post( + "/responses", + json={ + "model": _MODEL, + "input": "What is the capital of France?", + "background": True, + "store": True, + "conversation": conv_id, + }, + ) + assert r2.status_code == 200, f"Turn 2 failed: {r2.status_code} {r2.text}" + return r2.json() # type: ignore[no-any-return] + + t1 = asyncio.create_task(stream_turn1()) + r2 = await send_turn2() + + r2_id = r2["id"] + r2_status = r2["status"] + print(f"\n Turn 2 response ID : {r2_id}") + print(f" Turn 2 status : {r2_status}") + + if r2_status != "queued": + print( + f"\nUnexpected status {r2_status!r} — expected 'queued'.\n" + " If 'conflict': the server may not have steerable_conversations=True.\n" + " If 'in_progress': turn 1 completed before turn 2 arrived; " + "the model may be responding too quickly for this prompt." + ) + + # Wait for turn 1 to drain + await t1 + print(f"\n Turn 1 terminated : response.{turn1_terminal}") + print(f" Turn 1 text so far : {len(''.join(turn1_text))} chars") + + # Poll turn 2 until it completes, then print the agent's reply + print("\n>>> Waiting for turn 2 to complete...") + for _ in range(120): + await asyncio.sleep(0.5) + s2_resp = (await client.get(f"/responses/{r2_id}")).json() + s2 = s2_resp["status"] + print(f" status: {s2}") + if s2 in ("completed", "failed", "cancelled"): + if s2 == "completed": + output_text = "" + for item in s2_resp.get("output", []): + for part in item.get("content", []): + output_text += part.get("text", "") or part.get("content", "") + print(f"\n>>> Turn 2 answer: {output_text.strip()}") + break + else: + print("Turn 2 did not complete within the timeout.") + + +if __name__ == "__main__": + base_url = sys.argv[1] if len(sys.argv) > 1 else _DEFAULT_BASE + asyncio.run(demo(base_url)) diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/main.py new file mode 100644 index 00000000000..7422808bb16 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/main.py @@ -0,0 +1,42 @@ +# Copyright (c) Microsoft. All rights reserved. + +import os + +from agent_framework import Agent +from agent_framework.foundry import FoundryChatClient +from agent_framework_foundry_hosting import ResponsesHostServer +from azure.ai.agentserver.responses import ResponsesServerOptions +from azure.identity import DefaultAzureCredential +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + + +def main(): + client = FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=DefaultAzureCredential(), + ) + + agent = Agent( + client=client, + instructions="You are a friendly assistant. Keep your answers brief.", + default_options={"store": False}, + ) + + # steerable_conversations=True allows a client to send a new turn while + # the current one is still in progress. The new turn is queued and the + # running handler is cooperatively cancelled (via cancellation_signal) + # rather than the client receiving HTTP 409 conversation_locked. + # Once the current turn reaches a terminal event the queued turn runs. + server = ResponsesHostServer( + agent, + options=ResponsesServerOptions(steerable_conversations=True), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/requirements.txt b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/requirements.txt new file mode 100644 index 00000000000..74aee351414 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/13_steering/requirements.txt @@ -0,0 +1,2 @@ +agent-framework-foundry +agent-framework-foundry-hosting diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/.env.example b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/.env.example new file mode 100644 index 00000000000..9911d1a5a20 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/.env.example @@ -0,0 +1,18 @@ +# This sample makes no external model calls and requires no credentials. +# The variables below are optional tuning knobs for main.py (both have +# sensible defaults); demo.py sets WORKFLOW_STATE_DIR and +# WORKFLOW_STAGE_DELAY_SECONDS itself for its isolated crash-recovery run, +# overriding whatever is set here. + +# Directory for audit.jsonl and stage marker files. +# Default: a .workflow_state folder next to main.py. +# WORKFLOW_STATE_DIR="./.workflow_state" + +# Seconds each pipeline stage sleeps to simulate work. +# Default: 4 +# WORKFLOW_STAGE_DELAY_SECONDS="4" + +# The sample disables Azure Monitor's internal Statsbeat metrics when no +# Application Insights connection is configured. This avoids a local probe of +# the Azure instance metadata endpoint without disabling normal telemetry. +# APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL="true" diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/README.md new file mode 100644 index 00000000000..ddf4a21e896 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/README.md @@ -0,0 +1,199 @@ +# Durable workflow recovery + +This sample shows a long-running background request continuing after its host +process is interrupted and replaced. + +The request runs through a deterministic local pipeline: + +```text +ingest -> transform -> validate -> finalize +``` + +No model deployment, external service, credentials, or environment variables +are required. + +## Private preview wheel setup + +The private AgentServer preview wheels currently use the same package versions +as older public artifacts. Installing by package name or running the repository +workspace sync can therefore replace the preview build with the public build. + +Create an isolated environment and install the local wheel files explicitly. +The Agent Framework wheel directory must contain +`agent_framework_foundry_hosting-*.whl`. The AgentServer wheel directory must +contain the `core`, `invocations`, and `responses` wheels. The released +`agent-framework-core` package and any other Agent Framework packages are +resolved normally from the configured package index. + +PowerShell: + +```powershell +$afWheels = "C:\path\to\agent-framework-wheels" +$agentServerWheels = "C:\path\to\agent-server-wheels" + +uv venv .venv-preview +uv pip install --python .venv-preview\Scripts\python.exe ` + (Get-ChildItem "$afWheels\agent_framework_foundry_hosting-*.whl").FullName ` + (Get-ChildItem "$agentServerWheels\azure_ai_agentserver_core-*.whl").FullName ` + (Get-ChildItem "$agentServerWheels\azure_ai_agentserver_invocations-*.whl").FullName ` + (Get-ChildItem "$agentServerWheels\azure_ai_agentserver_responses-*.whl").FullName ` + httpx +``` + +macOS/Linux: + +```bash +export AF_WHEEL_DIR=/path/to/agent-framework-wheels +export AGENTSERVER_WHEEL_DIR=/path/to/agent-server-wheels + +uv venv .venv-preview +uv pip install --python .venv-preview/bin/python \ + "$AF_WHEEL_DIR"/agent_framework_foundry_hosting-*.whl \ + "$AGENTSERVER_WHEEL_DIR"/azure_ai_agentserver_core-*.whl \ + "$AGENTSERVER_WHEEL_DIR"/azure_ai_agentserver_invocations-*.whl \ + "$AGENTSERVER_WHEEL_DIR"/azure_ai_agentserver_responses-*.whl \ + httpx +``` + +Verify that the installed AgentServer build exposes the preview API: + +```powershell +.venv-preview\Scripts\python.exe -c "from azure.ai.agentserver.responses import ResponsesServerOptions; assert ResponsesServerOptions(resilient_background=True).resilient_background" +``` + +```bash +.venv-preview/bin/python -c "from azure.ai.agentserver.responses import ResponsesServerOptions; assert ResponsesServerOptions(resilient_background=True).resilient_background" +``` + +Do not run `uv sync` in this environment. When using the repository workspace +environment instead, reinstall the three local AgentServer wheels after every +sync and run commands with `uv run --no-sync`. + +## Run the demo + +From the repository's `python` directory, using the isolated environment above: + +```powershell +.venv-preview\Scripts\python.exe .\samples\04-hosting\foundry-hosted-agents\responses\15_workflow_resilience\demo.py +``` + +```bash +.venv-preview/bin/python ./samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/demo.py +``` + +Run `demo.py`, not `main.py`. `main.py` is the hosted agent server and waits for +API requests; `demo.py` starts that server and drives the complete scenario. + +## What the demo proves + +The demo: + +1. Starts one background workflow request. +2. Stops the host while `transform` is running. +3. Starts a replacement host, which recovers the existing request. +4. Stops the replacement host while `finalize` is running. +5. Starts another replacement host and waits for the original request to + complete. +6. Verifies that completed stages, intermediate progress output, and the final + response output survived both interruptions. + +The console shows only the customer-visible recovery story. Detailed server and +telemetry output is captured in an isolated diagnostic log and is printed only +if the demo fails. + +Expected output: + +```text +Durable workflow recovery demo +============================== +Pipeline: ingest -> transform -> validate -> finalize +The host will be interrupted twice while one request is running. + +[START] Starting the first host instance... +[OK] Host is ready. + +[REQUEST] Starting one background workflow request... +[OK] Request accepted: caresp_... +[OK] Initial status: in_progress + +[WORKFLOW] Waiting for the first stage to complete... +[OK] 'ingest' completed and its progress was saved. +[INTERRUPT] Stopping the host while 'transform' is running... +[INTERRUPT] Host stopped unexpectedly. + +[RECOVER] Starting a replacement host... +[OK] The existing request was recovered automatically. + +[WORKFLOW] Waiting for two more stages to complete... +[OK] 'transform' and 'validate' completed; saved progress was preserved. +[INTERRUPT] Stopping the host while 'finalize' is running... +[INTERRUPT] Host stopped unexpectedly. + +[RECOVER] Starting another replacement host... +[OK] The same request was recovered again. + +[REQUEST] Waiting for the original request to finish... + +Result +------ +[OK] The original request completed after two host interruptions. +[OK] Progress output from every completed stage was preserved. +[OK] Final output: Pipeline complete for 'run the resilient pipeline'. Stages executed: ingest, transform, validate, finalize. +[OK] Every completed stage ran exactly once: + ingest: 1 + transform: 1 + validate: 1 + finalize: 1 + +PASS: Durable progress and response output survived both interruptions. +``` + +## Application setup + +The durability configuration in `main.py` is intentionally small: + +```python +server = ResponsesHostServer( + agent, + options=ResponsesServerOptions(resilient_background=True), +) +``` + +The hosting layer manages durable workflow progress and recovers stored +background requests when a replacement host starts. The application does not +configure workflow checkpoint storage itself. + +## Idempotency + +An interrupted stage may restart from the beginning if its latest work had not +yet been saved. External side effects such as charging a payment, sending an +email, or writing to another system must use an idempotency key, upsert, or +equivalent duplicate-safe design. + +The demo uses an append-only audit ledger to detect repeated completed work and +asserts that each completed stage appears exactly once. + +## Manual server runs + +To start only the hosted agent server: + +```powershell +.venv-preview\Scripts\python.exe .\samples\04-hosting\foundry-hosted-agents\responses\15_workflow_resilience\main.py +``` + +```bash +.venv-preview/bin/python ./samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/main.py +``` + +The server then waits for Responses API requests on `http://localhost:8088`. +Use `Ctrl+C` to stop it. + +`.env.example` documents optional settings for the workflow state directory, +stage delay, and local Azure Monitor Statsbeat behavior. + +## Deploying to Foundry + +Follow the parent sample's +[Foundry deployment instructions](../../README.md#deploying-the-agent-to-foundry). +In Foundry, host replacements exercise the same durable request recovery +behavior demonstrated locally. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/demo.py b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/demo.py new file mode 100644 index 00000000000..1993e173397 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/demo.py @@ -0,0 +1,273 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Demonstrate that a long-running workflow survives host interruptions. + +The demo submits one background request to a four-stage pipeline, stops the +host twice while work is in progress, and starts a replacement host each time. +It then verifies that: + +- the original request still completes, +- completed stages are not repeated, +- interrupted work continues automatically, and +- intermediate progress and final response output are preserved. + +Server and telemetry output is written to an isolated diagnostic log instead +of the console. The temporary state is removed after a successful run. + +Usage: + .venv-preview/Scripts/python.exe demo.py # Windows + .venv-preview/bin/python demo.py # macOS/Linux + +No external services, model deployments, or credentials are required. The +private preview Agent Framework and AgentServer wheels must be installed as +described in README.md. +""" + +import asyncio +import inspect +import json +import os +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path +from typing import Any + +import httpx + +_BASE = "http://localhost:8088" +_MAIN = os.path.join(os.path.dirname(os.path.abspath(__file__)), "main.py") +_SERVER_LOG = "server.log" + +_STAGES = ("ingest", "transform", "validate", "finalize") + +# Allow the host to durably record a completed stage before interrupting the +# following stage. +_KILL_GRACE_SECONDS = 2.0 + + +def _require_private_preview() -> None: + """Fail before starting a child process when the public AgentServer build is installed.""" + from azure.ai.agentserver.responses import ResponsesServerOptions + + if "resilient_background" not in inspect.signature(ResponsesServerOptions).parameters: + raise RuntimeError( + "This interpreter does not contain the private AgentServer durability preview: " + f"{sys.executable}. Follow the README.md private preview wheel setup, then run " + "the demo with .venv-preview's Python directly. Do not use `uv run` without `--no-sync`." + ) + + +async def _wait_for_server(base_url: str, timeout: float = 30) -> bool: + """Poll /readiness until the server responds or the timeout expires.""" + deadline = time.monotonic() + timeout + async with httpx.AsyncClient() as probe: + while time.monotonic() < deadline: + try: + r = await probe.get(f"{base_url}/readiness", timeout=1.0) + if r.status_code == 200: + return True + except Exception: # noqa: BLE001 + pass + await asyncio.sleep(0.3) + return False + + +async def _wait_for_marker(markers_dir: Path, stage: str, timeout: float = 60) -> None: + """Poll the filesystem until .json appears under markers_dir.""" + marker = markers_dir / f"{stage}.json" + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if marker.exists(): + return + await asyncio.sleep(0.2) + raise TimeoutError(f"Timed out waiting for stage marker: {marker}") + + +def _start_server(state_dir: Path, stage_delay_seconds: float) -> subprocess.Popen[bytes]: + """Start main.py with isolated durable state and captured diagnostics.""" + env = dict(os.environ) + env["HOME"] = str(state_dir) + env["USERPROFILE"] = str(state_dir) + env["WORKFLOW_STATE_DIR"] = str(state_dir) + env["WORKFLOW_STAGE_DELAY_SECONDS"] = str(stage_delay_seconds) + with (state_dir / _SERVER_LOG).open("ab") as server_log: + server_log.write(b"\n--- starting host instance ---\n") + server_log.flush() + return subprocess.Popen( + [sys.executable, _MAIN], + cwd=str(state_dir), + env=env, + stdout=server_log, + stderr=subprocess.STDOUT, + ) + + +def _interrupt(server: subprocess.Popen[bytes], running_stage: str) -> None: + print(f"[INTERRUPT] Stopping the host while '{running_stage}' is running...") + server.terminate() + try: + server.wait(timeout=10) + except subprocess.TimeoutExpired: + server.kill() + server.wait(timeout=10) + print("[INTERRUPT] Host stopped unexpectedly.\n") + + +def _print_log_tail(state_dir: Path, line_count: int = 40) -> None: + log_path = state_dir / _SERVER_LOG + if not log_path.exists(): + return + lines = log_path.read_text(encoding="utf-8", errors="replace").splitlines() + print("\nLast server diagnostic lines:") + for line in lines[-line_count:]: + print(f" {line}") + + +def _read_audit(state_dir: Path) -> list[dict[str, Any]]: + audit_path = state_dir / "audit.jsonl" + if not audit_path.exists(): + return [] + with audit_path.open(encoding="utf-8") as f: + return [json.loads(line) for line in f if line.strip()] + + +async def demo() -> None: + """Run the customer-facing durability demonstration.""" + _require_private_preview() + state_dir = Path(tempfile.mkdtemp(prefix="workflow_resilience_demo_")) + markers_dir = state_dir / "markers" + stage_delay_seconds = 4.0 + succeeded = False + print("Durable workflow recovery demo") + print("==============================") + print("Pipeline: ingest -> transform -> validate -> finalize") + print("The host will be interrupted twice while one request is running.\n") + + server: subprocess.Popen[bytes] | None = None + try: + print("[START] Starting the first host instance...") + server = _start_server(state_dir, stage_delay_seconds) + if not await _wait_for_server(_BASE): + _interrupt(server, "startup") + raise RuntimeError("Server did not start within 30 seconds.") + print("[OK] Host is ready.\n") + + async with httpx.AsyncClient(base_url=_BASE, timeout=60) as client: + print("[REQUEST] Starting one background workflow request...") + r = await client.post( + "/responses", + json={ + "model": "n/a", # ignored: WorkflowAgent doesn't use runtime model options + "input": "run the resilient pipeline", + "background": True, + "store": True, + }, + ) + assert r.status_code == 200, f"POST failed: {r.status_code} {r.text}" + body: dict[str, Any] = r.json() + resp_id: str = body["id"] + print(f"[OK] Request accepted: {resp_id}") + print(f"[OK] Initial status: {body['status']}\n") + + print("[WORKFLOW] Waiting for the first stage to complete...") + await _wait_for_marker(markers_dir, "ingest") + print("[OK] 'ingest' completed and its progress was saved.") + await asyncio.sleep(_KILL_GRACE_SECONDS) + _interrupt(server, "transform") + + print("[RECOVER] Starting a replacement host...") + server = _start_server(state_dir, stage_delay_seconds) + if not await _wait_for_server(_BASE): + _interrupt(server, "recovery startup") + raise RuntimeError("Replacement host did not start within 30 seconds.") + print("[OK] The existing request was recovered automatically.\n") + + print("[WORKFLOW] Waiting for two more stages to complete...") + await _wait_for_marker(markers_dir, "validate") + print("[OK] 'transform' and 'validate' completed; saved progress was preserved.") + await asyncio.sleep(_KILL_GRACE_SECONDS) + _interrupt(server, "finalize") + + print("[RECOVER] Starting another replacement host...") + server = _start_server(state_dir, stage_delay_seconds) + if not await _wait_for_server(_BASE): + _interrupt(server, "recovery startup") + raise RuntimeError("Replacement host did not start within 30 seconds.") + print("[OK] The same request was recovered again.\n") + + print("[REQUEST] Waiting for the original request to finish...") + async with httpx.AsyncClient(base_url=_BASE, timeout=60) as client: + status = "unknown" + for _ in range(150): + await asyncio.sleep(0.5) + poll = (await client.get(f"/responses/{resp_id}")).json() + status = poll.get("status", "unknown") + if status in ("completed", "failed", "cancelled"): + break + assert status == "completed", f"Response did not complete: status={status!r}" + + output_texts = [ + "".join(part.get("text", "") or part.get("content", "") for part in item.get("content", [])) + for item in poll.get("output", []) + if item.get("type") == "message" + ] + expected_outputs = [ + "[ingest] received request: 'run the resilient pipeline'", + "[transform] normalized request: 'run the resilient pipeline'", + "[validate] validated request: 'run the resilient pipeline'", + ( + "Pipeline complete for 'run the resilient pipeline'. " + "Stages executed: ingest, transform, validate, finalize." + ), + ] + assert output_texts == expected_outputs, ( + f"Recovered response did not preserve the expected workflow outputs: {output_texts!r}" + ) + final_output = output_texts[-1] + + for stage in _STAGES: + marker = markers_dir / f"{stage}.json" + assert marker.exists(), f"Missing stage marker: {marker}" + + audit = _read_audit(state_dir) + counts = {stage: sum(1 for entry in audit if entry["stage"] == stage) for stage in _STAGES} + for stage, count in counts.items(): + assert count == 1, ( + f"Stage {stage!r} appears {count} times in audit.jsonl; expected exactly once. " + "A count > 1 means a completed stage replayed; a count of 0 means it never ran." + ) + + print("\nResult") + print("------") + print("[OK] The original request completed after two host interruptions.") + print("[OK] Progress output from every completed stage was preserved.") + print(f"[OK] Final output: {final_output}") + print("[OK] Every completed stage ran exactly once:") + for stage in _STAGES: + print(f" {stage}: {counts[stage]}") + print("\nPASS: Durable progress and response output survived both interruptions.") + succeeded = True + + except Exception: + print("\nFAIL: The durability demonstration did not complete.") + _print_log_tail(state_dir) + raise + + finally: + if server is not None and server.poll() is None: + server.terminate() + try: + server.wait(timeout=10) + except subprocess.TimeoutExpired: + server.kill() + if succeeded: + shutil.rmtree(state_dir, ignore_errors=True) + else: + print(f"\nDiagnostic state retained at: {state_dir}") + + +if __name__ == "__main__": + asyncio.run(demo()) diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/main.py new file mode 100644 index 00000000000..3ce23d7607f --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/main.py @@ -0,0 +1,161 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""A deterministic workflow used to demonstrate durable background requests. + +The local pipeline has four stages: + +``ingest -> transform -> validate -> finalize`` + +With ``resilient_background=True``, the hosting layer durably records workflow +progress. If the host stops while a request is running, a replacement host +continues the same request from its latest saved progress rather than starting +the entire workflow again. + +``demo.py`` interrupts the host twice and verifies that completed stages are +preserved, interrupted work continues, and the original response completes +with the expected output. No model deployment, external service, or credential +is required. + +Production note: a stage interrupted before its progress is saved may run +again. External side effects should therefore use idempotency keys or upsert +semantics. +""" + +import asyncio +import json +import os +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +from dotenv import load_dotenv + +# Load configuration before importing the hosting stack, whose telemetry distro +# installs its default resource-detector list during import. +load_dotenv() + +# Azure Monitor Statsbeat probes the Azure instance metadata endpoint after a +# short warmup. Disable only those internal SDK metrics when no Application +# Insights connection is configured, avoiding a noisy local-only connection +# error without disabling the sample's normal traces and metrics. +if not os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING"): + os.environ.setdefault("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL", "true") + +from agent_framework import Message, WorkflowBuilder, WorkflowContext, executor # noqa: E402 +from agent_framework_foundry_hosting import ResponsesHostServer # noqa: E402 +from azure.ai.agentserver.responses import ResponsesServerOptions # noqa: E402 +from typing_extensions import Never # noqa: E402 + +# Keep durable workflow state in built-in, serializable types. +PipelineState = dict[str, Any] + + +def _state_dir() -> Path: + """Directory where audit.jsonl and stage markers are written. + + Defaults to a folder next to this file for ad-hoc manual runs, but + ``demo.py`` always overrides ``WORKFLOW_STATE_DIR`` to an isolated + temporary directory so demo runs never touch real user state. + """ + raw = os.environ.get("WORKFLOW_STATE_DIR") + path = Path(raw) if raw else Path(__file__).parent / ".workflow_state" + (path / "markers").mkdir(parents=True, exist_ok=True) + return path + + +def _stage_delay_seconds() -> float: + """How long each stage sleeps to simulate work. See .env.example.""" + return float(os.environ.get("WORKFLOW_STAGE_DELAY_SECONDS", "4")) + + +async def _run_stage(stage_id: str) -> None: + """Simulate work for *stage_id*, then durably record that it ran. + + The delay happens before the audit entry so the demo can interrupt a stage + while it is still in progress. Only completed stage attempts are recorded. + """ + await asyncio.sleep(_stage_delay_seconds()) + + state_dir = _state_dir() + pid = os.getpid() + timestamp = datetime.now(timezone.utc).isoformat() + record = {"stage": stage_id, "pid": pid, "timestamp": timestamp} + + # Append-only so the demo can detect if completed work was repeated. + with (state_dir / "audit.jsonl").open("a", encoding="utf-8") as f: + f.write(json.dumps(record) + "\n") + + # The marker lets demo.py reliably interrupt the following stage. + (state_dir / "markers" / f"{stage_id}.json").write_text(json.dumps(record), encoding="utf-8") + + # Visible progress marker for anyone watching the server's stdout. + print(f"[{stage_id}] complete (pid={pid})", flush=True) + + +@executor(id="ingest") +async def ingest(messages: list[Message], ctx: WorkflowContext[PipelineState, str]) -> None: + """Stage 1: accept the incoming request and start the pipeline state.""" + await _run_stage("ingest") + prompt = messages[0].text if messages else "" + state: PipelineState = {"request_summary": prompt[:200], "stages_completed": ["ingest"]} + await ctx.yield_output(f"[ingest] received request: {state['request_summary']!r}") + await ctx.send_message(state) + + +@executor(id="transform") +async def transform(state: PipelineState, ctx: WorkflowContext[PipelineState, str]) -> None: + """Stage 2: pretend to normalize/transform the request.""" + await _run_stage("transform") + state["stages_completed"].append("transform") + await ctx.yield_output(f"[transform] normalized request: {state['request_summary']!r}") + await ctx.send_message(state) + + +@executor(id="validate") +async def validate(state: PipelineState, ctx: WorkflowContext[PipelineState, str]) -> None: + """Stage 3: pretend to validate the transformed request.""" + await _run_stage("validate") + state["stages_completed"].append("validate") + await ctx.yield_output(f"[validate] validated request: {state['request_summary']!r}") + await ctx.send_message(state) + + +@executor(id="finalize") +async def finalize(state: PipelineState, ctx: WorkflowContext[Never, str]) -> None: + """Stage 4 (terminal): emit the single Workflow Output summary.""" + await _run_stage("finalize") + state["stages_completed"].append("finalize") + await ctx.yield_output( + f"Pipeline complete for {state['request_summary']!r}. Stages executed: {', '.join(state['stages_completed'])}." + ) + + +def build_workflow(): + """Build the 4-stage deterministic pipeline with explicit output selection.""" + return ( + WorkflowBuilder( + start_executor=ingest, + # Only finalize's yield is the terminal Workflow Output. + output_from=[finalize], + # ingest/transform/validate yields are visible progress notes. + intermediate_output_from=[ingest, transform, validate], + ) + .add_chain([ingest, transform, validate, finalize]) + .build() + ) + + +def main() -> None: + agent = build_workflow().as_agent(name="resilient-pipeline") + + # Durable background execution is host-managed; no workflow checkpoint + # storage needs to be configured by the application. + server = ResponsesHostServer( + agent, + options=ResponsesServerOptions(resilient_background=True), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/requirements.txt b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/requirements.txt new file mode 100644 index 00000000000..a7fab076372 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/15_workflow_resilience/requirements.txt @@ -0,0 +1,11 @@ +# This sample makes no external model calls, so agent-framework-foundry +# (FoundryChatClient) is not needed -- only the hosting layer is. +# For the private durability preview, follow README.md and install the local +# Agent Framework and AgentServer wheel files explicitly instead of using this +# package-name-only requirements file. +agent-framework-foundry-hosting + +# demo.py's HTTP client. (Previously pulled in transitively via +# agent-framework-foundry -> agent-framework-openai -> openai; declared +# explicitly here since that transitive path no longer exists.) +httpx diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/.gitignore b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/.gitignore new file mode 100644 index 00000000000..f5e73ded3fa --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/.gitignore @@ -0,0 +1 @@ +.durability-response.json diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/README.md new file mode 100644 index 00000000000..407fa640568 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/README.md @@ -0,0 +1,147 @@ +# Resilient translation workflow + +This sample adapts the Foundry three-agent translation workflow into a durable +background request: + +```text +English -> French -> Spanish -> English +``` + +Each stage intentionally terminates its host once, then calls a real model +through `FoundryChatClient` after recovery. Before exiting, the stage atomically +writes and flushes a marker in the session's persistent home directory. The +replacement host sees that marker and continues instead of crashing again. +After the recovered stage completes, it removes its marker so a later workflow +request demonstrates the same three crashes again. +`ResponsesServerOptions(resilient_background=True)` keeps the original +background response and workflow checkpoints alive across all three restarts. + +`azure.yaml` defines the `azd` project and provisioned services. The source +directory's `agent.yaml` is an Agent Manifest with the required `template` +field. It is intentionally not a ContainerAgent-schema document; current `azd` +validates `agent.yaml` against the Agent Manifest schema. + +## Prerequisites + +1. Install `azd` and the Foundry agents extension declared by `azure.yaml`: + + ```powershell + azd extension install azure.ai.agents + azd extension upgrade azure.ai.agents + azd auth login + az login + ``` + +2. Obtain these private preview wheels: + + - `agent_framework_foundry_hosting-*.whl` + - `azure_ai_agentserver_core-*.whl` + - `azure_ai_agentserver_invocations-*.whl` + - `azure_ai_agentserver_responses-*.whl` + +The AgentServer preview uses the same package versions as public artifacts. +The wheels must therefore be installed by file, not resolved by package name. + +## Prepare the deployment + +From this directory: + +```powershell +python .\prepare_wheels.py ` + --agent-framework-wheels C:\path\to\agent-framework-wheels ` + --agent-server-wheels C:\path\to\agent-server-wheels +``` + +The script requires exactly one matching wheel for each package, verifies that +the Responses wheel contains the private durability API, copies the four files +into the container build context, and generates `wheelhouse/private-wheels.txt` +with their actual filenames. This rejects the same-version public Responses +wheel before deployment. The generated files are intentionally gitignored, but +`.agentignore` explicitly includes them in the Foundry code deployment ZIP. +`requirements.txt` includes the generated requirements fragment, so code +deployment does not fall back to public packages or depend on one build's wheel +filenames. + +Initialize or select an `azd` environment, then provision: + +```powershell +azd env new +azd provision +``` + +If you already have a Foundry project and model deployment, use the normal +`azd ai agent init` or environment configuration flow to target them. + +## Run locally + +```powershell +azd ai agent run +``` + +In another terminal: + +```powershell +uv run .\durability_client.py --endpoint http://localhost:8088/responses +``` + +With `WORKFLOW_CRASH_ONCE_PER_STAGE=true`, the local host exits at the start of +each stage. Restart `azd ai agent run` after each exit while the client keeps +polling. Set the variable to `false` to run without injected crashes. + +## Deploy and prove recovery + +Deploy the container: + +```powershell +azd deploy +azd ai agent show --output json +``` + +Create a hosted session with persistent filesystem state: + +```powershell +azd ai agent sessions create --output json +``` + +Copy `agent_session_id` from the result and the Responses endpoint from +`azd ai agent show`, then start one stored background response bound to that +session: + +```powershell +uv run .\durability_client.py ` + --endpoint "https://.services.ai.azure.com/api/projects//agents/resilient-translation-workflow/endpoint/protocols/openai/responses?api-version=v1" ` + --session-id "" +``` + +The first attempt at each translation stage persists a marker and exits with +code 70. Foundry automatically starts a replacement container with the same +session ID and persistent home directory. The client continues polling the +original response through all three replacements. The container logs show one +intentional termination and one recovered continuation for each stage. Each +marker is removed after its recovered stage succeeds, so another request in the +same session repeats the demonstration. + +A successful run ends with the persisted French, Spanish, and round-trip +English output followed by: + +```text +PASS: The original response completed. +``` + +The response ID and endpoint are also saved in `.durability-response.json`. If +the client itself is interrupted, resume polling with: + +```powershell +uv run .\durability_client.py ` + --endpoint "" ` + --session-id "" ` + --response-id "" +``` + +## Durability boundary + +Workflow progress is saved between executor supersteps. If a host stops during +a model call, that current stage may run again; completed prior stages are +restored. Model translation is side-effect free. Production executors that +write to external systems must use idempotency keys, upserts, or an equivalent +duplicate-safe design. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/azure.yaml b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/azure.yaml new file mode 100644 index 00000000000..6b01c708b90 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/azure.yaml @@ -0,0 +1,52 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +requiredVersions: + extensions: + azure.ai.agents: '>=1.0.0-beta.4' +name: resilient-translation-workflow +services: + ai-project: + host: azure.ai.project + deployments: + - name: gpt-5.4-mini + model: + format: OpenAI + name: gpt-5.4-mini + version: '2026-03-17' + sku: + name: GlobalStandard + capacity: 10 + resilient-translation-workflow: + host: azure.ai.agent + metadata: + tags: + - Agent Framework + - AI Agent Hosting + - Azure AI AgentServer + - Responses Protocol + - Durable Workflows + project: src/resilient-translation-workflow + language: python + codeConfiguration: + runtime: python_3_13 + entryPoint: main.py + uses: + - ai-project + kind: hosted + name: resilient-translation-workflow + displayName: Resilient Translation Workflow + description: A durable English to French to Spanish to English workflow. + protocols: + - protocol: responses + version: 2.0.0 + environmentVariables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME} + - name: WORKFLOW_CRASH_ONCE_PER_STAGE + value: "true" + container: + resources: + cpu: '0.5' + memory: 1Gi +infra: + provider: microsoft.foundry diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/durability_client.py b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/durability_client.py new file mode 100644 index 00000000000..4bc2e9c4eba --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/durability_client.py @@ -0,0 +1,161 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Submit and monitor one durable background response across host replacements.""" + +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "azure-identity>=1.25.2", +# "httpx>=0.28.1", +# ] +# /// + +from __future__ import annotations + +import argparse +import asyncio +import json +from pathlib import Path +from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit + +import httpx +from azure.identity.aio import AzureCliCredential + +TERMINAL_STATUSES = {"completed", "failed", "cancelled", "incomplete"} + + +def _is_local_endpoint(endpoint: str) -> bool: + return urlsplit(endpoint).hostname in {"localhost", "127.0.0.1"} + + +def _response_url(endpoint: str, response_id: str | None = None, session_id: str | None = None) -> str: + parsed = urlsplit(endpoint) + path = parsed.path.rstrip("/") + if response_id: + path = f"{path}/{response_id}" + query = dict(parse_qsl(parsed.query, keep_blank_values=True)) + if session_id: + query["agent_session_id"] = session_id + return urlunsplit((parsed.scheme, parsed.netloc, path, urlencode(query), parsed.fragment)) + + +def _output_text(response: dict[str, object]) -> str: + parts: list[str] = [] + output = response.get("output") + if not isinstance(output, list): + return "" + for item in output: + if not isinstance(item, dict): + continue + content = item.get("content") + if not isinstance(content, list): + continue + for part in content: + if isinstance(part, dict) and isinstance(part.get("text"), str): + parts.append(part["text"]) + return "\n".join(parts) + + +async def _authorization_headers(endpoint: str) -> dict[str, str]: + if _is_local_endpoint(endpoint): + return {} + credential = AzureCliCredential() + try: + token = await credential.get_token("https://ai.azure.com/.default") + return {"Authorization": f"Bearer {token.token}"} + finally: + await credential.close() + + +async def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--endpoint", required=True, help="Responses endpoint printed by `azd ai agent show`.") + parser.add_argument( + "--session-id", + help="Hosted session created by `azd ai agent sessions create`; required for remote endpoints.", + ) + parser.add_argument("--response-id", help="Resume monitoring an existing response instead of creating one.") + parser.add_argument( + "--input", + default="The quick brown fox jumps over the lazy dog.", + help="English text to send when creating a response.", + ) + parser.add_argument("--poll-seconds", type=float, default=5) + parser.add_argument("--state-file", type=Path, default=Path(".durability-response.json")) + args = parser.parse_args() + + if not _is_local_endpoint(args.endpoint) and not args.session_id: + parser.error("--session-id is required for remote endpoints") + + headers = await _authorization_headers(args.endpoint) + headers.update( + { + "Content-Type": "application/json", + "x-agent-user-isolation-key": "durability-demo-user", + "x-agent-chat-isolation-key": "durability-demo-chat", + } + ) + async with httpx.AsyncClient(headers=headers, timeout=60) as client: + response_id = args.response_id + if not response_id: + result = await client.post( + _response_url(args.endpoint, session_id=args.session_id), + json={ + "model": "workflow", + "input": args.input, + "background": True, + "store": True, + "stream": False, + }, + ) + result.raise_for_status() + body = result.json() + response_id = body["id"] + args.state_file.write_text( + json.dumps( + { + "endpoint": args.endpoint, + "session_id": args.session_id, + "response_id": response_id, + }, + indent=2, + ), + encoding="utf-8", + ) + if args.session_id: + print(f"Hosted session: {args.session_id}") + print(f"Created durable background response: {response_id}") + print(f"Recovery state saved to: {args.state_file.resolve()}") + print("Redeploy or replace the host while this client continues polling.\n") + + last_status: str | None = None + while True: + try: + result = await client.get(_response_url(args.endpoint, response_id, args.session_id)) + result.raise_for_status() + except (httpx.HTTPError, httpx.TimeoutException) as exc: + print(f"Host temporarily unavailable; retrying: {exc}") + await asyncio.sleep(args.poll_seconds) + continue + + body = result.json() + status = body.get("status") + if status != last_status: + print(f"Response status: {status}") + last_status = status if isinstance(status, str) else None + + if status in TERMINAL_STATUSES: + text = _output_text(body) + if text: + print("\nPersisted response output\n-------------------------") + print(text) + if status != "completed": + raise RuntimeError(json.dumps(body, indent=2)) + print("\nPASS: The original response completed.") + return + + await asyncio.sleep(args.poll_seconds) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/prepare_wheels.py b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/prepare_wheels.py new file mode 100644 index 00000000000..b9cb6d07db9 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/prepare_wheels.py @@ -0,0 +1,91 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Copy and validate the private preview wheels used by the deployment.""" + +from __future__ import annotations + +import argparse +import shutil +import zipfile +from pathlib import Path + +EXPECTED_WHEELS = ( + "agent_framework_foundry_hosting-*.whl", + "azure_ai_agentserver_core-*.whl", + "azure_ai_agentserver_invocations-*.whl", + "azure_ai_agentserver_responses-*.whl", +) +GENERATED_REQUIREMENTS = "private-wheels.txt" + + +def _find_one(pattern: str, directories: list[Path]) -> Path: + matches = [path for directory in directories for path in directory.glob(pattern)] + if len(matches) != 1: + locations = ", ".join(str(path) for path in directories) + raise RuntimeError(f"Expected exactly one {pattern!r} wheel in {locations}; found {len(matches)}.") + return matches[0] + + +def _validate_responses_preview(path: Path) -> None: + with zipfile.ZipFile(path) as wheel: + exposes_durability = any( + b"resilient_background" in wheel.read(name) for name in wheel.namelist() if name.endswith(".py") + ) + if not exposes_durability: + raise RuntimeError( + f"{path} does not expose the private durability API. " + "The public and private artifacts use the same version; select the wheel from the private build." + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--agent-framework-wheels", + type=Path, + required=True, + help="Directory containing agent_framework_foundry_hosting-*.whl.", + ) + parser.add_argument( + "--agent-server-wheels", + type=Path, + required=True, + help="Directory containing the private core, invocations, and responses AgentServer wheels.", + ) + args = parser.parse_args() + + agent_framework_directory = args.agent_framework_wheels.resolve() + agent_server_directory = args.agent_server_wheels.resolve() + source_directories = [agent_framework_directory, agent_server_directory] + for directory in source_directories: + if not directory.is_dir(): + raise RuntimeError(f"Wheel directory does not exist: {directory}") + + sources: list[Path] = [] + for index, pattern in enumerate(EXPECTED_WHEELS): + directories = [agent_framework_directory] if index == 0 else [agent_server_directory] + source = _find_one(pattern, directories) + if pattern == "azure_ai_agentserver_responses-*.whl": + _validate_responses_preview(source) + sources.append(source) + + destination = Path(__file__).parent / "src" / "resilient-translation-workflow" / "wheelhouse" + destination.mkdir(parents=True, exist_ok=True) + for old_wheel in destination.glob("*.whl"): + old_wheel.unlink() + + for source in sources: + shutil.copy2(source, destination / source.name) + print(f"Copied {source.name}") + + requirements = destination / GENERATED_REQUIREMENTS + requirements.write_text( + "".join(f"./wheelhouse/{source.name}\n" for source in sources), + encoding="utf-8", + ) + print(f"Generated {requirements.name}") + print(f"Prepared private deployment wheels in {destination}") + + +if __name__ == "__main__": + main() diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.agentignore b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.agentignore new file mode 100644 index 00000000000..44fece890f0 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.agentignore @@ -0,0 +1,9 @@ +.env +.venv/ +__pycache__/ +*.py[cod] +.durability-response.json + +!wheelhouse/ +!wheelhouse/*.whl +!wheelhouse/private-wheels.txt diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.azdignore b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.azdignore new file mode 100644 index 00000000000..c0376bbcf84 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.azdignore @@ -0,0 +1,8 @@ +.env +.venv/ +__pycache__/ +*.py[cod] +.durability-response.json + +!wheelhouse/ +!wheelhouse/*.whl diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.dockerignore new file mode 100644 index 00000000000..abfb3343318 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.dockerignore @@ -0,0 +1,5 @@ +.env +.venv/ +__pycache__/ +*.py[cod] +.durability-response.json diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.env.example b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.env.example new file mode 100644 index 00000000000..5d3423c5135 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/.env.example @@ -0,0 +1,3 @@ +FOUNDRY_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/ +AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-5.4-mini +WORKFLOW_CRASH_ONCE_PER_STAGE=true diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/Dockerfile b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/Dockerfile new file mode 100644 index 00000000000..8bf41301e0f --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.13-slim + +WORKDIR /app + +COPY . user_agent/ +WORKDIR /app/user_agent + +# requirements.txt references the private wheels by file so both container and +# Foundry code deployments install the same artifacts. +RUN python -m pip install --no-cache-dir -r requirements.txt \ + && python -c "from azure.ai.agentserver.responses import ResponsesServerOptions; assert ResponsesServerOptions(resilient_background=True).resilient_background" + +EXPOSE 8088 + +CMD ["python", "main.py"] diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/agent.yaml b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/agent.yaml new file mode 100644 index 00000000000..323fa579b90 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/agent.yaml @@ -0,0 +1,19 @@ +name: resilient-translation-workflow +description: > + A durable Agent Framework workflow that translates English to French to + Spanish and back to English. +template: + name: resilient-translation-workflow + kind: hosted + protocols: + - protocol: responses + version: 2.0.0 + environment_variables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: "{{chat}}" + - name: WORKFLOW_CRASH_ONCE_PER_STAGE + value: "true" +resources: + - name: chat + kind: model + id: gpt-5.4 diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/main.py new file mode 100644 index 00000000000..58d77a5ad49 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/main.py @@ -0,0 +1,163 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""A model-backed translation workflow with durable background execution.""" + +from __future__ import annotations + +import os +from pathlib import Path +from typing import Any + +from agent_framework import Agent, Message, WorkflowBuilder, WorkflowContext, executor +from agent_framework.foundry import FoundryChatClient +from agent_framework_foundry_hosting import ResponsesHostServer +from azure.ai.agentserver.responses import ResponsesServerOptions +from azure.identity import DefaultAzureCredential +from typing_extensions import Never + +TranslationState = dict[str, Any] +_agents: dict[str, Agent] = {} + + +def _crash_marker(stage: str) -> Path: + return Path.home() / ".workflow-resilience-crashes" / f"{stage}.crashed" + + +def _crash_once(stage: str) -> None: + """Terminate the host once for this stage within the persistent session home.""" + if os.getenv("WORKFLOW_CRASH_ONCE_PER_STAGE", "true").lower() not in {"1", "true", "yes"}: + return + + marker = _crash_marker(stage) + marker.parent.mkdir(parents=True, exist_ok=True) + try: + descriptor = os.open(marker, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) + except FileExistsError: + print(f"[{stage}] crash marker found; continuing recovered stage", flush=True) + return + + with os.fdopen(descriptor, "w", encoding="utf-8") as marker_file: + marker_file.write(f"Intentional crash for {stage}\n") + marker_file.flush() + os.fsync(marker_file.fileno()) + + print(f"[{stage}] crash marker persisted; terminating host intentionally", flush=True) + os._exit(70) + + +def _clear_crash_marker(stage: str) -> None: + _crash_marker(stage).unlink(missing_ok=True) + + +async def _translate(stage: str, text: str) -> str: + response = await _agents[stage].run(text) + return response.text.strip() + + +@executor(id="english-to-french") +async def english_to_french( + messages: list[Message], + ctx: WorkflowContext[TranslationState, str], +) -> None: + source = messages[0].text if messages else "" + _crash_once("english-to-french") + french = await _translate("english-to-french", source) + state: TranslationState = {"source": source, "french": french} + await ctx.yield_output(f"[French]\n{french}") + await ctx.send_message(state) + _clear_crash_marker("english-to-french") + + +@executor(id="french-to-spanish") +async def french_to_spanish( + state: TranslationState, + ctx: WorkflowContext[TranslationState, str], +) -> None: + _crash_once("french-to-spanish") + spanish = await _translate("french-to-spanish", state["french"]) + state["spanish"] = spanish + await ctx.yield_output(f"[Spanish]\n{spanish}") + await ctx.send_message(state) + _clear_crash_marker("french-to-spanish") + + +@executor(id="spanish-to-english") +async def spanish_to_english( + state: TranslationState, + ctx: WorkflowContext[Never, str], +) -> None: + _crash_once("spanish-to-english") + english = await _translate("spanish-to-english", state["spanish"]) + await ctx.yield_output( + "\n".join(( + f"[Original English]\n{state['source']}", + f"[French]\n{state['french']}", + f"[Spanish]\n{state['spanish']}", + f"[Round-trip English]\n{english}", + )) + ) + _clear_crash_marker("spanish-to-english") + + +def _create_agent(client: FoundryChatClient, *, name: str, instructions: str) -> Agent: + return Agent(client=client, name=name, instructions=instructions) + + +def build_workflow() -> Any: + client = FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=DefaultAzureCredential(), + ) + _agents.update({ + "english-to-french": _create_agent( + client, + name="english-to-french", + instructions=( + "Translate the user's English text into French. " + "Return only the translation, without explanations or labels." + ), + ), + "french-to-spanish": _create_agent( + client, + name="french-to-spanish", + instructions=( + "Translate the user's French text into Spanish. " + "Return only the translation, without explanations or labels." + ), + ), + "spanish-to-english": _create_agent( + client, + name="spanish-to-english", + instructions=( + "Translate the user's Spanish text into English. " + "Return only the translation, without explanations or labels." + ), + ), + }) + + return ( + WorkflowBuilder( + start_executor=english_to_french, + output_from=[spanish_to_english], + intermediate_output_from=[english_to_french, french_to_spanish], + ) + .add_chain([english_to_french, french_to_spanish, spanish_to_english]) + .build() + ) + + +def main() -> None: + workflow_agent = build_workflow().as_agent( + name="resilient-translation-workflow", + description="Durable English to French to Spanish to English translation workflow.", + ) + server = ResponsesHostServer( + workflow_agent, + options=ResponsesServerOptions(resilient_background=True), + ) + server.run() + + +if __name__ == "__main__": + main() diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/requirements.txt b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/requirements.txt new file mode 100644 index 00000000000..d5628924cb4 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/requirements.txt @@ -0,0 +1,2 @@ +-r ./wheelhouse/private-wheels.txt +agent-framework-foundry>=1.10.1,<2 diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/wheelhouse/.gitignore b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/wheelhouse/.gitignore new file mode 100644 index 00000000000..19832e38f7a --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/16_foundry_workflow_resilience/src/resilient-translation-workflow/wheelhouse/.gitignore @@ -0,0 +1,3 @@ +*.whl +private-wheels.txt +!.gitignore diff --git a/python/uv.lock b/python/uv.lock index 1e29e68513f..878adeac2bf 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -79,15 +79,15 @@ name = "a2a-sdk" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "culsans", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, - { name = "google-api-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx-sse", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "json-rpc", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "culsans", marker = "python_full_version < '3.13'" }, + { name = "google-api-core" }, + { name = "googleapis-common-protos" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "json-rpc" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/7e/8ac10bbf8b15b16574355f39b17dbdf617a282c27b41c7ff2116e30336df/a2a_sdk-1.1.0.tar.gz", hash = "sha256:e8102dad1b36709dbdc3d19319e38e6dfa3b3a79c30416030eb2d482576be204", size = 375726, upload-time = "2026-05-29T09:34:43.015Z" } wheels = [ @@ -108,7 +108,7 @@ name = "ag-ui-protocol" version = "0.1.19" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/10/4ad299267a7d04b89935aa99eef62979758fcf95aee9f8bb5d70c35b1be1/ag_ui_protocol-0.1.19.tar.gz", hash = "sha256:43c27f60d41712dcad0e9e0a203cbdf1c8e248b22417374c5c68321c448af4ea", size = 10720, upload-time = "2026-06-02T17:26:15.627Z" } wheels = [ @@ -120,34 +120,34 @@ name = "agent-framework" version = "1.11.0" source = { virtual = "." } dependencies = [ - { name = "agent-framework-core", extra = ["all"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core", extra = ["all"] }, ] [package.dev-dependencies] dev = [ - { name = "flit", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mypy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "poethepoet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "prek", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyrefly", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyright", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest-asyncio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest-cov", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest-retry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest-timeout", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest-xdist", extra = ["psutil"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ruff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ty", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "zuban", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "flit" }, + { name = "mypy" }, + { name = "opentelemetry-sdk" }, + { name = "poethepoet" }, + { name = "prek" }, + { name = "pyrefly" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-retry" }, + { name = "pytest-timeout" }, + { name = "pytest-xdist", extra = ["psutil"] }, + { name = "rich" }, + { name = "ruff" }, + { name = "tomli" }, + { name = "ty" }, + { name = "uv" }, + { name = "zuban" }, ] test = [ - { name = "azure-monitor-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-monitor-opentelemetry" }, + { name = "mcp", extra = ["ws"] }, ] [package.metadata] @@ -185,8 +185,8 @@ name = "agent-framework-a2a" version = "1.0.0b260709" source = { editable = "packages/a2a" } dependencies = [ - { name = "a2a-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "a2a-sdk" }, + { name = "agent-framework-core" }, ] [package.metadata] @@ -200,17 +200,17 @@ name = "agent-framework-ag-ui" version = "1.0.0rc8" source = { editable = "packages/ag-ui" } dependencies = [ - { name = "ag-ui-protocol", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sse-starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "ag-ui-protocol" }, + { name = "agent-framework-core" }, + { name = "fastapi" }, + { name = "sse-starlette" }, + { name = "uvicorn", extra = ["standard"] }, ] [package.optional-dependencies] dev = [ - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "httpx" }, + { name = "pytest" }, ] [package.metadata] @@ -230,8 +230,8 @@ name = "agent-framework-anthropic" version = "1.0.0b260709" source = { editable = "packages/anthropic" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "anthropic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "anthropic" }, ] [package.metadata] @@ -245,8 +245,8 @@ name = "agent-framework-azure-ai-search" version = "1.0.0b260709" source = { editable = "packages/azure-ai-search" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-search-documents", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "azure-search-documents" }, ] [package.metadata] @@ -260,11 +260,11 @@ name = "agent-framework-azure-contentunderstanding" version = "1.0.0a260618" source = { editable = "packages/azure-contentunderstanding" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-foundry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-contentunderstanding", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "filetype", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "agent-framework-foundry" }, + { name = "aiohttp" }, + { name = "azure-ai-contentunderstanding" }, + { name = "filetype" }, ] [package.metadata] @@ -281,8 +281,8 @@ name = "agent-framework-azure-cosmos" version = "1.0.0b260521" source = { editable = "packages/azure-cosmos" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-cosmos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "azure-cosmos" }, ] [package.metadata] @@ -296,10 +296,10 @@ name = "agent-framework-azurefunctions" version = "1.0.0b260709" source = { editable = "packages/azurefunctions" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-durabletask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-functions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-functions-durable", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "agent-framework-durabletask" }, + { name = "azure-functions" }, + { name = "azure-functions-durable" }, ] [package.metadata] @@ -315,9 +315,9 @@ name = "agent-framework-bedrock" version = "1.0.0b260709" source = { editable = "packages/bedrock" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "boto3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "botocore", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "boto3" }, + { name = "botocore" }, ] [package.metadata] @@ -332,8 +332,8 @@ name = "agent-framework-chatkit" version = "1.0.0b260528" source = { editable = "packages/chatkit" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai-chatkit", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "openai-chatkit" }, ] [package.metadata] @@ -347,8 +347,8 @@ name = "agent-framework-claude" version = "1.0.0b260709" source = { editable = "packages/claude" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "claude-agent-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "claude-agent-sdk" }, ] [package.metadata] @@ -362,8 +362,8 @@ name = "agent-framework-copilotstudio" version = "1.0.0b260709" source = { editable = "packages/copilotstudio" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "microsoft-agents-copilotstudio-client", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "microsoft-agents-copilotstudio-client" }, ] [package.metadata] @@ -377,46 +377,46 @@ name = "agent-framework-core" version = "1.11.0" source = { editable = "packages/core" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-extensions" }, ] [package.optional-dependencies] all = [ - { name = "agent-framework-a2a", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-ag-ui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-anthropic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-azure-ai-search", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-azure-cosmos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-azurefunctions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-bedrock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-chatkit", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-claude", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-copilotstudio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-declarative", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-devui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-durabletask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-foundry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-foundry-local", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-github-copilot", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "agent-framework-a2a" }, + { name = "agent-framework-ag-ui" }, + { name = "agent-framework-anthropic" }, + { name = "agent-framework-azure-ai-search" }, + { name = "agent-framework-azure-cosmos" }, + { name = "agent-framework-azurefunctions" }, + { name = "agent-framework-bedrock" }, + { name = "agent-framework-chatkit" }, + { name = "agent-framework-claude" }, + { name = "agent-framework-copilotstudio" }, + { name = "agent-framework-declarative" }, + { name = "agent-framework-devui" }, + { name = "agent-framework-durabletask" }, + { name = "agent-framework-foundry" }, + { name = "agent-framework-foundry-local" }, + { name = "agent-framework-github-copilot", marker = "python_full_version >= '3.11'" }, { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, - { name = "agent-framework-lab", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-mem0", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-ollama", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-orchestrations", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-purview", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-lab" }, + { name = "agent-framework-mem0" }, + { name = "agent-framework-ollama" }, + { name = "agent-framework-openai" }, + { name = "agent-framework-orchestrations" }, + { name = "agent-framework-purview" }, + { name = "agent-framework-redis" }, + { name = "mcp", extra = ["ws"] }, ] [package.dev-dependencies] dev = [ - { name = "agent-framework-azure-contentunderstanding", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-tools", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-agentserver-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-azure-contentunderstanding" }, + { name = "agent-framework-tools" }, + { name = "azure-ai-agentserver-core" }, ] [package.metadata] @@ -465,15 +465,15 @@ name = "agent-framework-declarative" version = "1.0.0rc2" source = { editable = "packages/declarative" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "powerfx", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "httpx" }, + { name = "powerfx", marker = "python_full_version < '3.14'" }, + { name = "pyyaml" }, ] [package.dev-dependencies] dev = [ - { name = "types-pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "types-pyyaml" }, ] [package.metadata] @@ -492,22 +492,22 @@ name = "agent-framework-devui" version = "1.0.0b260709" source = { editable = "packages/devui" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "fastapi" }, + { name = "openai" }, + { name = "opentelemetry-sdk" }, + { name = "uvicorn", extra = ["standard"] }, ] [package.optional-dependencies] all = [ - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "watchdog", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pytest" }, + { name = "watchdog" }, ] dev = [ - { name = "agent-framework-orchestrations", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "watchdog", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-orchestrations" }, + { name = "pytest" }, + { name = "watchdog" }, ] [package.metadata] @@ -530,15 +530,15 @@ name = "agent-framework-durabletask" version = "1.0.0b260709" source = { editable = "packages/durabletask" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "durabletask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "durabletask-azuremanaged", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "durabletask" }, + { name = "durabletask-azuremanaged" }, + { name = "python-dateutil" }, ] [package.dev-dependencies] dev = [ - { name = "types-python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "types-python-dateutil" }, ] [package.metadata] @@ -557,11 +557,11 @@ name = "agent-framework-foundry" version = "1.10.1" source = { editable = "packages/foundry" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-inference", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-projects", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "agent-framework-openai" }, + { name = "aiohttp" }, + { name = "azure-ai-inference" }, + { name = "azure-ai-projects" }, ] [package.metadata] @@ -578,12 +578,12 @@ name = "agent-framework-foundry-hosting" version = "1.0.0a260709" source = { editable = "packages/foundry_hosting" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-agentserver-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-agentserver-invocations", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-agentserver-responses", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, + { name = "httpx" }, + { name = "mcp", extra = ["ws"] }, ] [package.metadata] @@ -601,9 +601,9 @@ name = "agent-framework-foundry-local" version = "1.0.0b260521" source = { editable = "packages/foundry_local" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "foundry-local-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "agent-framework-openai" }, + { name = "foundry-local-sdk" }, ] [package.metadata] @@ -618,8 +618,8 @@ name = "agent-framework-gemini" version = "1.0.0a260709" source = { editable = "packages/gemini" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "google-genai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "google-genai" }, ] [package.metadata] @@ -633,8 +633,8 @@ name = "agent-framework-github-copilot" version = "1.0.0rc3" source = { editable = "packages/github_copilot" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "github-copilot-sdk", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "agent-framework-core" }, + { name = "github-copilot-sdk", marker = "python_full_version >= '3.11'" }, ] [package.metadata] @@ -648,7 +648,7 @@ name = "agent-framework-hosting" version = "1.0.0a260709" source = { editable = "packages/hosting" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, ] [package.metadata] @@ -659,15 +659,15 @@ name = "agent-framework-hosting-responses" version = "1.0.0a260709" source = { editable = "packages/hosting-responses" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-hosting", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "agent-framework-hosting" }, + { name = "openai" }, ] [package.dev-dependencies] test = [ - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "fastapi" }, + { name = "httpx" }, ] [package.metadata] @@ -688,10 +688,10 @@ name = "agent-framework-hyperlight" version = "1.0.0b260709" source = { editable = "packages/hyperlight" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "hyperlight-sandbox", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "hyperlight-sandbox" }, { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, - { name = "hyperlight-sandbox-python-guest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "hyperlight-sandbox-python-guest" }, ] [package.metadata] @@ -707,49 +707,49 @@ name = "agent-framework-lab" version = "1.0.0b260709" source = { editable = "packages/lab" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, ] [package.optional-dependencies] gaia = [ - { name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "orjson", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyarrow", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "huggingface-hub" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "orjson" }, + { name = "pyarrow" }, + { name = "pydantic" }, + { name = "tqdm" }, ] lightning = [ - { name = "agentlightning", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agentlightning" }, ] math = [ - { name = "sympy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "sympy" }, ] tau2 = [ - { name = "loguru", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tiktoken", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "loguru" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pydantic" }, + { name = "tiktoken" }, ] [package.dev-dependencies] dev = [ - { name = "mypy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "poethepoet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "prek", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyright", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ruff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli-w", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "mypy" }, + { name = "poethepoet" }, + { name = "prek" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "rich" }, + { name = "ruff" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "uv" }, ] tau2 = [ - { name = "tau2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "tau2" }, ] [package.metadata] @@ -791,8 +791,8 @@ name = "agent-framework-mem0" version = "1.0.0b260709" source = { editable = "packages/mem0" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mem0ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "mem0ai" }, ] [package.metadata] @@ -806,8 +806,8 @@ name = "agent-framework-mistral" version = "1.0.0a260709" source = { editable = "packages/mistral" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mistralai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "mistralai" }, ] [package.metadata] @@ -821,8 +821,8 @@ name = "agent-framework-monty" version = "1.0.0a260709" source = { editable = "packages/monty" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic-monty", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "pydantic-monty" }, ] [package.metadata] @@ -836,8 +836,8 @@ name = "agent-framework-ollama" version = "1.0.0b260709" source = { editable = "packages/ollama" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ollama", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "ollama" }, ] [package.metadata] @@ -851,8 +851,8 @@ name = "agent-framework-openai" version = "1.10.1" source = { editable = "packages/openai" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "openai" }, ] [package.metadata] @@ -866,7 +866,7 @@ name = "agent-framework-orchestrations" version = "1.0.0" source = { editable = "packages/orchestrations" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, ] [package.metadata] @@ -877,9 +877,9 @@ name = "agent-framework-purview" version = "1.0.0b260709" source = { editable = "packages/purview" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "azure-core" }, + { name = "httpx" }, ] [package.metadata] @@ -894,12 +894,12 @@ name = "agent-framework-redis" version = "1.0.0b260521" source = { editable = "packages/redis" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "redisvl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "redis" }, + { name = "redisvl" }, ] [package.metadata] @@ -915,8 +915,8 @@ name = "agent-framework-tools" version = "1.0.0a260630" source = { editable = "packages/tools" } dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-core" }, + { name = "psutil" }, ] [package.metadata] @@ -930,26 +930,26 @@ name = "agentlightning" version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "agentops", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "aiologic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "flask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "gpustat", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "graphviz", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "gunicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "litellm", extra = ["proxy"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "portpicker", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "setproctitle", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn-worker", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agentops" }, + { name = "aiohttp" }, + { name = "aiologic" }, + { name = "fastapi" }, + { name = "flask" }, + { name = "gpustat" }, + { name = "graphviz" }, + { name = "gunicorn" }, + { name = "litellm", extra = ["proxy"] }, + { name = "openai" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp" }, + { name = "opentelemetry-sdk" }, + { name = "portpicker" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "rich" }, + { name = "setproctitle" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "uvicorn-worker" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b2/8f/1bed06f70d52ba4b2ed698605fa955a82ee5aaec3addee5a21e3fd7cd0cb/agentlightning-0.3.0.tar.gz", hash = "sha256:35cd702bce54ff7c8c097d8e73aaf688c6649e4ee81e6e5e0379000465b75d43", size = 1345454, upload-time = "2025-12-24T01:49:31.24Z" } wheels = [ @@ -961,20 +961,20 @@ name = "agentops" version = "0.4.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ordered-set", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "termcolor", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohttp" }, + { name = "httpx" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-sdk" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "ordered-set" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "termcolor" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0a/c4/023fe976169c57b1edd71f4c08d6dedaf66814f5b25ecf59b3a8540311ab/agentops-0.4.21.tar.gz", hash = "sha256:47759c6dfd6ea58bad2f7764257e4778cb2e34ae180cef642f60f56adced6510", size = 430861, upload-time = "2025-08-29T06:36:55.323Z" } wheels = [ @@ -995,15 +995,15 @@ name = "aiohttp" version = "3.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohappyeyeballs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "aiosignal", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "async-timeout", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "frozenlist", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "propcache", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, - { name = "yarl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "yarl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } wheels = [ @@ -1128,9 +1128,9 @@ name = "aiologic" version = "0.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f1/7a/d51f2fde1e8ae8a83431f8e97b7a71e9358cdb1d4d2ce6be387fa44d68de/aiologic-0.17.1.tar.gz", hash = "sha256:2e1b93b9e88ced318c2a63ad7b382688f40cbfe40e3d42258d49dc9c5aea179d", size = 252354, upload-time = "2026-06-27T20:41:33.25Z" } wheels = [ @@ -1142,8 +1142,8 @@ name = "aiosignal" version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "frozenlist", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ @@ -1182,14 +1182,14 @@ name = "anthropic" version = "0.116.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "distro", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "docstring-parser", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jiter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/a2/d31f14e28d49bae983a3634e38dfb4b31c50110b5e403596c5c6a20b23f8/anthropic-0.116.0.tar.gz", hash = "sha256:5fc248fbb9fe03ef686f8a774f81586bca31a043260aab88b387ea3660f4a396", size = 949149, upload-time = "2026-07-02T19:08:10.534Z" } wheels = [ @@ -1201,9 +1201,9 @@ name = "anyio" version = "4.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } wheels = [ @@ -1224,7 +1224,7 @@ name = "apscheduler" version = "3.11.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "tzlocal", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "tzlocal" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8c/6b/eeff360196bb20b312c9e762a820fd1b2c6d809466c755ef57863478e454/apscheduler-3.11.3.tar.gz", hash = "sha256:cd2fcc9330039a81a5893472ad49facf23a6d5604cbe1d918c835c6de7834d5a", size = 110312, upload-time = "2026-06-28T19:39:22.493Z" } wheels = [ @@ -1236,7 +1236,7 @@ name = "asgiref" version = "3.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } wheels = [ @@ -1316,11 +1316,12 @@ name = "azure-ai-agentserver-core" version = "2.0.0b7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "hypercorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "microsoft-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "hypercorn" }, + { name = "microsoft-opentelemetry" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/6c/5e3a796274e70e899eac739bc4e81e7645de6e5146fb18b1a5b3d79297e5/azure_ai_agentserver_core-2.0.0b7.tar.gz", hash = "sha256:272265f7ab6dcda3cb518a5028394b6684992e0abde9cfc2dfc2e851a289eab7", size = 52702, upload-time = "2026-06-28T14:29:52.329Z" } wheels = [ @@ -1332,7 +1333,7 @@ name = "azure-ai-agentserver-invocations" version = "1.0.0b6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-ai-agentserver-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-ai-agentserver-core" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/f4/c4ff1399795dd92fd56290ac3a1014817125e7fd44eb5a205fce043f0b46/azure_ai_agentserver_invocations-1.0.0b6.tar.gz", hash = "sha256:b8c04aa71dc42491f75443c5123d7ecf9c40318e35a05bb056e9786e98585fbd", size = 60512, upload-time = "2026-06-28T14:52:25.808Z" } wheels = [ @@ -1344,10 +1345,10 @@ name = "azure-ai-agentserver-responses" version = "1.0.0b8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-agentserver-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/1f/7e7563705100f2d21c952a44d5a2e93a8193cc0a6013941bac9f52ad8874/azure_ai_agentserver_responses-1.0.0b8.tar.gz", hash = "sha256:bc0365fd70b7dabf9c9394dac5bbab08f772b59f865319d401cfde317b6832ef", size = 450099, upload-time = "2026-06-28T14:52:32.155Z" } wheels = [ @@ -1359,9 +1360,9 @@ name = "azure-ai-contentunderstanding" version = "1.2.0b2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/6c/f9af836a30b5299b10304d6b5ec645f8dbb1857429fa0191e41f86825d70/azure_ai_contentunderstanding-1.2.0b2.tar.gz", hash = "sha256:0ccef3c8087759ca788aabcc9af7b22cd8ada2df0236bf63563f4974c2d8cfcd", size = 265922, upload-time = "2026-06-11T02:24:56.951Z" } wheels = [ @@ -1373,9 +1374,9 @@ name = "azure-ai-inference" version = "1.0.0b9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4e/6a/ed85592e5c64e08c291992f58b1a94dab6869f28fb0f40fd753dced73ba6/azure_ai_inference-1.0.0b9.tar.gz", hash = "sha256:1feb496bd84b01ee2691befc04358fa25d7c344d8288e99364438859ad7cd5a4", size = 182408, upload-time = "2025-02-15T00:37:28.464Z" } wheels = [ @@ -1387,12 +1388,12 @@ name = "azure-ai-projects" version = "2.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-storage-blob", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "azure-identity" }, + { name = "azure-storage-blob" }, + { name = "isodate" }, + { name = "openai" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/24342aea74fe75b0a8378b6eff665b9c1cb63f855c1a96f70a0095e474a2/azure_ai_projects-2.2.0.tar.gz", hash = "sha256:58ee31bb031cfb004051145c545294bb0d32de679c670c312ef384845bd72cef", size = 668496, upload-time = "2026-05-30T00:20:59.099Z" } wheels = [ @@ -1404,8 +1405,8 @@ name = "azure-core" version = "1.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "requests" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a6/f3/b416179e408990df5db0d516283022dde0f5d0111d98c1a848e41853e81c/azure_core-1.41.0.tar.gz", hash = "sha256:f46ff5dfcd230f25cf1c19e8a34b8dc08a337b2503e268bb600a16c00db8ad5a", size = 381042, upload-time = "2026-05-07T23:30:54.302Z" } wheels = [ @@ -1417,8 +1418,8 @@ name = "azure-core-tracing-opentelemetry" version = "1.0.0b13" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "opentelemetry-api" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/ab/a937e4af8afec9d437d55252f2a3a4419fc3fc7d5e5d54022622bd11b2b6/azure_core_tracing_opentelemetry-1.0.0b13.tar.gz", hash = "sha256:6cb2f8dfd5dee6c11843db0205fc92e2434e1a272c169c953afe92483aafc7eb", size = 25832, upload-time = "2026-05-01T00:59:57.941Z" } wheels = [ @@ -1430,8 +1431,8 @@ name = "azure-cosmos" version = "4.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fe/2a/0f2bba256e56626ba2cec97ab81dd002ff47ead1329767760b619afd927a/azure_cosmos-4.16.1.tar.gz", hash = "sha256:fa15d13702b470265a67e2dd9c0794021e6b776856dac6c223dcacc4d8e1d8d1", size = 2377651, upload-time = "2026-06-02T01:08:07.656Z" } wheels = [ @@ -1443,7 +1444,7 @@ name = "azure-functions" version = "1.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "werkzeug", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "werkzeug" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/be/5535830e0658e9668093941b3c33b0ea03eceadbf6bd6b7870aa37ef071a/azure_functions-1.24.0.tar.gz", hash = "sha256:18ea1607c7a7268b7a1e1bd0cc28c5cc57a9db6baaacddb39ba0e9f865728187", size = 134495, upload-time = "2025-10-06T19:08:08.612Z" } wheels = [ @@ -1455,13 +1456,13 @@ name = "azure-functions-durable" version = "1.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-functions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "furl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohttp" }, + { name = "azure-functions" }, + { name = "furl" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "python-dateutil" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/40/a5aaa966fd4b1220c03fbde019ac532bcb3db619defae5dbf1eb4476b07c/azure_functions_durable-1.6.0.tar.gz", hash = "sha256:30feb3968aed71fc481e62b88fbaea5c89e9e8dacd703e0a1d5b86aa12ad8c92", size = 201246, upload-time = "2026-07-09T19:02:19.426Z" } wheels = [ @@ -1473,11 +1474,11 @@ name = "azure-identity" version = "1.25.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "msal", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "msal-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "cryptography" }, + { name = "msal" }, + { name = "msal-extensions" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/0e/3a63efb48aa4a5ae2cfca61ee152fbcb668092134d3eb8bfda472dd5c617/azure_identity-1.25.3.tar.gz", hash = "sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6", size = 286304, upload-time = "2026-03-13T01:12:20.892Z" } wheels = [ @@ -1489,19 +1490,19 @@ name = "azure-monitor-opentelemetry" version = "1.8.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-core-tracing-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-monitor-opentelemetry-exporter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-django", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-flask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-logging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-psycopg2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-urllib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-resource-detector-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "azure-core-tracing-opentelemetry" }, + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "opentelemetry-instrumentation-django" }, + { name = "opentelemetry-instrumentation-fastapi" }, + { name = "opentelemetry-instrumentation-flask" }, + { name = "opentelemetry-instrumentation-logging" }, + { name = "opentelemetry-instrumentation-psycopg2" }, + { name = "opentelemetry-instrumentation-requests" }, + { name = "opentelemetry-instrumentation-urllib" }, + { name = "opentelemetry-instrumentation-urllib3" }, + { name = "opentelemetry-resource-detector-azure" }, + { name = "opentelemetry-sdk" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/9f/75f517dd019ebc1a6476187c7380ea440f040b7c933c7a6b7159260e2c37/azure_monitor_opentelemetry-1.8.9.tar.gz", hash = "sha256:9cda4481c52e02cb3e8e11a34d2491fb07000ed32ee655d75de0747e7d24fea7", size = 80026, upload-time = "2026-07-01T17:48:58.369Z" } wheels = [ @@ -1513,12 +1514,12 @@ name = "azure-monitor-opentelemetry-exporter" version = "1.0.0b55" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "msrest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "azure-identity" }, + { name = "msrest" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "psutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/21/178fed00e9738e4c953e147d46f5f803bfeb3fde50b11efdb99ae016f05f/azure_monitor_opentelemetry_exporter-1.0.0b55.tar.gz", hash = "sha256:6701307124e5eeb1837b269ddafab0affb69020fdb842d7c5ff1a3122514595a", size = 342308, upload-time = "2026-07-01T23:59:14.53Z" } wheels = [ @@ -1530,9 +1531,9 @@ name = "azure-search-documents" version = "12.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/59/dc/bb4db263381aa5b29414e280a8535a343d877a3831a501ef39332174c85c/azure_search_documents-12.0.0.tar.gz", hash = "sha256:8e6d73ec0ed1623083435b757e34324db65d72d4e09cca061a59fc7e90c8ddbc", size = 386222, upload-time = "2026-05-01T20:28:22.269Z" } wheels = [ @@ -1544,10 +1545,10 @@ name = "azure-storage-blob" version = "12.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "cryptography" }, + { name = "isodate" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3b/48/84a820d898267f662b5c06f7cd76fdb8a9e272b44aa9376cef3ec0f6a294/azure_storage_blob-12.30.0.tar.gz", hash = "sha256:2cd74d4d5731e5eb6b8d5c5056ee115a5e88f8fdf22517b739836fda685018be", size = 618229, upload-time = "2026-06-08T11:45:35.575Z" } wheels = [ @@ -1577,8 +1578,8 @@ name = "blessed" version = "1.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jinxed", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wcwidth", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "jinxed" }, + { name = "wcwidth" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/45/ad23d265373cdb7f255d2e3ed5f122b62914bd3c425bb21bca01ef699e5c/blessed-1.47.0.tar.gz", hash = "sha256:ea13e06ae40f24710325411c5fa9b689d215cf170276cf1fda41feddaec8d3e0", size = 14035743, upload-time = "2026-07-09T00:43:10.055Z" } wheels = [ @@ -1599,9 +1600,9 @@ name = "boto3" version = "1.43.45" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "botocore", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jmespath", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "s3transfer", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/c0/bcbe0a924ed89f8982a87727cd0f88a73397fa423a60df17ae76aa013514/boto3-1.43.45.tar.gz", hash = "sha256:65e0ae541a9948ac41b706d5c7165d96ebf155812562b6518b862be027ee7347", size = 112666, upload-time = "2026-07-09T19:30:06.944Z" } wheels = [ @@ -1613,9 +1614,9 @@ name = "botocore" version = "1.43.45" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jmespath", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/fa/fb07ee4025ec257ff7b45d40411a922c86da3a4eaa98783da74404071d99/botocore-1.43.45.tar.gz", hash = "sha256:a2da80ff3caa2873769b9a32ae5f1fb18b3571ab63a981896639955611ca01e6", size = 15689068, upload-time = "2026-07-09T19:29:55.584Z" } wheels = [ @@ -1755,7 +1756,7 @@ name = "cffi" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", marker = "(implementation_name != 'PyPy' and sys_platform == 'darwin') or (implementation_name != 'PyPy' and sys_platform == 'linux') or (implementation_name != 'PyPy' and sys_platform == 'win32')" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ @@ -1924,10 +1925,10 @@ name = "claude-agent-sdk" version = "0.2.115" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "anyio" }, + { name = "mcp", extra = ["ws"] }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/c1898153ffb0c75cfc5504c18d892d3c57a080073c9ede3df7addb8fc4a0/claude_agent_sdk-0.2.115.tar.gz", hash = "sha256:230017fbba74fb3aa1a1d86b2986277c7fe26dd2e33eea865c90553d93f4e26d", size = 268644, upload-time = "2026-07-09T23:46:43.739Z" } wheels = [ @@ -1955,7 +1956,7 @@ name = "clr-loader" version = "0.2.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" } wheels = [ @@ -1981,7 +1982,7 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ @@ -2065,8 +2066,8 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } wheels = [ @@ -2243,7 +2244,7 @@ wheels = [ [package.optional-dependencies] toml = [ - { name = "tomli", marker = "(python_full_version <= '3.11' and sys_platform == 'darwin') or (python_full_version <= '3.11' and sys_platform == 'linux') or (python_full_version <= '3.11' and sys_platform == 'win32')" }, + { name = "tomli", marker = "python_full_version <= '3.11'" }, ] [[package]] @@ -2251,7 +2252,7 @@ name = "croniter" version = "6.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "python-dateutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/37/57/2e2a65aee2a70483cb28e2b7e15a072d00a523207593b44400d4717bb100/croniter-6.2.4.tar.gz", hash = "sha256:fc124f751b1b04805c2a04b061898b436b45ab2320b045e1e052ea895de65189", size = 166267, upload-time = "2026-07-10T09:52:59.955Z" } wheels = [ @@ -2263,8 +2264,8 @@ name = "cryptography" version = "48.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/12/45/870e7f4bef50e5f53b9f51d4428aee5290eedf58ba443f16b1ebb7ab8e66/cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a", size = 832989, upload-time = "2026-06-09T22:32:31.8Z" } wheels = [ @@ -2323,8 +2324,8 @@ name = "culsans" version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiologic", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "aiologic" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d9/e3/49afa1bc180e0d28008ec6bcdf82a4072d1c7a41032b5b759b60814ca4b0/culsans-0.11.0.tar.gz", hash = "sha256:0b43d0d05dce6106293d114c86e3fb4bfc63088cfe8ff08ed3fe36891447fe33", size = 107546, upload-time = "2025-12-31T23:15:38.196Z" } wheels = [ @@ -2345,8 +2346,8 @@ name = "deepdiff" version = "9.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cachebox", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "orderly-set", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cachebox" }, + { name = "orderly-set" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f9/6b/6a4a5aaf38535eb332c2856aa08e73ed7c549d0851b1215401af0a2db1a7/deepdiff-9.1.0.tar.gz", hash = "sha256:07e9e366fab4297755153c4eab795ad4ef3cbd0d51660e847f5751c6bd727687", size = 382149, upload-time = "2026-05-15T20:18:05.751Z" } wheels = [ @@ -2394,10 +2395,10 @@ name = "durabletask" version = "1.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "asyncio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "asyncio" }, + { name = "grpcio" }, + { name = "packaging" }, + { name = "protobuf" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/c4/bb2d676f12c37b12856a6b47cbf0918c58f3fe0c1d5e7867a78a2e6aaa19/durabletask-1.7.2.tar.gz", hash = "sha256:b22c234d859c8ac3ad621d324132ffb7ba546c011ae3df8e22c2bd7ca7b108d2", size = 169101, upload-time = "2026-07-09T21:39:22.91Z" } wheels = [ @@ -2409,8 +2410,8 @@ name = "durabletask-azuremanaged" version = "1.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "durabletask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-identity" }, + { name = "durabletask" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/9a/362661fb9a2637cb6960bca6131d9c224864bc50fd71ceec055cc6ba92c7/durabletask_azuremanaged-1.7.2.tar.gz", hash = "sha256:a0f257256ac9814ac202aec47ea47aa70819e671c99e5f04c9d7e415a2519e36", size = 18389, upload-time = "2026-07-09T21:54:12.37Z" } wheels = [ @@ -2422,8 +2423,8 @@ name = "email-validator" version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "dnspython", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "dnspython" }, + { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ @@ -2444,7 +2445,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2465,7 +2466,7 @@ name = "expression" version = "5.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/c7/bb061623b5815566bda69f5e9d156e38a97ebb383b8db3d2dedb26415466/expression-5.6.0.tar.gz", hash = "sha256:454f6fe138347194a43c7f878d958efe9b84b9cc770e462010c7a52e18058065", size = 59147, upload-time = "2025-02-19T09:37:37.432Z" } wheels = [ @@ -2477,11 +2478,11 @@ name = "fastapi" version = "0.138.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "annotated-doc", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/58/ff455d9fe47c60abadb34b9e05a304b1f05f5ab8000ac01565156b6f5e43/fastapi-0.138.0.tar.gz", hash = "sha256:d445a4877636ad191e7053e08c9bf98cb921a6756776848400bb773d1740c061", size = 419240, upload-time = "2026-06-20T01:18:05.259Z" } wheels = [ @@ -2493,11 +2494,11 @@ name = "fastapi-sso" version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", extra = ["email"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyjwt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "oauthlib" }, + { name = "pydantic", extra = ["email"] }, + { name = "pyjwt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/41/38/1288b0248f91822bba254ce852466857cb51217b817c3d62bcc21cfd4d9e/fastapi_sso-0.21.1.tar.gz", hash = "sha256:c6f730b075caf537efa7c0f531095cf2d963a6fa27d059b3300e5eb19b282adb", size = 18031, upload-time = "2026-06-22T15:34:56.086Z" } wheels = [ @@ -2590,12 +2591,12 @@ name = "flask" version = "3.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "blinker", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "itsdangerous", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "werkzeug", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } wheels = [ @@ -2607,11 +2608,11 @@ name = "flit" version = "3.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "flit-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pip", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli-w", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "flit-core" }, + { name = "pip" }, + { name = "requests" }, + { name = "tomli-w" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/9c/0608c91a5b6c013c63548515ae31cff6399cd9ce891bd9daee8c103da09b/flit-3.12.0.tar.gz", hash = "sha256:1c80f34dd96992e7758b40423d2809f48f640ca285d0b7821825e50745ec3740", size = 155038, upload-time = "2025-03-25T08:03:22.505Z" } wheels = [ @@ -2689,9 +2690,9 @@ name = "foundry-local-sdk" version = "0.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "tqdm" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ed/6b/76a7fe8f9f4c52cc84eaa1cd1b66acddf993496d55d6ea587bf0d0854d1c/foundry_local_sdk-0.5.1-py3-none-any.whl", hash = "sha256:f3639a3666bc3a94410004a91671338910ac2e1b8094b1587cc4db0f4a7df07e", size = 14003, upload-time = "2025-11-21T05:39:58.099Z" }, @@ -2823,9 +2824,9 @@ name = "fs" version = "2.4.16" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "setuptools", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "appdirs" }, + { name = "setuptools" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5d/a9/af5bfd5a92592c16cdae5c04f68187a309be8a146b528eac3c6e30edbad2/fs-2.4.16.tar.gz", hash = "sha256:ae97c7d51213f4b70b6a958292530289090de3a7e15841e108fbe144f069d313", size = 187441, upload-time = "2022-05-02T09:25:54.22Z" } wheels = [ @@ -2846,8 +2847,8 @@ name = "furl" version = "2.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "orderedmultidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "orderedmultidict" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/53/e4/203a76fa2ef46cdb0a618295cc115220cbb874229d4d8721068335eb87f0/furl-2.1.4.tar.gz", hash = "sha256:877657501266c929269739fb5f5980534a41abd6bbabcb367c136d1d3b2a6015", size = 57526, upload-time = "2025-03-09T05:36:21.175Z" } wheels = [ @@ -2859,8 +2860,8 @@ name = "github-copilot-sdk" version = "1.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "python-dateutil", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "pydantic" }, + { name = "python-dateutil" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/2c/3d3ecfe500c0ba7d3127737b1aa22f19ff1a19e6e86360bfdca3f02a2c09/github_copilot_sdk-1.0.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:856dfc8370f36f6efd8a2aa1dd40f82c1a6d0573d0577eaff1f6affb73ed29ad", size = 97329153, upload-time = "2026-06-18T00:56:20.653Z" }, @@ -2876,11 +2877,11 @@ name = "google-api-core" version = "2.31.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "google-auth", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "proto-plus", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c6/22/155cadf1d49272a9cf48f3168c0f3874fa13397297e611a5ea00cd093880/google_api_core-2.31.0.tar.gz", hash = "sha256:2be84ee0f584c48e6bde1b36766e23348b361fb7e55e56135fc76ce1c397f9c2", size = 176492, upload-time = "2026-06-03T14:52:17.257Z" } wheels = [ @@ -2892,8 +2893,8 @@ name = "google-auth" version = "2.55.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyasn1-modules", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cryptography" }, + { name = "pyasn1-modules" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/b9/e370d86fea3da13ec0256df30323dd26c0cb9c8c85f0c6ec42ac9df0106b/google_auth-2.55.2.tar.gz", hash = "sha256:97ae7790ff740f2bc9db60eb864a7804f4ac19f5f02c38b3d942f2fea6e9b9ae", size = 361414, upload-time = "2026-07-07T18:43:21.227Z" } wheels = [ @@ -2902,7 +2903,7 @@ wheels = [ [package.optional-dependencies] requests = [ - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "requests" }, ] [[package]] @@ -2910,16 +2911,16 @@ name = "google-genai" version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "distro", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "google-auth", extra = ["requests"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tenacity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, + { name = "distro" }, + { name = "google-auth", extra = ["requests"] }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "sniffio" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "websockets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a6/01/e7b5f3aac89200c78318ed7643401e7f5ed3131b0cd353c07483606b1e61/google_genai-2.11.0.tar.gz", hash = "sha256:4c5e524d24b145c96be327f9a7f8f04b0fe4efee0533877795e9848afed01749", size = 622366, upload-time = "2026-07-09T17:49:43.862Z" } wheels = [ @@ -2931,7 +2932,7 @@ name = "googleapis-common-protos" version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "protobuf" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035, upload-time = "2026-05-07T08:04:49.423Z" } wheels = [ @@ -2943,9 +2944,9 @@ name = "gpustat" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "blessed", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "nvidia-ml-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "blessed" }, + { name = "nvidia-ml-py" }, + { name = "psutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/c4/46d005aec3bf911cb030467d91e062a5386ff4a03e51874424cacc0f60c1/gpustat-1.1.1.tar.gz", hash = "sha256:c18d3ed5518fc16300c42d694debc70aebb3be55cae91f1db64d63b5fa8af9d8", size = 98052, upload-time = "2023-08-22T19:39:06.062Z" } @@ -2954,7 +2955,7 @@ name = "granian" version = "2.7.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "click" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b0/cc/9c752e6173df02c5e37c0df7bffd50c1341109e4b4f8e5073bfd3a72dc82/granian-2.7.9.tar.gz", hash = "sha256:096d9a3396b13826bc63d2cf424ed04daf1ea077beed361d48dca2d55cb4b527", size = 129789, upload-time = "2026-07-03T12:42:03.314Z" } wheels = [ @@ -3131,7 +3132,7 @@ name = "grpcio" version = "1.82.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/bc/656b89387d6f4ed7e0686c7b64c2ae7e554a759aa58122c8e5fb99392c32/grpcio-1.82.1.tar.gz", hash = "sha256:707b24abd90fcb1e45bcc080577da1dbf9971d107490589b9539af8e1e77b4b5", size = 13187300, upload-time = "2026-07-08T12:36:16.588Z" } wheels = [ @@ -3192,7 +3193,7 @@ name = "gunicorn" version = "23.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } wheels = [ @@ -3213,8 +3214,8 @@ name = "h2" version = "4.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "hpack", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "hyperframe", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "hpack" }, + { name = "hyperframe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } wheels = [ @@ -3267,8 +3268,8 @@ name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "certifi" }, + { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ @@ -3330,10 +3331,10 @@ name = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpcore", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ @@ -3342,7 +3343,7 @@ wheels = [ [package.optional-dependencies] http2 = [ - { name = "h2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "h2" }, ] [[package]] @@ -3359,15 +3360,15 @@ name = "huggingface-hub" version = "1.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fsspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "hf-xet", marker = "(platform_machine == 'AMD64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'darwin') or (platform_machine == 'amd64' and sys_platform == 'darwin') or (platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'AMD64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'amd64' and sys_platform == 'linux') or (platform_machine == 'arm64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'win32') or (platform_machine == 'amd64' and sys_platform == 'win32') or (platform_machine == 'arm64' and sys_platform == 'win32') or (platform_machine == 'x86_64' and sys_platform == 'win32')" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "click" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1a/8f/999e4dda11c6187c78f090eac00895a47e11a0049308f07579bcb7aa3aa2/huggingface_hub-1.23.0.tar.gz", hash = "sha256:c04997fb8bbdace1e57b7703d30ed7678af51f70d00d241819ff411b92ae9a88", size = 919163, upload-time = "2026-07-09T14:49:32.315Z" } wheels = [ @@ -3379,14 +3380,14 @@ name = "hypercorn" version = "0.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "h2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "priority", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "taskgroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "wsproto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "taskgroup", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "wsproto" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } wheels = [ @@ -3449,7 +3450,7 @@ name = "importlib-metadata" version = "8.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e7/72/c600ae4f68c28fc19f9c31b9403053e5dbb8cace2e6842c7b7c3e4d42fe9/importlib_metadata-8.9.0.tar.gz", hash = "sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee", size = 56140, upload-time = "2026-03-20T16:56:26.362Z" } wheels = [ @@ -3497,7 +3498,7 @@ name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "markupsafe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ @@ -3656,11 +3657,11 @@ name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jsonschema-specifications", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "referencing", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -3672,7 +3673,7 @@ name = "jsonschema-specifications" version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "referencing", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "referencing" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ @@ -3808,14 +3809,14 @@ name = "langfuse" version = "4.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "backoff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "backoff" }, + { name = "httpx" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/74/00/16324f1c0d244fee3c4e4ac00e8a422f1afb8873e963182dac7fbf8b34e8/langfuse-4.13.2.tar.gz", hash = "sha256:4484c5d949dd1c5cabab74c179c7e5c19be203940852bf90d1ba5870215eac6a", size = 362128, upload-time = "2026-07-08T15:54:07.889Z" } wheels = [ @@ -3914,18 +3915,18 @@ name = "litellm" version = "1.91.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastuuid", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "importlib-metadata", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jsonschema", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tiktoken", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tokenizers", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohttp" }, + { name = "click" }, + { name = "fastuuid" }, + { name = "httpx" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "tiktoken" }, + { name = "tokenizers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/70/1b419158085e0cc1615642dd62c7a5d4c3254db3de77df65dade3b25165b/litellm-1.91.1.tar.gz", hash = "sha256:49a24593df7e37262c52243a8e07572d451d37c100aa1b1f347d80d501d2e386", size = 14872532, upload-time = "2026-07-08T23:07:04.559Z" } wheels = [ @@ -3934,36 +3935,36 @@ wheels = [ [package.optional-dependencies] proxy = [ - { name = "apscheduler", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-storage-blob", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "backoff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "boto3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "expression", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastapi-sso", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "granian", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "gunicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "litellm-enterprise", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "litellm-proxy-extras", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "orjson", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "polars", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic-settings", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyjwt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pynacl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyroscope-io", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, - { name = "python-multipart", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "restrictedpython", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rq", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "soundfile", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvloop", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, - { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "apscheduler" }, + { name = "azure-identity" }, + { name = "azure-storage-blob" }, + { name = "backoff" }, + { name = "boto3" }, + { name = "cryptography" }, + { name = "expression" }, + { name = "fastapi" }, + { name = "fastapi-sso" }, + { name = "granian" }, + { name = "gunicorn" }, + { name = "litellm-enterprise" }, + { name = "litellm-proxy-extras" }, + { name = "mcp", extra = ["ws"] }, + { name = "orjson" }, + { name = "polars" }, + { name = "pydantic-settings" }, + { name = "pyjwt" }, + { name = "pynacl" }, + { name = "pyroscope-io", marker = "sys_platform != 'win32'" }, + { name = "python-multipart" }, + { name = "pyyaml" }, + { name = "restrictedpython" }, + { name = "rich" }, + { name = "rq" }, + { name = "soundfile" }, + { name = "starlette" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "uvloop", marker = "sys_platform != 'win32'" }, + { name = "websockets" }, ] [[package]] @@ -4002,7 +4003,7 @@ name = "markdown-it-py" version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdurl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ @@ -4104,15 +4105,15 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'win32'", ] dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "cycler", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "fonttools", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "kiwisolver", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "packaging", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "pillow", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "pyparsing", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "python-dateutil", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" } }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } wheels = [ @@ -4194,16 +4195,16 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ - { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "cycler", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "fonttools", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "kiwisolver", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "packaging", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "pillow", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "pyparsing", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "python-dateutil", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" } }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } wheels = [ @@ -4259,20 +4260,20 @@ name = "mcp" version = "1.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx-sse", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jsonschema", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic-settings", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyjwt", extra = ["crypto"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-multipart", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "sse-starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", extra = ["standard"] }, ] sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" } wheels = [ @@ -4281,7 +4282,7 @@ wheels = [ [package.optional-dependencies] ws = [ - { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "websockets" }, ] [[package]] @@ -4298,14 +4299,14 @@ name = "mem0ai" version = "2.0.11" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "posthog", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytz", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "qdrant-client", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sqlalchemy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "httpx" }, + { name = "openai" }, + { name = "posthog" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "pytz" }, + { name = "qdrant-client" }, + { name = "sqlalchemy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/81/4f/9368c71195cb9a81fe16d5621317938f621f08157a1f89acf8972ea383db/mem0ai-2.0.11.tar.gz", hash = "sha256:bca405548e11c642ee75009134ffa7f69ab41ba3b1f72465816ba5ff0612e18f", size = 237552, upload-time = "2026-07-01T16:58:05.426Z" } wheels = [ @@ -4317,7 +4318,7 @@ name = "microsoft-agents-activity" version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/6a/dfc2fc0316b7dc4f6d24792b4a31a873b026be76792af1e0c3e65f843ef0/microsoft_agents_activity-0.3.1.tar.gz", hash = "sha256:c7567fc30f8e6f2a2d74cd65a1f7f31ade0d7ec9dd94531677d0d7b0648c77ee", size = 44886, upload-time = "2025-09-09T23:19:43.044Z" } wheels = [ @@ -4329,7 +4330,7 @@ name = "microsoft-agents-copilotstudio-client" version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "microsoft-agents-hosting-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "microsoft-agents-hosting-core" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/a5/2381ffd14d6a584f9f7ab80c7b6c634f658ea651b38702eb403c930d8396/microsoft_agents_copilotstudio_client-0.3.1.tar.gz", hash = "sha256:c529209241c9d11b7a6e8696f96a3d43121c10b49e44f00e5066f9cf5256f4f3", size = 5024, upload-time = "2025-09-09T23:19:44.833Z" } wheels = [ @@ -4341,11 +4342,11 @@ name = "microsoft-agents-hosting-core" version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "microsoft-agents-activity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyjwt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "isodate" }, + { name = "microsoft-agents-activity" }, + { name = "pyjwt" }, + { name = "python-dotenv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6a/14/a1365e0bab1486c2d16aabeb192ca90715794edf4e68be4815c245884420/microsoft_agents_hosting_core-0.3.1.tar.gz", hash = "sha256:0b76bda10e7a54ff3c86e56cbabaad5ac7a4c2a076c9833af3b2f4c86fa85e89", size = 81137, upload-time = "2025-09-09T23:19:46.73Z" } wheels = [ @@ -4357,30 +4358,30 @@ name = "microsoft-opentelemetry" version = "1.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-core-tracing-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-monitor-opentelemetry-exporter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-django", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-flask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-logging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-openai-agents-v2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-openai-v2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-psycopg2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-urllib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-resource-detector-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-genai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyjwt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohttp" }, + { name = "azure-core" }, + { name = "azure-core-tracing-opentelemetry" }, + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-django" }, + { name = "opentelemetry-instrumentation-fastapi" }, + { name = "opentelemetry-instrumentation-flask" }, + { name = "opentelemetry-instrumentation-httpx" }, + { name = "opentelemetry-instrumentation-logging" }, + { name = "opentelemetry-instrumentation-openai-agents-v2" }, + { name = "opentelemetry-instrumentation-openai-v2" }, + { name = "opentelemetry-instrumentation-psycopg2" }, + { name = "opentelemetry-instrumentation-requests" }, + { name = "opentelemetry-instrumentation-urllib" }, + { name = "opentelemetry-instrumentation-urllib3" }, + { name = "opentelemetry-resource-detector-azure" }, + { name = "opentelemetry-sdk" }, + { name = "opentelemetry-util-genai" }, + { name = "pyjwt" }, + { name = "requests" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a8/68/ce4cd11cc8f86d19b3e9bf1a579fdeb486a246e79f61597f47ffcbcef448/microsoft_opentelemetry-1.3.5.tar.gz", hash = "sha256:099e9b971d3c0a9aab185f1a15f45478748583d79bc00554144cfbb86b7dc127", size = 188900, upload-time = "2026-07-01T19:13:14.693Z" } wheels = [ @@ -4392,16 +4393,16 @@ name = "mistralai" version = "1.12.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "eval-type-backport", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "invoke", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "eval-type-backport" }, + { name = "httpx" }, + { name = "invoke" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-sdk" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/aa/12/c3476c53e907255b5f485f085ba50dd9a84b40fe662e9a888d6ded26fa7b/mistralai-1.12.4.tar.gz", hash = "sha256:e52b53bab58025dcd208eeac13e3c3df5778d4112eeca1f08124096c7738929f", size = 243129, upload-time = "2026-02-20T17:55:13.73Z" } wheels = [ @@ -4413,9 +4414,9 @@ name = "ml-dtypes" version = "0.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } wheels = [ @@ -4469,9 +4470,9 @@ name = "msal" version = "1.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyjwt", extra = ["crypto"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cryptography" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/99/d840198ecf6e8057bbc937f129ae940404485d736cda73253bbff9537f01/msal-1.37.0.tar.gz", hash = "sha256:1b1672a33ee467c1d70b341bb16cafd51bb3c817147a95b93263794b03971bec", size = 182444, upload-time = "2026-05-29T19:49:05.561Z" } wheels = [ @@ -4483,7 +4484,7 @@ name = "msal-extensions" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "msal", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "msal" }, ] sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } wheels = [ @@ -4495,11 +4496,11 @@ name = "msrest" version = "0.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests-oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core" }, + { name = "certifi" }, + { name = "isodate" }, + { name = "requests" }, + { name = "requests-oauthlib" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332, upload-time = "2022-06-13T22:41:25.111Z" } wheels = [ @@ -4511,7 +4512,7 @@ name = "multidict" version = "6.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } wheels = [ @@ -4649,12 +4650,12 @@ name = "mypy" version = "2.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ast-serialize", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "librt", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" }, - { name = "mypy-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pathspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "ast-serialize" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/7e/be536678c6ae49ef058aba4b483d8c7bc104f471479016066f345bc1f5f8/mypy-2.2.0.tar.gz", hash = "sha256:2cdd99d48590dce6f6b7f1961eda75386364398fcdaad86923bc0f0231bf9baf", size = 3950939, upload-time = "2026-07-08T01:37:27.335Z" } wheels = [ @@ -4970,8 +4971,8 @@ name = "ollama" version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "httpx" }, + { name = "pydantic" }, ] sdist = { url = "https://files.pythonhosted.org/packages/91/6d/ae96027416dcc2e98c944c050c492789502d7d7c0b95a740f0bb39268632/ollama-0.5.3.tar.gz", hash = "sha256:40b6dff729df3b24e56d4042fd9d37e231cee8e528677e0d085413a1d6692394", size = 43331, upload-time = "2025-08-07T21:44:10.422Z" } wheels = [ @@ -4983,14 +4984,14 @@ name = "openai" version = "2.45.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "distro", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "jiter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/60/d4219875289b11d2c2f7da93c36283da224a2e55865ed865ab64e0ce9217/openai-2.45.0.tar.gz", hash = "sha256:10d34ca9c5643bce775852fddbfc172505cb1d4de1ccd101696c3ecff358765d", size = 1109653, upload-time = "2026-07-09T18:02:44.091Z" } wheels = [ @@ -5002,13 +5003,13 @@ name = "openai-agents" version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "griffelib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "griffelib" }, + { name = "mcp", extra = ["ws"] }, + { name = "openai" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "websockets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d7/b0/4ee0eda742e395fa48f625a96ef0713c53b49f397f9c5fadcef417d021b3/openai_agents-0.18.1.tar.gz", hash = "sha256:689ad88c8f64435413dde707ca45fb42d55217ff6ef63aab3d638c33f78e04bf", size = 5521444, upload-time = "2026-07-09T23:39:18.907Z" } wheels = [ @@ -5020,11 +5021,11 @@ name = "openai-chatkit" version = "1.6.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "openai-agents", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "jinja2" }, + { name = "openai" }, + { name = "openai-agents" }, + { name = "pydantic" }, + { name = "uvicorn", extra = ["standard"] }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/07/c4b4ea034f34f25e73cf1a872deb349e3acab6c929a5531d547a9a994890/openai_chatkit-1.6.5.tar.gz", hash = "sha256:903e9702bf26cd8a2b23d4e7b199b657bee4379758e0ca11ebaee09362d2889e", size = 65057, upload-time = "2026-05-19T05:05:14.954Z" } wheels = [ @@ -5036,7 +5037,7 @@ name = "opentelemetry-api" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/cc/e4c9584181f86494df0f6bdec1a4f3280c50db44704dc2a407e994fc87bb/opentelemetry_api-1.43.0.tar.gz", hash = "sha256:107d0d03857ea8fc7c5fcbbbd83f800c281f0d560553d61c1d675fccfd1761c1", size = 73476, upload-time = "2026-06-24T15:19:55.323Z" } wheels = [ @@ -5048,8 +5049,8 @@ name = "opentelemetry-exporter-otlp" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/47/b77366bcbe719373a8cac2b4e8ad01f8ff3c9f2c223374d77ece280aae6f/opentelemetry_exporter_otlp-1.43.0.tar.gz", hash = "sha256:65aded6c50ee7dd2b9948c9d0e59ddb4ed4eea6e8532fba95cbe6a4a64a566ba", size = 6086, upload-time = "2026-06-24T15:19:58.003Z" } wheels = [ @@ -5061,7 +5062,7 @@ name = "opentelemetry-exporter-otlp-proto-common" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-proto" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/c1/e8098490ab15abf116dcaf9fa89ededcb35547c7d08d4b5a62f573dc1e63/opentelemetry_exporter_otlp_proto_common-1.43.0.tar.gz", hash = "sha256:c4e32ba6d6b13bdb2b8f6764c4fd28d00192826561aa04f6d14eedfce7ac076f", size = 20197, upload-time = "2026-06-24T15:20:00.247Z" } wheels = [ @@ -5073,13 +5074,13 @@ name = "opentelemetry-exporter-otlp-proto-grpc" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-common", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e2/1d/6336453716ca0a240d4417d19e6d5b77a5e7163e5670ec4f7ec4d3ede7bf/opentelemetry_exporter_otlp_proto_grpc-1.43.0.tar.gz", hash = "sha256:1b3e0627daa9bc21884d4a13946807c255eb558bfe5bdd543dffb6f4c9faee0d", size = 27213, upload-time = "2026-06-24T15:20:00.907Z" } wheels = [ @@ -5091,13 +5092,13 @@ name = "opentelemetry-exporter-otlp-proto-http" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-exporter-otlp-proto-common", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/92/0b9f56412483a8891d4843890294796c9df8ab42417bd9bad8035d840cb3/opentelemetry_exporter_otlp_proto_http-1.43.0.tar.gz", hash = "sha256:fa8a42bb7d00ee5391f4c0b04d8e6a46c03caa437903296ab73a81dc11ba118f", size = 25406, upload-time = "2026-06-24T15:20:01.515Z" } wheels = [ @@ -5109,10 +5110,10 @@ name = "opentelemetry-instrumentation" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "packaging" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/97/02fe6e1c8b1ffac42d0b429c18080edb24e0e0d18c86612edf72b5752382/opentelemetry_instrumentation-0.64b0.tar.gz", hash = "sha256:b47d528dead6271d7743114417eb67fc915bd9258111c48dbf9a4951d2efa88d", size = 41935, upload-time = "2026-06-24T15:19:12.951Z" } wheels = [ @@ -5124,11 +5125,11 @@ name = "opentelemetry-instrumentation-asgi" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "asgiref" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/85/0c/71c696fccb86d37af383ea1604af4729fabad0af2fabaf203e4c79c1e859/opentelemetry_instrumentation_asgi-0.64b0.tar.gz", hash = "sha256:4dd3eee566a4303f8e6b9b84f2a0a7abc57a6640df768926c68a3868bf5b2090", size = 26136, upload-time = "2026-06-24T15:19:17.003Z" } wheels = [ @@ -5140,10 +5141,10 @@ name = "opentelemetry-instrumentation-dbapi" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/9f/3c6ce6f4394faa1540f48b7d442f455582ca76449952506ce767a54f09bf/opentelemetry_instrumentation_dbapi-0.64b0.tar.gz", hash = "sha256:8e3a1528fc9753a04190a7e7bf0180c5abd059f3aa68ec3857edb363c9847c44", size = 20138, upload-time = "2026-06-24T15:19:24.097Z" } wheels = [ @@ -5155,11 +5156,11 @@ name = "opentelemetry-instrumentation-django" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-wsgi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-wsgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/2e/8f6084eab186533b0574683a8c39e51803e6c34c21fc33e194f58352d704/opentelemetry_instrumentation_django-0.64b0.tar.gz", hash = "sha256:3d2673b4f77156b15b1b119c8849b50e3644cfc325f7a24c03602596de2dbba4", size = 25387, upload-time = "2026-06-24T15:19:24.795Z" } wheels = [ @@ -5171,11 +5172,11 @@ name = "opentelemetry-instrumentation-fastapi" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-asgi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-asgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/a1/52282e2cc5c08f4df13b087896d9907258fe2ff4f34035d3b21aa92684c3/opentelemetry_instrumentation_fastapi-0.64b0.tar.gz", hash = "sha256:05f75149929e433c1630de381688e650bf651c1e1cce7f9a7b649a807dac8a98", size = 26236, upload-time = "2026-06-24T15:19:27.219Z" } wheels = [ @@ -5187,12 +5188,12 @@ name = "opentelemetry-instrumentation-flask" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-wsgi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-wsgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4a/6d/cf9a9cf18603e3aaebba96633ec3c1ced463706f545d8672419b715c1e59/opentelemetry_instrumentation_flask-0.64b0.tar.gz", hash = "sha256:74d07edba426beae1214bd0aec69bd31a9d0d22c29747b497d5d94c516d1c860", size = 24150, upload-time = "2026-06-24T15:19:27.847Z" } wheels = [ @@ -5204,11 +5205,11 @@ name = "opentelemetry-instrumentation-httpx" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/2a/2893a8781b93894f1e8014904c0342da7e4302de6597c2d5c0cb6c1a552e/opentelemetry_instrumentation_httpx-0.64b0.tar.gz", hash = "sha256:c2cfcd03d3665762860ebd0c28038c6e47fbb48d7942dec31dd75fc634d25c92", size = 23555, upload-time = "2026-06-24T15:19:29.107Z" } wheels = [ @@ -5220,9 +5221,9 @@ name = "opentelemetry-instrumentation-logging" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/88/8e/3b7191ba5817f139867612e960fbe06a69fe0d522d3b2c5e9c8e89b3ef8c/opentelemetry_instrumentation_logging-0.64b0.tar.gz", hash = "sha256:6435b4d215bf8183e15f7e3416a10ebe1c411810d3411fbfc62667daae3abacb", size = 20000, upload-time = "2026-06-24T15:19:30.946Z" } wheels = [ @@ -5234,10 +5235,10 @@ name = "opentelemetry-instrumentation-openai-agents-v2" version = "0.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-genai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-genai" }, ] sdist = { url = "https://files.pythonhosted.org/packages/00/15/b6a303454d2800d772cdebc490c1d598d06d0e541619db80195eb9ea85c6/opentelemetry_instrumentation_openai_agents_v2-0.1.0.tar.gz", hash = "sha256:1033f4b261ce07f65d197ac0e9c499302c805eae987a6cc4e7f99bb279363477", size = 22423, upload-time = "2025-10-15T19:04:59.912Z" } wheels = [ @@ -5249,9 +5250,9 @@ name = "opentelemetry-instrumentation-openai-v2" version = "2.3b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/4e/21f8cd16ccb471dd217ed85eb817796a10c4f2718ae2c91e752a57180cf0/opentelemetry_instrumentation_openai_v2-2.3b0.tar.gz", hash = "sha256:5de9d70cc9536eea1fe48ea016e0c5f25735fa9a13709076a64b20657fadb6ba", size = 170838, upload-time = "2025-12-24T13:20:58.33Z" } wheels = [ @@ -5263,9 +5264,9 @@ name = "opentelemetry-instrumentation-psycopg2" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation-dbapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-dbapi" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/2d/0062ecf86b6bd407398820ae34d8acf7259f2b32531fa22e1c3b7748c6d0/opentelemetry_instrumentation_psycopg2-0.64b0.tar.gz", hash = "sha256:57df449718297b93e7bb57c76d3439c475eb5a5451600fcd6a9024d74822d972", size = 12065, upload-time = "2026-06-24T15:19:33.994Z" } wheels = [ @@ -5277,10 +5278,10 @@ name = "opentelemetry-instrumentation-requests" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/97/d5fb7b3f329c24ff2f6478f1a0c0c516ba942d3df2acbb197d276af31816/opentelemetry_instrumentation_requests-0.64b0.tar.gz", hash = "sha256:8213a20b6578c41aa05cc5b48419aea75e1584924a4904dfcf36524597e7cc96", size = 18106, upload-time = "2026-06-24T15:19:39.522Z" } wheels = [ @@ -5292,10 +5293,10 @@ name = "opentelemetry-instrumentation-urllib" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/87/20/547126ab1706e65629c2079cdbb956ef06ccc58a7cbad520c3505886e4cc/opentelemetry_instrumentation_urllib-0.64b0.tar.gz", hash = "sha256:954ab9908f08dc9127ca3d259f88a37b52f54c03f6fa4c384fd9fcfe9c85cd76", size = 16668, upload-time = "2026-06-24T15:19:45.096Z" } wheels = [ @@ -5307,11 +5308,11 @@ name = "opentelemetry-instrumentation-urllib3" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/30/e2/9e6f747018f639b9ffed97f715f1eb16c95dd87ba572c880ce451a65a18f/opentelemetry_instrumentation_urllib3-0.64b0.tar.gz", hash = "sha256:647c6d1b012e14168323b18d4e60fca9a5d280764821b99ea0a5639450fbd74e", size = 18945, upload-time = "2026-06-24T15:19:45.738Z" } wheels = [ @@ -5323,10 +5324,10 @@ name = "opentelemetry-instrumentation-wsgi" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, ] sdist = { url = "https://files.pythonhosted.org/packages/95/4f/0b5232a0d635eee5da7c85af503d2f00fdcac61cc6d4d4a69aadf1fa7a0e/opentelemetry_instrumentation_wsgi-0.64b0.tar.gz", hash = "sha256:1cce6ea28d1800c154e6ccdf52f05bae2f05c5e28ee44f0dddb17ada26208986", size = 19667, upload-time = "2026-06-24T15:19:46.407Z" } wheels = [ @@ -5338,7 +5339,7 @@ name = "opentelemetry-proto" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "protobuf" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e0/b9/d357faefb40bda1d4799913e6af611171ff22a2dedcb93576bc92242d056/opentelemetry_proto-1.43.0.tar.gz", hash = "sha256:224778df17e1f3fafeaaa21d874236ca5f6ffc2f86e0899298ec7351aac27924", size = 46481, upload-time = "2026-06-24T15:20:07.625Z" } wheels = [ @@ -5350,7 +5351,7 @@ name = "opentelemetry-resource-detector-azure" version = "0.1.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-sdk" }, ] sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503, upload-time = "2024-05-16T21:54:58.994Z" } wheels = [ @@ -5362,9 +5363,9 @@ name = "opentelemetry-sdk" version = "1.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3e/eb/5041074274ac0956b03637cc039d434569112468e875eddfcc9a0674ce06/opentelemetry_sdk-1.43.0.tar.gz", hash = "sha256:d8187c81c162df9913e4003dd6485f7390d9a24fc17026ec7387b8b8218b08e9", size = 254744, upload-time = "2026-06-24T15:20:08.467Z" } wheels = [ @@ -5376,8 +5377,8 @@ name = "opentelemetry-semantic-conventions" version = "0.64b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/30/5f26df29509eccd86b99b481ac9ffa39da49ba9577cc69071c552ae30447/opentelemetry_semantic_conventions-0.64b0.tar.gz", hash = "sha256:72f76fb2d1582d9d033dd1fcd84532e961e6ff3d90d24ba6fabc72975a83864c", size = 148340, upload-time = "2026-06-24T15:20:09.267Z" } wheels = [ @@ -5389,9 +5390,9 @@ name = "opentelemetry-util-genai" version = "0.3b0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/d8/4dd2fb622d26ec45b10ef63eb87fd512f5d7467c7bd35ce390629bd6dff8/opentelemetry_util_genai-0.3b0.tar.gz", hash = "sha256:83e127789a9ad615b8ca65f05fc36955a67ce257b06142bfd46159a3b7ed73d3", size = 31800, upload-time = "2026-02-20T16:16:14.807Z" } wheels = [ @@ -5421,7 +5422,7 @@ name = "orderedmultidict" version = "1.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/62/61ad51f6c19d495970230a7747147ce7ed3c3a63c2af4ebfdb1f6d738703/orderedmultidict-1.0.2.tar.gz", hash = "sha256:16a7ae8432e02cc987d2d6d5af2df5938258f87c870675c73ee77a0920e6f4a6", size = 13973, upload-time = "2025-11-18T08:00:42.649Z" } wheels = [ @@ -5537,10 +5538,10 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "python-dateutil", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "pytz", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "tzdata", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -5615,10 +5616,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "python-dateutil", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "tzdata", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } wheels = [ @@ -5797,8 +5798,8 @@ name = "plotly" version = "6.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "narwhals", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "narwhals" }, + { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/07/795c79dbce40c39bece88e69d049babbd23ffa95b5d117f248db8ea03abb/plotly-6.9.0.tar.gz", hash = "sha256:967ad33e8c704fed051800d11d985eb206a9c795c14206b30a6f463ed9c67d0d", size = 6919903, upload-time = "2026-07-09T14:55:59.982Z" } wheels = [ @@ -5819,9 +5820,9 @@ name = "poethepoet" version = "0.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pastel", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "pastel" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/92/93a4af9511b8c7c647874521d9e6c904266be98067c2ee1eb2e74520d208/poethepoet-0.48.0.tar.gz", hash = "sha256:a06f49d244fadfc2e2e7faa78b54e64a9694727e4ce1d50e08f23cea3ded74f1", size = 148679, upload-time = "2026-07-05T21:48:30.106Z" } wheels = [ @@ -5833,7 +5834,7 @@ name = "polars" version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "polars-runtime-32", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "polars-runtime-32" }, ] sdist = { url = "https://files.pythonhosted.org/packages/27/99/fe77f10a13a778705ef05b499fc708c9a0b0a3680d9eb6bc6e1b6a6b9914/polars-1.42.1.tar.gz", hash = "sha256:2fe94f3059334650bd850ae19a9c165dcd5d9cb12cd95ea04de2201662e70e8a", size = 741532, upload-time = "2026-06-30T04:57:51.504Z" } wheels = [ @@ -5873,7 +5874,7 @@ name = "portpicker" version = "1.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "psutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/d0/cda2fc582f09510c84cd6b7d7b9e22a02d4e45dbad2b2ef1c6edd7847e00/portpicker-1.6.0.tar.gz", hash = "sha256:bd507fd6f96f65ee02781f2e674e9dc6c99bbfa6e3c39992e3916204c9d431fa", size = 25676, upload-time = "2023-08-15T04:37:08.865Z" } wheels = [ @@ -5885,10 +5886,10 @@ name = "posthog" version = "7.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "backoff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "distro", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "backoff" }, + { name = "distro" }, + { name = "requests" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/46/52/edf4ccfe197684d337d163e29ce2eb3cb36a9c8741e6fce3fd2ab8880f1a/posthog-7.22.0.tar.gz", hash = "sha256:b734c83eeb9d9a2fc9e2b8f4f02d13441f6a0baa0ac53e27758608e773d8a01f", size = 328897, upload-time = "2026-07-06T21:44:12.92Z" } wheels = [ @@ -5900,8 +5901,8 @@ name = "powerfx" version = "0.0.34" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi" }, + { name = "pythonnet" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/6c4bf87e0c74ca1c563921ce89ca1c5785b7576bca932f7255cdf81082a7/powerfx-0.0.34.tar.gz", hash = "sha256:956992e7afd272657ed16d80f4cad24ec95d9e4a79fb9dfa4a068a09e136af32", size = 3237555, upload-time = "2025-12-22T15:50:59.682Z" } wheels = [ @@ -6074,7 +6075,7 @@ name = "proto-plus" version = "1.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "protobuf" }, ] sdist = { url = "https://files.pythonhosted.org/packages/87/44/767757fd2cdd4a60d7e4440d9f7b491d6131103d313638d2c03e06c268fb/proto_plus-1.28.1.tar.gz", hash = "sha256:832e68e7fe064cf90ab153b6e5eb935b27891bb89aaeb68b115e9b702f6cb168", size = 57166, upload-time = "2026-07-08T17:04:02.367Z" } wheels = [ @@ -6175,7 +6176,7 @@ name = "pyasn1-modules" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyasn1", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pyasn1" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } wheels = [ @@ -6196,10 +6197,10 @@ name = "pydantic" version = "2.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "annotated-types", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ @@ -6208,7 +6209,7 @@ wheels = [ [package.optional-dependencies] email = [ - { name = "email-validator", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "email-validator" }, ] [[package]] @@ -6216,7 +6217,7 @@ name = "pydantic-argparse" version = "0.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/ea/e63d587294c20d3b83e9c312b5d577c9ec28962ee8490839ca9996672849/pydantic_argparse-0.10.0.tar.gz", hash = "sha256:d57eb0a84c8f0af6605376157d3f445cfd786700f2e596ba9d48d15d557185eb", size = 15928, upload-time = "2025-02-09T08:18:30.425Z" } wheels = [ @@ -6228,7 +6229,7 @@ name = "pydantic-core" version = "2.46.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ @@ -6344,7 +6345,7 @@ name = "pydantic-monty" version = "0.0.18" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/5b/bb6a8bfdf13eb9808c966bdac064a40ce9ac881ec6d64dba3e055888f22b/pydantic_monty-0.0.18.tar.gz", hash = "sha256:c43794c7c4664fa1403d4841459d0e23f01b4f552283db638f5b40ced4dac6a1", size = 1197105, upload-time = "2026-05-29T08:31:41.077Z" } wheels = [ @@ -6415,9 +6416,9 @@ name = "pydantic-settings" version = "2.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } wheels = [ @@ -6438,7 +6439,7 @@ name = "pyjwt" version = "2.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } wheels = [ @@ -6447,7 +6448,7 @@ wheels = [ [package.optional-dependencies] crypto = [ - { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cryptography" }, ] [[package]] @@ -6455,7 +6456,7 @@ name = "pynacl" version = "1.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" }, + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" } wheels = [ @@ -6518,8 +6519,8 @@ name = "pyright" version = "1.1.411" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nodeenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nodeenv" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" } wheels = [ @@ -6531,7 +6532,7 @@ name = "pyroscope-io" version = "0.8.16" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "cffi" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/a8/50/607b38b120ba8adad954119ba512c53590c793f0cf7f009ba6549e4e1d77/pyroscope_io-0.8.16-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:e07edcfd59f5bdce42948b92c9b118c824edbd551730305f095a6b9af401a9e8", size = 3138869, upload-time = "2026-01-22T06:23:24.664Z" }, @@ -6546,12 +6547,12 @@ version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "iniconfig", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ @@ -6563,9 +6564,9 @@ name = "pytest-asyncio" version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "backports-asyncio-runner", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } wheels = [ @@ -6577,9 +6578,9 @@ name = "pytest-cov" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage", extra = ["toml"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ @@ -6591,7 +6592,7 @@ name = "pytest-retry" version = "1.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/5b/607b017994cca28de3a1ad22a3eee8418e5d428dcd8ec25b26b18e995a73/pytest_retry-1.7.0.tar.gz", hash = "sha256:f8d52339f01e949df47c11ba9ee8d5b362f5824dff580d3870ec9ae0057df80f", size = 19977, upload-time = "2025-01-19T01:56:13.115Z" } wheels = [ @@ -6603,7 +6604,7 @@ name = "pytest-timeout" version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } wheels = [ @@ -6615,8 +6616,8 @@ name = "pytest-xdist" version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "execnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "execnet" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } wheels = [ @@ -6625,7 +6626,7 @@ wheels = [ [package.optional-dependencies] psutil = [ - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "psutil" }, ] [[package]] @@ -6633,7 +6634,7 @@ name = "python-dateutil" version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ @@ -6672,7 +6673,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "clr-loader" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [ @@ -6782,15 +6783,15 @@ name = "qdrant-client" version = "1.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "httpx", extra = ["http2"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "portalocker", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "grpcio" }, + { name = "httpx", extra = ["http2"] }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "portalocker" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/65/45/5b1bdd15a3c7730eefb9c113600829e20d689b82b5a23f9e07d107094004/qdrant_client-1.18.0.tar.gz", hash = "sha256:52e8ece1a7d40519801bf0b70713bfa0f6b7ae28c7275bbe0b0286fbed7f6db4", size = 352580, upload-time = "2026-05-11T14:12:38.702Z" } wheels = [ @@ -6802,7 +6803,7 @@ name = "redis" version = "7.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "async-timeout", marker = "(python_full_version < '3.11.3' and sys_platform == 'darwin') or (python_full_version < '3.11.3' and sys_platform == 'linux') or (python_full_version < '3.11.3' and sys_platform == 'win32')" }, + { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f7/80/2971931d27651affa88a44c0ad7b8c4a19dc29c998abb20b23868d319b59/redis-7.1.1.tar.gz", hash = "sha256:a2814b2bda15b39dad11391cc48edac4697214a8a5a4bd10abe936ab4892eb43", size = 4800064, upload-time = "2026-02-09T18:39:40.292Z" } wheels = [ @@ -6814,16 +6815,16 @@ name = "redisvl" version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jsonpath-ng", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ml-dtypes", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-ulid", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tenacity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "jsonpath-ng" }, + { name = "ml-dtypes" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pydantic" }, + { name = "python-ulid" }, + { name = "pyyaml" }, + { name = "redis" }, + { name = "tenacity" }, ] sdist = { url = "https://files.pythonhosted.org/packages/72/1a/f1f0ff963622c34a9e9a9f2a0c6ad82bfbd05c082ecc89e38e092e3e9069/redisvl-0.15.0.tar.gz", hash = "sha256:0e382e9b6cd8378dfe1515b18f92d125cfba905f6f3c5fe9b8904b3ca840d1ca", size = 861480, upload-time = "2026-02-27T14:02:33.366Z" } wheels = [ @@ -6835,10 +6836,10 @@ name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "attrs" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ @@ -6971,10 +6972,10 @@ name = "requests" version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "charset-normalizer", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ @@ -6986,8 +6987,8 @@ name = "requests-oauthlib" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "oauthlib" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } wheels = [ @@ -7008,9 +7009,9 @@ name = "rich" version = "13.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } wheels = [ @@ -7289,9 +7290,9 @@ name = "rq" version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "croniter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "click" }, + { name = "croniter" }, + { name = "redis" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/a8/7bf65cda593feb5888214a4d1e64aae6fc5eb0bbbb74df922c916099a3e5/rq-2.10.0.tar.gz", hash = "sha256:2d8c533dd27500fedabec06295f18db595966e4f22744e6988fe31155b8f7a21", size = 754610, upload-time = "2026-06-20T03:11:45.919Z" } wheels = [ @@ -7328,7 +7329,7 @@ name = "s3transfer" version = "0.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "botocore", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "botocore" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/94/dcdaeb1713cab9c84def276cfac7388b17c7d9855bbcfe88d77e4dbafd44/s3transfer-0.19.0.tar.gz", hash = "sha256:ce436931687addc4c1712d52d40b32f53e88315723f107ffa20ba82b05a0f685", size = 165171, upload-time = "2026-06-16T19:44:51.599Z" } wheels = [ @@ -7345,10 +7346,10 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'win32'", ] dependencies = [ - { name = "joblib", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "threadpoolctl", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "joblib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -7406,13 +7407,13 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ - { name = "joblib", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "narwhals", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "threadpoolctl", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "joblib" }, + { name = "narwhals" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "threadpoolctl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767, upload-time = "2026-06-02T11:54:32.706Z" } wheels = [ @@ -7458,7 +7459,7 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } wheels = [ @@ -7519,7 +7520,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -7604,7 +7605,7 @@ resolution-markers = [ "python_full_version == '3.12.*' and sys_platform == 'win32'", ] dependencies = [ - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" } wheels = [ @@ -7655,13 +7656,13 @@ name = "seaborn" version = "0.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "matplotlib", version = "3.10.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "matplotlib", version = "3.10.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } wheels = [ @@ -7784,11 +7785,11 @@ name = "soundfile" version = "0.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, - { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.12' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/db/949331952a6fb1c5b12e9de80fd08747966c2039d1a61db4764fbd3981c2/soundfile-0.14.0.tar.gz", hash = "sha256:ba1c1a2d618bca5c406647c83b89f07cc8810fa506a50622a6993ba130c1de11", size = 47842, upload-time = "2026-06-06T08:58:47.869Z" } wheels = [ @@ -7807,8 +7808,8 @@ name = "sqlalchemy" version = "2.0.51" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "greenlet", marker = "(platform_machine == 'AMD64' and sys_platform == 'darwin') or (platform_machine == 'WIN32' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'darwin') or (platform_machine == 'amd64' and sys_platform == 'darwin') or (platform_machine == 'ppc64le' and sys_platform == 'darwin') or (platform_machine == 'win32' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'AMD64' and sys_platform == 'linux') or (platform_machine == 'WIN32' and sys_platform == 'linux') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'amd64' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and sys_platform == 'linux') or (platform_machine == 'win32' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'WIN32' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'win32') or (platform_machine == 'amd64' and sys_platform == 'win32') or (platform_machine == 'ppc64le' and sys_platform == 'win32') or (platform_machine == 'win32' and sys_platform == 'win32') or (platform_machine == 'x86_64' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" } wheels = [ @@ -7862,8 +7863,8 @@ name = "sse-starlette" version = "3.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, + { name = "starlette" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/1b/bc9e3e7a72dcdad7dc7888758f5d00f56f8909ed5cfdff822bd72bb4c520/sse_starlette-3.4.5.tar.gz", hash = "sha256:83072538bc211a2f68b7b0422226c4af3e9b62e106e07034664b832ca019842a", size = 35249, upload-time = "2026-06-20T17:36:58.322Z" } wheels = [ @@ -7875,8 +7876,8 @@ name = "starlette" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } wheels = [ @@ -7888,7 +7889,7 @@ name = "sympy" version = "1.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mpmath", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "mpmath" }, ] sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ @@ -7909,8 +7910,8 @@ name = "taskgroup" version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "exceptiongroup" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f0/8d/e218e0160cc1b692e6e0e5ba34e8865dbb171efeb5fc9a704544b3020605/taskgroup-0.2.2.tar.gz", hash = "sha256:078483ac3e78f2e3f973e2edbf6941374fbea81b9c5d0a96f51d297717f4752d", size = 11504, upload-time = "2025-01-03T09:24:13.761Z" } wheels = [ @@ -7922,34 +7923,34 @@ name = "tau2" version = "0.0.1" source = { git = "https://github.com/sierra-research/tau2-bench?rev=5ba9e3e56db57c5e4114bf7f901291f09b2c5619#5ba9e3e56db57c5e4114bf7f901291f09b2c5619" } dependencies = [ - { name = "addict", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "deepdiff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "docstring-parser", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "fs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "langfuse", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "litellm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "loguru", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "matplotlib", version = "3.10.9", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "plotly", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pydantic-argparse", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "rich", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "ruff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, - { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, - { name = "seaborn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tabulate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "tenacity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "toml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "watchdog", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "addict" }, + { name = "deepdiff" }, + { name = "docstring-parser" }, + { name = "fastapi" }, + { name = "fs" }, + { name = "langfuse" }, + { name = "litellm" }, + { name = "loguru" }, + { name = "matplotlib", version = "3.10.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "plotly" }, + { name = "psutil" }, + { name = "pydantic-argparse" }, + { name = "pytest" }, + { name = "pyyaml" }, + { name = "redis" }, + { name = "rich" }, + { name = "ruff" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "seaborn" }, + { name = "tabulate" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "watchdog" }, ] [[package]] @@ -7984,8 +7985,8 @@ name = "tiktoken" version = "0.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "regex", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "regex" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/e5/5f3cb2159769d0f4324c0e9e87f9de3c4b1cd45848a96b2eb3566ad5ca77/tiktoken-0.13.0.tar.gz", hash = "sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1", size = 38986, upload-time = "2026-05-15T04:51:27.153Z" } wheels = [ @@ -8045,7 +8046,7 @@ name = "tokenizers" version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "huggingface-hub" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c1/60/21f715d9faba5f5407ff759472ade058ec4a507ad62bcea47cb847239a73/tokenizers-0.23.1.tar.gz", hash = "sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa", size = 365748, upload-time = "2026-04-27T14:43:25.606Z" } wheels = [ @@ -8208,7 +8209,7 @@ name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ @@ -8276,9 +8277,9 @@ name = "uvicorn" version = "0.51.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "click" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c91e1e973bbe2a878591538655d6e1d47f24ba1c51f3/uvicorn-0.51.0.tar.gz", hash = "sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0", size = 94412, upload-time = "2026-07-08T10:59:05.962Z" } wheels = [ @@ -8287,12 +8288,12 @@ wheels = [ [package.optional-dependencies] standard = [ - { name = "httptools", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvloop", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux')" }, - { name = "watchfiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, ] [[package]] @@ -8300,8 +8301,8 @@ name = "uvicorn-worker" version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "gunicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "uvicorn", extra = ["standard"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "gunicorn" }, + { name = "uvicorn", extra = ["standard"] }, ] sdist = { url = "https://files.pythonhosted.org/packages/80/59/9101b9c0680fd80e9d26c07deb822a5d18a324339fcf9cd017885ee808ad/uvicorn_worker-0.4.0.tar.gz", hash = "sha256:8ee5306070d8f38dce124adce488c3c0b50f20cf0c0222b12c66188da7214493", size = 9361, upload-time = "2025-09-20T10:47:01.218Z" } wheels = [ @@ -8389,7 +8390,7 @@ name = "watchfiles" version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "anyio" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } wheels = [ @@ -8574,7 +8575,7 @@ name = "werkzeug" version = "3.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "markupsafe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } wheels = [ @@ -8664,7 +8665,7 @@ name = "wsproto" version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } wheels = [ @@ -8676,9 +8677,9 @@ name = "yarl" version = "1.24.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "propcache", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } wheels = [