diff --git a/docs/durable_execution/overview.md b/docs/durable_execution/overview.md index 0753f1a4c1..3c1993a83d 100644 --- a/docs/durable_execution/overview.md +++ b/docs/durable_execution/overview.md @@ -2,9 +2,10 @@ Pydantic AI allows you to build durable agents that can preserve their progress across transient API failures and application errors or restarts, and handle long-running, asynchronous, and human-in-the-loop workflows with production-grade reliability. Durable agents have full support for [streaming](../agents.md#streaming-all-events) and [MCP](../mcp/client.md), with the added benefit of fault tolerance. -Pydantic AI natively supports two durable execution solutions: +Pydantic AI natively supports three durable execution solutions: - [Temporal](./temporal.md) - [DBOS](./dbos.md) +- [Prefect](./prefect.md) -These integrations only uses Pydantic AI's public interface, so they also serve as a reference for integrating with other durable systems. +These integrations only use Pydantic AI's public interface, so they also serve as a reference for integrating with other durable systems. diff --git a/docs/durable_execution/prefect.md b/docs/durable_execution/prefect.md new file mode 100644 index 0000000000..ce5c4cd6bd --- /dev/null +++ b/docs/durable_execution/prefect.md @@ -0,0 +1,287 @@ +# Durable Execution with Prefect + +[Prefect](https://www.prefect.io/) is a workflow orchestration framework for building resilient data pipelines in Python, natively integrated with Pydantic AI. + +## Durable Execution + +Prefect 3.0 brings [transactional semantics](https://www.prefect.io/blog/transactional-ml-pipelines-with-prefect-3-0) to your Python workflows, allowing you to group tasks into atomic units and define failure modes. If any part of a transaction fails, the entire transaction can be rolled back to a clean state. + +* **Flows** are the top-level entry points for your workflow. They can contain tasks and other flows. +* **Tasks** are individual units of work that can be retried, cached, and monitored independently. + +Prefect 3.0's approach to transactional orchestration makes your workflows automatically **idempotent**: rerunnable without duplication or inconsistency across any environment. Every task is executed within a transaction that governs when and where the task's result record is persisted. If the task runs again under an identical context, it will not re-execute but instead load its previous result. + +The diagram below shows the overall architecture of an agentic application with Prefect. +Prefect uses client-side task orchestration by default, with optional server connectivity for advanced features like scheduling and monitoring. + +```text + +---------------------+ + | Prefect Server | (Monitoring, + | or Cloud | scheduling, UI, + +---------------------+ orchestration) + ^ + | + Flow state, | Schedule flows, + metadata, | track execution + logs | + | ++------------------------------------------------------+ +| Application Process | +| +----------------------------------------------+ | +| | Flow (Agent.run) | | +| +----------------------------------------------+ | +| | | | | +| v v v | +| +-----------+ +------------+ +-------------+ | +| | Task | | Task | | Task | | +| | (Tool) | | (MCP Tool) | | (Model API) | | +| +-----------+ +------------+ +-------------+ | +| | | | | +| Cache & Cache & Cache & | +| persist persist persist | +| to to to | +| v v v | +| +----------------------------------------------+ | +| | Result Storage (Local FS, S3, etc.) | | +| +----------------------------------------------+ | ++------------------------------------------------------+ + | | | + v v v + [External APIs, services, databases, etc.] +``` + +See the [Prefect documentation](https://docs.prefect.io/) for more information. + +## Durable Agent + +Any agent can be wrapped in a [`PrefectAgent`][pydantic_ai.durable_exec.prefect.PrefectAgent] to get durable execution. `PrefectAgent` automatically: + +* Wraps [`Agent.run`][pydantic_ai.Agent.run] and [`Agent.run_sync`][pydantic_ai.Agent.run_sync] as Prefect flows. +* Wraps [model requests](../models/overview.md) as Prefect tasks. +* Wraps [tool calls](../tools.md) as Prefect tasks (configurable per-tool). +* Wraps [MCP communication](../mcp/client.md) as Prefect tasks. + +Event stream handlers are **not automatically wrapped** by Prefect. If they involve I/O or non-deterministic behavior, you can explicitly decorate them with `@task` from Prefect. For examples, see the [streaming docs](../agents.md#streaming-all-events) + +The original agent, model, and MCP server can still be used as normal outside the Prefect flow. + +Here is a simple but complete example of wrapping an agent for durable execution. All it requires is to install Pydantic AI with Prefect: + +```bash +pip/uv-add pydantic-ai[prefect] +``` + +Or if you're using the slim package, you can install it with the `prefect` optional group: + +```bash +pip/uv-add pydantic-ai-slim[prefect] +``` + +```python {title="prefect_agent.py" test="skip"} +from pydantic_ai import Agent +from pydantic_ai.durable_exec.prefect import PrefectAgent + +agent = Agent( + 'gpt-4o', + instructions="You're an expert in geography.", + name='geography', # (1)! +) + +prefect_agent = PrefectAgent(agent) # (2)! + +async def main(): + result = await prefect_agent.run('What is the capital of Mexico?') # (3)! + print(result.output) + #> Mexico City (Ciudad de México, CDMX) +``` + +1. The agent's `name` is used to uniquely identify its flows and tasks. +2. Wrapping the agent with `PrefectAgent` enables durable execution for all agent runs. +3. [`PrefectAgent.run()`][pydantic_ai.durable_exec.prefect.PrefectAgent.run] works like [`Agent.run()`][pydantic_ai.Agent.run], but runs as a Prefect flow and executes model requests, decorated tool calls, and MCP communication as Prefect tasks. + +_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_ + +For more information on how to use Prefect in Python applications, see their [Python documentation](https://docs.prefect.io/v3/how-to-guides/workflows/write-and-run). + +## Prefect Integration Considerations + +When using Prefect with Pydantic AI agents, there are a few important considerations to ensure workflows behave correctly. + +### Agent Requirements + +Each agent instance must have a unique `name` so Prefect can correctly identify and track its flows and tasks. + +### Tool Wrapping + +Agent tools are automatically wrapped as Prefect tasks, which means they benefit from: + +* **Retry logic**: Failed tool calls can be retried automatically +* **Caching**: Tool results are cached based on their inputs +* **Observability**: Tool execution is tracked in the Prefect UI + +You can customize tool task behavior using `tool_task_config` (applies to all tools) or `tool_task_config_by_name` (per-tool configuration): + +```python {title="prefect_agent_config.py" test="skip"} +from pydantic_ai import Agent +from pydantic_ai.durable_exec.prefect import PrefectAgent, TaskConfig + +agent = Agent('gpt-4o', name='my_agent') + +@agent.tool_plain +def fetch_data(url: str) -> str: + # This tool will be wrapped as a Prefect task + ... + +prefect_agent = PrefectAgent( + agent, + tool_task_config=TaskConfig(retries=3), # Default for all tools + tool_task_config_by_name={ + 'fetch_data': TaskConfig(timeout_seconds=10.0), # Specific to fetch_data + 'simple_tool': None, # Disable task wrapping for simple_tool + }, +) +``` + +Set a tool's config to `None` in `tool_task_config_by_name` to disable task wrapping for that specific tool. + +### Streaming + +When running inside a Prefect flow, [`Agent.run_stream()`][pydantic_ai.Agent.run_stream] works but doesn't provide real-time streaming because Prefect tasks consume their entire execution before returning results. The method will execute fully and return the complete result at once. + +For real-time streaming behavior inside Prefect flows, you can set an [`event_stream_handler`][pydantic_ai.agent.EventStreamHandler] on the `Agent` or `PrefectAgent` instance and use [`PrefectAgent.run()`][pydantic_ai.durable_exec.prefect.PrefectAgent.run]. + +**Note**: Event stream handlers behave differently when running inside a Prefect flow versus outside: +- **Outside a flow**: The handler receives events as they stream from the model +- **Inside a flow**: Each event is wrapped as a Prefect task for durability, which may affect timing but ensures reliability + +The event stream handler function will receive the agent [run context][pydantic_ai.tools.RunContext] and an async iterable of events from the model's streaming response and the agent's execution of tools. For examples, see the [streaming docs](../agents.md#streaming-all-events). + +## Task Configuration + +You can customize Prefect task behavior, such as retries and timeouts, by passing [`TaskConfig`][pydantic_ai.durable_exec.prefect.TaskConfig] objects to the `PrefectAgent` constructor: + +- `mcp_task_config`: Configuration for MCP server communication tasks +- `model_task_config`: Configuration for model request tasks +- `tool_task_config`: Default configuration for all tool calls +- `tool_task_config_by_name`: Per-tool task configuration (overrides `tool_task_config`) + +Available `TaskConfig` options: + +- `retries`: Maximum number of retries for the task (default: `0`) +- `retry_delay_seconds`: Delay between retries in seconds (can be a single value or list for exponential backoff, default: `1.0`) +- `timeout_seconds`: Maximum time in seconds for the task to complete +- `cache_policy`: Custom Prefect cache policy for the task +- `persist_result`: Whether to persist the task result +- `result_storage`: Prefect result storage for the task (e.g., `'s3-bucket/my-storage'` or a `WritableFileSystem` block) +- `log_prints`: Whether to log print statements from the task (default: `False`) + +Example: + +```python {title="prefect_agent_config.py" test="skip"} +from pydantic_ai import Agent +from pydantic_ai.durable_exec.prefect import PrefectAgent, TaskConfig + +agent = Agent( + 'gpt-4o', + instructions="You're an expert in geography.", + name='geography', +) + +prefect_agent = PrefectAgent( + agent, + model_task_config=TaskConfig( + retries=3, + retry_delay_seconds=[1.0, 2.0, 4.0], # Exponential backoff + timeout_seconds=30.0, + ), +) + +async def main(): + result = await prefect_agent.run('What is the capital of France?') + print(result.output) + #> Paris +``` + +_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_ + +### Retry Considerations + +Pydantic AI and provider API clients have their own retry logic. When using Prefect, you may want to: + +* Disable [HTTP Request Retries](../retries.md) in Pydantic AI +* Turn off your provider API client's retry logic (e.g., `max_retries=0` on a [custom OpenAI client](../models/openai.md#custom-openai-client)) +* Rely on Prefect's task-level retry configuration for consistency + +This prevents requests from being retried multiple times at different layers. + +## Caching and Idempotency + +Prefect 3.0 provides built-in caching and transactional semantics. Tasks with identical inputs will not re-execute if their results are already cached, making workflows naturally idempotent and resilient to failures. + +* **Task inputs**: Messages, settings, parameters, tool arguments, and serializable dependencies + +**Note**: For user dependencies to be included in cache keys, they must be serializable (e.g., Pydantic models or basic Python types). Non-serializable dependencies are automatically excluded from cache computation. + +## Observability with Prefect and Logfire + +Prefect provides a built-in UI for monitoring flow runs, task executions, and failures. You can: + +* View real-time flow run status +* Debug failures with full stack traces +* Set up alerts and notifications + +To access the Prefect UI, you can either: + +1. Use [Prefect Cloud](https://www.prefect.io/cloud) (managed service) +2. Run a local [Prefect server](https://docs.prefect.io/v3/how-to-guides/self-hosted/server-cli) with `prefect server start` + +You can also use [Pydantic Logfire](../logfire.md) for detailed observability. When using both Prefect and Logfire, you'll get complementary views: + +* **Prefect**: Workflow-level orchestration, task status, and retry history +* **Logfire**: Fine-grained tracing of agent runs, model requests, and tool invocations + +When using Logfire with Prefect, you can enable distributed tracing to see spans for your Prefect runs included with your agent runs, model requests, and tool invocations. + +For more information about Prefect monitoring, see the [Prefect documentation](https://docs.prefect.io/). + +## Deployments and Scheduling + +To deploy and schedule a `PrefectAgent`, wrap it in a Prefect flow and use the flow's [`serve()`](https://docs.prefect.io/v3/how-to-guides/deployments/create-deployments#create-a-deployment-with-serve) or [`deploy()`](https://docs.prefect.io/v3/how-to-guides/deployments/deploy-via-python) methods: + +```python {title="serve_agent.py" test="skip"} +from prefect import flow + +from pydantic_ai import Agent +from pydantic_ai.durable_exec.prefect import PrefectAgent + +agent = Agent( + 'openai:gpt-4o', + name='daily_report_agent', + instructions='Generate a daily summary report.', +) + +prefect_agent = PrefectAgent(agent) + +@flow +async def daily_report_flow(user_prompt: str): + """Generate a daily report using the agent.""" + result = await prefect_agent.run(user_prompt) + return result.output + +# Serve the flow with a daily schedule +if __name__ == '__main__': + daily_report_flow.serve( + name='daily-report-deployment', + cron='0 9 * * *', # Run daily at 9am + parameters={'user_prompt': "Generate today's report"}, + tags=['production', 'reports'], + ) +``` + +The `serve()` method accepts scheduling options: + +- **`cron`**: Cron schedule string (e.g., `'0 9 * * *'` for daily at 9am) +- **`interval`**: Schedule interval in seconds or as a timedelta +- **`rrule`**: iCalendar RRule schedule string + +For production deployments with Docker, Kubernetes, or other infrastructure, use the flow's [`deploy()`](https://docs.prefect.io/v3/how-to-guides/deployments/deploy-via-python) method. See the [Prefect deployment documentation](https://docs.prefect.io/v3/how-to-guides/deployments/create-deploymentsy) for more information. diff --git a/docs/install.md b/docs/install.md index b4fb7147b4..600f81bd68 100644 --- a/docs/install.md +++ b/docs/install.md @@ -58,6 +58,7 @@ pip/uv-add "pydantic-ai-slim[openai]" * `a2a` - installs `fasta2a` [PyPI ↗](https://pypi.org/project/fasta2a){:target="_blank"} * `ag-ui` - installs `ag-ui-protocol` [PyPI ↗](https://pypi.org/project/ag-ui-protocol){:target="_blank"} and `starlette` [PyPI ↗](https://pypi.org/project/starlette){:target="_blank"} * `dbos` - installs [`dbos`](durable_execution/dbos.md) [PyPI ↗](https://pypi.org/project/dbos){:target="_blank"} +* `prefect` - installs [`prefect`](durable_execution/prefect.md) [PyPI ↗](https://pypi.org/project/prefect){:target="_blank"} See the [models](models/overview.md) documentation for information on which optional dependencies are required for each model. diff --git a/mkdocs.yml b/mkdocs.yml index 2b6d2e2097..fdf0066466 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,7 @@ nav: - Overview: durable_execution/overview.md - Temporal: durable_execution/temporal.md - DBOS: durable_execution/dbos.md + - Prefect: durable_execution/prefect.md - Agent-User Interaction (AG-UI): ag-ui.md - Agent2Agent (A2A): a2a.md diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/__init__.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/__init__.py new file mode 100644 index 0000000000..3106c3dd59 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/__init__.py @@ -0,0 +1,15 @@ +from ._agent import PrefectAgent +from ._cache_policies import DEFAULT_PYDANTIC_AI_CACHE_POLICY +from ._function_toolset import PrefectFunctionToolset +from ._mcp_server import PrefectMCPServer +from ._model import PrefectModel +from ._types import TaskConfig + +__all__ = [ + 'PrefectAgent', + 'PrefectModel', + 'PrefectMCPServer', + 'PrefectFunctionToolset', + 'TaskConfig', + 'DEFAULT_PYDANTIC_AI_CACHE_POLICY', +] diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_agent.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_agent.py new file mode 100644 index 0000000000..7c212ee8c7 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_agent.py @@ -0,0 +1,830 @@ +from __future__ import annotations + +from collections.abc import AsyncIterable, AsyncIterator, Callable, Iterator, Sequence +from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager +from contextvars import ContextVar +from typing import Any, overload + +from prefect import flow, task +from prefect.context import FlowRunContext +from prefect.utilities.asyncutils import run_coro_as_sync +from typing_extensions import Never + +from pydantic_ai import ( + AbstractToolset, + AgentRunResultEvent, + _utils, + messages as _messages, + models, + usage as _usage, +) +from pydantic_ai.agent import AbstractAgent, AgentRun, AgentRunResult, EventStreamHandler, WrapperAgent +from pydantic_ai.agent.abstract import Instructions, RunOutputDataT +from pydantic_ai.builtin_tools import AbstractBuiltinTool +from pydantic_ai.exceptions import UserError +from pydantic_ai.models import Model +from pydantic_ai.output import OutputDataT, OutputSpec +from pydantic_ai.result import StreamedRunResult +from pydantic_ai.settings import ModelSettings +from pydantic_ai.tools import ( + AgentDepsT, + DeferredToolResults, + RunContext, + Tool, + ToolFuncEither, +) + +from ._model import PrefectModel +from ._toolset import prefectify_toolset +from ._types import TaskConfig, default_task_config + + +class PrefectAgent(WrapperAgent[AgentDepsT, OutputDataT]): + def __init__( + self, + wrapped: AbstractAgent[AgentDepsT, OutputDataT], + *, + name: str | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + mcp_task_config: TaskConfig | None = None, + model_task_config: TaskConfig | None = None, + tool_task_config: TaskConfig | None = None, + tool_task_config_by_name: dict[str, TaskConfig | None] | None = None, + prefectify_toolset_func: Callable[ + [AbstractToolset[AgentDepsT], TaskConfig, TaskConfig, dict[str, TaskConfig | None]], + AbstractToolset[AgentDepsT], + ] = prefectify_toolset, + ): + """Wrap an agent to enable it with Prefect durable flows, by automatically offloading model requests, tool calls, and MCP server communication to Prefect tasks. + + After wrapping, the original agent can still be used as normal outside of the Prefect flow. + + Args: + wrapped: The agent to wrap. + name: Optional unique agent name to use as the Prefect flow name prefix. If not provided, the agent's `name` will be used. + event_stream_handler: Optional event stream handler to use instead of the one set on the wrapped agent. + mcp_task_config: The base Prefect task config to use for MCP server tasks. If no config is provided, use the default settings of Prefect. + model_task_config: The Prefect task config to use for model request tasks. If no config is provided, use the default settings of Prefect. + tool_task_config: The default Prefect task config to use for tool calls. If no config is provided, use the default settings of Prefect. + tool_task_config_by_name: Per-tool task configuration. Keys are tool names, values are TaskConfig or None (None disables task wrapping for that tool). + prefectify_toolset_func: Optional function to use to prepare toolsets for Prefect by wrapping them in a `PrefectWrapperToolset` that moves methods that require IO to Prefect tasks. + If not provided, only `FunctionToolset` and `MCPServer` will be prepared for Prefect. + The function takes the toolset, the task config, the tool-specific task config, and the tool-specific task config by name. + """ + super().__init__(wrapped) + + self._name = name or wrapped.name + self._event_stream_handler = event_stream_handler + if self._name is None: + raise UserError( + "An agent needs to have a unique `name` in order to be used with Prefect. The name will be used to identify the agent's flows and tasks." + ) + + # Merge the config with the default Prefect config + self._mcp_task_config = default_task_config | (mcp_task_config or {}) + self._model_task_config = default_task_config | (model_task_config or {}) + self._tool_task_config = default_task_config | (tool_task_config or {}) + self._tool_task_config_by_name = tool_task_config_by_name or {} + + if not isinstance(wrapped.model, Model): + raise UserError( + 'An agent needs to have a `model` in order to be used with Prefect, it cannot be set at agent run time.' + ) + + prefect_model = PrefectModel( + wrapped.model, + task_config=self._model_task_config, + event_stream_handler=self.event_stream_handler, + ) + self._model = prefect_model + + def _prefectify_toolset(toolset: AbstractToolset[AgentDepsT]) -> AbstractToolset[AgentDepsT]: + """Convert a toolset to its Prefect equivalent.""" + return prefectify_toolset_func( + toolset, + self._mcp_task_config, + self._tool_task_config, + self._tool_task_config_by_name, + ) + + prefect_toolsets = [toolset.visit_and_replace(_prefectify_toolset) for toolset in wrapped.toolsets] + self._toolsets = prefect_toolsets + + # Context variable to track when we're inside this agent's Prefect flow + self._in_prefect_agent_flow: ContextVar[bool] = ContextVar( + f'_in_prefect_agent_flow_{self._name}', default=False + ) + + @property + def name(self) -> str | None: + return self._name + + @name.setter + def name(self, value: str | None) -> None: # pragma: no cover + raise UserError( + 'The agent name cannot be changed after creation. If you need to change the name, create a new agent.' + ) + + @property + def model(self) -> Model: + return self._model + + @property + def event_stream_handler(self) -> EventStreamHandler[AgentDepsT] | None: + handler = self._event_stream_handler or super().event_stream_handler + if handler is None: + return None + elif FlowRunContext.get() is not None: + # Special case if it's in a Prefect flow, we need to iterate through all events and call the handler. + return self._call_event_stream_handler_in_flow + else: + return handler + + async def _call_event_stream_handler_in_flow( + self, ctx: RunContext[AgentDepsT], stream: AsyncIterable[_messages.AgentStreamEvent] + ) -> None: + handler = self._event_stream_handler or super().event_stream_handler + assert handler is not None + + # Create a task to handle each event + @task(name='Handle Stream Event') + async def event_stream_handler_task(event: _messages.AgentStreamEvent) -> None: + async def streamed_response(): + yield event + + await handler(ctx, streamed_response()) + + async for event in stream: + await event_stream_handler_task(event) + + @property + def toolsets(self) -> Sequence[AbstractToolset[AgentDepsT]]: + with self._prefect_overrides(): + return super().toolsets + + @contextmanager + def _prefect_overrides(self) -> Iterator[None]: + # Override with PrefectModel and PrefectMCPServer in the toolsets. + with super().override(model=self._model, toolsets=self._toolsets, tools=[]): + yield + + @overload + async def run( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + ) -> AgentRunResult[OutputDataT]: ... + + @overload + async def run( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT], + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + ) -> AgentRunResult[RunOutputDataT]: ... + + async def run( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT] | None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + **_deprecated_kwargs: Never, + ) -> AgentRunResult[Any]: + """Run the agent with a user prompt in async mode. + + This method builds an internal agent graph (using system prompts, tools and result schemas) and then + runs the graph to completion. The result of the run is returned. + + Example: + ```python + from pydantic_ai import Agent + + agent = Agent('openai:gpt-4o') + + async def main(): + agent_run = await agent.run('What is the capital of France?') + print(agent_run.output) + #> The capital of France is Paris. + ``` + + Args: + user_prompt: User input to start/continue the conversation. + output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no + output validators since output validators would expect an argument that matches the agent's output type. + message_history: History of the conversation so far. + deferred_tool_results: Optional results for deferred tool calls in the message history. + model: Optional model to use for this run, required if `model` was not set when creating the agent. + deps: Optional dependencies to use for this run. + model_settings: Optional settings to use for this model's request. + usage_limits: Optional limits on model request count or token usage. + usage: Optional usage to start with, useful for resuming a conversation or agents used in tools. + infer_name: Whether to try to infer the agent name from the call frame if it's not set. + toolsets: Optional additional toolsets for this run. + event_stream_handler: Optional event stream handler to use for this run. + builtin_tools: Optional additional builtin tools for this run. + + Returns: + The result of the run. + """ + + @flow(name=f'{self._name} Run') + async def wrapped_run_flow() -> AgentRunResult[Any]: + # Mark that we're inside a PrefectAgent flow + token = self._in_prefect_agent_flow.set(True) + try: + with self._prefect_overrides(): + result = await super(WrapperAgent, self).run( + user_prompt, + output_type=output_type, + message_history=message_history, + deferred_tool_results=deferred_tool_results, + model=model, + deps=deps, + model_settings=model_settings, + usage_limits=usage_limits, + usage=usage, + infer_name=infer_name, + toolsets=toolsets, + event_stream_handler=event_stream_handler, + ) + return result + finally: + self._in_prefect_agent_flow.reset(token) + + return await wrapped_run_flow() + + @overload + def run_sync( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + ) -> AgentRunResult[OutputDataT]: ... + + @overload + def run_sync( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT], + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + ) -> AgentRunResult[RunOutputDataT]: ... + + def run_sync( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT] | None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + **_deprecated_kwargs: Never, + ) -> AgentRunResult[Any]: + """Synchronously run the agent with a user prompt. + + This is a convenience method that wraps [`self.run`][pydantic_ai.agent.AbstractAgent.run] with `loop.run_until_complete(...)`. + You therefore can't use this method inside async code or if there's an active event loop. + + Example: + ```python + from pydantic_ai import Agent + + agent = Agent('openai:gpt-4o') + + result_sync = agent.run_sync('What is the capital of Italy?') + print(result_sync.output) + #> The capital of Italy is Rome. + ``` + + Args: + user_prompt: User input to start/continue the conversation. + output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no + output validators since output validators would expect an argument that matches the agent's output type. + message_history: History of the conversation so far. + deferred_tool_results: Optional results for deferred tool calls in the message history. + model: Optional model to use for this run, required if `model` was not set when creating the agent. + deps: Optional dependencies to use for this run. + model_settings: Optional settings to use for this model's request. + usage_limits: Optional limits on model request count or token usage. + usage: Optional usage to start with, useful for resuming a conversation or agents used in tools. + infer_name: Whether to try to infer the agent name from the call frame if it's not set. + toolsets: Optional additional toolsets for this run. + event_stream_handler: Optional event stream handler to use for this run. + builtin_tools: Optional additional builtin tools for this run. + + Returns: + The result of the run. + """ + + @flow(name=f'{self._name} Sync Run') + def wrapped_run_sync_flow() -> AgentRunResult[Any]: + # Mark that we're inside a PrefectAgent flow + token = self._in_prefect_agent_flow.set(True) + try: + with self._prefect_overrides(): + # Using `run_coro_as_sync` from Prefect with async `run` to avoid event loop conflicts. + result = run_coro_as_sync( + super(PrefectAgent, self).run( + user_prompt, + output_type=output_type, + message_history=message_history, + deferred_tool_results=deferred_tool_results, + model=model, + deps=deps, + model_settings=model_settings, + usage_limits=usage_limits, + usage=usage, + infer_name=infer_name, + toolsets=toolsets, + event_stream_handler=event_stream_handler, + ) + ) + return result + finally: + self._in_prefect_agent_flow.reset(token) + + return wrapped_run_sync_flow() + + @overload + def run_stream( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + ) -> AbstractAsyncContextManager[StreamedRunResult[AgentDepsT, OutputDataT]]: ... + + @overload + def run_stream( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT], + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + ) -> AbstractAsyncContextManager[StreamedRunResult[AgentDepsT, RunOutputDataT]]: ... + + @asynccontextmanager + async def run_stream( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT] | None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + event_stream_handler: EventStreamHandler[AgentDepsT] | None = None, + **_deprecated_kwargs: Never, + ) -> AsyncIterator[StreamedRunResult[AgentDepsT, Any]]: + """Run the agent with a user prompt in async mode, returning a streamed response. + + Example: + ```python + from pydantic_ai import Agent + + agent = Agent('openai:gpt-4o') + + async def main(): + async with agent.run_stream('What is the capital of the UK?') as response: + print(await response.get_output()) + #> The capital of the UK is London. + ``` + + Args: + user_prompt: User input to start/continue the conversation. + output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no + output validators since output validators would expect an argument that matches the agent's output type. + message_history: History of the conversation so far. + deferred_tool_results: Optional results for deferred tool calls in the message history. + model: Optional model to use for this run, required if `model` was not set when creating the agent. + deps: Optional dependencies to use for this run. + model_settings: Optional settings to use for this model's request. + usage_limits: Optional limits on model request count or token usage. + usage: Optional usage to start with, useful for resuming a conversation or agents used in tools. + infer_name: Whether to try to infer the agent name from the call frame if it's not set. + toolsets: Optional additional toolsets for this run. + builtin_tools: Optional additional builtin tools for this run. + event_stream_handler: Optional event stream handler to use for this run. It will receive all the events up until the final result is found, which you can then read or stream from inside the context manager. + + Returns: + The result of the run. + """ + if FlowRunContext.get() is not None: + raise UserError( + '`agent.run_stream()` cannot be used inside a Prefect flow. ' + 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.' + ) + + async with super().run_stream( + user_prompt, + output_type=output_type, + message_history=message_history, + deferred_tool_results=deferred_tool_results, + model=model, + deps=deps, + model_settings=model_settings, + usage_limits=usage_limits, + usage=usage, + infer_name=infer_name, + toolsets=toolsets, + event_stream_handler=event_stream_handler, + builtin_tools=builtin_tools, + **_deprecated_kwargs, + ) as result: + yield result + + @overload + def run_stream_events( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[OutputDataT]]: ... + + @overload + def run_stream_events( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT], + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[RunOutputDataT]]: ... + + def run_stream_events( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT] | None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[Any]]: + """Run the agent with a user prompt in async mode and stream events from the run. + + This is a convenience method that wraps [`self.run`][pydantic_ai.agent.AbstractAgent.run] and + uses the `event_stream_handler` kwarg to get a stream of events from the run. + + Example: + ```python + from pydantic_ai import Agent, AgentRunResultEvent, AgentStreamEvent + + agent = Agent('openai:gpt-4o') + + async def main(): + events: list[AgentStreamEvent | AgentRunResultEvent] = [] + async for event in agent.run_stream_events('What is the capital of France?'): + events.append(event) + print(events) + ''' + [ + PartStartEvent(index=0, part=TextPart(content='The capital of ')), + FinalResultEvent(tool_name=None, tool_call_id=None), + PartDeltaEvent(index=0, delta=TextPartDelta(content_delta='France is Paris. ')), + AgentRunResultEvent( + result=AgentRunResult(output='The capital of France is Paris. ') + ), + ] + ''' + ``` + + Arguments are the same as for [`self.run`][pydantic_ai.agent.AbstractAgent.run], + except that `event_stream_handler` is now allowed. + + Args: + user_prompt: User input to start/continue the conversation. + output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no + output validators since output validators would expect an argument that matches the agent's output type. + message_history: History of the conversation so far. + deferred_tool_results: Optional results for deferred tool calls in the message history. + model: Optional model to use for this run, required if `model` was not set when creating the agent. + deps: Optional dependencies to use for this run. + model_settings: Optional settings to use for this model's request. + usage_limits: Optional limits on model request count or token usage. + usage: Optional usage to start with, useful for resuming a conversation or agents used in tools. + infer_name: Whether to try to infer the agent name from the call frame if it's not set. + toolsets: Optional additional toolsets for this run. + builtin_tools: Optional additional builtin tools for this run. + + Returns: + An async iterable of stream events `AgentStreamEvent` and finally a `AgentRunResultEvent` with the final + run result. + """ + if FlowRunContext.get() is not None: + raise UserError( + '`agent.run_stream_events()` cannot be used inside a Prefect flow. ' + 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.' + ) + + return super().run_stream_events( + user_prompt, + output_type=output_type, + message_history=message_history, + deferred_tool_results=deferred_tool_results, + model=model, + deps=deps, + model_settings=model_settings, + usage_limits=usage_limits, + usage=usage, + infer_name=infer_name, + toolsets=toolsets, + builtin_tools=builtin_tools, + ) + + @overload + def iter( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, OutputDataT]]: ... + + @overload + def iter( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT], + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ... + + @asynccontextmanager + async def iter( + self, + user_prompt: str | Sequence[_messages.UserContent] | None = None, + *, + output_type: OutputSpec[RunOutputDataT] | None = None, + message_history: Sequence[_messages.ModelMessage] | None = None, + deferred_tool_results: DeferredToolResults | None = None, + model: models.Model | models.KnownModelName | str | None = None, + deps: AgentDepsT = None, + model_settings: ModelSettings | None = None, + usage_limits: _usage.UsageLimits | None = None, + usage: _usage.RunUsage | None = None, + infer_name: bool = True, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, + builtin_tools: Sequence[AbstractBuiltinTool] | None = None, + ) -> AsyncIterator[AgentRun[AgentDepsT, Any]]: + """A contextmanager which can be used to iterate over the agent graph's nodes as they are executed. + + This method builds an internal agent graph (using system prompts, tools and output schemas) and then returns an + `AgentRun` object. The `AgentRun` can be used to async-iterate over the nodes of the graph as they are + executed. This is the API to use if you want to consume the outputs coming from each LLM model response, or the + stream of events coming from the execution of tools. + + The `AgentRun` also provides methods to access the full message history, new messages, and usage statistics, + and the final result of the run once it has completed. + + For more details, see the documentation of `AgentRun`. + + Example: + ```python + from pydantic_ai import Agent + + agent = Agent('openai:gpt-4o') + + async def main(): + nodes = [] + async with agent.iter('What is the capital of France?') as agent_run: + async for node in agent_run: + nodes.append(node) + print(nodes) + ''' + [ + UserPromptNode( + user_prompt='What is the capital of France?', + instructions_functions=[], + system_prompts=(), + system_prompt_functions=[], + system_prompt_dynamic_functions={}, + ), + ModelRequestNode( + request=ModelRequest( + parts=[ + UserPromptPart( + content='What is the capital of France?', + timestamp=datetime.datetime(...), + ) + ] + ) + ), + CallToolsNode( + model_response=ModelResponse( + parts=[TextPart(content='The capital of France is Paris.')], + usage=RequestUsage(input_tokens=56, output_tokens=7), + model_name='gpt-4o', + timestamp=datetime.datetime(...), + ) + ), + End(data=FinalResult(output='The capital of France is Paris.')), + ] + ''' + print(agent_run.result.output) + #> The capital of France is Paris. + ``` + + Args: + user_prompt: User input to start/continue the conversation. + output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no + output validators since output validators would expect an argument that matches the agent's output type. + message_history: History of the conversation so far. + deferred_tool_results: Optional results for deferred tool calls in the message history. + model: Optional model to use for this run, required if `model` was not set when creating the agent. + deps: Optional dependencies to use for this run. + model_settings: Optional settings to use for this model's request. + usage_limits: Optional limits on model request count or token usage. + usage: Optional usage to start with, useful for resuming a conversation or agents used in tools. + infer_name: Whether to try to infer the agent name from the call frame if it's not set. + toolsets: Optional additional toolsets for this run. + builtin_tools: Optional additional builtin tools for this run. + + Returns: + The result of the run. + """ + if model is not None and not isinstance(model, PrefectModel): + raise UserError( + 'Non-Prefect model cannot be set at agent run time inside a Prefect flow, it must be set at agent creation time.' + ) + + with self._prefect_overrides(): + async with super().iter( + user_prompt=user_prompt, + output_type=output_type, + message_history=message_history, + deferred_tool_results=deferred_tool_results, + model=model, + deps=deps, + model_settings=model_settings, + usage_limits=usage_limits, + usage=usage, + infer_name=infer_name, + toolsets=toolsets, + ) as run: + yield run + + @contextmanager + def override( + self, + *, + name: str | _utils.Unset = _utils.UNSET, + deps: AgentDepsT | _utils.Unset = _utils.UNSET, + model: models.Model | models.KnownModelName | str | _utils.Unset = _utils.UNSET, + toolsets: Sequence[AbstractToolset[AgentDepsT]] | _utils.Unset = _utils.UNSET, + tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] | _utils.Unset = _utils.UNSET, + instructions: Instructions[AgentDepsT] | _utils.Unset = _utils.UNSET, + ) -> Iterator[None]: + """Context manager to temporarily override agent dependencies, model, toolsets, tools, or instructions. + + This is particularly useful when testing. + You can find an example of this [here](../testing.md#overriding-model-via-pytest-fixtures). + + Args: + name: The name to use instead of the name passed to the agent constructor and agent run. + deps: The dependencies to use instead of the dependencies passed to the agent run. + model: The model to use instead of the model passed to the agent run. + toolsets: The toolsets to use instead of the toolsets passed to the agent constructor and agent run. + tools: The tools to use instead of the tools registered with the agent. + instructions: The instructions to use instead of the instructions registered with the agent. + """ + if _utils.is_set(model) and not isinstance(model, PrefectModel): + raise UserError( + 'Non-Prefect model cannot be contextually overridden inside a Prefect flow, it must be set at agent creation time.' + ) + + with super().override( + name=name, deps=deps, model=model, toolsets=toolsets, tools=tools, instructions=instructions + ): + yield diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_cache_policies.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_cache_policies.py new file mode 100644 index 0000000000..3dd7c2375e --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_cache_policies.py @@ -0,0 +1,102 @@ +from dataclasses import fields, is_dataclass +from typing import Any, TypeGuard + +from prefect.cache_policies import INPUTS, RUN_ID, TASK_SOURCE, CachePolicy +from prefect.context import TaskRunContext + +from pydantic_ai import ToolsetTool +from pydantic_ai.tools import RunContext + + +def _is_dict(obj: Any) -> TypeGuard[dict[str, Any]]: + return isinstance(obj, dict) + + +def _is_list(obj: Any) -> TypeGuard[list[Any]]: + return isinstance(obj, list) + + +def _is_tuple(obj: Any) -> TypeGuard[tuple[Any, ...]]: + return isinstance(obj, tuple) + + +def _is_toolset_tool(obj: Any) -> TypeGuard[ToolsetTool]: + return isinstance(obj, ToolsetTool) + + +def _replace_run_context( + inputs: dict[str, Any], +) -> Any: + """Replace RunContext objects with a dict containing only hashable fields.""" + for key, value in inputs.items(): + if isinstance(value, RunContext): + inputs[key] = { + 'retries': value.retries, + 'tool_call_id': value.tool_call_id, + 'tool_name': value.tool_name, + 'tool_call_approved': value.tool_call_approved, + 'retry': value.retry, + 'max_retries': value.max_retries, + 'run_step': value.run_step, + } + + return inputs + + +def _strip_timestamps( + obj: Any | dict[str, Any] | list[Any] | tuple[Any, ...], +) -> Any: + """Recursively convert dataclasses to dicts, excluding timestamp fields.""" + if is_dataclass(obj) and not isinstance(obj, type): + result: dict[str, Any] = {} + for f in fields(obj): + if f.name != 'timestamp': + value = getattr(obj, f.name) + result[f.name] = _strip_timestamps(value) + return result + elif _is_dict(obj): + return {k: _strip_timestamps(v) for k, v in obj.items() if k != 'timestamp'} + elif _is_list(obj): + return [_strip_timestamps(item) for item in obj] + elif _is_tuple(obj): + return tuple(_strip_timestamps(item) for item in obj) + return obj + + +def _replace_toolsets( + inputs: dict[str, Any], +) -> Any: + """Replace Toolset objects with a dict containing only hashable fields.""" + inputs = inputs.copy() + for key, value in inputs.items(): + if _is_toolset_tool(value): + inputs[key] = {field.name: getattr(value, field.name) for field in fields(value) if field.name != 'toolset'} + return inputs + + +class PrefectAgentInputs(CachePolicy): + """Cache policy designed to handle input hashing for PrefectAgent cache keys. + + Computes a cache key based on inputs, ignoring nested 'timestamp' fields + and serializing RunContext objects to only include hashable fields. + """ + + def compute_key( + self, + task_ctx: TaskRunContext, + inputs: dict[str, Any], + flow_parameters: dict[str, Any], + **kwargs: Any, + ) -> str | None: + """Compute cache key from inputs with timestamps removed and RunContext serialized.""" + if not inputs: + return None + + inputs_without_toolsets = _replace_toolsets(inputs) + inputs_with_hashable_context = _replace_run_context(inputs_without_toolsets) + filtered_inputs = _strip_timestamps(inputs_with_hashable_context) + + return INPUTS.compute_key(task_ctx, filtered_inputs, flow_parameters, **kwargs) + + +DEFAULT_PYDANTIC_AI_CACHE_POLICY = PrefectAgentInputs() + TASK_SOURCE + RUN_ID diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_function_toolset.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_function_toolset.py new file mode 100644 index 0000000000..756578216c --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_function_toolset.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from typing import Any + +from prefect import task + +from pydantic_ai import FunctionToolset, ToolsetTool +from pydantic_ai.tools import AgentDepsT, RunContext + +from ._toolset import PrefectWrapperToolset +from ._types import TaskConfig, default_task_config + + +class PrefectFunctionToolset(PrefectWrapperToolset[AgentDepsT]): + """A wrapper for FunctionToolset that integrates with Prefect, turning tool calls into Prefect tasks.""" + + def __init__( + self, + wrapped: FunctionToolset[AgentDepsT], + *, + task_config: TaskConfig, + tool_task_config: dict[str, TaskConfig | None], + ): + super().__init__(wrapped) + self._task_config = default_task_config | (task_config or {}) + self._tool_task_config = tool_task_config or {} + + @task + async def _call_tool_task( + tool_name: str, + tool_args: dict[str, Any], + ctx: RunContext[AgentDepsT], + tool: ToolsetTool[AgentDepsT], + ) -> Any: + return await super(PrefectFunctionToolset, self).call_tool(tool_name, tool_args, ctx, tool) + + self._call_tool_task = _call_tool_task + + async def call_tool( + self, + name: str, + tool_args: dict[str, Any], + ctx: RunContext[AgentDepsT], + tool: ToolsetTool[AgentDepsT], + ) -> Any: + """Call a tool, wrapped as a Prefect task with a descriptive name.""" + # Check if this specific tool has custom config or is disabled + tool_specific_config = self._tool_task_config.get(name, default_task_config) + if tool_specific_config is None: + # None means this tool should not be wrapped as a task + return await super().call_tool(name, tool_args, ctx, tool) + + # Merge tool-specific config with default config + merged_config = self._task_config | tool_specific_config + + return await self._call_tool_task.with_options(name=f'Call Tool: {name}', **merged_config)( + name, tool_args, ctx, tool + ) diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_mcp_server.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_mcp_server.py new file mode 100644 index 0000000000..7b4f29d51b --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_mcp_server.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from abc import ABC +from typing import TYPE_CHECKING, Any + +from prefect import task +from typing_extensions import Self + +from pydantic_ai import ToolsetTool +from pydantic_ai.tools import AgentDepsT, RunContext + +from ._toolset import PrefectWrapperToolset +from ._types import TaskConfig, default_task_config + +if TYPE_CHECKING: + from pydantic_ai.mcp import MCPServer, ToolResult + + +class PrefectMCPServer(PrefectWrapperToolset[AgentDepsT], ABC): + """A wrapper for MCPServer that integrates with Prefect, turning call_tool and get_tools into Prefect tasks.""" + + def __init__( + self, + wrapped: MCPServer, + *, + task_config: TaskConfig, + ): + super().__init__(wrapped) + self._task_config = default_task_config | (task_config or {}) + self._mcp_id = wrapped.id + + @task + async def _call_tool_task( + tool_name: str, + tool_args: dict[str, Any], + ctx: RunContext[AgentDepsT], + tool: ToolsetTool[AgentDepsT], + ) -> ToolResult: + return await super(PrefectMCPServer, self).call_tool(tool_name, tool_args, ctx, tool) + + self._call_tool_task = _call_tool_task + + async def __aenter__(self) -> Self: + await self.wrapped.__aenter__() + return self + + async def __aexit__(self, *args: Any) -> bool | None: + return await self.wrapped.__aexit__(*args) + + async def call_tool( + self, + name: str, + tool_args: dict[str, Any], + ctx: RunContext[AgentDepsT], + tool: ToolsetTool[AgentDepsT], + ) -> ToolResult: + """Call an MCP tool, wrapped as a Prefect task with a descriptive name.""" + return await self._call_tool_task.with_options(name=f'Call MCP Tool: {name}', **self._task_config)( + name, tool_args, ctx, tool + ) diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_model.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_model.py new file mode 100644 index 0000000000..7a8e812677 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_model.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager +from datetime import datetime +from typing import Any + +from prefect import task +from prefect.context import FlowRunContext + +from pydantic_ai import ( + ModelMessage, + ModelResponse, + ModelResponseStreamEvent, +) +from pydantic_ai.agent import EventStreamHandler +from pydantic_ai.models import ModelRequestParameters, StreamedResponse +from pydantic_ai.models.wrapper import WrapperModel +from pydantic_ai.settings import ModelSettings +from pydantic_ai.tools import RunContext +from pydantic_ai.usage import RequestUsage + +from ._types import TaskConfig, default_task_config + + +class PrefectStreamedResponse(StreamedResponse): + """A non-streaming response wrapper for Prefect tasks. + + When a model request is executed inside a Prefect flow, the entire stream + is consumed within the task, and this wrapper is returned containing the + final response. + """ + + def __init__(self, model_request_parameters: ModelRequestParameters, response: ModelResponse): + super().__init__(model_request_parameters) + self.response = response + + async def _get_event_iterator(self) -> AsyncIterator[ModelResponseStreamEvent]: + """Return an empty iterator since the stream has already been consumed.""" + return + # noinspection PyUnreachableCode + yield + + def get(self) -> ModelResponse: + return self.response + + def usage(self) -> RequestUsage: + return self.response.usage # pragma: no cover + + @property + def model_name(self) -> str: + return self.response.model_name or '' # pragma: no cover + + @property + def provider_name(self) -> str: + return self.response.provider_name or '' # pragma: no cover + + @property + def timestamp(self) -> datetime: + return self.response.timestamp # pragma: no cover + + +class PrefectModel(WrapperModel): + """A wrapper for Model that integrates with Prefect, turning request and request_stream into Prefect tasks.""" + + def __init__( + self, + model: Any, + *, + task_config: TaskConfig, + event_stream_handler: EventStreamHandler[Any] | None = None, + ): + super().__init__(model) + self.task_config = default_task_config | (task_config or {}) + self.event_stream_handler = event_stream_handler + + @task + async def wrapped_request( + messages: list[ModelMessage], + model_settings: ModelSettings | None, + model_request_parameters: ModelRequestParameters, + ) -> ModelResponse: + response = await super(PrefectModel, self).request(messages, model_settings, model_request_parameters) + return response + + self._wrapped_request = wrapped_request + + @task + async def request_stream_task( + messages: list[ModelMessage], + model_settings: ModelSettings | None, + model_request_parameters: ModelRequestParameters, + ctx: RunContext[Any] | None, + ) -> ModelResponse: + async with super(PrefectModel, self).request_stream( + messages, model_settings, model_request_parameters, ctx + ) as streamed_response: + if self.event_stream_handler is not None: + assert ctx is not None, ( + 'A Prefect model cannot be used with `pydantic_ai.direct.model_request_stream()` as it requires a `run_context`. ' + 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.' + ) + await self.event_stream_handler(ctx, streamed_response) + + # Consume the entire stream + async for _ in streamed_response: + pass + response = streamed_response.get() + return response + + self._wrapped_request_stream = request_stream_task + + async def request( + self, + messages: list[ModelMessage], + model_settings: ModelSettings | None, + model_request_parameters: ModelRequestParameters, + ) -> ModelResponse: + """Make a model request, wrapped as a Prefect task when in a flow.""" + return await self._wrapped_request.with_options( + name=f'Model Request: {self.wrapped.model_name}', **self.task_config + )(messages, model_settings, model_request_parameters) + + @asynccontextmanager + async def request_stream( + self, + messages: list[ModelMessage], + model_settings: ModelSettings | None, + model_request_parameters: ModelRequestParameters, + run_context: RunContext[Any] | None = None, + ) -> AsyncIterator[StreamedResponse]: + """Make a streaming model request. + + When inside a Prefect flow, the stream is consumed within a task and + a non-streaming response is returned. When not in a flow, behaves normally. + """ + # Check if we're in a flow context + flow_run_context = FlowRunContext.get() + + # If not in a flow, just call the wrapped request_stream method + if flow_run_context is None: + async with super().request_stream( + messages, model_settings, model_request_parameters, run_context + ) as streamed_response: + yield streamed_response + return + + # If in a flow, consume the stream in a task and return the final response + response = await self._wrapped_request_stream.with_options( + name=f'Model Request (Streaming): {self.wrapped.model_name}', **self.task_config + )(messages, model_settings, model_request_parameters, run_context) + yield PrefectStreamedResponse(model_request_parameters, response) diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_toolset.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_toolset.py new file mode 100644 index 0000000000..9c559f7fb1 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_toolset.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from abc import ABC +from collections.abc import Callable +from typing import TYPE_CHECKING + +from pydantic_ai import AbstractToolset, FunctionToolset, WrapperToolset +from pydantic_ai.tools import AgentDepsT + +from ._types import TaskConfig + +if TYPE_CHECKING: + pass + + +class PrefectWrapperToolset(WrapperToolset[AgentDepsT], ABC): + """Base class for Prefect-wrapped toolsets.""" + + @property + def id(self) -> str | None: + # Prefect toolsets should have IDs for better task naming + return self.wrapped.id + + def visit_and_replace( + self, visitor: Callable[[AbstractToolset[AgentDepsT]], AbstractToolset[AgentDepsT]] + ) -> AbstractToolset[AgentDepsT]: + # Prefect-ified toolsets cannot be swapped out after the fact. + return self + + +def prefectify_toolset( + toolset: AbstractToolset[AgentDepsT], + mcp_task_config: TaskConfig, + tool_task_config: TaskConfig, + tool_task_config_by_name: dict[str, TaskConfig | None], +) -> AbstractToolset[AgentDepsT]: + """Wrap a toolset to integrate it with Prefect. + + Args: + toolset: The toolset to wrap. + mcp_task_config: The Prefect task config to use for MCP server tasks. + tool_task_config: The default Prefect task config to use for tool calls. + tool_task_config_by_name: Per-tool task configuration. Keys are tool names, values are TaskConfig or None. + """ + if isinstance(toolset, FunctionToolset): + from ._function_toolset import PrefectFunctionToolset + + return PrefectFunctionToolset( + wrapped=toolset, + task_config=tool_task_config, + tool_task_config=tool_task_config_by_name, + ) + + try: + from pydantic_ai.mcp import MCPServer + + from ._mcp_server import PrefectMCPServer + except ImportError: + pass + else: + if isinstance(toolset, MCPServer): + return PrefectMCPServer( + wrapped=toolset, + task_config=mcp_task_config, + ) + + return toolset diff --git a/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_types.py b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_types.py new file mode 100644 index 0000000000..0cdbb65689 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_types.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from prefect.cache_policies import CachePolicy +from prefect.results import ResultStorage +from typing_extensions import TypedDict + +from pydantic_ai.durable_exec.prefect._cache_policies import DEFAULT_PYDANTIC_AI_CACHE_POLICY + + +class TaskConfig(TypedDict, total=False): + """Configuration for a task in Prefect. + + These options are passed to the `@task` decorator. + """ + + retries: int + """Maximum number of retries for the task.""" + + retry_delay_seconds: float | list[float] + """Delay between retries in seconds. Can be a single value or a list for custom backoff.""" + + timeout_seconds: float + """Maximum time in seconds for the task to complete.""" + + cache_policy: CachePolicy + """Prefect cache policy for the task.""" + + persist_result: bool + """Whether to persist the task result.""" + + result_storage: ResultStorage + """Prefect result storage for the task. Should be a storage block or a block slug like `s3-bucket/my-storage`.""" + + log_prints: bool + """Whether to log print statements from the task.""" + + +default_task_config = TaskConfig( + retries=0, + retry_delay_seconds=1.0, + persist_result=True, + log_prints=False, + cache_policy=DEFAULT_PYDANTIC_AI_CACHE_POLICY, +) diff --git a/pydantic_ai_slim/pyproject.toml b/pydantic_ai_slim/pyproject.toml index f73ffcdab4..b6c5bf5682 100644 --- a/pydantic_ai_slim/pyproject.toml +++ b/pydantic_ai_slim/pyproject.toml @@ -100,6 +100,8 @@ retries = ["tenacity>=8.2.3"] temporal = ["temporalio==1.18.0"] # DBOS dbos = ["dbos>=1.14.0"] +# Prefect +prefect = ["prefect>=3.4.21"] [tool.hatch.metadata] allow-direct-references = true diff --git a/pyproject.toml b/pyproject.toml index c4f36b681d..b09a172045 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ dependencies = [ examples = ["pydantic-ai-examples=={{ version }}"] a2a = ["fasta2a>=0.4.1"] dbos = ["pydantic-ai-slim[dbos]=={{ version }}"] +prefect = ["pydantic-ai-slim[prefect]=={{ version }}"] [project.urls] Homepage = "https://ai.pydantic.dev" diff --git a/tests/cassettes/test_prefect/test_complex_agent_run_in_flow.yaml b/tests/cassettes/test_prefect/test_complex_agent_run_in_flow.yaml new file mode 100644 index 0000000000..97ffe84d06 --- /dev/null +++ b/tests/cassettes/test_prefect/test_complex_agent_run_in_flow.yaml @@ -0,0 +1,988 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '4707' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: 'Tell me: the capital of the country; the weather there; the product name' + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + tool_choice: required + tools: + - function: + description: '' + name: get_weather + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + - function: + description: '' + name: get_country + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Convert Celsius to Fahrenheit.\n\n Args:\n celsius: Temperature in Celsius\n\n Returns:\n + \ Temperature in Fahrenheit\n " + name: celsius_to_fahrenheit + parameters: + additionalProperties: false + properties: + celsius: + type: number + required: + - celsius + type: object + strict: true + type: function + - function: + description: "Get the weather forecast for a location.\n\n Args:\n location: The location to get the weather + forecast for.\n\n Returns:\n The weather forecast for the location.\n " + name: get_weather_forecast + parameters: + additionalProperties: false + properties: + location: + type: string + required: + - location + type: object + strict: true + type: function + - function: + description: '' + name: get_image_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_dict + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_error + parameters: + additionalProperties: false + properties: + value: + default: false + type: boolean + type: object + type: function + - function: + description: '' + name: get_none + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_multiple_items + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Get the current log level.\n\n Returns:\n The current log level.\n " + name: get_log_level + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Echo the run context.\n\n Args:\n ctx: Context object containing request and session information.\n\n + \ Returns:\n Dictionary with an echo message and the deps.\n " + name: echo_deps + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: Use sampling callback. + name: use_sampling + parameters: + additionalProperties: false + properties: + foo: + type: string + required: + - foo + type: object + strict: true + type: function + - function: + description: Use elicitation callback to ask the user a question. + name: use_elicitation + parameters: + additionalProperties: false + properties: + question: + type: string + required: + - question + type: object + strict: true + type: function + - function: + description: '' + name: external + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: The final response which ends this conversation + name: final_result + parameters: + $defs: + Answer: + additionalProperties: false + properties: + answer: + type: string + label: + type: string + required: + - label + - answer + type: object + additionalProperties: false + properties: + answers: + items: + $ref: '#/$defs/Answer' + type: array + required: + - answers + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKAtIAKW1eiFzDAZmCcrbvHgyoSw","object":"chat.completion.chunk","created":1759436803,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_rI3WKPYvVwlOgCGRjsPP2hEx","type":"function","function":{"name":"get_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"BEZvnVuFF"} + + data: {"id":"chatcmpl-CMKAtIAKW1eiFzDAZmCcrbvHgyoSw","object":"chat.completion.chunk","created":1759436803,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"eRjR"} + + data: {"id":"chatcmpl-CMKAtIAKW1eiFzDAZmCcrbvHgyoSw","object":"chat.completion.chunk","created":1759436803,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"N9cT"} + + data: {"id":"chatcmpl-CMKAtIAKW1eiFzDAZmCcrbvHgyoSw","object":"chat.completion.chunk","created":1759436803,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":398,"completion_tokens":10,"total_tokens":408,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"T3AOhzO9FQxEX"} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '536' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '4948' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: 'Tell me: the capital of the country; the weather there; the product name' + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{}' + name: get_country + id: call_rI3WKPYvVwlOgCGRjsPP2hEx + type: function + - content: Mexico + role: tool + tool_call_id: call_rI3WKPYvVwlOgCGRjsPP2hEx + model: gpt-4o + stream: true + stream_options: + include_usage: true + tool_choice: required + tools: + - function: + description: '' + name: get_weather + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + - function: + description: '' + name: get_country + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Convert Celsius to Fahrenheit.\n\n Args:\n celsius: Temperature in Celsius\n\n Returns:\n + \ Temperature in Fahrenheit\n " + name: celsius_to_fahrenheit + parameters: + additionalProperties: false + properties: + celsius: + type: number + required: + - celsius + type: object + strict: true + type: function + - function: + description: "Get the weather forecast for a location.\n\n Args:\n location: The location to get the weather + forecast for.\n\n Returns:\n The weather forecast for the location.\n " + name: get_weather_forecast + parameters: + additionalProperties: false + properties: + location: + type: string + required: + - location + type: object + strict: true + type: function + - function: + description: '' + name: get_image_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_dict + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_error + parameters: + additionalProperties: false + properties: + value: + default: false + type: boolean + type: object + type: function + - function: + description: '' + name: get_none + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_multiple_items + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Get the current log level.\n\n Returns:\n The current log level.\n " + name: get_log_level + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Echo the run context.\n\n Args:\n ctx: Context object containing request and session information.\n\n + \ Returns:\n Dictionary with an echo message and the deps.\n " + name: echo_deps + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: Use sampling callback. + name: use_sampling + parameters: + additionalProperties: false + properties: + foo: + type: string + required: + - foo + type: object + strict: true + type: function + - function: + description: Use elicitation callback to ask the user a question. + name: use_elicitation + parameters: + additionalProperties: false + properties: + question: + type: string + required: + - question + type: object + strict: true + type: function + - function: + description: '' + name: external + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: The final response which ends this conversation + name: final_result + parameters: + $defs: + Answer: + additionalProperties: false + properties: + answer: + type: string + label: + type: string + required: + - label + - answer + type: object + additionalProperties: false + properties: + answers: + items: + $ref: '#/$defs/Answer' + type: array + required: + - answers + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}],"obfuscation":"zLrnbPKVod1"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_NS4iQj14cDFwc0BnrKqDHavt","type":"function","function":{"name":"get_weather","arguments":""}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"hviVzIamG0"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"ci"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"Z"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\": "}}]},"logprobs":null,"finish_reason":null}],"obfuscation":""} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Mexic"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"VNuG3GbZb96UswM"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"o Ci"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"iK"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ty\"}"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"P"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_SkGkkGDvHQEEk0CGbnAh2AQw","type":"function","function":{"name":"get_product_name","arguments":""}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"z3CND"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{}"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"4lLc"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"s19T"} + + data: {"id":"chatcmpl-CMKAuuvC6E26nL6HLZzTrysEvC8Ln","object":"chat.completion.chunk","created":1759436804,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":417,"completion_tokens":44,"total_tokens":461,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"yKNAZ68MWAElO"} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '883' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '5413' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: 'Tell me: the capital of the country; the weather there; the product name' + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{}' + name: get_country + id: call_rI3WKPYvVwlOgCGRjsPP2hEx + type: function + - content: Mexico + role: tool + tool_call_id: call_rI3WKPYvVwlOgCGRjsPP2hEx + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"city": "Mexico City"}' + name: get_weather + id: call_NS4iQj14cDFwc0BnrKqDHavt + type: function + - function: + arguments: '{}' + name: get_product_name + id: call_SkGkkGDvHQEEk0CGbnAh2AQw + type: function + - content: sunny + role: tool + tool_call_id: call_NS4iQj14cDFwc0BnrKqDHavt + - content: Pydantic AI + role: tool + tool_call_id: call_SkGkkGDvHQEEk0CGbnAh2AQw + model: gpt-4o + stream: true + stream_options: + include_usage: true + tool_choice: required + tools: + - function: + description: '' + name: get_weather + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + - function: + description: '' + name: get_country + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Convert Celsius to Fahrenheit.\n\n Args:\n celsius: Temperature in Celsius\n\n Returns:\n + \ Temperature in Fahrenheit\n " + name: celsius_to_fahrenheit + parameters: + additionalProperties: false + properties: + celsius: + type: number + required: + - celsius + type: object + strict: true + type: function + - function: + description: "Get the weather forecast for a location.\n\n Args:\n location: The location to get the weather + forecast for.\n\n Returns:\n The weather forecast for the location.\n " + name: get_weather_forecast + parameters: + additionalProperties: false + properties: + location: + type: string + required: + - location + type: object + strict: true + type: function + - function: + description: '' + name: get_image_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_dict + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_error + parameters: + additionalProperties: false + properties: + value: + default: false + type: boolean + type: object + type: function + - function: + description: '' + name: get_none + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_multiple_items + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Get the current log level.\n\n Returns:\n The current log level.\n " + name: get_log_level + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Echo the run context.\n\n Args:\n ctx: Context object containing request and session information.\n\n + \ Returns:\n Dictionary with an echo message and the deps.\n " + name: echo_deps + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: Use sampling callback. + name: use_sampling + parameters: + additionalProperties: false + properties: + foo: + type: string + required: + - foo + type: object + strict: true + type: function + - function: + description: Use elicitation callback to ask the user a question. + name: use_elicitation + parameters: + additionalProperties: false + properties: + question: + type: string + required: + - question + type: object + strict: true + type: function + - function: + description: '' + name: external + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: The final response which ends this conversation + name: final_result + parameters: + $defs: + Answer: + additionalProperties: false + properties: + answer: + type: string + label: + type: string + required: + - label + - answer + type: object + additionalProperties: false + properties: + answers: + items: + $ref: '#/$defs/Answer' + type: array + required: + - answers + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","type":"function","function":{"name":"final_result","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AaWxQzy5"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Y2j"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answers"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"gjAvBXnGHOazDwM"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":["}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6l"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Hdw"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"label"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"S"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"S"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Capital"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"HIPRfoXRTPJErLA"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" of"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"aty"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" the"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Wv"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" country"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"QarCtU57NFsrKI"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"h"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answer"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"a"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Mexico"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"j"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"},{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"fy3sGW4IH5AJXdm"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"label"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"V"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Weather"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Yh2nEVLedwC5UHS"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" in"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"OSU"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" the"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"7B"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" capital"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"W3WXHuJwA1xvMk"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"B"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answer"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"T"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Sunny"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"F"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"},{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"i0VPa763JMvVj1o"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"label"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"S"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"i"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Product"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"r9Nmief0btHez2T"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" name"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"b"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"S"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answer"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"b"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"P"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6cQw7"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"yd"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"y0Vq"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"antic"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"C"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" AI"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"jOt"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"0kz"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"]}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"b5F8"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"JcNe"} + + data: {"id":"chatcmpl-CMKAvMbnOLyW5XeC6KvbEQWnTGlKj","object":"chat.completion.chunk","created":1759436805,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":481,"completion_tokens":49,"total_tokens":530,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"7j5RxfHqEcxPG"} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '680' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_iter_in_flow.yaml b/tests/cassettes/test_prefect/test_iter_in_flow.yaml new file mode 100644 index 0000000000..f5c3818a11 --- /dev/null +++ b/tests/cassettes/test_prefect/test_iter_in_flow.yaml @@ -0,0 +1,81 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '144' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"rNVhS0YEF3q2M2"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"xA1TnUUUG9iFS"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"uMvPF63K"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ftODC66QGq8So"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AcPtxTwbZ"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4Kx3yz6cHAVF0"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"zZ1vm14aK"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" City"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AGScWvTtfzM"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"V7K30EkmDuaikiT"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"6cn7HTeDSf"} + + data: {"id":"chatcmpl-CMKBAwIeVPhQ8BzhAgq2hye6j3ewH","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":8,"total_tokens":22,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":""} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '250' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_multiple_agents.yaml b/tests/cassettes/test_prefect/test_multiple_agents.yaml new file mode 100644 index 0000000000..0c392776e8 --- /dev/null +++ b/tests/cassettes/test_prefect/test_multiple_agents.yaml @@ -0,0 +1,1116 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '395' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436807 + id: chatcmpl-CMKAxI1j1i8pyRCGCAxRcQlfvt49J + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '4707' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: 'Tell me: the capital of the country; the weather there; the product name' + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + tool_choice: required + tools: + - function: + description: '' + name: get_weather + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + - function: + description: '' + name: get_country + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Convert Celsius to Fahrenheit.\n\n Args:\n celsius: Temperature in Celsius\n\n Returns:\n + \ Temperature in Fahrenheit\n " + name: celsius_to_fahrenheit + parameters: + additionalProperties: false + properties: + celsius: + type: number + required: + - celsius + type: object + strict: true + type: function + - function: + description: "Get the weather forecast for a location.\n\n Args:\n location: The location to get the weather + forecast for.\n\n Returns:\n The weather forecast for the location.\n " + name: get_weather_forecast + parameters: + additionalProperties: false + properties: + location: + type: string + required: + - location + type: object + strict: true + type: function + - function: + description: '' + name: get_image_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_dict + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_error + parameters: + additionalProperties: false + properties: + value: + default: false + type: boolean + type: object + type: function + - function: + description: '' + name: get_none + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_multiple_items + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Get the current log level.\n\n Returns:\n The current log level.\n " + name: get_log_level + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Echo the run context.\n\n Args:\n ctx: Context object containing request and session information.\n\n + \ Returns:\n Dictionary with an echo message and the deps.\n " + name: echo_deps + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: Use sampling callback. + name: use_sampling + parameters: + additionalProperties: false + properties: + foo: + type: string + required: + - foo + type: object + strict: true + type: function + - function: + description: Use elicitation callback to ask the user a question. + name: use_elicitation + parameters: + additionalProperties: false + properties: + question: + type: string + required: + - question + type: object + strict: true + type: function + - function: + description: '' + name: external + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: The final response which ends this conversation + name: final_result + parameters: + $defs: + Answer: + additionalProperties: false + properties: + answer: + type: string + label: + type: string + required: + - label + - answer + type: object + additionalProperties: false + properties: + answers: + items: + $ref: '#/$defs/Answer' + type: array + required: + - answers + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null},"logprobs":null,"finish_reason":null}],"obfuscation":"3V3AbYBE5dG"} + + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_YLpBLd2Jc52M9Haen7Wg7eD6","type":"function","function":{"name":"get_country","arguments":""}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"o4uS7rYKOP"} + + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{}"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"dian"} + + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_Gvsr5eUu5FioxDbaq5yglsVP","type":"function","function":{"name":"get_product_name","arguments":""}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"7fIwy"} + + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","usage":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{}"}}]},"logprobs":null,"finish_reason":null}],"obfuscation":"AexQ"} + + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"takK"} + + data: {"id":"chatcmpl-CMKAyUumTn5CnEH23XUT0k1X8mUbu","object":"chat.completion.chunk","created":1759436808,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":398,"completion_tokens":40,"total_tokens":438,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"FlYkAKU1h0LO3"} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '756' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '5148' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: 'Tell me: the capital of the country; the weather there; the product name' + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{}' + name: get_country + id: call_YLpBLd2Jc52M9Haen7Wg7eD6 + type: function + - function: + arguments: '{}' + name: get_product_name + id: call_Gvsr5eUu5FioxDbaq5yglsVP + type: function + - content: Mexico + role: tool + tool_call_id: call_YLpBLd2Jc52M9Haen7Wg7eD6 + - content: Pydantic AI + role: tool + tool_call_id: call_Gvsr5eUu5FioxDbaq5yglsVP + model: gpt-4o + stream: true + stream_options: + include_usage: true + tool_choice: required + tools: + - function: + description: '' + name: get_weather + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + - function: + description: '' + name: get_country + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Convert Celsius to Fahrenheit.\n\n Args:\n celsius: Temperature in Celsius\n\n Returns:\n + \ Temperature in Fahrenheit\n " + name: celsius_to_fahrenheit + parameters: + additionalProperties: false + properties: + celsius: + type: number + required: + - celsius + type: object + strict: true + type: function + - function: + description: "Get the weather forecast for a location.\n\n Args:\n location: The location to get the weather + forecast for.\n\n Returns:\n The weather forecast for the location.\n " + name: get_weather_forecast + parameters: + additionalProperties: false + properties: + location: + type: string + required: + - location + type: object + strict: true + type: function + - function: + description: '' + name: get_image_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_dict + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_error + parameters: + additionalProperties: false + properties: + value: + default: false + type: boolean + type: object + type: function + - function: + description: '' + name: get_none + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_multiple_items + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Get the current log level.\n\n Returns:\n The current log level.\n " + name: get_log_level + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Echo the run context.\n\n Args:\n ctx: Context object containing request and session information.\n\n + \ Returns:\n Dictionary with an echo message and the deps.\n " + name: echo_deps + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: Use sampling callback. + name: use_sampling + parameters: + additionalProperties: false + properties: + foo: + type: string + required: + - foo + type: object + strict: true + type: function + - function: + description: Use elicitation callback to ask the user a question. + name: use_elicitation + parameters: + additionalProperties: false + properties: + question: + type: string + required: + - question + type: object + strict: true + type: function + - function: + description: '' + name: external + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: The final response which ends this conversation + name: final_result + parameters: + $defs: + Answer: + additionalProperties: false + properties: + answer: + type: string + label: + type: string + required: + - label + - answer + type: object + additionalProperties: false + properties: + answers: + items: + $ref: '#/$defs/Answer' + type: array + required: + - answers + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_jHlZLWaFnmlufAj8mwu4Ty3g","type":"function","function":{"name":"get_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ciMXtk3At"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"3Zm"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"city"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"LV"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"O"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Mexico"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"3"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"NHJ"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"qxkz"} + + data: {"id":"chatcmpl-CMKAzuw71tu2Qv3CCwMsPkVXL6m3H","object":"chat.completion.chunk","created":1759436809,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":457,"completion_tokens":15,"total_tokens":472,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"wCZkMJEb7Fpn8"} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '658' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '5412' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: 'Tell me: the capital of the country; the weather there; the product name' + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{}' + name: get_country + id: call_YLpBLd2Jc52M9Haen7Wg7eD6 + type: function + - function: + arguments: '{}' + name: get_product_name + id: call_Gvsr5eUu5FioxDbaq5yglsVP + type: function + - content: Mexico + role: tool + tool_call_id: call_YLpBLd2Jc52M9Haen7Wg7eD6 + - content: Pydantic AI + role: tool + tool_call_id: call_Gvsr5eUu5FioxDbaq5yglsVP + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"city":"Mexico City"}' + name: get_weather + id: call_jHlZLWaFnmlufAj8mwu4Ty3g + type: function + - content: sunny + role: tool + tool_call_id: call_jHlZLWaFnmlufAj8mwu4Ty3g + model: gpt-4o + stream: true + stream_options: + include_usage: true + tool_choice: required + tools: + - function: + description: '' + name: get_weather + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + - function: + description: '' + name: get_country + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Convert Celsius to Fahrenheit.\n\n Args:\n celsius: Temperature in Celsius\n\n Returns:\n + \ Temperature in Fahrenheit\n " + name: celsius_to_fahrenheit + parameters: + additionalProperties: false + properties: + celsius: + type: number + required: + - celsius + type: object + strict: true + type: function + - function: + description: "Get the weather forecast for a location.\n\n Args:\n location: The location to get the weather + forecast for.\n\n Returns:\n The weather forecast for the location.\n " + name: get_weather_forecast + parameters: + additionalProperties: false + properties: + location: + type: string + required: + - location + type: object + strict: true + type: function + - function: + description: '' + name: get_image_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_audio_resource_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_product_name_link + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_image + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_dict + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_error + parameters: + additionalProperties: false + properties: + value: + default: false + type: boolean + type: object + type: function + - function: + description: '' + name: get_none + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: '' + name: get_multiple_items + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Get the current log level.\n\n Returns:\n The current log level.\n " + name: get_log_level + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: "Echo the run context.\n\n Args:\n ctx: Context object containing request and session information.\n\n + \ Returns:\n Dictionary with an echo message and the deps.\n " + name: echo_deps + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: Use sampling callback. + name: use_sampling + parameters: + additionalProperties: false + properties: + foo: + type: string + required: + - foo + type: object + strict: true + type: function + - function: + description: Use elicitation callback to ask the user a question. + name: use_elicitation + parameters: + additionalProperties: false + properties: + question: + type: string + required: + - question + type: object + strict: true + type: function + - function: + description: '' + name: external + parameters: + additionalProperties: false + properties: {} + type: object + type: function + - function: + description: The final response which ends this conversation + name: final_result + parameters: + $defs: + Answer: + additionalProperties: false + properties: + answer: + type: string + label: + type: string + required: + - label + - answer + type: object + additionalProperties: false + properties: + answers: + items: + $ref: '#/$defs/Answer' + type: array + required: + - answers + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_TJi2Gf3aj68Ijw5LdRJXWmzA","type":"function","function":{"name":"final_result","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"v8zy8ft9"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AYQ"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answers"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"IxuNpYbNgtsPlgI"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":["}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ii"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"K9C"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"label"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"j"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"k"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Capital"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"JtvwWepk6yBKxTH"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" of"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"zOz"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" the"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"IL"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Country"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"efY1cOdst5TVQi"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"n"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answer"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"P"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"The"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"smF"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" capital"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"dQmWkuqXE0CeIZ"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" of"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"EmP"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Mexico"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"58P1Mp5hfHf0xMX"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" is"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"rtV"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Mexico"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Dh9mGtwF3FvMaXm"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"S"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"3CL"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"},{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"M"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"label"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Q"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"a"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Weather"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"jrLoCkRnnRpKOOa"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" in"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"1GP"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" the"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"tD"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Capital"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"IlFvgx29CSltKn"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"B"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answer"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"0"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"The"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"j6C"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" weather"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"CaddHmNDphcd75"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" in"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"v5t"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Mexico"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"l599SR14aAOnFwt"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" City"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"T"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" is"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"USa"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" currently"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Al8AyyPWxfV5"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" sunny"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"N4t"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"},{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"p"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"label"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"0"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"M"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Product"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"m1SmP9Mjiu7e0Ah"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Name"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Y"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"8"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"answer"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"F"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"The"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"j80"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" product"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"jHtiX5MZMwYT4J"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" name"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"z"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" is"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"oXo"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" P"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"cmNY"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"yd"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"eCMt"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"antic"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"v"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" AI"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Gsh"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"JzJ"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"XcbQm"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"]}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"yNax"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"yr1S"} + + data: {"id":"chatcmpl-CMKB0u5q9WXh4O7ndt9D07U3Rs7Sh","object":"chat.completion.chunk","created":1759436810,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":482,"completion_tokens":68,"total_tokens":550,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"YwxPjYfoXZDYS"} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '483' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_prefect_agent_iter.yaml b/tests/cassettes/test_prefect/test_prefect_agent_iter.yaml new file mode 100644 index 0000000000..f3590d98a4 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_iter.yaml @@ -0,0 +1,81 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '144' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"SsgVgvqWNTz9EU"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"92fa7CSGVwzLT"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"5kxBmMtQ"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"9I4ser5Rs6CHV"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"MOqBx8Fxm"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"XvvNotsIfE7ht"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"9xgVxA4jF"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" City"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"t8SeIYqtXoe"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"20EPJHQt3KcxgJg"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"yXfbidKm0R"} + + data: {"id":"chatcmpl-CMKB5PJNylRj8HPTW1RXOd2Xrfnwa","object":"chat.completion.chunk","created":1759436815,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":8,"total_tokens":22,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":""} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '321' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_prefect_agent_override_deps.yaml b/tests/cassettes/test_prefect/test_prefect_agent_override_deps.yaml new file mode 100644 index 0000000000..bd0cdf0bf4 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_override_deps.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '478' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436824 + id: chatcmpl-CMKBERf51PEIMVQwqKWcUguS4XxS4 + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_1827dd0c55 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_override_tools.yaml b/tests/cassettes/test_prefect/test_prefect_agent_override_tools.yaml new file mode 100644 index 0000000000..6070832042 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_override_tools.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '293' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436823 + id: chatcmpl-CMKBDe7wumQqGYu6O0VVOb27WBBA2 + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_override_toolsets.yaml b/tests/cassettes/test_prefect/test_prefect_agent_override_toolsets.yaml new file mode 100644 index 0000000000..4ffba8a117 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_override_toolsets.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '281' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436823 + id: chatcmpl-CMKBDWolt72vmpE6ZTAXIr8796Zpf + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_run.yaml b/tests/cassettes/test_prefect/test_prefect_agent_run.yaml new file mode 100644 index 0000000000..590eaf0b9e --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_run.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '192' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436812 + id: chatcmpl-CMKB2zKd9FIICG7iouTBKPCs0fLnL + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_run_stream.yaml b/tests/cassettes/test_prefect/test_prefect_agent_run_stream.yaml new file mode 100644 index 0000000000..0e81b9f116 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_run_stream.yaml @@ -0,0 +1,81 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '144' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"puNLxk04l6qiua"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4Oxy6YfjXTmQk"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"GR2ZqJGr"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"McJPiJx5hX14l"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"8woAWlKVG"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"EnaoCf8rYeJXk"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"dF30PReop"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" City"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"1MTP95TlpXh"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"u54rAyQOlY3Erin"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"ZKIcSN2qEI"} + + data: {"id":"chatcmpl-CMKB4CzjM9AbvNcftqEsFcWBSrZNT","object":"chat.completion.chunk","created":1759436814,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":8,"total_tokens":22,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":""} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '163' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_prefect_agent_run_stream_events.yaml b/tests/cassettes/test_prefect/test_prefect_agent_run_stream_events.yaml new file mode 100644 index 0000000000..59c4cbc998 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_run_stream_events.yaml @@ -0,0 +1,78 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '144' + content-type: + - application/json + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"GmahJEMIl7nQy4"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"pIfys15QtbIy6"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"vUl7M9hs"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4esBSFkNdPBkg"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"WswOcLSG4"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wrbY70akMp6ru"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"17213oGdV"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" City"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"XEAeN8ajbAt"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"CBIatQSGHvp1Ckv"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"yu1bsKEkrP"} + + data: {"id":"chatcmpl-COrTbn1vT5ddb2i6ZlOOM8Q6pOvBa","object":"chat.completion.chunk","created":1760041471,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":8,"total_tokens":22,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":""} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '275' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_prefect_agent_run_sync.yaml b/tests/cassettes/test_prefect/test_prefect_agent_run_sync.yaml new file mode 100644 index 0000000000..c677f8744b --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_run_sync.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '523' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436813 + id: chatcmpl-CMKB3vgLyJEUCZlQEixY5epVT6PMo + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_with_hitl_tool.yaml b/tests/cassettes/test_prefect/test_prefect_agent_with_hitl_tool.yaml new file mode 100644 index 0000000000..153b231955 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_with_hitl_tool.yaml @@ -0,0 +1,252 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '639' + content-type: + - application/json + cookie: + - __cf_bm=7JOBuSwLq23njOxapsNW7kv.IVvf410mwgFtyO2Kfgg-1759433561-1.0.1.1-_wqxOi8W0pa7kHLhhrqIUpt3eMJgBPHHgXdqlUQYuvB9J.jFJ5xrabFPJZDaEyvwvyUdSlz33xTFu1K9TYa4F8_Lc_REcoH6QBTVqwb.2dc; + _cfuvid=lrjFtnlzlQWjwsB3p.f8tzYO5FvnTgxmFOc.KaMjUnE-1759433561384-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: Just call tools without asking for confirmation. + role: system + - content: Delete the file `.env` and create `test.txt` + role: user + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: create_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + - function: + description: '' + name: delete_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '1319' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '613' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: tool_calls + index: 0 + logprobs: null + message: + annotations: [] + content: null + refusal: null + role: assistant + tool_calls: + - function: + arguments: '{"path": ".env"}' + name: delete_file + id: call_HMKxpFuWMpNPfuK5352En5En + type: function + - function: + arguments: '{"path": "test.txt"}' + name: create_file + id: call_CAES42XVgl0EvrUmnIoHkMSS + type: function + created: 1759433578 + id: chatcmpl-CMJKsR2K5s2NDFSfDc6DpbanoVePu + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 46 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 71 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 117 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '1109' + content-type: + - application/json + cookie: + - __cf_bm=7JOBuSwLq23njOxapsNW7kv.IVvf410mwgFtyO2Kfgg-1759433561-1.0.1.1-_wqxOi8W0pa7kHLhhrqIUpt3eMJgBPHHgXdqlUQYuvB9J.jFJ5xrabFPJZDaEyvwvyUdSlz33xTFu1K9TYa4F8_Lc_REcoH6QBTVqwb.2dc; + _cfuvid=lrjFtnlzlQWjwsB3p.f8tzYO5FvnTgxmFOc.KaMjUnE-1759433561384-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: Just call tools without asking for confirmation. + role: system + - content: Delete the file `.env` and create `test.txt` + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"path": ".env"}' + name: delete_file + id: call_HMKxpFuWMpNPfuK5352En5En + type: function + - function: + arguments: '{"path": "test.txt"}' + name: create_file + id: call_CAES42XVgl0EvrUmnIoHkMSS + type: function + - content: 'true' + role: tool + tool_call_id: call_HMKxpFuWMpNPfuK5352En5En + - content: Success + role: tool + tool_call_id: call_CAES42XVgl0EvrUmnIoHkMSS + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: create_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + - function: + description: '' + name: delete_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '883' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '701' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The file `.env` has been deleted, and `test.txt` has been successfully created. + refusal: null + role: assistant + created: 1759433579 + id: chatcmpl-CMJKtEA5SwYUsQ3kQjGuPv9wcLXza + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 20 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 133 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 153 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_with_hitl_tool_sync.yaml b/tests/cassettes/test_prefect/test_prefect_agent_with_hitl_tool_sync.yaml new file mode 100644 index 0000000000..c0a442f828 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_with_hitl_tool_sync.yaml @@ -0,0 +1,252 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '639' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: Just call tools without asking for confirmation. + role: system + - content: Delete the file `.env` and create `test.txt` + role: user + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: create_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + - function: + description: '' + name: delete_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '1319' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '635' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: tool_calls + index: 0 + logprobs: null + message: + annotations: [] + content: null + refusal: null + role: assistant + tool_calls: + - function: + arguments: '{"path": ".env"}' + name: delete_file + id: call_8fbzpUqmtMDk2qNYs1GdpxHr + type: function + - function: + arguments: '{"path": "test.txt"}' + name: create_file + id: call_fPoyfekeDdKhPPYKRSyhawdr + type: function + created: 1759436826 + id: chatcmpl-CMKBGi9aqulxtEtoJ1lmHcP69FqTZ + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 46 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 71 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 117 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '1109' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: Just call tools without asking for confirmation. + role: system + - content: Delete the file `.env` and create `test.txt` + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"path": ".env"}' + name: delete_file + id: call_8fbzpUqmtMDk2qNYs1GdpxHr + type: function + - function: + arguments: '{"path": "test.txt"}' + name: create_file + id: call_fPoyfekeDdKhPPYKRSyhawdr + type: function + - content: 'true' + role: tool + tool_call_id: call_8fbzpUqmtMDk2qNYs1GdpxHr + - content: Success + role: tool + tool_call_id: call_fPoyfekeDdKhPPYKRSyhawdr + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: create_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + - function: + description: '' + name: delete_file + parameters: + additionalProperties: false + properties: + path: + type: string + required: + - path + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '892' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '401' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The file `.env` has been deleted, and the file `test.txt` has been created successfully. + refusal: null + role: assistant + created: 1759436827 + id: chatcmpl-CMKBHqM1G37DSY6tYbAJhUzUvUcTs + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 22 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 133 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 155 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_with_model_retry.yaml b/tests/cassettes/test_prefect/test_prefect_agent_with_model_retry.yaml new file mode 100644 index 0000000000..50202f54b8 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_with_model_retry.yaml @@ -0,0 +1,338 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '347' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the weather in CDMX? + role: user + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: get_weather_in_city + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '1086' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '666' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: tool_calls + index: 0 + logprobs: null + message: + annotations: [] + content: null + refusal: null + role: assistant + tool_calls: + - function: + arguments: '{"city":"CDMX"}' + name: get_weather_in_city + id: call_EpsjIY9eR0MmTjkqqtRm82oV + type: function + created: 1759436828 + id: chatcmpl-CMKBIv9d9ZDcFYdPe9EqkP3IHUWTl + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 17 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 47 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 64 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '665' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the weather in CDMX? + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"city":"CDMX"}' + name: get_weather_in_city + id: call_EpsjIY9eR0MmTjkqqtRm82oV + type: function + - content: |- + Did you mean Mexico City? + + Fix the errors and try again. + role: tool + tool_call_id: call_EpsjIY9eR0MmTjkqqtRm82oV + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: get_weather_in_city + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '1094' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '524' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: tool_calls + index: 0 + logprobs: null + message: + annotations: [] + content: null + refusal: null + role: assistant + tool_calls: + - function: + arguments: '{"city":"Mexico City"}' + name: get_weather_in_city + id: call_2IrUdlpgInWUCEEqKKvUZ7pR + type: function + created: 1759436829 + id: chatcmpl-CMKBJ4jh8JKiVo6zlzdzlYkubYF3w + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 17 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 87 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 104 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '937' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the weather in CDMX? + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"city":"CDMX"}' + name: get_weather_in_city + id: call_EpsjIY9eR0MmTjkqqtRm82oV + type: function + - content: |- + Did you mean Mexico City? + + Fix the errors and try again. + role: tool + tool_call_id: call_EpsjIY9eR0MmTjkqqtRm82oV + - content: null + role: assistant + tool_calls: + - function: + arguments: '{"city":"Mexico City"}' + name: get_weather_in_city + id: call_2IrUdlpgInWUCEEqKKvUZ7pR + type: function + - content: sunny + role: tool + tool_call_id: call_2IrUdlpgInWUCEEqKKvUZ7pR + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: get_weather_in_city + parameters: + additionalProperties: false + properties: + city: + type: string + required: + - city + type: object + strict: true + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '850' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '635' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The weather in Mexico City is currently sunny. + refusal: null + role: assistant + created: 1759436830 + id: chatcmpl-CMKBKP3QbtwSh3CEB5kLGFk0aCkp9 + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 10 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 116 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 126 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_prefect_agent_with_unserializable_deps.yaml b/tests/cassettes/test_prefect/test_prefect_agent_with_unserializable_deps.yaml new file mode 100644 index 0000000000..49201d5354 --- /dev/null +++ b/tests/cassettes/test_prefect/test_prefect_agent_with_unserializable_deps.yaml @@ -0,0 +1,196 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '254' + content-type: + - application/json + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: Test + role: user + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: get_data + parameters: + additionalProperties: false + properties: {} + type: object + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '1058' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '367' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: tool_calls + index: 0 + logprobs: null + message: + annotations: [] + content: null + refusal: null + role: assistant + tool_calls: + - function: + arguments: '{}' + name: get_data + id: call_ZpchjjJF0zZucsrt9uox8LbI + type: function + created: 1759437409 + id: chatcmpl-CMKKf6b3ucz04gFT16x9CL2MB9Lov + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 10 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 32 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 42 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '488' + content-type: + - application/json + cookie: + - __cf_bm=YoWGgMpBznbHYqCUwd_rdLL8YagVIEsOUunxx0kyYyc-1759437409-1.0.1.1-c3raKeyK1YqEy51kky7GwJOuK2HL7a0ISNAOSPDyb0EDCXpB8LRSuar6BbKbpPHecFESctRJRsOEi4pzWx2RIYauvQ9_T8LGtqI1IzML36A; + _cfuvid=UwNu7qoZSij0N1FVup_eixICt2m.tKCCLD4XnSuIGF4-1759437409991-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: Test + role: user + - content: null + role: assistant + tool_calls: + - function: + arguments: '{}' + name: get_data + id: call_ZpchjjJF0zZucsrt9uox8LbI + type: function + - content: '42' + role: tool + tool_call_id: call_ZpchjjJF0zZucsrt9uox8LbI + model: gpt-4o + stream: false + tool_choice: auto + tools: + - function: + description: '' + name: get_data + parameters: + additionalProperties: false + properties: {} + type: object + type: function + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '833' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '718' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The result from the test is 42. + refusal: null + role: assistant + created: 1759437410 + id: chatcmpl-CMKKgYzDnjpWxgGyFiRbBhPZGwSAA + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 10 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 51 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 61 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_run_stream_in_flow.yaml b/tests/cassettes/test_prefect/test_run_stream_in_flow.yaml new file mode 100644 index 0000000000..6147d4d044 --- /dev/null +++ b/tests/cassettes/test_prefect/test_run_stream_in_flow.yaml @@ -0,0 +1,81 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '144' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: true + stream_options: + include_usage: true + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |+ + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"uaF2tiolOC6T92"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"9TqFtgojc4OKI"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"km44OCnM"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"CgfMBMHcygAQm"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"nNn45zljG"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"aUQgMnS3u0ZTP"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" Mexico"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"8R0dP3b1h"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":" City"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"1YsopLFHiNj"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"b311ST2heqoENlJ"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"qg77HbneEM"} + + data: {"id":"chatcmpl-CMKBAQ4vWbdeHIVnqDgk62In6psws","object":"chat.completion.chunk","created":1759436820,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_f33640a400","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":8,"total_tokens":22,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":""} + + data: [DONE] + + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '406' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + status: + code: 200 + message: OK +version: 1 +... diff --git a/tests/cassettes/test_prefect/test_run_sync_in_flow.yaml b/tests/cassettes/test_prefect/test_run_sync_in_flow.yaml new file mode 100644 index 0000000000..4236f05ea6 --- /dev/null +++ b/tests/cassettes/test_prefect/test_run_sync_in_flow.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + cookie: + - __cf_bm=EGlv.LzYbJ3w9GCuZFkTZc6oq0QWEOM0NG7aMJPGjvk-1759436802-1.0.1.1-6oxojh3oohGyLcgU4JW0tlAp58gjOtU_gxXDExM6TYMtkY__jPait0ymNkQmMfL4if.C.4i5xBwDmMoWScw7U9pDwoE_d_f999NvYqOWu0k; + _cfuvid=X9yhA1__xIYNmD8jrktglFeG98OLAwcfPSs_ukpzW4c-1759436802603-0.0.1.1-604800000 + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '755' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436818 + id: chatcmpl-CMKB8RLookcCYrckGyaSdt67QMBAi + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_prefect/test_simple_agent_run_in_flow.yaml b/tests/cassettes/test_prefect/test_simple_agent_run_in_flow.yaml new file mode 100644 index 0000000000..82b6047e4c --- /dev/null +++ b/tests/cassettes/test_prefect/test_simple_agent_run_in_flow.yaml @@ -0,0 +1,79 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '105' + content-type: + - application/json + host: + - api.openai.com + method: POST + parsed_body: + messages: + - content: What is the capital of Mexico? + role: user + model: gpt-4o + stream: false + uri: https://api.openai.com/v1/chat/completions + response: + headers: + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + connection: + - keep-alive + content-length: + - '838' + content-type: + - application/json + openai-organization: + - user-xgcwoiiqpw3hpu7oxktn2g9g + openai-processing-ms: + - '412' + openai-project: + - proj_nKlzMl1B51a3SCL1Rndf7JTx + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + choices: + - finish_reason: stop + index: 0 + logprobs: null + message: + annotations: [] + content: The capital of Mexico is Mexico City. + refusal: null + role: assistant + created: 1759436802 + id: chatcmpl-CMKAsCLvDAxfgEbsZ8xiTlz1DVVo4 + model: gpt-4o-2024-08-06 + object: chat.completion + service_tier: default + system_fingerprint: fp_cbf1785567 + usage: + completion_tokens: 8 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens: 14 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + total_tokens: 22 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_prefect.py b/tests/test_prefect.py new file mode 100644 index 0000000000..ed8836ca71 --- /dev/null +++ b/tests/test_prefect.py @@ -0,0 +1,1260 @@ +from __future__ import annotations + +import os +import warnings +from collections.abc import AsyncIterable, AsyncIterator, Iterator +from contextlib import contextmanager +from dataclasses import dataclass, field +from datetime import datetime, timedelta +from typing import Literal +from unittest.mock import MagicMock + +import pytest +from pydantic import BaseModel + +from pydantic_ai import ( + Agent, + AgentRunResult, + AgentRunResultEvent, + AgentStreamEvent, + ExternalToolset, + FunctionToolset, + ModelMessage, + ModelRequest, + ModelResponse, + ModelSettings, + RunContext, + TextPart, + UserPromptPart, +) +from pydantic_ai.exceptions import ApprovalRequired, CallDeferred, ModelRetry, UserError +from pydantic_ai.models import cached_async_http_client +from pydantic_ai.models.function import AgentInfo, FunctionModel +from pydantic_ai.models.test import TestModel +from pydantic_ai.tools import DeferredToolRequests, DeferredToolResults, ToolDefinition +from pydantic_ai.usage import RequestUsage + +try: + from prefect import flow, task + from prefect.testing.utilities import prefect_test_harness + + from pydantic_ai.durable_exec.prefect import ( + DEFAULT_PYDANTIC_AI_CACHE_POLICY, + PrefectAgent, + PrefectFunctionToolset, + PrefectMCPServer, + PrefectModel, + ) + from pydantic_ai.durable_exec.prefect._cache_policies import PrefectAgentInputs +except ImportError: # pragma: lax no cover + pytest.skip('Prefect is not installed', allow_module_level=True) + +try: + import logfire + from logfire.testing import CaptureLogfire +except ImportError: # pragma: lax no cover + pytest.skip('logfire not installed', allow_module_level=True) + +try: + from pydantic_ai.mcp import MCPServerStdio +except ImportError: # pragma: lax no cover + pytest.skip('mcp not installed', allow_module_level=True) + +try: + from pydantic_ai.models.openai import OpenAIChatModel + from pydantic_ai.providers.openai import OpenAIProvider +except ImportError: # pragma: lax no cover + pytest.skip('openai not installed', allow_module_level=True) + +from inline_snapshot import snapshot + +from .conftest import IsStr + +pytestmark = [ + pytest.mark.anyio, + pytest.mark.vcr, + pytest.mark.xdist_group(name='prefect'), +] + +# We need to use a custom cached HTTP client here as the default one created for OpenAIProvider will be closed automatically +# at the end of each test, but we need this one to live longer. +http_client = cached_async_http_client(provider='prefect') + + +@pytest.fixture(autouse=True, scope='module') +async def close_cached_httpx_client(anyio_backend: str) -> AsyncIterator[None]: + try: + yield + finally: + await http_client.aclose() + + +@pytest.fixture(autouse=True) +def setup_logfire_instrumentation() -> Iterator[None]: + # Set up logfire for the tests. Prefect sets the `traceparent` header, so we explicitly enable + # distributed tracing the tests to avoid the warning. + logfire.configure(metrics=False, distributed_tracing=False) + + yield + + +@pytest.fixture(autouse=True, scope='session') +def setup_prefect_test_harness() -> Iterator[None]: + """Set up Prefect test harness for all tests.""" + with prefect_test_harness(server_startup_timeout=60): + yield + + +@contextmanager +def flow_raises(exc_type: type[Exception], exc_message: str) -> Iterator[None]: + """Helper for asserting that a Prefect flow fails with the expected error.""" + with pytest.raises(Exception) as exc_info: + yield + assert isinstance(exc_info.value, Exception) + assert str(exc_info.value) == exc_message + + +model = OpenAIChatModel( + 'gpt-4o', + provider=OpenAIProvider( + api_key=os.getenv('OPENAI_API_KEY', 'mock-api-key'), + http_client=http_client, + ), +) + +# Simple agent for basic testing +simple_agent = Agent(model, name='simple_agent') +simple_prefect_agent = PrefectAgent(simple_agent) + + +async def test_simple_agent_run_in_flow(allow_model_requests: None) -> None: + """Test that a simple agent can run in a Prefect flow.""" + + @flow(name='test_simple_agent_run_in_flow') + async def run_simple_agent() -> str: + result = await simple_prefect_agent.run('What is the capital of Mexico?') + return result.output + + output = await run_simple_agent() + assert output == snapshot('The capital of Mexico is Mexico City.') + + +class Deps(BaseModel): + country: str + + +@task(name='event_stream_handler') +async def event_stream_handler( + ctx: RunContext[Deps], + stream: AsyncIterable[AgentStreamEvent], +): + logfire.info(f'{ctx.run_step=}') + async for event in stream: + logfire.info('event', event=event) + + +# This doesn't need to be a task +async def get_country(ctx: RunContext[Deps]) -> str: + return ctx.deps.country + + +class WeatherArgs(BaseModel): + city: str + + +@task(name='get_weather') +def get_weather(args: WeatherArgs) -> str: + if args.city == 'Mexico City': + return 'sunny' + else: + return 'unknown' # pragma: no cover + + +@dataclass +class Answer: + label: str + answer: str + + +@dataclass +class Response: + answers: list[Answer] + + +@dataclass +class BasicSpan: + content: str + children: list[BasicSpan] = field(default_factory=list) + parent_id: int | None = field(repr=False, compare=False, default=None) + + +complex_agent = Agent( + model, + deps_type=Deps, + output_type=Response, + toolsets=[ + FunctionToolset[Deps](tools=[get_country], id='country'), + MCPServerStdio('python', ['-m', 'tests.mcp_server'], timeout=20, id='mcp'), + ExternalToolset(tool_defs=[ToolDefinition(name='external')], id='external'), + ], + tools=[get_weather], + event_stream_handler=event_stream_handler, + instrument=True, # Enable instrumentation for testing + name='complex_agent', +) +complex_prefect_agent = PrefectAgent(complex_agent) + + +async def test_complex_agent_run_in_flow(allow_model_requests: None, capfire: CaptureLogfire) -> None: + """Test a complex agent with tools, MCP servers, and event stream handler.""" + + @flow(name='test_complex_agent_run_in_flow') + async def run_complex_agent() -> Response: + result = await complex_prefect_agent.run( + 'Tell me: the capital of the country; the weather there; the product name', deps=Deps(country='Mexico') + ) + return result.output + + # Prefect sets the `traceparent` header, so we explicitly disable distributed tracing for the tests to avoid the warning, + # but we can't set that configuration for the capfire fixture, so we ignore the warning here. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=RuntimeWarning) + output = await run_complex_agent() + assert output == snapshot( + Response( + answers=[ + Answer(label='Capital of the country', answer='Mexico City'), + Answer(label='Weather in the capital', answer='Sunny'), + Answer(label='Product name', answer='Pydantic AI'), + ] + ) + ) + + # Verify logfire instrumentation with full span tree + exporter = capfire.exporter + spans = exporter.exported_spans_as_dict() + basic_spans_by_id = { + span['context']['span_id']: BasicSpan( + parent_id=span['parent']['span_id'] if span['parent'] else None, + content=attributes.get('event') or attributes['logfire.msg'], + ) + for span in spans + if (attributes := span.get('attributes')) + } + root_span = None + for basic_span in basic_spans_by_id.values(): + if basic_span.parent_id is None: + root_span = basic_span + else: + parent_id = basic_span.parent_id + parent_span = basic_spans_by_id[parent_id] + parent_span.children.append(basic_span) + + assert root_span == snapshot( + BasicSpan( + content=IsStr(regex=r'\w+-\w+'), # Random Prefect flow run name + children=[ + BasicSpan( + content='Found propagated trace context. See https://logfire.pydantic.dev/docs/how-to-guides/distributed-tracing/#unintentional-distributed-tracing.' + ), + BasicSpan( + content=IsStr(regex=r'\w+-\w+'), # Random Prefect flow run name + children=[ + BasicSpan( + content='complex_agent run', + children=[ + BasicSpan( + content='chat gpt-4o', + children=[ + BasicSpan( + content=IsStr(regex=r'Model Request \(Streaming\): gpt-4o-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=1'), + BasicSpan( + content='{"index":0,"part":{"tool_name":"get_country","args":"","tool_call_id":"call_rI3WKPYvVwlOgCGRjsPP2hEx","part_kind":"tool-call"},"event_kind":"part_start"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"{}","tool_call_id":"call_rI3WKPYvVwlOgCGRjsPP2hEx","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + ], + ) + ], + ) + ], + ), + BasicSpan( + content=IsStr(regex=r'Handle Stream Event-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=1'), + BasicSpan( + content='{"part":{"tool_name":"get_country","args":"{}","tool_call_id":"call_rI3WKPYvVwlOgCGRjsPP2hEx","part_kind":"tool-call"},"event_kind":"function_tool_call"}' + ), + ], + ) + ], + ), + BasicSpan( + content='running 1 tool', + children=[ + BasicSpan( + content='running tool: get_country', + children=[BasicSpan(content=IsStr(regex=r'Call Tool: get_country-\w+'))], + ), + BasicSpan( + content=IsStr(regex=r'Handle Stream Event-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=1'), + BasicSpan( + content=IsStr( + regex=r'{"result":{"tool_name":"get_country","content":"Mexico","tool_call_id":"call_rI3WKPYvVwlOgCGRjsPP2hEx","metadata":null,"timestamp":".+?","part_kind":"tool-return"},"content":null,"event_kind":"function_tool_result"}' + ) + ), + ], + ) + ], + ), + ], + ), + BasicSpan( + content='chat gpt-4o', + children=[ + BasicSpan( + content=IsStr(regex=r'Model Request \(Streaming\): gpt-4o-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=2'), + BasicSpan( + content='{"index":0,"part":{"tool_name":"get_weather","args":"","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_kind":"tool-call"},"event_kind":"part_start"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"{\\"ci","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"ty\\": ","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\"Mexic","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"o Ci","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"ty\\"}","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":1,"part":{"tool_name":"get_product_name","args":"","tool_call_id":"call_SkGkkGDvHQEEk0CGbnAh2AQw","part_kind":"tool-call"},"event_kind":"part_start"}' + ), + BasicSpan( + content='{"index":1,"delta":{"tool_name_delta":null,"args_delta":"{}","tool_call_id":"call_SkGkkGDvHQEEk0CGbnAh2AQw","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + ], + ) + ], + ) + ], + ), + BasicSpan( + content=IsStr(regex=r'Handle Stream Event-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=2'), + BasicSpan( + content='{"part":{"tool_name":"get_weather","args":"{\\"city\\": \\"Mexico City\\"}","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","part_kind":"tool-call"},"event_kind":"function_tool_call"}' + ), + ], + ) + ], + ), + BasicSpan( + content=IsStr(regex=r'Handle Stream Event-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=2'), + BasicSpan( + content='{"part":{"tool_name":"get_product_name","args":"{}","tool_call_id":"call_SkGkkGDvHQEEk0CGbnAh2AQw","part_kind":"tool-call"},"event_kind":"function_tool_call"}' + ), + ], + ) + ], + ), + BasicSpan( + content='running 2 tools', + children=[ + BasicSpan( + content='running tool: get_product_name', + children=[ + BasicSpan(content=IsStr(regex=r'Call MCP Tool: get_product_name-\w+')) + ], + ), + BasicSpan( + content=IsStr(regex=r'Handle Stream Event-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=2'), + BasicSpan( + content=IsStr( + regex=r'{"result":{"tool_name":"get_product_name","content":"Pydantic AI","tool_call_id":"call_SkGkkGDvHQEEk0CGbnAh2AQw","metadata":null,"timestamp":".+?","part_kind":"tool-return"},"content":null,"event_kind":"function_tool_result"}' + ) + ), + ], + ) + ], + ), + BasicSpan( + content='running tool: get_weather', + children=[ + BasicSpan( + content=IsStr(regex=r'Call Tool: get_weather-\w+'), + children=[BasicSpan(content=IsStr(regex=r'get_weather-\w+'))], + ) + ], + ), + BasicSpan( + content=IsStr(regex=r'Handle Stream Event-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=2'), + BasicSpan( + content=IsStr( + regex=r'{"result":{"tool_name":"get_weather","content":"sunny","tool_call_id":"call_NS4iQj14cDFwc0BnrKqDHavt","metadata":null,"timestamp":".+?","part_kind":"tool-return"},"content":null,"event_kind":"function_tool_result"}' + ) + ), + ], + ) + ], + ), + ], + ), + BasicSpan( + content='chat gpt-4o', + children=[ + BasicSpan( + content=IsStr(regex=r'Model Request \(Streaming\): gpt-4o-\w+'), + children=[ + BasicSpan( + content=IsStr(regex=r'event_stream_handler-\w+'), + children=[ + BasicSpan(content='ctx.run_step=3'), + BasicSpan( + content='{"index":0,"part":{"tool_name":"final_result","args":"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_kind":"tool-call"},"event_kind":"part_start"}' + ), + BasicSpan( + content='{"tool_name":"final_result","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","event_kind":"final_result"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"{\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"answers","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":[","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"{\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"label","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"Capital","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" of","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" the","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" country","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\",\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"answer","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"Mexico","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" City","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\"},{\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"label","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"Weather","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" in","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" the","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" capital","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\",\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"answer","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"Sunny","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\"},{\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"label","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"Product","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" name","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\",\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"answer","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\":\\"","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"P","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"yd","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"antic","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":" AI","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"\\"}","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + BasicSpan( + content='{"index":0,"delta":{"tool_name_delta":null,"args_delta":"]}","tool_call_id":"call_QcKhHXwXzqOXJUUHJb1TB2V5","part_delta_kind":"tool_call"},"event_kind":"part_delta"}' + ), + ], + ) + ], + ) + ], + ), + ], + ) + ], + ), + ], + ) + ) + + +async def test_multiple_agents(allow_model_requests: None) -> None: + """Test that multiple agents can run in a Prefect flow.""" + + @flow(name='test_multiple_agents') + async def run_multiple_agents() -> tuple[str, Response]: + result1 = await simple_prefect_agent.run('What is the capital of Mexico?') + result2 = await complex_prefect_agent.run( + 'Tell me: the capital of the country; the weather there; the product name', deps=Deps(country='Mexico') + ) + return result1.output, result2.output + + output1, output2 = await run_multiple_agents() + assert output1 == snapshot('The capital of Mexico is Mexico City.') + assert output2 == snapshot( + Response( + answers=[ + Answer(label='Capital of the Country', answer='The capital of Mexico is Mexico City.'), + Answer(label='Weather in the Capital', answer='The weather in Mexico City is currently sunny.'), + Answer(label='Product Name', answer='The product name is Pydantic AI.'), + ] + ) + ) + + +async def test_agent_requires_name() -> None: + """Test that PrefectAgent requires a name.""" + agent_without_name = Agent(model) + + with pytest.raises(UserError) as exc_info: + PrefectAgent(agent_without_name) + + assert 'unique' in str(exc_info.value).lower() and 'name' in str(exc_info.value).lower() + + +async def test_agent_requires_model_at_creation() -> None: + """Test that PrefectAgent requires model to be set at creation time.""" + agent_without_model = Agent(name='test_agent') + + with pytest.raises(UserError) as exc_info: + PrefectAgent(agent_without_model) + + assert 'model' in str(exc_info.value).lower() + + +async def test_toolset_without_id(): + """Test that agents can be created with toolsets without IDs.""" + # This is allowed in Prefect + PrefectAgent(Agent(model=model, name='test_agent', toolsets=[FunctionToolset()])) + + +async def test_prefect_agent(): + """Test that PrefectAgent properly wraps model and toolsets.""" + assert isinstance(complex_prefect_agent.model, PrefectModel) + assert complex_prefect_agent.model.wrapped == complex_agent.model + + # Prefect wraps MCP servers and function toolsets + toolsets = complex_prefect_agent.toolsets + # Note: toolsets include the output toolset which is not wrapped + assert len(toolsets) >= 4 + + # Find the wrapped toolsets (skip the internal output toolset) + prefect_function_toolsets = [ts for ts in toolsets if isinstance(ts, PrefectFunctionToolset)] + prefect_mcp_toolsets = [ts for ts in toolsets if isinstance(ts, PrefectMCPServer)] + external_toolsets = [ts for ts in toolsets if isinstance(ts, ExternalToolset)] + + # Verify we have the expected wrapped toolsets + assert len(prefect_function_toolsets) >= 2 # agent tools + country toolset + assert len(prefect_mcp_toolsets) == 1 # mcp toolset + assert len(external_toolsets) == 1 # external toolset + + # Verify MCP server is wrapped + mcp_toolset = prefect_mcp_toolsets[0] + assert mcp_toolset.id == 'mcp' + # The wrapped toolset is the MCPServerStdio instance from the complex_agent + # complex_agent.toolsets[0] is FunctionToolset for get_country + # complex_agent.toolsets[1] is MCPServerStdio for mcp + assert isinstance(mcp_toolset.wrapped, MCPServerStdio) + + # Verify external toolset is NOT wrapped (passed through) + external_toolset = external_toolsets[0] + assert external_toolset.id == 'external' + + +async def test_prefect_agent_run(allow_model_requests: None) -> None: + """Test that agent.run() works (auto-wrapped as flow).""" + result = await simple_prefect_agent.run('What is the capital of Mexico?') + assert result.output == snapshot('The capital of Mexico is Mexico City.') + + +def test_prefect_agent_run_sync(allow_model_requests: None): + """Test that agent.run_sync() works.""" + result = simple_prefect_agent.run_sync('What is the capital of Mexico?') + assert result.output == snapshot('The capital of Mexico is Mexico City.') + + +async def test_prefect_agent_run_stream(allow_model_requests: None): + """Test that agent.run_stream() works outside of flows.""" + async with simple_prefect_agent.run_stream('What is the capital of Mexico?') as result: + assert [c async for c in result.stream_text(debounce_by=None)] == snapshot( + [ + 'The', + 'The capital', + 'The capital of', + 'The capital of Mexico', + 'The capital of Mexico is', + 'The capital of Mexico is Mexico', + 'The capital of Mexico is Mexico City', + 'The capital of Mexico is Mexico City.', + ] + ) + + +async def test_prefect_agent_run_stream_events(allow_model_requests: None): + """Test that agent.run_stream_events() works.""" + events = [event async for event in simple_prefect_agent.run_stream_events('What is the capital of Mexico?')] + assert events == snapshot( + [AgentRunResultEvent(result=AgentRunResult(output='The capital of Mexico is Mexico City.'))] + ) + + +async def test_prefect_agent_iter(allow_model_requests: None): + """Test that agent.iter() works.""" + outputs: list[str] = [] + async with simple_prefect_agent.iter('What is the capital of Mexico?') as run: + async for node in run: + if Agent.is_model_request_node(node): + async with node.stream(run.ctx) as stream: + async for chunk in stream.stream_text(debounce_by=None): + outputs.append(chunk) + assert outputs == snapshot( + [ + 'The', + 'The capital', + 'The capital of', + 'The capital of Mexico', + 'The capital of Mexico is', + 'The capital of Mexico is Mexico', + 'The capital of Mexico is Mexico City', + 'The capital of Mexico is Mexico City.', + ] + ) + + +def test_run_sync_in_flow(allow_model_requests: None) -> None: + """Test that run_sync works inside a Prefect flow.""" + + @flow(name='test_run_sync_in_flow') + def run_simple_agent_sync() -> str: + result = simple_prefect_agent.run_sync('What is the capital of Mexico?') + return result.output + + output = run_simple_agent_sync() + assert output == snapshot('The capital of Mexico is Mexico City.') + + +async def test_run_stream_in_flow(allow_model_requests: None) -> None: + """Test that run_stream errors when used inside a Prefect flow.""" + + @flow(name='test_run_stream_in_flow') + async def run_stream_workflow(): + async with simple_prefect_agent.run_stream('What is the capital of Mexico?') as result: + return await result.get_output() # pragma: no cover + + with flow_raises( + UserError, + snapshot( + '`agent.run_stream()` cannot be used inside a Prefect flow. ' + 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.' + ), + ): + await run_stream_workflow() + + +async def test_run_stream_events_in_flow(allow_model_requests: None) -> None: + """Test that run_stream_events errors when used inside a Prefect flow.""" + + @flow(name='test_run_stream_events_in_flow') + async def run_stream_events_workflow(): + return [event async for event in simple_prefect_agent.run_stream_events('What is the capital of Mexico?')] + + with flow_raises( + UserError, + snapshot( + '`agent.run_stream_events()` cannot be used inside a Prefect flow. ' + 'Set an `event_stream_handler` on the agent and use `agent.run()` instead.' + ), + ): + await run_stream_events_workflow() + + +async def test_iter_in_flow(allow_model_requests: None) -> None: + """Test that iter works inside a Prefect flow.""" + + @flow(name='test_iter_in_flow') + async def run_iter_workflow(): + outputs: list[str] = [] + async with simple_prefect_agent.iter('What is the capital of Mexico?') as run: + async for node in run: + if Agent.is_model_request_node(node): + async with node.stream(run.ctx) as stream: + async for chunk in stream.stream_text(debounce_by=None): + outputs.append(chunk) + return outputs + + outputs = await run_iter_workflow() + # If called in a workflow, the output is a single concatenated string. + assert outputs == snapshot( + [ + 'The capital of Mexico is Mexico City.', + ] + ) + + +async def test_prefect_agent_run_with_model(allow_model_requests: None) -> None: + """Test that passing model at runtime errors appropriately.""" + with flow_raises( + UserError, + snapshot( + 'Non-Prefect model cannot be set at agent run time inside a Prefect flow, it must be set at agent creation time.' + ), + ): + await simple_prefect_agent.run('What is the capital of Mexico?', model=model) + + +async def test_prefect_agent_override_model() -> None: + """Test that overriding model in a flow context errors.""" + + @flow(name='test_override_model') + async def override_model_flow(): + with simple_prefect_agent.override(model=model): + pass + + with flow_raises( + UserError, + snapshot( + 'Non-Prefect model cannot be contextually overridden inside a Prefect flow, it must be set at agent creation time.' + ), + ): + await override_model_flow() + + +async def test_prefect_agent_override_toolsets(allow_model_requests: None) -> None: + """Test that overriding toolsets works.""" + + @flow(name='test_override_toolsets') + async def override_toolsets_flow(): + with simple_prefect_agent.override(toolsets=[FunctionToolset()]): + result = await simple_prefect_agent.run('What is the capital of Mexico?') + return result.output + + output = await override_toolsets_flow() + assert output == snapshot('The capital of Mexico is Mexico City.') + + +async def test_prefect_agent_override_tools(allow_model_requests: None) -> None: + """Test that overriding tools works.""" + + @flow(name='test_override_tools') + async def override_tools_flow(): + with simple_prefect_agent.override(tools=[get_weather]): + result = await simple_prefect_agent.run('What is the capital of Mexico?') + return result.output + + output = await override_tools_flow() + assert output == snapshot('The capital of Mexico is Mexico City.') + + +async def test_prefect_agent_override_deps(allow_model_requests: None) -> None: + """Test that overriding deps works.""" + + @flow(name='test_override_deps') + async def override_deps_flow(): + with simple_prefect_agent.override(deps=None): + result = await simple_prefect_agent.run('What is the capital of Mexico?') + return result.output + + output = await override_deps_flow() + assert output == snapshot('The capital of Mexico is Mexico City.') + + +# Test human-in-the-loop with HITL tool +hitl_agent = Agent( + model, + name='hitl_agent', + output_type=[str, DeferredToolRequests], + instructions='Just call tools without asking for confirmation.', +) + + +@task(name='create_file') +@hitl_agent.tool +def create_file(ctx: RunContext[None], path: str) -> None: + raise CallDeferred + + +@task(name='delete_file') +@hitl_agent.tool +def delete_file(ctx: RunContext[None], path: str) -> bool: + if not ctx.tool_call_approved: + raise ApprovalRequired + return True + + +hitl_prefect_agent = PrefectAgent(hitl_agent) + + +async def test_prefect_agent_with_hitl_tool(allow_model_requests: None) -> None: + """Test human-in-the-loop with deferred tool calls and approvals.""" + + @flow(name='test_hitl_tool') + async def hitl_main_loop(prompt: str) -> AgentRunResult[str | DeferredToolRequests]: + messages: list[ModelMessage] = [ModelRequest.user_text_prompt(prompt)] + deferred_tool_results: DeferredToolResults | None = None + + result = await hitl_prefect_agent.run(message_history=messages, deferred_tool_results=deferred_tool_results) + messages = result.all_messages() + + if isinstance(result.output, DeferredToolRequests): # pragma: no branch + # Handle deferred requests + results = DeferredToolResults() + for tool_call in result.output.approvals: + results.approvals[tool_call.tool_call_id] = True + for tool_call in result.output.calls: + results.calls[tool_call.tool_call_id] = 'Success' + + # Second run with results + result = await hitl_prefect_agent.run(message_history=messages, deferred_tool_results=results) + + return result + + result = await hitl_main_loop('Delete the file `.env` and create `test.txt`') + assert isinstance(result.output, str) + assert 'deleted' in result.output.lower() or 'created' in result.output.lower() + + +def test_prefect_agent_with_hitl_tool_sync(allow_model_requests: None) -> None: + """Test human-in-the-loop with sync version.""" + + @flow(name='test_hitl_tool_sync') + def hitl_main_loop_sync(prompt: str) -> AgentRunResult[str | DeferredToolRequests]: + messages: list[ModelMessage] = [ModelRequest.user_text_prompt(prompt)] + deferred_tool_results: DeferredToolResults | None = None + + result = hitl_prefect_agent.run_sync(message_history=messages, deferred_tool_results=deferred_tool_results) + messages = result.all_messages() + + if isinstance(result.output, DeferredToolRequests): # pragma: no branch + results = DeferredToolResults() + for tool_call in result.output.approvals: + results.approvals[tool_call.tool_call_id] = True + for tool_call in result.output.calls: + results.calls[tool_call.tool_call_id] = 'Success' + + result = hitl_prefect_agent.run_sync(message_history=messages, deferred_tool_results=results) + + return result + + result = hitl_main_loop_sync('Delete the file `.env` and create `test.txt`') + assert isinstance(result.output, str) + + +# Test model retry +model_retry_agent = Agent(model, name='model_retry_agent') + + +@task(name='get_weather_in_city') +@model_retry_agent.tool_plain +def get_weather_in_city(city: str) -> str: + if city != 'Mexico City': + raise ModelRetry('Did you mean Mexico City?') + return 'sunny' + + +model_retry_prefect_agent = PrefectAgent(model_retry_agent) + + +async def test_prefect_agent_with_model_retry(allow_model_requests: None) -> None: + """Test that ModelRetry works correctly.""" + result = await model_retry_prefect_agent.run('What is the weather in CDMX?') + assert 'sunny' in result.output.lower() or 'mexico city' in result.output.lower() + + +# Test dynamic toolsets +@dataclass +class ToggleableDeps: + active: Literal['weather', 'datetime'] + + def toggle(self): + if self.active == 'weather': + self.active = 'datetime' + else: + self.active = 'weather' + + +@task(name='temperature_celsius') +def temperature_celsius(city: str) -> float: + return 21.0 + + +@task(name='temperature_fahrenheit') +def temperature_fahrenheit(city: str) -> float: + return 69.8 + + +@task(name='conditions') +def conditions(city: str) -> str: + # Simplified version without RunContext + return "It's raining" + + +weather_toolset = FunctionToolset(tools=[temperature_celsius, temperature_fahrenheit, conditions]) + +datetime_toolset = FunctionToolset() + + +@task(name='now') +def now_func() -> datetime: + return datetime.now() + + +datetime_toolset.add_function(now_func, name='now') + +test_model = TestModel() +dynamic_agent = Agent(name='dynamic_agent', model=test_model, deps_type=ToggleableDeps) + + +@dynamic_agent.toolset # type: ignore +def toggleable_toolset(ctx: RunContext[ToggleableDeps]) -> FunctionToolset[None]: + if ctx.deps.active == 'weather': + return weather_toolset + else: + return datetime_toolset + + +@dynamic_agent.tool +def toggle(ctx: RunContext[ToggleableDeps]): + ctx.deps.toggle() + + +dynamic_prefect_agent = PrefectAgent(dynamic_agent) + + +def test_dynamic_toolset(): + """Test that dynamic toolsets work correctly.""" + weather_deps = ToggleableDeps('weather') + + result = dynamic_prefect_agent.run_sync('Toggle the toolset', deps=weather_deps) + assert isinstance(result.output, str) + + result = dynamic_prefect_agent.run_sync('Toggle the toolset', deps=weather_deps) + assert isinstance(result.output, str) + + +# Test cache policies +async def test_cache_policy_default(): + """Test that the default cache policy is set correctly.""" + assert DEFAULT_PYDANTIC_AI_CACHE_POLICY is not None + # It's a CompoundCachePolicy instance with policies attribute + assert hasattr(DEFAULT_PYDANTIC_AI_CACHE_POLICY, 'policies') + + +async def test_cache_policy_custom(): + """ + Test that custom cache policy PrefectAgentInputs works. + Timestamps must be excluded from computed cache keys to avoid + duplicate calls when runs are restarted. + """ + cache_policy = PrefectAgentInputs() + + # Create two sets of messages with same content but different timestamps + time1 = datetime.now() + time2 = time1 + timedelta(minutes=5) + + # First set of messages + messages1 = [ + ModelRequest(parts=[UserPromptPart(content='What is the capital of France?', timestamp=time1)]), + ModelResponse( + parts=[TextPart(content='The capital of France is Paris.')], + usage=RequestUsage(input_tokens=10, output_tokens=10), + model_name='test-model', + timestamp=time1, + ), + ] + + # Second set of messages - same content, different timestamps + messages2 = [ + ModelRequest(parts=[UserPromptPart(content='What is the capital of France?', timestamp=time2)]), + ModelResponse( + parts=[TextPart(content='The capital of France is Paris.')], + usage=RequestUsage(input_tokens=10, output_tokens=10), + model_name='test-model', + timestamp=time2, + ), + ] + + mock_task_ctx = MagicMock() + + # Compute hashes using the cache policy + hash1 = cache_policy.compute_key( + task_ctx=mock_task_ctx, + inputs={'messages': messages1}, + flow_parameters={}, + ) + + hash2 = cache_policy.compute_key( + task_ctx=mock_task_ctx, + inputs={'messages': messages2}, + flow_parameters={}, + ) + + # The hashes should be the same since timestamps are excluded + assert hash1 == hash2 + + # Also test that different content produces different hashes + messages3 = [ + ModelRequest(parts=[UserPromptPart(content='What is the capital of Spain?', timestamp=time1)]), + ModelResponse( + parts=[TextPart(content='The capital of Spain is Madrid.')], + usage=RequestUsage(input_tokens=10, output_tokens=10), + model_name='test-model', + timestamp=time1, + ), + ] + + hash3 = cache_policy.compute_key( + task_ctx=mock_task_ctx, + inputs={'messages': messages3}, + flow_parameters={}, + ) + + # This hash should be different from the others + assert hash3 != hash1 + + +async def test_cache_policy_with_tuples(): + """Test that cache policy handles tuples with timestamps correctly.""" + cache_policy = PrefectAgentInputs() + mock_task_ctx = MagicMock() + + time1 = datetime.now() + time2 = time1 + timedelta(minutes=5) + + time3 = time2 + timedelta(minutes=5) + time4 = time3 + timedelta(minutes=5) + + # Create a tuple with timestamps + data_with_tuple_1 = ( + UserPromptPart(content='Question', timestamp=time1), + TextPart(content='Answer'), + UserPromptPart(content='Follow-up', timestamp=time2), + ) + + data_with_tuple_2 = ( + UserPromptPart(content='Question', timestamp=time3), + TextPart(content='Answer'), + UserPromptPart(content='Follow-up', timestamp=time4), + ) + + assert cache_policy.compute_key( + task_ctx=mock_task_ctx, + inputs={'messages': data_with_tuple_1}, + flow_parameters={}, + ) == cache_policy.compute_key( + task_ctx=mock_task_ctx, + inputs={'messages': data_with_tuple_2}, + flow_parameters={}, + ) + + +async def test_cache_policy_empty_inputs(): + """Test that cache policy returns None for empty inputs.""" + cache_policy = PrefectAgentInputs() + + mock_task_ctx = MagicMock() + + # Test with empty inputs + result = cache_policy.compute_key( + task_ctx=mock_task_ctx, + inputs={}, + flow_parameters={}, + ) + + assert result is None + + +# Test custom model settings +class CustomModelSettings(ModelSettings, total=False): + custom_setting: str + + +def return_settings(messages: list[ModelMessage], agent_info: AgentInfo) -> ModelResponse: + return ModelResponse(parts=[TextPart(str(agent_info.model_settings))]) + + +model_settings = CustomModelSettings(max_tokens=123, custom_setting='custom_value') +function_model = FunctionModel(return_settings, settings=model_settings) + +settings_agent = Agent(function_model, name='settings_agent') +settings_prefect_agent = PrefectAgent(settings_agent) + + +async def test_custom_model_settings(allow_model_requests: None): + """Test that custom model settings are passed through correctly.""" + result = await settings_prefect_agent.run('Give me those settings') + assert result.output == snapshot("{'max_tokens': 123, 'custom_setting': 'custom_value'}") + + +@dataclass +class SimpleDeps: + value: str + + +async def test_tool_call_outside_flow(): + """Test that tools work when called outside a Prefect flow.""" + + # Create an agent with a simple tool + test_agent = Agent(TestModel(), deps_type=SimpleDeps, name='test_outside_flow') + + @test_agent.tool + def simple_tool(ctx: RunContext[SimpleDeps]) -> str: + return f'Tool called with: {ctx.deps.value}' + + test_prefect_agent = PrefectAgent(test_agent) + + # Call run() outside a flow - tools should still work + result = await test_prefect_agent.run('Call the tool', deps=SimpleDeps(value='test')) + # Check that the tool was actually called by looking at the messages + messages = result.all_messages() + assert any('simple_tool' in str(msg) for msg in messages) + + +async def test_disabled_tool(): + """Test that tools can be disabled via tool_task_config_by_name.""" + + # Create an agent with a tool + test_agent = Agent(TestModel(), name='test_disabled_tool') + + @test_agent.tool_plain + def my_tool() -> str: + return 'Tool executed' + + # Create PrefectAgent with the tool disabled + test_prefect_agent = PrefectAgent( + test_agent, + tool_task_config_by_name={ + 'my_tool': None, + }, + ) + + # Test outside a flow + result = await test_prefect_agent.run('Call my_tool') + messages = result.all_messages() + assert any('my_tool' in str(msg) for msg in messages) + + # Test inside a flow to ensure disabled tools work there too + @flow + async def test_flow(): + result = await test_prefect_agent.run('Call my_tool') + return result + + flow_result = await test_flow() + flow_messages = flow_result.all_messages() + assert any('my_tool' in str(msg) for msg in flow_messages) diff --git a/uv.lock b/uv.lock index f496e39ff8..249754eb89 100644 --- a/uv.lock +++ b/uv.lock @@ -146,6 +146,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload-time = "2024-12-13T17:10:38.469Z" }, ] +[[package]] +name = "aiosqlite" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454, upload-time = "2025-02-03T07:30:16.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, +] + +[[package]] +name = "alembic" +version = "1.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/ca/4dc52902cf3491892d464f5265a81e9dff094692c8a049a3ed6a05fe7ee8/alembic-1.16.5.tar.gz", hash = "sha256:a88bb7f6e513bd4301ecf4c7f2206fe93f9913f9b48dac3b78babde2d6fe765e", size = 1969868, upload-time = "2025-08-27T18:02:05.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/4a/4c61d4c84cfd9befb6fa08a702535b27b21fff08c946bc2f6139decbf7f7/alembic-1.16.5-py3-none-any.whl", hash = "sha256:e845dfe090c5ffa7b92593ae6687c5cb1a101e91fa53868497dbd79847f9dbe3", size = 247355, upload-time = "2025-08-27T18:02:07.37Z" }, +] + [[package]] name = "algoliasearch" version = "4.13.2" @@ -206,6 +233,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041, upload-time = "2025-01-05T13:13:07.985Z" }, ] +[[package]] +name = "apprise" +version = "1.9.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "click" }, + { name = "markdown" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/16/e39338b8310af9466fab6f4482b542e24cb1fcbb7e36bf00c089c4e015e7/apprise-1.9.5.tar.gz", hash = "sha256:8f3be318bb429c2017470e33928a2e313cbf7600fc74b8184782a37060db366a", size = 1877134, upload-time = "2025-09-30T15:57:28.046Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/f1/318762320d966e528dfb9e6be5953fe7df2952156f15ba857cbccafb630c/apprise-1.9.5-py3-none-any.whl", hash = "sha256:1873a8a1b8cf9e44fcbefe0486ed260b590652aea12427f545b37c8566142961", size = 1421011, upload-time = "2025-09-30T15:57:26.268Z" }, +] + [[package]] name = "argcomplete" version = "3.5.3" @@ -481,7 +526,8 @@ name = "cairocffi" version = "1.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi" }, + { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or platform_python_implementation == 'PyPy'" }, + { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/70/c5/1a4dc131459e68a173cbdab5fad6b524f53f9c1ef7861b7698e998b837cc/cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b", size = 88096, upload-time = "2024-06-18T10:56:06.741Z" } wheels = [ @@ -517,8 +563,15 @@ wheels = [ name = "cffi" version = "1.17.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", + "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", + "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", +] dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "python_full_version < '3.11' or platform_python_implementation == 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } wheels = [ @@ -570,6 +623,93 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, ] +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", +] +dependencies = [ + { name = "pycparser", marker = "python_full_version >= '3.11' and implementation_name != 'PyPy' and platform_python_implementation != '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 = [ + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + [[package]] name = "chardet" version = "5.2.0" @@ -662,6 +802,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] +[[package]] +name = "cloudpickle" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload-time = "2025-01-14T17:02:05.085Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload-time = "2025-01-14T17:02:02.417Z" }, +] + [[package]] name = "cohere" version = "5.18.0" @@ -691,6 +840,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "coolname" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/c6/1eaa4495ff4640e80d9af64f540e427ba1596a20f735d4c4750fe0386d07/coolname-2.2.0.tar.gz", hash = "sha256:6c5d5731759104479e7ca195a9b64f7900ac5bead40183c09323c7d0be9e75c7", size = 59006, upload-time = "2023-01-09T14:50:41.724Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/b1/5745d7523d8ce53b87779f46ef6cf5c5c342997939c2fe967e607b944e43/coolname-2.2.0-py2.py3-none-any.whl", hash = "sha256:4d1563186cfaf71b394d5df4c744f8c41303b6846413645e31d31915cdeb13e8", size = 37849, upload-time = "2023-01-09T14:50:39.897Z" }, +] + [[package]] name = "coverage" version = "7.10.7" @@ -795,6 +953,129 @@ toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] +[[package]] +name = "cryptography" +version = "45.0.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_python_implementation != 'PyPy'", + "python_full_version < '3.11' and platform_python_implementation == 'PyPy'", +] +dependencies = [ + { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980, upload-time = "2025-09-01T11:15:03.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105, upload-time = "2025-09-01T11:13:59.684Z" }, + { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799, upload-time = "2025-09-01T11:14:02.517Z" }, + { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504, upload-time = "2025-09-01T11:14:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542, upload-time = "2025-09-01T11:14:06.309Z" }, + { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244, upload-time = "2025-09-01T11:14:08.152Z" }, + { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975, upload-time = "2025-09-01T11:14:09.755Z" }, + { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082, upload-time = "2025-09-01T11:14:11.229Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397, upload-time = "2025-09-01T11:14:12.924Z" }, + { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244, upload-time = "2025-09-01T11:14:14.431Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862, upload-time = "2025-09-01T11:14:16.185Z" }, + { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578, upload-time = "2025-09-01T11:14:17.638Z" }, + { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400, upload-time = "2025-09-01T11:14:18.958Z" }, + { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824, upload-time = "2025-09-01T11:14:20.954Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233, upload-time = "2025-09-01T11:14:22.454Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075, upload-time = "2025-09-01T11:14:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517, upload-time = "2025-09-01T11:14:25.679Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893, upload-time = "2025-09-01T11:14:27.1Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132, upload-time = "2025-09-01T11:14:28.58Z" }, + { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086, upload-time = "2025-09-01T11:14:30.572Z" }, + { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383, upload-time = "2025-09-01T11:14:32.046Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186, upload-time = "2025-09-01T11:14:33.95Z" }, + { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639, upload-time = "2025-09-01T11:14:35.343Z" }, + { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552, upload-time = "2025-09-01T11:14:36.929Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742, upload-time = "2025-09-01T11:14:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/e42f1528ca1ea82256b835191eab1be014e0f9f934b60d98b0be8a38ed70/cryptography-45.0.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252", size = 3572442, upload-time = "2025-09-01T11:14:39.836Z" }, + { url = "https://files.pythonhosted.org/packages/59/aa/e947693ab08674a2663ed2534cd8d345cf17bf6a1facf99273e8ec8986dc/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083", size = 4142233, upload-time = "2025-09-01T11:14:41.305Z" }, + { url = "https://files.pythonhosted.org/packages/24/06/09b6f6a2fc43474a32b8fe259038eef1500ee3d3c141599b57ac6c57612c/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130", size = 4376202, upload-time = "2025-09-01T11:14:43.047Z" }, + { url = "https://files.pythonhosted.org/packages/00/f2/c166af87e95ce6ae6d38471a7e039d3a0549c2d55d74e059680162052824/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4", size = 4141900, upload-time = "2025-09-01T11:14:45.089Z" }, + { url = "https://files.pythonhosted.org/packages/16/b9/e96e0b6cb86eae27ea51fa8a3151535a18e66fe7c451fa90f7f89c85f541/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141", size = 4375562, upload-time = "2025-09-01T11:14:47.166Z" }, + { url = "https://files.pythonhosted.org/packages/36/d0/36e8ee39274e9d77baf7d0dafda680cba6e52f3936b846f0d56d64fec915/cryptography-45.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7", size = 3322781, upload-time = "2025-09-01T11:14:48.747Z" }, + { url = "https://files.pythonhosted.org/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde", size = 3584634, upload-time = "2025-09-01T11:14:50.593Z" }, + { url = "https://files.pythonhosted.org/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34", size = 4149533, upload-time = "2025-09-01T11:14:52.091Z" }, + { url = "https://files.pythonhosted.org/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9", size = 4385557, upload-time = "2025-09-01T11:14:53.551Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae", size = 4149023, upload-time = "2025-09-01T11:14:55.022Z" }, + { url = "https://files.pythonhosted.org/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b", size = 4385722, upload-time = "2025-09-01T11:14:57.319Z" }, + { url = "https://files.pythonhosted.org/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63", size = 3332908, upload-time = "2025-09-01T11:14:58.78Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", + "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", +] +dependencies = [ + { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/9b/e301418629f7bfdf72db9e80ad6ed9d1b83c487c471803eaa6464c511a01/cryptography-46.0.2.tar.gz", hash = "sha256:21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe", size = 749293, upload-time = "2025-10-01T00:29:11.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/98/7a8df8c19a335c8028414738490fc3955c0cecbfdd37fcc1b9c3d04bd561/cryptography-46.0.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663", size = 7261255, upload-time = "2025-10-01T00:27:22.947Z" }, + { url = "https://files.pythonhosted.org/packages/c6/38/b2adb2aa1baa6706adc3eb746691edd6f90a656a9a65c3509e274d15a2b8/cryptography-46.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02", size = 4297596, upload-time = "2025-10-01T00:27:25.258Z" }, + { url = "https://files.pythonhosted.org/packages/e4/27/0f190ada240003119488ae66c897b5e97149292988f556aef4a6a2a57595/cryptography-46.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135", size = 4450899, upload-time = "2025-10-01T00:27:27.458Z" }, + { url = "https://files.pythonhosted.org/packages/85/d5/e4744105ab02fdf6bb58ba9a816e23b7a633255987310b4187d6745533db/cryptography-46.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92", size = 4300382, upload-time = "2025-10-01T00:27:29.091Z" }, + { url = "https://files.pythonhosted.org/packages/33/fb/bf9571065c18c04818cb07de90c43fc042c7977c68e5de6876049559c72f/cryptography-46.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659", size = 4017347, upload-time = "2025-10-01T00:27:30.767Z" }, + { url = "https://files.pythonhosted.org/packages/35/72/fc51856b9b16155ca071080e1a3ad0c3a8e86616daf7eb018d9565b99baa/cryptography-46.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa", size = 4983500, upload-time = "2025-10-01T00:27:32.741Z" }, + { url = "https://files.pythonhosted.org/packages/c1/53/0f51e926799025e31746d454ab2e36f8c3f0d41592bc65cb9840368d3275/cryptography-46.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08", size = 4482591, upload-time = "2025-10-01T00:27:34.869Z" }, + { url = "https://files.pythonhosted.org/packages/86/96/4302af40b23ab8aa360862251fb8fc450b2a06ff24bc5e261c2007f27014/cryptography-46.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5", size = 4300019, upload-time = "2025-10-01T00:27:37.029Z" }, + { url = "https://files.pythonhosted.org/packages/9b/59/0be12c7fcc4c5e34fe2b665a75bc20958473047a30d095a7657c218fa9e8/cryptography-46.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc", size = 4950006, upload-time = "2025-10-01T00:27:40.272Z" }, + { url = "https://files.pythonhosted.org/packages/55/1d/42fda47b0111834b49e31590ae14fd020594d5e4dadd639bce89ad790fba/cryptography-46.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b", size = 4482088, upload-time = "2025-10-01T00:27:42.668Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/60f583f69aa1602c2bdc7022dae86a0d2b837276182f8c1ec825feb9b874/cryptography-46.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1", size = 4425599, upload-time = "2025-10-01T00:27:44.616Z" }, + { url = "https://files.pythonhosted.org/packages/d1/57/d8d4134cd27e6e94cf44adb3f3489f935bde85f3a5508e1b5b43095b917d/cryptography-46.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b", size = 4697458, upload-time = "2025-10-01T00:27:46.209Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2b/531e37408573e1da33adfb4c58875013ee8ac7d548d1548967d94a0ae5c4/cryptography-46.0.2-cp311-abi3-win32.whl", hash = "sha256:8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee", size = 3056077, upload-time = "2025-10-01T00:27:48.424Z" }, + { url = "https://files.pythonhosted.org/packages/a8/cd/2f83cafd47ed2dc5a3a9c783ff5d764e9e70d3a160e0df9a9dcd639414ce/cryptography-46.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb", size = 3512585, upload-time = "2025-10-01T00:27:50.521Z" }, + { url = "https://files.pythonhosted.org/packages/00/36/676f94e10bfaa5c5b86c469ff46d3e0663c5dc89542f7afbadac241a3ee4/cryptography-46.0.2-cp311-abi3-win_arm64.whl", hash = "sha256:218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470", size = 2927474, upload-time = "2025-10-01T00:27:52.91Z" }, + { url = "https://files.pythonhosted.org/packages/6f/cc/47fc6223a341f26d103cb6da2216805e08a37d3b52bee7f3b2aee8066f95/cryptography-46.0.2-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8", size = 7198626, upload-time = "2025-10-01T00:27:54.8Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/d66a8591207c28bbe4ac7afa25c4656dc19dc0db29a219f9809205639ede/cryptography-46.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a", size = 4287584, upload-time = "2025-10-01T00:27:57.018Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/fac3ab6302b928e0398c269eddab5978e6c1c50b2b77bb5365ffa8633b37/cryptography-46.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b", size = 4433796, upload-time = "2025-10-01T00:27:58.631Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d8/24392e5d3c58e2d83f98fe5a2322ae343360ec5b5b93fe18bc52e47298f5/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20", size = 4292126, upload-time = "2025-10-01T00:28:00.643Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/3d9f9359b84c16c49a5a336ee8be8d322072a09fac17e737f3bb11f1ce64/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73", size = 3993056, upload-time = "2025-10-01T00:28:02.8Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a3/4c44fce0d49a4703cc94bfbe705adebf7ab36efe978053742957bc7ec324/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee", size = 4967604, upload-time = "2025-10-01T00:28:04.783Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c2/49d73218747c8cac16bb8318a5513fde3129e06a018af3bc4dc722aa4a98/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2", size = 4465367, upload-time = "2025-10-01T00:28:06.864Z" }, + { url = "https://files.pythonhosted.org/packages/1b/64/9afa7d2ee742f55ca6285a54386ed2778556a4ed8871571cb1c1bfd8db9e/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f", size = 4291678, upload-time = "2025-10-01T00:28:08.982Z" }, + { url = "https://files.pythonhosted.org/packages/50/48/1696d5ea9623a7b72ace87608f6899ca3c331709ac7ebf80740abb8ac673/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e", size = 4931366, upload-time = "2025-10-01T00:28:10.74Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/9dfc778401a334db3b24435ee0733dd005aefb74afe036e2d154547cb917/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36", size = 4464738, upload-time = "2025-10-01T00:28:12.491Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b1/abcde62072b8f3fd414e191a6238ce55a0050e9738090dc6cded24c12036/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a", size = 4419305, upload-time = "2025-10-01T00:28:14.145Z" }, + { url = "https://files.pythonhosted.org/packages/c7/1f/3d2228492f9391395ca34c677e8f2571fb5370fe13dc48c1014f8c509864/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c", size = 4681201, upload-time = "2025-10-01T00:28:15.951Z" }, + { url = "https://files.pythonhosted.org/packages/de/77/b687745804a93a55054f391528fcfc76c3d6bfd082ce9fb62c12f0d29fc1/cryptography-46.0.2-cp314-cp314t-win32.whl", hash = "sha256:e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1", size = 3022492, upload-time = "2025-10-01T00:28:17.643Z" }, + { url = "https://files.pythonhosted.org/packages/60/a5/8d498ef2996e583de0bef1dcc5e70186376f00883ae27bf2133f490adf21/cryptography-46.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9", size = 3496215, upload-time = "2025-10-01T00:28:19.272Z" }, + { url = "https://files.pythonhosted.org/packages/56/db/ee67aaef459a2706bc302b15889a1a8126ebe66877bab1487ae6ad00f33d/cryptography-46.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0", size = 2919255, upload-time = "2025-10-01T00:28:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/d5/bb/fa95abcf147a1b0bb94d95f53fbb09da77b24c776c5d87d36f3d94521d2c/cryptography-46.0.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685", size = 7248090, upload-time = "2025-10-01T00:28:22.846Z" }, + { url = "https://files.pythonhosted.org/packages/b7/66/f42071ce0e3ffbfa80a88feadb209c779fda92a23fbc1e14f74ebf72ef6b/cryptography-46.0.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b", size = 4293123, upload-time = "2025-10-01T00:28:25.072Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/1fdbd2e5c1ba822828d250e5a966622ef00185e476d1cd2726b6dd135e53/cryptography-46.0.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1", size = 4439524, upload-time = "2025-10-01T00:28:26.808Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c1/5e4989a7d102d4306053770d60f978c7b6b1ea2ff8c06e0265e305b23516/cryptography-46.0.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c", size = 4297264, upload-time = "2025-10-01T00:28:29.327Z" }, + { url = "https://files.pythonhosted.org/packages/28/78/b56f847d220cb1d6d6aef5a390e116ad603ce13a0945a3386a33abc80385/cryptography-46.0.2-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af", size = 4011872, upload-time = "2025-10-01T00:28:31.479Z" }, + { url = "https://files.pythonhosted.org/packages/e1/80/2971f214b066b888944f7b57761bf709ee3f2cf805619a18b18cab9b263c/cryptography-46.0.2-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b", size = 4978458, upload-time = "2025-10-01T00:28:33.267Z" }, + { url = "https://files.pythonhosted.org/packages/a5/84/0cb0a2beaa4f1cbe63ebec4e97cd7e0e9f835d0ba5ee143ed2523a1e0016/cryptography-46.0.2-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21", size = 4472195, upload-time = "2025-10-01T00:28:36.039Z" }, + { url = "https://files.pythonhosted.org/packages/30/8b/2b542ddbf78835c7cd67b6fa79e95560023481213a060b92352a61a10efe/cryptography-46.0.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6", size = 4296791, upload-time = "2025-10-01T00:28:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/78/12/9065b40201b4f4876e93b9b94d91feb18de9150d60bd842a16a21565007f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023", size = 4939629, upload-time = "2025-10-01T00:28:39.654Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9e/6507dc048c1b1530d372c483dfd34e7709fc542765015425f0442b08547f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e", size = 4471988, upload-time = "2025-10-01T00:28:41.822Z" }, + { url = "https://files.pythonhosted.org/packages/b1/86/d025584a5f7d5c5ec8d3633dbcdce83a0cd579f1141ceada7817a4c26934/cryptography-46.0.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90", size = 4422989, upload-time = "2025-10-01T00:28:43.608Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/536370418b38a15a61bbe413006b79dfc3d2b4b0eafceb5581983f973c15/cryptography-46.0.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be", size = 4685578, upload-time = "2025-10-01T00:28:45.361Z" }, + { url = "https://files.pythonhosted.org/packages/15/52/ea7e2b1910f547baed566c866fbb86de2402e501a89ecb4871ea7f169a81/cryptography-46.0.2-cp38-abi3-win32.whl", hash = "sha256:0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c", size = 3036711, upload-time = "2025-10-01T00:28:47.096Z" }, + { url = "https://files.pythonhosted.org/packages/71/9e/171f40f9c70a873e73c2efcdbe91e1d4b1777a03398fa1c4af3c56a2477a/cryptography-46.0.2-cp38-abi3-win_amd64.whl", hash = "sha256:f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62", size = 3500007, upload-time = "2025-10-01T00:28:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/3e/7c/15ad426257615f9be8caf7f97990cf3dcbb5b8dd7ed7e0db581a1c4759dd/cryptography-46.0.2-cp38-abi3-win_arm64.whl", hash = "sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1", size = 2918153, upload-time = "2025-10-01T00:28:51.003Z" }, + { url = "https://files.pythonhosted.org/packages/25/b2/067a7db693488f19777ecf73f925bcb6a3efa2eae42355bafaafa37a6588/cryptography-46.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f25a41f5b34b371a06dad3f01799706631331adc7d6c05253f5bca22068c7a34", size = 3701860, upload-time = "2025-10-01T00:28:53.003Z" }, + { url = "https://files.pythonhosted.org/packages/87/12/47c2aab2c285f97c71a791169529dbb89f48fc12e5f62bb6525c3927a1a2/cryptography-46.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e12b61e0b86611e3f4c1756686d9086c1d36e6fd15326f5658112ad1f1cc8807", size = 3429917, upload-time = "2025-10-01T00:28:55.03Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8c/1aabe338149a7d0f52c3e30f2880b20027ca2a485316756ed6f000462db3/cryptography-46.0.2-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1d3b3edd145953832e09607986f2bd86f85d1dc9c48ced41808b18009d9f30e5", size = 3714495, upload-time = "2025-10-01T00:28:57.222Z" }, + { url = "https://files.pythonhosted.org/packages/e3/0a/0d10eb970fe3e57da9e9ddcfd9464c76f42baf7b3d0db4a782d6746f788f/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fe245cf4a73c20592f0f48da39748b3513db114465be78f0a36da847221bd1b4", size = 4243379, upload-time = "2025-10-01T00:28:58.989Z" }, + { url = "https://files.pythonhosted.org/packages/7d/60/e274b4d41a9eb82538b39950a74ef06e9e4d723cb998044635d9deb1b435/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2b9cad9cf71d0c45566624ff76654e9bae5f8a25970c250a26ccfc73f8553e2d", size = 4409533, upload-time = "2025-10-01T00:29:00.785Z" }, + { url = "https://files.pythonhosted.org/packages/19/9a/fb8548f762b4749aebd13b57b8f865de80258083fe814957f9b0619cfc56/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9bd26f2f75a925fdf5e0a446c0de2714f17819bf560b44b7480e4dd632ad6c46", size = 4243120, upload-time = "2025-10-01T00:29:02.515Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/883f24147fd4a0c5cab74ac7e36a1ff3094a54ba5c3a6253d2ff4b19255b/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:7282d8f092b5be7172d6472f29b0631f39f18512a3642aefe52c3c0e0ccfad5a", size = 4408940, upload-time = "2025-10-01T00:29:04.42Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b5/c5e179772ec38adb1c072b3aa13937d2860509ba32b2462bf1dda153833b/cryptography-46.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c4b93af7920cdf80f71650769464ccf1fb49a4b56ae0024173c24c48eb6b1612", size = 3438518, upload-time = "2025-10-01T00:29:06.139Z" }, +] + [[package]] name = "cssselect2" version = "0.7.0" @@ -832,6 +1113,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/62/eb8157afb21bd229c864521c1ab4fa8e9b4f1b06bafdd8c4668a7a31b5dd/datasets-4.0.0-py3-none-any.whl", hash = "sha256:7ef95e62025fd122882dbce6cb904c8cd3fbc829de6669a5eb939c77d50e203d", size = 494825, upload-time = "2025-07-09T14:35:50.658Z" }, ] +[[package]] +name = "dateparser" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "regex" }, + { name = "tzlocal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/30/064144f0df1749e7bb5faaa7f52b007d7c2d08ec08fed8411aba87207f68/dateparser-1.2.2.tar.gz", hash = "sha256:986316f17cb8cdc23ea8ce563027c5ef12fc725b6fb1d137c14ca08777c5ecf7", size = 329840, upload-time = "2025-06-26T09:29:23.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/22/f020c047ae1346613db9322638186468238bcfa8849b4668a22b97faad65/dateparser-1.2.2-py3-none-any.whl", hash = "sha256:5a5d7211a09013499867547023a2a0c91d5a27d15dd4dbcea676ea9fe66f2482", size = 315453, upload-time = "2025-06-26T09:29:21.412Z" }, +] + [[package]] name = "dbos" version = "2.0.0" @@ -940,6 +1236,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] +[[package]] +name = "docker" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, +] + [[package]] name = "docstring-parser" version = "0.17.0" @@ -1322,6 +1632,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/52/4fe9dfc2239e7b748ad8dc3b80ad8755f5c9378432715193586c3ab74bf9/gradio_client-1.7.1-py3-none-any.whl", hash = "sha256:d7737bc473a2093549c06004379c42f0a3510a98095cf7cea9033837e252149f", size = 321994, upload-time = "2025-02-19T20:05:21.305Z" }, ] +[[package]] +name = "graphviz" +version = "0.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b3/3ac91e9be6b761a4b30d66ff165e54439dcd48b83f4e20d644867215f6ca/graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78", size = 200434, upload-time = "2025-06-15T09:35:05.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, +] + [[package]] name = "greenlet" version = "3.2.4" @@ -1498,6 +1817,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[package.optional-dependencies] +http2 = [ + { name = "h2" }, +] + [[package]] name = "httpx-sse" version = "0.4.0" @@ -1531,6 +1855,15 @@ inference = [ { name = "aiohttp" }, ] +[[package]] +name = "humanize" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/1d/3062fcc89ee05a715c0b9bfe6490c00c576314f27ffee3a704122c6fd259/humanize-4.13.0.tar.gz", hash = "sha256:78f79e68f76f0b04d711c4e55d32bebef5be387148862cb1ef83d2b58e7935a0", size = 81884, upload-time = "2025-08-25T09:39:20.04Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/c7/316e7ca04d26695ef0635dc81683d628350810eb8e9b2299fc08ba49f366/humanize-4.13.0-py3-none-any.whl", hash = "sha256:b810820b31891813b1673e8fec7f1ed3312061eab2f26e3fa192c393d11ed25f", size = 128869, upload-time = "2025-08-25T09:39:18.54Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1597,14 +1930,27 @@ wheels = [ [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674, upload-time = "2024-12-21T18:30:22.828Z" } +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 = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jinja2-humanize-extension" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "humanize" }, + { name = "jinja2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/77/0bba383819dd4e67566487c11c49479ced87e77c3285d8e7f7a3401cf882/jinja2_humanize_extension-0.4.0.tar.gz", hash = "sha256:e7d69b1c20f32815bbec722330ee8af14b1287bb1c2b0afa590dbf031cadeaa0", size = 4746, upload-time = "2023-09-01T12:52:42.781Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596, upload-time = "2024-12-21T18:30:19.133Z" }, + { url = "https://files.pythonhosted.org/packages/26/b4/08c9d297edd5e1182506edecccbb88a92e1122a057953068cadac420ca5d/jinja2_humanize_extension-0.4.0-py3-none-any.whl", hash = "sha256:b6326e2da0f7d425338bebf58848e830421defbce785f12ae812e65128518156", size = 4769, upload-time = "2023-09-01T12:52:41.098Z" }, ] [[package]] @@ -1675,6 +2021,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" }, ] +[[package]] +name = "jsonpatch" +version = "1.33" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonpointer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699, upload-time = "2023-06-26T12:07:29.144Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898, upload-time = "2023-06-16T21:01:28.466Z" }, +] + +[[package]] +name = "jsonpointer" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, +] + [[package]] name = "jsonschema" version = "4.25.0" @@ -1826,6 +2193,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/29/00b9b0322a473aee6cda87473401c9abb19506cd650cc69a8aa38277ea74/lxml-5.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499", size = 3487718, upload-time = "2025-02-10T07:50:31.231Z" }, ] +[[package]] +name = "mako" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, +] + [[package]] name = "markdown" version = "3.7" @@ -2400,6 +2779,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/7f/d322a4125405920401450118dbdc52e0384026bd669939484670ce8b2ab9/numpy-2.2.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4", size = 12839607, upload-time = "2025-02-13T17:00:22.005Z" }, ] +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + [[package]] name = "openai" version = "1.107.2" @@ -2751,6 +3139,65 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] +[[package]] +name = "pendulum" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil", marker = "python_full_version < '3.13'" }, + { name = "tzdata", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/7c/009c12b86c7cc6c403aec80f8a4308598dfc5995e5c523a5491faaa3952e/pendulum-3.1.0.tar.gz", hash = "sha256:66f96303560f41d097bee7d2dc98ffca716fbb3a832c4b3062034c2d45865015", size = 85930, upload-time = "2025-04-19T14:30:01.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/d8/398cd27903a6899d0ae47b896d88e0b15849fc334931a6732e7ce3be9a45/pendulum-3.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aa545a59e6517cf43597455a6fb44daa4a6e08473d67a7ad34e4fa951efb9620", size = 338637, upload-time = "2025-04-19T14:00:56.429Z" }, + { url = "https://files.pythonhosted.org/packages/aa/9d/a125554919c6db14e189393254c7781ee98ed5a121b6c05652d353b03c12/pendulum-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:299df2da6c490ede86bb8d58c65e33d7a2a42479d21475a54b467b03ccb88531", size = 326003, upload-time = "2025-04-19T14:00:58.192Z" }, + { url = "https://files.pythonhosted.org/packages/53/9f/43a5a902f904e06252c259c2f6cf2dceafbb25aef158df08f79c0089dfd7/pendulum-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbaa66e3ab179a2746eec67462f852a5d555bd709c25030aef38477468dd008e", size = 344335, upload-time = "2025-04-19T14:00:59.985Z" }, + { url = "https://files.pythonhosted.org/packages/ca/24/00fcd6abd1f7623d2bbcca048b45f01aa8bb6b647e0477c3a8ea6094335c/pendulum-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3907ab3744c32e339c358d88ec80cd35fa2d4b25c77a3c67e6b39e99b7090c5", size = 382169, upload-time = "2025-04-19T14:01:01.411Z" }, + { url = "https://files.pythonhosted.org/packages/32/bc/20a87f24c26c6c4daf3c69311208b28130b4d19c006da16efc0e55715963/pendulum-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8244958c5bc4ed1c47ee84b098ddd95287a3fc59e569ca6e2b664c6396138ec4", size = 436675, upload-time = "2025-04-19T14:01:03.068Z" }, + { url = "https://files.pythonhosted.org/packages/1d/eb/3b1818a796408a250b8e6cfaa5372b991c0cbec768e02e0f9a226755383d/pendulum-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca5722b3993b85ff7dfced48d86b318f863c359877b6badf1a3601e35199ef8f", size = 353728, upload-time = "2025-04-19T14:01:04.483Z" }, + { url = "https://files.pythonhosted.org/packages/36/23/755ef61f863b2777925171a59509540205b561a9e07ee7de0b5be9226bea/pendulum-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5b77a3dc010eea1a4916ef3771163d808bfc3e02b894c37df311287f18e5b764", size = 524465, upload-time = "2025-04-19T14:01:05.865Z" }, + { url = "https://files.pythonhosted.org/packages/07/1f/a3e5f08890d13d93eee725778bfeaa233db5c55463e526857dffbc1a47e4/pendulum-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d6e1eff4a15fdb8fb3867c5469e691c2465eef002a6a541c47b48a390ff4cf4", size = 525690, upload-time = "2025-04-19T14:01:07.707Z" }, + { url = "https://files.pythonhosted.org/packages/43/c5/bf8ce472b81e8f5f074e8ba39899d288acce417c2c4a9ec7486d56970e28/pendulum-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:73de43ec85b46ac75db848c8e2f3f5d086e90b11cd9c7f029e14c8d748d920e2", size = 260356, upload-time = "2025-04-19T14:01:09.339Z" }, + { url = "https://files.pythonhosted.org/packages/5e/6e/d28d3c22e6708b819a94c05bd05a3dfaed5c685379e8b6dc4b34b473b942/pendulum-3.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:61a03d14f8c64d13b2f7d5859e4b4053c4a7d3b02339f6c71f3e4606bfd67423", size = 338596, upload-time = "2025-04-19T14:01:11.306Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e6/43324d58021d463c2eeb6146b169d2c935f2f840f9e45ac2d500453d954c/pendulum-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e674ed2d158afa5c361e60f1f67872dc55b492a10cacdaa7fcd7b7da5f158f24", size = 325854, upload-time = "2025-04-19T14:01:13.156Z" }, + { url = "https://files.pythonhosted.org/packages/b0/a7/d2ae79b960bfdea94dab67e2f118697b08bc9e98eb6bd8d32c4d99240da3/pendulum-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c75377eb16e58bbe7e03ea89eeea49be6fc5de0934a4aef0e263f8b4fa71bc2", size = 344334, upload-time = "2025-04-19T14:01:15.151Z" }, + { url = "https://files.pythonhosted.org/packages/96/94/941f071212e23c29aae7def891fb636930c648386e059ce09ea0dcd43933/pendulum-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:656b8b0ce070f0f2e5e2668247d3c783c55336534aa1f13bd0969535878955e1", size = 382259, upload-time = "2025-04-19T14:01:16.924Z" }, + { url = "https://files.pythonhosted.org/packages/51/ad/a78a701656aec00d16fee636704445c23ca11617a0bfe7c3848d1caa5157/pendulum-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48962903e6c1afe1f13548cb6252666056086c107d59e3d64795c58c9298bc2e", size = 436361, upload-time = "2025-04-19T14:01:18.796Z" }, + { url = "https://files.pythonhosted.org/packages/da/93/83f59ccbf4435c29dca8c63a6560fcbe4783079a468a5f91d9f886fd21f0/pendulum-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d364ec3f8e65010fefd4b0aaf7be5eb97e5df761b107a06f5e743b7c3f52c311", size = 353653, upload-time = "2025-04-19T14:01:20.159Z" }, + { url = "https://files.pythonhosted.org/packages/6f/0f/42d6644ec6339b41066f594e52d286162aecd2e9735aaf994d7e00c9e09d/pendulum-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd52caffc2afb86612ec43bbeb226f204ea12ebff9f3d12f900a7d3097210fcc", size = 524567, upload-time = "2025-04-19T14:01:21.457Z" }, + { url = "https://files.pythonhosted.org/packages/de/45/d84d909202755ab9d3379e5481fdf70f53344ebefbd68d6f5803ddde98a6/pendulum-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d439fccaa35c91f686bd59d30604dab01e8b5c1d0dd66e81648c432fd3f8a539", size = 525571, upload-time = "2025-04-19T14:01:23.329Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e0/4de160773ce3c2f7843c310db19dd919a0cd02cc1c0384866f63b18a6251/pendulum-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:43288773a86d9c5c0ddb645f88f615ff6bd12fd1410b34323662beccb18f3b49", size = 260259, upload-time = "2025-04-19T14:01:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7f/ffa278f78112c6c6e5130a702042f52aab5c649ae2edf814df07810bbba5/pendulum-3.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:569ea5072ae0f11d625e03b36d865f8037b76e838a3b621f6967314193896a11", size = 253899, upload-time = "2025-04-19T14:01:26.442Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d7/b1bfe15a742f2c2713acb1fdc7dc3594ff46ef9418ac6a96fcb12a6ba60b/pendulum-3.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4dfd53e7583ccae138be86d6c0a0b324c7547df2afcec1876943c4d481cf9608", size = 336209, upload-time = "2025-04-19T14:01:27.815Z" }, + { url = "https://files.pythonhosted.org/packages/eb/87/0392da0c603c828b926d9f7097fbdddaafc01388cb8a00888635d04758c3/pendulum-3.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a6e06a28f3a7d696546347805536f6f38be458cb79de4f80754430696bea9e6", size = 323130, upload-time = "2025-04-19T14:01:29.336Z" }, + { url = "https://files.pythonhosted.org/packages/c0/61/95f1eec25796be6dddf71440ee16ec1fd0c573fc61a73bd1ef6daacd529a/pendulum-3.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e68d6a51880708084afd8958af42dc8c5e819a70a6c6ae903b1c4bfc61e0f25", size = 341509, upload-time = "2025-04-19T14:01:31.1Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7b/eb0f5e6aa87d5e1b467a1611009dbdc92f0f72425ebf07669bfadd8885a6/pendulum-3.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e3f1e5da39a7ea7119efda1dd96b529748c1566f8a983412d0908455d606942", size = 378674, upload-time = "2025-04-19T14:01:32.974Z" }, + { url = "https://files.pythonhosted.org/packages/29/68/5a4c1b5de3e54e16cab21d2ec88f9cd3f18599e96cc90a441c0b0ab6b03f/pendulum-3.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9af1e5eeddb4ebbe1b1c9afb9fd8077d73416ade42dd61264b3f3b87742e0bb", size = 436133, upload-time = "2025-04-19T14:01:34.349Z" }, + { url = "https://files.pythonhosted.org/packages/87/5d/f7a1d693e5c0f789185117d5c1d5bee104f5b0d9fbf061d715fb61c840a8/pendulum-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20f74aa8029a42e327bfc150472e0e4d2358fa5d795f70460160ba81b94b6945", size = 351232, upload-time = "2025-04-19T14:01:35.669Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/c97617eb31f1d0554edb073201a294019b9e0a9bd2f73c68e6d8d048cd6b/pendulum-3.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:cf6229e5ee70c2660148523f46c472e677654d0097bec010d6730f08312a4931", size = 521562, upload-time = "2025-04-19T14:01:37.05Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/0d0ef3393303877e757b848ecef8a9a8c7627e17e7590af82d14633b2cd1/pendulum-3.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:350cabb23bf1aec7c7694b915d3030bff53a2ad4aeabc8c8c0d807c8194113d6", size = 523221, upload-time = "2025-04-19T14:01:38.444Z" }, + { url = "https://files.pythonhosted.org/packages/99/f3/aefb579aa3cebd6f2866b205fc7a60d33e9a696e9e629024752107dc3cf5/pendulum-3.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:42959341e843077c41d47420f28c3631de054abd64da83f9b956519b5c7a06a7", size = 260502, upload-time = "2025-04-19T14:01:39.814Z" }, + { url = "https://files.pythonhosted.org/packages/02/74/4332b5d6e34c63d4df8e8eab2249e74c05513b1477757463f7fdca99e9be/pendulum-3.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:006758e2125da2e624493324dfd5d7d1b02b0c44bc39358e18bf0f66d0767f5f", size = 253089, upload-time = "2025-04-19T14:01:41.171Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1f/af928ba4aa403dac9569f787adcf024005e7654433d71f7a84e608716837/pendulum-3.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:28658b0baf4b30eb31d096a375983cfed033e60c0a7bbe94fa23f06cd779b50b", size = 336209, upload-time = "2025-04-19T14:01:42.775Z" }, + { url = "https://files.pythonhosted.org/packages/b6/16/b010643007ba964c397da7fa622924423883c1bbff1a53f9d1022cd7f024/pendulum-3.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b114dcb99ce511cb8f5495c7b6f0056b2c3dba444ef1ea6e48030d7371bd531a", size = 323132, upload-time = "2025-04-19T14:01:44.577Z" }, + { url = "https://files.pythonhosted.org/packages/64/19/c3c47aeecb5d9bceb0e89faafd800d39809b696c5b7bba8ec8370ad5052c/pendulum-3.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2404a6a54c80252ea393291f0b7f35525a61abae3d795407f34e118a8f133a18", size = 341509, upload-time = "2025-04-19T14:01:46.084Z" }, + { url = "https://files.pythonhosted.org/packages/38/cf/c06921ff6b860ff7e62e70b8e5d4dc70e36f5abb66d168bd64d51760bc4e/pendulum-3.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d06999790d9ee9962a1627e469f98568bf7ad1085553fa3c30ed08b3944a14d7", size = 378674, upload-time = "2025-04-19T14:01:47.727Z" }, + { url = "https://files.pythonhosted.org/packages/62/0b/a43953b9eba11e82612b033ac5133f716f1b76b6108a65da6f408b3cc016/pendulum-3.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94751c52f6b7c306734d1044c2c6067a474237e1e5afa2f665d1fbcbbbcf24b3", size = 436133, upload-time = "2025-04-19T14:01:49.126Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a0/ec3d70b3b96e23ae1d039f132af35e17704c22a8250d1887aaefea4d78a6/pendulum-3.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5553ac27be05e997ec26d7f004cf72788f4ce11fe60bb80dda604a64055b29d0", size = 351232, upload-time = "2025-04-19T14:01:50.575Z" }, + { url = "https://files.pythonhosted.org/packages/f4/97/aba23f1716b82f6951ba2b1c9178a2d107d1e66c102762a9bf19988547ea/pendulum-3.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f8dee234ca6142bf0514368d01a72945a44685aaa2fc4c14c98d09da9437b620", size = 521563, upload-time = "2025-04-19T14:01:51.9Z" }, + { url = "https://files.pythonhosted.org/packages/01/33/2c0d5216cc53d16db0c4b3d510f141ee0a540937f8675948541190fbd48b/pendulum-3.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7378084fe54faab4ee481897a00b710876f2e901ded6221671e827a253e643f2", size = 523221, upload-time = "2025-04-19T14:01:53.275Z" }, + { url = "https://files.pythonhosted.org/packages/51/89/8de955c339c31aeae77fd86d3225509b998c81875e9dba28cb88b8cbf4b3/pendulum-3.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8539db7ae2c8da430ac2515079e288948c8ebf7eb1edd3e8281b5cdf433040d6", size = 260501, upload-time = "2025-04-19T14:01:54.749Z" }, + { url = "https://files.pythonhosted.org/packages/15/c3/226a3837363e94f8722461848feec18bfdd7d5172564d53aa3c3397ff01e/pendulum-3.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:1ce26a608e1f7387cd393fba2a129507c4900958d4f47b90757ec17656856571", size = 253087, upload-time = "2025-04-19T14:01:55.998Z" }, + { url = "https://files.pythonhosted.org/packages/66/10/3258c084653606d2be2c7168998eda4a57cf1559cecb43cf1100000fda5f/pendulum-3.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d2cac744940299d8da41a3ed941aa1e02b5abbc9ae2c525f3aa2ae30c28a86b5", size = 339442, upload-time = "2025-04-19T14:02:12.512Z" }, + { url = "https://files.pythonhosted.org/packages/98/d5/98a1a10cd1cfb3390fbf070864e9a10de8e70a9d4509832132f4d900d655/pendulum-3.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ffb39c3f3906a9c9a108fa98e5556f18b52d2c6451984bbfe2f14436ec4fc9d4", size = 326609, upload-time = "2025-04-19T14:02:13.838Z" }, + { url = "https://files.pythonhosted.org/packages/0a/2e/448abdebc11b9c54e190d273cb084162643199fc184cb1bb6bff7900e67f/pendulum-3.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe18b1c2eb364064cc4a68a65900f1465cac47d0891dab82341766bcc05b40c", size = 344777, upload-time = "2025-04-19T14:02:15.512Z" }, + { url = "https://files.pythonhosted.org/packages/ed/91/ee857bbd51168bf08b89c3a4705c920725eee0f830ccc513b8370f6ce71d/pendulum-3.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9e9b28a35cec9fcd90f224b4878456129a057dbd694fc8266a9393834804995", size = 354404, upload-time = "2025-04-19T14:02:16.91Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d4/e63a57df65e2b2d10f3aa917a4069be9abf5ac7d56d11336e0510742d8a6/pendulum-3.1.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a3be19b73a9c6a866724419295482f817727e635ccc82f07ae6f818943a1ee96", size = 524948, upload-time = "2025-04-19T14:02:18.808Z" }, + { url = "https://files.pythonhosted.org/packages/93/87/04e74600c5a5674e5f341b8888b530a9de9b84b31889f80fac3bee3e9e87/pendulum-3.1.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:24a53b523819bda4c70245687a589b5ea88711f7caac4be5f276d843fe63076b", size = 526340, upload-time = "2025-04-19T14:02:20.242Z" }, + { url = "https://files.pythonhosted.org/packages/48/27/d3577a5f6f7d1fbf1138d87ce21ebab363c78642513b991d1c424d658d09/pendulum-3.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bd701789414fbd0be3c75f46803f31e91140c23821e4bcb0fa2bddcdd051c425", size = 261089, upload-time = "2025-04-19T14:02:21.631Z" }, + { url = "https://files.pythonhosted.org/packages/6e/23/e98758924d1b3aac11a626268eabf7f3cf177e7837c28d47bf84c64532d0/pendulum-3.1.0-py3-none-any.whl", hash = "sha256:f9178c2a8e291758ade1e8dd6371b1d26d08371b4c7730a6e9a3ef8b16ebae0f", size = 111799, upload-time = "2025-04-19T14:02:34.739Z" }, +] + [[package]] name = "pillow" version = "10.4.0" @@ -2837,6 +3284,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] +[[package]] +name = "prefect" +version = "3.4.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiosqlite" }, + { name = "alembic" }, + { name = "anyio" }, + { name = "apprise" }, + { name = "asgi-lifespan" }, + { name = "asyncpg" }, + { name = "cachetools" }, + { name = "click" }, + { name = "cloudpickle" }, + { name = "coolname" }, + { name = "cryptography", version = "45.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cryptography", version = "46.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "dateparser" }, + { name = "docker" }, + { name = "exceptiongroup" }, + { name = "fastapi" }, + { name = "fsspec" }, + { name = "graphviz" }, + { name = "griffe" }, + { name = "httpcore" }, + { name = "httpx", extra = ["http2"] }, + { name = "humanize" }, + { name = "jinja2" }, + { name = "jinja2-humanize-extension" }, + { name = "jsonpatch" }, + { name = "jsonschema" }, + { name = "opentelemetry-api" }, + { name = "orjson" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pendulum", marker = "python_full_version < '3.13'" }, + { name = "prometheus-client" }, + { name = "pydantic" }, + { name = "pydantic-core" }, + { name = "pydantic-extra-types" }, + { name = "pydantic-settings" }, + { name = "python-dateutil" }, + { name = "python-slugify" }, + { name = "python-socks", extra = ["asyncio"] }, + { name = "pytz" }, + { name = "pyyaml" }, + { name = "readchar" }, + { name = "rfc3339-validator" }, + { name = "rich" }, + { name = "ruamel-yaml" }, + { name = "semver" }, + { name = "sniffio" }, + { name = "sqlalchemy", extra = ["asyncio"] }, + { name = "toml" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "uv" }, + { name = "uvicorn" }, + { name = "websockets" }, + { name = "whenever", marker = "python_full_version >= '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/ed/faf33ab9f344992ca7df351fb16c1e5d9634ecb6280fa65cc708bcdd647a/prefect-3.4.22.tar.gz", hash = "sha256:4b46a793e9907a4c8539b04bb2fdbc609c9810f690d7673a3bc8d0c9aebdfc84", size = 5617829, upload-time = "2025-10-03T18:30:57.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dc/424fcdb9605bc2100902ac634daa607dfd6752c203e134883de8a831c72b/prefect-3.4.22-py3-none-any.whl", hash = "sha256:41743e2817195e1ef8fdef2e65c4b16da1308cf8f5dd1a85e679c8cbe7186710", size = 6138167, upload-time = "2025-10-03T18:30:54.844Z" }, +] + [[package]] name = "primp" version = "0.15.0" @@ -2853,6 +3366,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/dd/f0183ed0145e58cf9d286c1b2c14f63ccee987a4ff79ac85acc31b5d86bd/primp-0.15.0-cp38-abi3-win_amd64.whl", hash = "sha256:aeb6bd20b06dfc92cfe4436939c18de88a58c640752cf7f30d9e4ae893cdec32", size = 3149967, upload-time = "2025-04-17T11:41:07.067Z" }, ] +[[package]] +name = "prometheus-client" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.50" @@ -3142,6 +3664,9 @@ dbos = [ examples = [ { name = "pydantic-ai-examples" }, ] +prefect = [ + { name = "pydantic-ai-slim", extra = ["prefect"] }, +] [package.dev-dependencies] dev = [ @@ -3192,8 +3717,9 @@ requires-dist = [ { name = "pydantic-ai-examples", marker = "extra == 'examples'", editable = "examples" }, { name = "pydantic-ai-slim", extras = ["ag-ui", "anthropic", "bedrock", "cli", "cohere", "evals", "google", "groq", "huggingface", "logfire", "mcp", "mistral", "openai", "retries", "temporal", "vertexai"], editable = "pydantic_ai_slim" }, { name = "pydantic-ai-slim", extras = ["dbos"], marker = "extra == 'dbos'", editable = "pydantic_ai_slim" }, + { name = "pydantic-ai-slim", extras = ["prefect"], marker = "extra == 'prefect'", editable = "pydantic_ai_slim" }, ] -provides-extras = ["a2a", "dbos", "examples"] +provides-extras = ["a2a", "dbos", "examples", "prefect"] [package.metadata.requires-dev] dev = [ @@ -3345,6 +3871,9 @@ mistral = [ openai = [ { name = "openai" }, ] +prefect = [ + { name = "prefect" }, +] retries = [ { name = "tenacity" }, ] @@ -3382,6 +3911,7 @@ requires-dist = [ { name = "mistralai", marker = "extra == 'mistral'", specifier = ">=1.9.10" }, { name = "openai", marker = "extra == 'openai'", specifier = ">=1.107.2" }, { name = "opentelemetry-api", specifier = ">=1.28.0" }, + { name = "prefect", marker = "extra == 'prefect'", specifier = ">=3.4.21" }, { name = "prompt-toolkit", marker = "extra == 'cli'", specifier = ">=3" }, { name = "pydantic", specifier = ">=2.10" }, { name = "pydantic-evals", marker = "extra == 'evals'", editable = "pydantic_evals" }, @@ -3395,7 +3925,7 @@ requires-dist = [ { name = "tenacity", marker = "extra == 'retries'", specifier = ">=8.2.3" }, { name = "typing-inspection", specifier = ">=0.4.0" }, ] -provides-extras = ["a2a", "ag-ui", "anthropic", "bedrock", "cli", "cohere", "dbos", "duckduckgo", "evals", "google", "groq", "huggingface", "logfire", "mcp", "mistral", "openai", "retries", "tavily", "temporal", "vertexai"] +provides-extras = ["a2a", "ag-ui", "anthropic", "bedrock", "cli", "cohere", "dbos", "duckduckgo", "evals", "google", "groq", "huggingface", "logfire", "mcp", "mistral", "openai", "prefect", "retries", "tavily", "temporal", "vertexai"] [[package]] name = "pydantic-core" @@ -3513,6 +4043,19 @@ requires-dist = [ ] provides-extras = ["logfire"] +[[package]] +name = "pydantic-extra-types" +version = "2.10.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/ba/4178111ec4116c54e1dc7ecd2a1ff8f54256cdbd250e576882911e8f710a/pydantic_extra_types-2.10.5.tar.gz", hash = "sha256:1dcfa2c0cf741a422f088e0dbb4690e7bfadaaf050da3d6f80d6c3cf58a2bad8", size = 138429, upload-time = "2025-06-02T09:31:52.713Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/1a/5f4fd9e7285f10c44095a4f9fe17d0f358d1702a7c74a9278c794e8a7537/pydantic_extra_types-2.10.5-py3-none-any.whl", hash = "sha256:b60c4e23d573a69a4f1a16dd92888ecc0ef34fb0e655b4f305530377fa70e7a8", size = 38315, upload-time = "2025-06-02T09:31:51.229Z" }, +] + [[package]] name = "pydantic-graph" source = { editable = "pydantic_graph" } @@ -3707,6 +4250,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] +[[package]] +name = "python-slugify" +version = "8.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "text-unidecode" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921, upload-time = "2024-02-08T18:32:45.488Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051, upload-time = "2024-02-08T18:32:43.911Z" }, +] + +[[package]] +name = "python-socks" +version = "2.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/fb/49fc4c3d61dbc8404879bed6c94c0595e654951ac9145645b057c4883966/python_socks-2.7.2.tar.gz", hash = "sha256:4c845d4700352bc7e7382f302dfc6baf0af0de34d2a6d70ba356b2539d4dbb62", size = 229950, upload-time = "2025-08-01T06:47:05.488Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/e6/1fdebffa733e79e67b43ee8930e4e5049eb51eae3608caeafc83518798aa/python_socks-2.7.2-py3-none-any.whl", hash = "sha256:d311aefbacc0ddfaa1fa1c32096c436d4fe75b899c24d78e677e1b0623c52c48", size = 55048, upload-time = "2025-08-01T06:47:03.734Z" }, +] + +[package.optional-dependencies] +asyncio = [ + { name = "async-timeout", marker = "python_full_version < '3.11'" }, +] + [[package]] name = "pytz" version = "2025.1" @@ -3794,6 +4363,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911, upload-time = "2020-11-12T02:38:24.638Z" }, ] +[[package]] +name = "readchar" +version = "4.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/f8/8657b8cbb4ebeabfbdf991ac40eca8a1d1bd012011bd44ad1ed10f5cb494/readchar-4.2.1.tar.gz", hash = "sha256:91ce3faf07688de14d800592951e5575e9c7a3213738ed01d394dcc949b79adb", size = 9685, upload-time = "2024-11-04T18:28:07.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/10/e4b1e0e5b6b6745c8098c275b69bc9d73e9542d5c7da4f137542b499ed44/readchar-4.2.1-py3-none-any.whl", hash = "sha256:a769305cd3994bb5fa2764aa4073452dc105a4ec39068ffe6efd3c20c60acc77", size = 9350, upload-time = "2024-11-04T18:28:02.859Z" }, +] + [[package]] name = "referencing" version = "0.36.2" @@ -3892,6 +4470,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, ] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { 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 = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, +] + [[package]] name = "rich" version = "13.9.4" @@ -4044,6 +4647,70 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315, upload-time = "2022-07-20T10:28:34.978Z" }, ] +[[package]] +name = "ruamel-yaml" +version = "0.18.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ruamel-yaml-clib", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/db/f3950f5e5031b618aae9f423a39bf81a55c148aecd15a34527898e752cf4/ruamel.yaml-0.18.15.tar.gz", hash = "sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700", size = 146865, upload-time = "2025-08-19T11:15:10.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/e5/f2a0621f1781b76a38194acae72f01e37b1941470407345b6e8653ad7640/ruamel.yaml-0.18.15-py3-none-any.whl", hash = "sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701", size = 119702, upload-time = "2025-08-19T11:15:07.696Z" }, +] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/e9/39ec4d4b3f91188fad1842748f67d4e749c77c37e353c4e545052ee8e893/ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e", size = 225394, upload-time = "2025-09-22T19:51:23.753Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/56/35a0a752415ae01992c68f5a6513bdef0e1b6fbdb60d7619342ce12346a0/ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4", size = 269216, upload-time = "2025-09-23T14:24:09.742Z" }, + { url = "https://files.pythonhosted.org/packages/98/6a/9a68184ab93619f4607ff1675e4ef01e8accfcbff0d482f4ca44c10d8eab/ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451", size = 137092, upload-time = "2025-09-22T19:50:26.906Z" }, + { url = "https://files.pythonhosted.org/packages/2b/3f/cfed5f088628128a9ec66f46794fd4d165642155c7b78c26d83b16c6bf7b/ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0ac90efbc7a77b0d796c03c8cc4e62fd710b3f1e4c32947713ef2ef52e09543", size = 633768, upload-time = "2025-09-22T19:50:31.228Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d5/5ce2cc156c1da48160171968d91f066d305840fbf930ee955a509d025a44/ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bf6b699223afe6c7fe9f2ef76e0bfa6dd892c21e94ce8c957478987ade76cd8", size = 721253, upload-time = "2025-09-22T19:50:28.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/71/d0b56bc902b38ebe4be8e270f730f929eec4edaf8a0fa7028f4ef64fa950/ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d73a0187718f6eec5b2f729b0f98e4603f7bd9c48aa65d01227d1a5dcdfbe9e8", size = 683823, upload-time = "2025-09-22T19:50:29.993Z" }, + { url = "https://files.pythonhosted.org/packages/4b/db/1f37449dd89c540218598316ccafc1a0aed60215e72efa315c5367cfd015/ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81f6d3b19bc703679a5705c6a16dabdc79823c71d791d73c65949be7f3012c02", size = 690370, upload-time = "2025-09-23T18:42:46.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/53/c498b30f35efcd9f47cb084d7ad9374f2b907470f73913dec6396b81397d/ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b28caeaf3e670c08cb7e8de221266df8494c169bd6ed8875493fab45be9607a4", size = 703578, upload-time = "2025-09-22T19:50:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/34/79/492cfad9baed68914840c39e5f3c1cc251f51a897ddb3f532601215cbb12/ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94f3efb718f8f49b031f2071ec7a27dd20cbfe511b4dfd54ecee54c956da2b31", size = 722544, upload-time = "2025-09-22T19:50:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f5/479ebfd5ba396e209ade90f7282d84b90c57b3e07be8dc6fcd02a6df7ffc/ruamel.yaml.clib-0.2.14-cp310-cp310-win32.whl", hash = "sha256:27c070cf3888e90d992be75dd47292ff9aa17dafd36492812a6a304a1aedc182", size = 100375, upload-time = "2025-09-22T19:50:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/57/31/a044520fdb3bd409889f67f1efebda0658033c7ab3f390cee37531cc9a9e/ruamel.yaml.clib-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:4f4a150a737fccae13fb51234d41304ff2222e3b7d4c8e9428ed1a6ab48389b8", size = 118129, upload-time = "2025-09-22T19:50:35.545Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/3c51e9578b8c36fcc4bdd271a1a5bb65963a74a4b6ad1a989768a22f6c2a/ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5bae1a073ca4244620425cd3d3aa9746bde590992b98ee8c7c8be8c597ca0d4e", size = 270207, upload-time = "2025-09-23T14:24:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/4a/16/cb02815bc2ae9c66760c0c061d23c7358f9ba51dae95ac85247662b7fbe2/ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:0a54e5e40a7a691a426c2703b09b0d61a14294d25cfacc00631aa6f9c964df0d", size = 137780, upload-time = "2025-09-22T19:50:37.734Z" }, + { url = "https://files.pythonhosted.org/packages/31/c6/fc687cd1b93bff8e40861eea46d6dc1a6a778d9a085684e4045ff26a8e40/ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:10d9595b6a19778f3269399eff6bab642608e5966183abc2adbe558a42d4efc9", size = 641590, upload-time = "2025-09-22T19:50:41.978Z" }, + { url = "https://files.pythonhosted.org/packages/45/5d/65a2bc08b709b08576b3f307bf63951ee68a8e047cbbda6f1c9864ecf9a7/ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba72975485f2b87b786075e18a6e5d07dc2b4d8973beb2732b9b2816f1bad70", size = 738090, upload-time = "2025-09-22T19:50:39.152Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d0/a70a03614d9a6788a3661ab1538879ed2aae4e84d861f101243116308a37/ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29757bdb7c142f9595cc1b62ec49a3d1c83fab9cef92db52b0ccebaad4eafb98", size = 700744, upload-time = "2025-09-22T19:50:40.811Z" }, + { url = "https://files.pythonhosted.org/packages/77/30/c93fa457611f79946d5cb6cc97493ca5425f3f21891d7b1f9b44eaa1b38e/ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:557df28dbccf79b152fe2d1b935f6063d9cc431199ea2b0e84892f35c03bb0ee", size = 742321, upload-time = "2025-09-23T18:42:48.916Z" }, + { url = "https://files.pythonhosted.org/packages/40/85/e2c54ad637117cd13244a4649946eaa00f32edcb882d1f92df90e079ab00/ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:26a8de280ab0d22b6e3ec745b4a5a07151a0f74aad92dd76ab9c8d8d7087720d", size = 743805, upload-time = "2025-09-22T19:50:43.58Z" }, + { url = "https://files.pythonhosted.org/packages/81/50/f899072c38877d8ef5382e0b3d47f8c4346226c1f52d6945d6f64fec6a2f/ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e501c096aa3889133d674605ebd018471bc404a59cbc17da3c5924421c54d97c", size = 769529, upload-time = "2025-09-22T19:50:45.707Z" }, + { url = "https://files.pythonhosted.org/packages/99/7c/96d4b5075e30c65ea2064e40c2d657c7c235d7b6ef18751cf89a935b9041/ruamel.yaml.clib-0.2.14-cp311-cp311-win32.whl", hash = "sha256:915748cfc25b8cfd81b14d00f4bfdb2ab227a30d6d43459034533f4d1c207a2a", size = 100256, upload-time = "2025-09-22T19:50:48.26Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8c/73ee2babd04e8bfcf1fd5c20aa553d18bf0ebc24b592b4f831d12ae46cc0/ruamel.yaml.clib-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:4ccba93c1e5a40af45b2f08e4591969fa4697eae951c708f3f83dcbf9f6c6bb1", size = 118234, upload-time = "2025-09-22T19:50:47.019Z" }, + { url = "https://files.pythonhosted.org/packages/b4/42/ccfb34a25289afbbc42017e4d3d4288e61d35b2e00cfc6b92974a6a1f94b/ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6aeadc170090ff1889f0d2c3057557f9cd71f975f17535c26a5d37af98f19c27", size = 271775, upload-time = "2025-09-23T14:24:12.771Z" }, + { url = "https://files.pythonhosted.org/packages/82/73/e628a92e80197ff6a79ab81ec3fa00d4cc082d58ab78d3337b7ba7043301/ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5e56ac47260c0eed992789fa0b8efe43404a9adb608608631a948cee4fc2b052", size = 138842, upload-time = "2025-09-22T19:50:49.156Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c5/346c7094344a60419764b4b1334d9e0285031c961176ff88ffb652405b0c/ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:a911aa73588d9a8b08d662b9484bc0567949529824a55d3885b77e8dd62a127a", size = 647404, upload-time = "2025-09-22T19:50:52.921Z" }, + { url = "https://files.pythonhosted.org/packages/df/99/65080c863eb06d4498de3d6c86f3e90595e02e159fd8529f1565f56cfe2c/ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05ba88adf3d7189a974b2de7a9d56731548d35dc0a822ec3dc669caa7019b29", size = 753141, upload-time = "2025-09-22T19:50:50.294Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e3/0de85f3e3333f8e29e4b10244374a202a87665d1131798946ee22cf05c7c/ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb04c5650de6668b853623eceadcdb1a9f2fee381f5d7b6bc842ee7c239eeec4", size = 703477, upload-time = "2025-09-22T19:50:51.508Z" }, + { url = "https://files.pythonhosted.org/packages/d9/25/0d2f09d8833c7fd77ab8efeff213093c16856479a9d293180a0d89f6bed9/ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df3ec9959241d07bc261f4983d25a1205ff37703faf42b474f15d54d88b4f8c9", size = 741157, upload-time = "2025-09-23T18:42:50.408Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8c/959f10c2e2153cbdab834c46e6954b6dd9e3b109c8f8c0a3cf1618310985/ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbc08c02e9b147a11dfcaa1ac8a83168b699863493e183f7c0c8b12850b7d259", size = 745859, upload-time = "2025-09-22T19:50:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6b/e580a7c18b485e1a5f30a32cda96b20364b0ba649d9d2baaf72f8bd21f83/ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c099cafc1834d3c5dac305865d04235f7c21c167c8dd31ebc3d6bbc357e2f023", size = 770200, upload-time = "2025-09-22T19:50:55.718Z" }, + { url = "https://files.pythonhosted.org/packages/ef/44/3455eebc761dc8e8fdced90f2b0a3fa61e32ba38b50de4130e2d57db0f21/ruamel.yaml.clib-0.2.14-cp312-cp312-win32.whl", hash = "sha256:b5b0f7e294700b615a3bcf6d28b26e6da94e8eba63b079f4ec92e9ba6c0d6b54", size = 98829, upload-time = "2025-09-22T19:50:58.895Z" }, + { url = "https://files.pythonhosted.org/packages/76/ab/5121f7f3b651db93de546f8c982c241397aad0a4765d793aca1dac5eadee/ruamel.yaml.clib-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:a37f40a859b503304dd740686359fcf541d6fb3ff7fc10f539af7f7150917c68", size = 115570, upload-time = "2025-09-22T19:50:57.981Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ae/e3811f05415594025e96000349d3400978adaed88d8f98d494352d9761ee/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32", size = 269205, upload-time = "2025-09-23T14:24:15.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/06/7d51f4688d6d72bb72fa74254e1593c4f5ebd0036be5b41fe39315b275e9/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85", size = 137417, upload-time = "2025-09-22T19:50:59.82Z" }, + { url = "https://files.pythonhosted.org/packages/5a/08/b4499234a420ef42960eeb05585df5cc7eb25ccb8c980490b079e6367050/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e", size = 642558, upload-time = "2025-09-22T19:51:03.388Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ba/1975a27dedf1c4c33306ee67c948121be8710b19387aada29e2f139c43ee/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb", size = 744087, upload-time = "2025-09-22T19:51:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/8a19a13d27f3bd09fa18813add8380a29115a47b553845f08802959acbce/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d", size = 699709, upload-time = "2025-09-22T19:51:02.075Z" }, + { url = "https://files.pythonhosted.org/packages/19/ee/8d6146a079ad21e534b5083c9ee4a4c8bec42f79cf87594b60978286b39a/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59", size = 708926, upload-time = "2025-09-23T18:42:51.707Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/426b714abdc222392e68f3b8ad323930d05a214a27c7e7a0f06c69126401/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca", size = 740202, upload-time = "2025-09-22T19:51:04.673Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ac/3c5c2b27a183f4fda8a57c82211721c016bcb689a4a175865f7646db9f94/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6", size = 765196, upload-time = "2025-09-22T19:51:05.916Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/06f56a71fd55021c993ed6e848c9b2e5e9cfce180a42179f0ddd28253f7c/ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2", size = 98635, upload-time = "2025-09-22T19:51:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/51/79/76aba16a1689b50528224b182f71097ece338e7a4ab55e84c2e73443b78a/ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78", size = 115238, upload-time = "2025-09-22T19:51:07.081Z" }, + { url = "https://files.pythonhosted.org/packages/21/e2/a59ff65c26aaf21a24eb38df777cb9af5d87ba8fc8107c163c2da9d1e85e/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f", size = 271441, upload-time = "2025-09-23T14:24:16.498Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fa/3234f913fe9a6525a7b97c6dad1f51e72b917e6872e051a5e2ffd8b16fbb/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83", size = 137970, upload-time = "2025-09-22T19:51:09.472Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ec/4edbf17ac2c87fa0845dd366ef8d5852b96eb58fcd65fc1ecf5fe27b4641/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27", size = 739639, upload-time = "2025-09-22T19:51:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/15/18/b0e1fafe59051de9e79cdd431863b03593ecfa8341c110affad7c8121efc/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640", size = 764456, upload-time = "2025-09-22T19:51:11.736Z" }, +] + [[package]] name = "ruff" version = "0.9.7" @@ -4102,6 +4769,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, ] +[[package]] +name = "semver" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730, upload-time = "2025-01-24T13:19:27.617Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload-time = "2025-01-24T13:19:24.949Z" }, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -4195,6 +4871,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, ] +[package.optional-dependencies] +asyncio = [ + { name = "greenlet" }, +] + [[package]] name = "sse-starlette" version = "2.2.1" @@ -4284,6 +4965,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165, upload-time = "2024-07-05T07:25:29.591Z" }, ] +[[package]] +name = "text-unidecode" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, +] + [[package]] name = "tiktoken" version = "0.9.0" @@ -4541,6 +5231,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762, upload-time = "2025-01-21T19:49:37.187Z" }, ] +[[package]] +name = "tzlocal" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, +] + [[package]] name = "urllib3" version = "2.3.0" @@ -4550,6 +5252,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, ] +[[package]] +name = "uv" +version = "0.8.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/85/6ae7e6a003bf815a1774c11e08de56f2037e1dad0bbbe050c7d3bd57be14/uv-0.8.23.tar.gz", hash = "sha256:1d3ee6f88b77429454172048a9672b8058607abcdf66cb8229707f0312a6752c", size = 3667341, upload-time = "2025-10-04T18:23:53.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/50/19a48639b2f61bf3f42d92e556494e3b39ccb58287b8b3244236b9d9df45/uv-0.8.23-py3-none-linux_armv6l.whl", hash = "sha256:58879ab3544ed0d7996dc5d9f87ce6a9770bd8f7886d8504298f62c481ecd9fd", size = 20599279, upload-time = "2025-10-04T18:23:06.64Z" }, + { url = "https://files.pythonhosted.org/packages/d7/26/36b3b37ca79bfff6998d7e9567465e6e3b4acf3fe1c7b226302272369240/uv-0.8.23-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3a5c6cfad0a7b92e2a2ddf121e86171c7b850ccbdbc82b49afb8014e60d7dd84", size = 19580214, upload-time = "2025-10-04T18:23:10.551Z" }, + { url = "https://files.pythonhosted.org/packages/97/a1/e64b4c9a4db6c6ce6ee286991ce98e9cbf472402a5d09f216b85f2287465/uv-0.8.23-py3-none-macosx_11_0_arm64.whl", hash = "sha256:092404eb361f2f6cddf2c0a195c3f4bd2bc8baae60ed8b43409f93f672992b40", size = 18193303, upload-time = "2025-10-04T18:23:12.955Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d7/8dfd344ca878b4de2d6e43636792ecef9d4870dd3735b3cb4951cfc22b0b/uv-0.8.23-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8d891aa0f82d67ed32811e1f3e6f314c6b0759a01b5b7676b4969196caf74295", size = 19968823, upload-time = "2025-10-04T18:23:15.861Z" }, + { url = "https://files.pythonhosted.org/packages/02/91/cbf2ebd1642577af2054842fb22cf21f7fa71d59a05ef5bda8f065ea8cc0/uv-0.8.23-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f59f3823750da8187d9b3c65b87eb2436c398f385befa0ec48a1118d2accea51", size = 20178276, upload-time = "2025-10-04T18:23:19.056Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d3/fefd0589f235c4c1d9b66baaad1472705c4621edc2eb7dabb0d98fc84d72/uv-0.8.23-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96c1abfcd2c44c3ac8dec5079e1e51497f371a04c97978fed2a16d1c7343e471", size = 21059931, upload-time = "2025-10-04T18:23:21.316Z" }, + { url = "https://files.pythonhosted.org/packages/f7/98/7c7237d891c5d8a350bb9d59593fc103add12269ff983e13bd18bbb52b3e/uv-0.8.23-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:042c2e8b701a55078e0eb6f164c7bf550b6040437036dc572db39115cdaa5a23", size = 22547863, upload-time = "2025-10-04T18:23:24.108Z" }, + { url = "https://files.pythonhosted.org/packages/03/79/c5043180fc6c1f68e4752e0067ffbb8273fea6bafc3ff4e3e1be9c69d63c/uv-0.8.23-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db02664898449af91a60b4caedc4a18944efc6375d814aa2e424204e975a43d7", size = 22172574, upload-time = "2025-10-04T18:23:26.775Z" }, + { url = "https://files.pythonhosted.org/packages/f6/73/2c472e40fc31f0fd61a41499bf1559af4d662ffa884b4d575f09c695b52e/uv-0.8.23-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e310bd2b77f0e1cf5d8d31f918292e36d884eb7eed76561198a195baee72f277", size = 21272611, upload-time = "2025-10-04T18:23:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2f/4f4e49dd04a90d982e76abbe0b6747187006a42b04f611042b525bb05c4b/uv-0.8.23-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:126ca80b6f72859998e2036679ce21c9b5024df0a09d8112698adc4153c3a1a7", size = 21239185, upload-time = "2025-10-04T18:23:31.258Z" }, + { url = "https://files.pythonhosted.org/packages/2b/35/2932f49ab0c6991e51e870bbf9fdef2a82f77cb5ed038a15b05dc9ce2c3e/uv-0.8.23-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1a695477c367cb5568a33c8cf075280853eebbdec387c861e27e7d9201985d5f", size = 20097560, upload-time = "2025-10-04T18:23:33.975Z" }, + { url = "https://files.pythonhosted.org/packages/53/ef/d34f514d759b3a2068c50d9dd68672fc5b6be9c8cd585eb311ded73a2b20/uv-0.8.23-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:cf4b8b167ff38ebcdc2dbd26b9918a2b3d937f4f2811280cbebdd937a838184e", size = 21187580, upload-time = "2025-10-04T18:23:36.365Z" }, + { url = "https://files.pythonhosted.org/packages/b0/a9/52ef1b04419d1bb9ba870fc6fae4fa7b217e5dea079fb3dd7f212965fb89/uv-0.8.23-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:2884ab47ac18dcd24e366544ab93938ce8ab1aea38d896e87d92c44f08bb0bc1", size = 20136752, upload-time = "2025-10-04T18:23:38.68Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/6ab974393935d37c4f6fa05ee27096ba5fd28850ae8ae049fad6f11febf8/uv-0.8.23-py3-none-musllinux_1_1_i686.whl", hash = "sha256:287430978458afbeab22aa2aafbfe3f5ec90f1054e7d4faec4156282930c44cb", size = 20494797, upload-time = "2025-10-04T18:23:40.947Z" }, + { url = "https://files.pythonhosted.org/packages/1b/f6/250531420babcd2e121c0998611e785335b7766989496ad405d42ef5f580/uv-0.8.23-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:16cbae67231acdd704e5e589d6fd37e16222d9fff32ca117a0cb3213be65fdf8", size = 21432784, upload-time = "2025-10-04T18:23:43.671Z" }, + { url = "https://files.pythonhosted.org/packages/7a/38/0c65b9c2305cd07e938b19d074dd6db5a7fd0a9dac86813b17d0d53e1759/uv-0.8.23-py3-none-win32.whl", hash = "sha256:f52c8068569d50e1b6d7670f709f5894f0e2f09c094d578d7d12cff0166924ad", size = 19331051, upload-time = "2025-10-04T18:23:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1e/46f242f974e4480b157ee8276d8c21fb2a975b842e321b72497e27889f8f/uv-0.8.23-py3-none-win_amd64.whl", hash = "sha256:39bc5cd9310ef7a4f567885ba48fd4f6174029986351321fcfa5887076f82380", size = 21380950, upload-time = "2025-10-04T18:23:48.959Z" }, + { url = "https://files.pythonhosted.org/packages/82/6b/37f0cfa325bb4a4a462aee8e0e2d1d1f409b7f4dcfa84b19022f28be8a5b/uv-0.8.23-py3-none-win_arm64.whl", hash = "sha256:cc1725b546edae8d66d9b10aa2616ac5f93c3fa62c1ec72087afcb4b4b802e99", size = 19821125, upload-time = "2025-10-04T18:23:51.584Z" }, +] + [[package]] name = "uvicorn" version = "0.34.0" @@ -4814,6 +5542,88 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416, upload-time = "2025-01-19T21:00:54.843Z" }, ] +[[package]] +name = "whenever" +version = "0.8.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "python_full_version >= '3.13' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/b8/ab68eac6c8f0f13542fa01f1aac6837e70aa5f4556891e57424eb1ada80b/whenever-0.8.9.tar.gz", hash = "sha256:2df8bdbd2b7898f404a2fd61f6eeb8641ccd191d4878b5d1853f3507bd22b2c6", size = 240155, upload-time = "2025-09-21T12:55:48.654Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/aa/b23092c7929ee0439e72515a3c7e468f7a0a8ff66173988e614f59ba944a/whenever-0.8.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:de89a952072a0957a4d2963ae21127978c31d3d2a235a5923eb8e5b80aa8c899", size = 390008, upload-time = "2025-09-21T12:55:26.424Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9f/f70d74a42ff602d98311536a6c011cc52ccc68690529f2ca93ff2503e10e/whenever-0.8.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c97f0f186af29c9c35bc57de8c00b2633b47edbb527d7507a6efea5d4a9b351", size = 374942, upload-time = "2025-09-21T12:55:18.451Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ac/e17d19918a295af09a106278b2febcbe92c46c261d6536162a03a0cb7878/whenever-0.8.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0231150269429025077f27ef47e4061cf5931515c9882c0833abf4080d6b53e0", size = 397120, upload-time = "2025-09-21T12:54:04.257Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/85796c55d20c5adf1de9afabad6fe3b01cbf345031fdac492e2bd85cf3fa/whenever-0.8.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75cacda66d847bca0669974a04f818596a59686fb6b4df448c32f0d61278e95f", size = 436678, upload-time = "2025-09-21T12:54:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/a7/04/029eb96d07d1662c89f16c580c919853f5bbb94ae3dcf1b88a5e513646e2/whenever-0.8.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac05b1ab24d96ff2fe4a4f403ae496c2bd45746312cd8b6be7aee5380d792a4", size = 430739, upload-time = "2025-09-21T12:54:35.092Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c9/d13f633f90e984557edf266dd10e5bf26ec7b0bf9e60b4ebe6c70b394170/whenever-0.8.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:953aa9ea89f9c2464001aed99dc3d8a8c2f73320b92e610b4dda71803a5c5fa3", size = 450812, upload-time = "2025-09-21T12:54:42.162Z" }, + { url = "https://files.pythonhosted.org/packages/a4/57/b3eecbe6ecbd1697fe82d3e031f46a096704dfe75d9a144c5985c771b158/whenever-0.8.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af3875daee685419d93a66128a40a26e2ec5504150a86538ef81315bf7fa5d87", size = 412021, upload-time = "2025-09-21T12:55:04.298Z" }, + { url = "https://files.pythonhosted.org/packages/99/59/4f4372eef7b5f42240af2e6fe580d6546cb934a43bcd7182cadefee92e7c/whenever-0.8.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1cbfc055012cd5caca69f535aae44042b28ec9da97e15932da556223932e58f7", size = 450875, upload-time = "2025-09-21T12:54:48.54Z" }, + { url = "https://files.pythonhosted.org/packages/84/92/b17145910713312948b16ec9aa39cfac30462fe26969e612f84dcbee4f3f/whenever-0.8.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7f5bfd000de72a3b04042028845affbfc316a19128fe77b06f43761848e1588", size = 575452, upload-time = "2025-09-21T12:54:12.141Z" }, + { url = "https://files.pythonhosted.org/packages/7d/9b/30d8905db022e99901ea2095e1a9dabf3208dd627990463ead73babb11f7/whenever-0.8.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a383d3b132a60497ee1e2100da02358319e8953a892c148e4de84b3e566084d9", size = 701371, upload-time = "2025-09-21T12:54:27.682Z" }, + { url = "https://files.pythonhosted.org/packages/81/29/e950121b7db61130685fd17e146086c8a9d9d5bda08f54db571b9309fded/whenever-0.8.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d9a333e1114e38e702ddca99a8a6c2c0007cdc5c5c233a5b25754b4fb774b6fe", size = 625316, upload-time = "2025-09-21T12:54:56.316Z" }, + { url = "https://files.pythonhosted.org/packages/c1/cb/12ca97b849a14c03672f46d0807310498556f8a06634558a3b0a8d5561ea/whenever-0.8.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5d8291c7398df060c96c09d23d3ad495a3a24751f769834e19599b5a9a24d3dd", size = 583082, upload-time = "2025-09-21T12:55:11.182Z" }, + { url = "https://files.pythonhosted.org/packages/79/4d/699a16fd25cacf8e4570610968bf4f4b2b4795d8e21e10636978941d7ebc/whenever-0.8.9-cp310-cp310-win32.whl", hash = "sha256:de71af104b0bc5981671b34efd7f22f7246e166fcb1a7d3557e95a169fd5c3af", size = 328301, upload-time = "2025-09-21T12:55:33.417Z" }, + { url = "https://files.pythonhosted.org/packages/27/67/e64df8ca0e0aaf549198742ac1ad388f7782726b3f2c943106c222f68038/whenever-0.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:f14dc54c50b46d9f0ad63b9e4c8424ef36fffa3583deadef77ca68e94cc9df47", size = 320873, upload-time = "2025-09-21T12:55:39.943Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2d/3148e9b18e604b433271c2327068498805b123e0862e27a2e548ec0453b3/whenever-0.8.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ebf93032000ebdb369d35ac0bc203fb3d5c047088976d00e102df829fcea2839", size = 390009, upload-time = "2025-09-21T12:55:27.468Z" }, + { url = "https://files.pythonhosted.org/packages/7a/62/eaabfd52681296da73af60bc79741bff793cdb8dd16e996642d8763d76c3/whenever-0.8.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1997b2dd7295ea81351eb171609ad0f198744c2e39256e1efdc7f9b35ad4e424", size = 374941, upload-time = "2025-09-21T12:55:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ef/63621255c337ef5b1efd056b63af372d22f04b6038e2b222d5e139dc8b86/whenever-0.8.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42be1f0986f2ed363f7609cf6d4ff044d6c6345ceb892be36c82cbe5d53b4a06", size = 397120, upload-time = "2025-09-21T12:54:06.46Z" }, + { url = "https://files.pythonhosted.org/packages/55/14/18a84816e62fdb50a745ee7a087ad32dcc7a33229b17a60671240a65cf4d/whenever-0.8.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d47729dc2bb2aa9cef403ee5801ee827ae1c4a88f866bb725289ee439cac5831", size = 436677, upload-time = "2025-09-21T12:54:21.244Z" }, + { url = "https://files.pythonhosted.org/packages/2f/f8/749ae43ced683373afe2ba966c4a3347b7eb3fcb70f0256df309e3b26053/whenever-0.8.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4094630e37d8c023f95746720b5390fbe83724b67406aa064331d0ec5b53c638", size = 430739, upload-time = "2025-09-21T12:54:36.415Z" }, + { url = "https://files.pythonhosted.org/packages/f6/95/4bbc1d777da72843d1b91c85831b316f9d81f36cf32fefb69b6e3b3a35fe/whenever-0.8.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84607f66f9a4c7f22599107e91ed90bd065314ae43dee2e4d109d8b09081d704", size = 450811, upload-time = "2025-09-21T12:54:43.252Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c3/796dd4a54bbd2714d99aec1aaeb0b53af9db3abaed81006b521bee910564/whenever-0.8.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa59d68646eddb68342338ad9e49dc0a0b28039dab01b7c3d001233b3bee2f36", size = 412020, upload-time = "2025-09-21T12:55:05.363Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cd/dbee35132a8c8375e73e6b438be3d519d373917936d2ddd0f33be3537bab/whenever-0.8.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:96dae067af3611b6b762d656d5a49a2144374327ab73c34382ac95b6b6978221", size = 450873, upload-time = "2025-09-21T12:54:50.339Z" }, + { url = "https://files.pythonhosted.org/packages/56/42/c0053a0646e17c440427da917065896a4fcc3b3d83e9d01fc0381ec2ba88/whenever-0.8.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2394cb8a716a2f075a53d9d31515c8da4b2b2cd15b3e4a200731979c44a27099", size = 575456, upload-time = "2025-09-21T12:54:13.471Z" }, + { url = "https://files.pythonhosted.org/packages/a3/d9/8ff6c3e937a0e553ded4f7d48f7ee8dd95db4489419ca710a4ad764166e6/whenever-0.8.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c919cbf343bdeab6637de96546d32597647fee5bb21127bfccfbcf2914d3da07", size = 701374, upload-time = "2025-09-21T12:54:29.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/6e/5b6f8a67f26730d1b91c661a1aaa2f0129485d06d9c2d22c38209f87cd4a/whenever-0.8.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7a164ada9f2b16f99d3e1d80fd65b17e299ba6ac59bad2287e8bfd389cf77ee3", size = 625320, upload-time = "2025-09-21T12:54:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ae/88d204a886d2c4a0120cda693e2da9a5b3fd9c27ef52e30d297d2d7cadb4/whenever-0.8.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cefe601336bc255bf64da907596ddd6dbeb52a6b747bc31041a4b5f2fd3a16a3", size = 583086, upload-time = "2025-09-21T12:55:12.315Z" }, + { url = "https://files.pythonhosted.org/packages/02/34/f4473dc5a932d3c4b503647d66f76a35143a3e5f7d1998cdc223ca003cd8/whenever-0.8.9-cp311-cp311-win32.whl", hash = "sha256:590f8d991a75dedcd27572429fc0c8b3ae7b1bb6e816123ae03575e0b40c520f", size = 328298, upload-time = "2025-09-21T12:55:34.521Z" }, + { url = "https://files.pythonhosted.org/packages/33/38/c89f5045c9cb2e1dc25bc7d94ff9fc5cc31cfd18c631130c6c8e06fff374/whenever-0.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:a1fc7861f0f3f99e10e14fd76bc3b144cd16bf3e94e87d62b403310d5221542f", size = 320871, upload-time = "2025-09-21T12:55:41.06Z" }, + { url = "https://files.pythonhosted.org/packages/6d/69/6378f3c6f42525fa2c85c17ae53b813c8427f3f5d56dad4a759b46992a9f/whenever-0.8.9-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1ec00dde447350a5222ce48e22034f3deca9108af7e3a12e304326ec9fc49d46", size = 391222, upload-time = "2025-09-21T12:55:28.529Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7f/4e2dc970d71e36302ecc3191c59bec995394eabc8218b82a3a11aa614ecd/whenever-0.8.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c39e110c76c005c8eff7c1039ede61c257bdec14076db323273b59a6c4325ddc", size = 375129, upload-time = "2025-09-21T12:55:20.677Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2c/f6dab73edf504cc00a4788d8c54a47a5aa2617cd131c75aea431a5fd10a4/whenever-0.8.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd4d52ec4c8ed7a668566bf6d4dd3c955c219e9a81d8077b55281b468d155b", size = 396772, upload-time = "2025-09-21T12:54:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/87/c4/d51518c678e56c7bc2b3e5aa90413f684eabf1b735c66bb1dfa5178242c6/whenever-0.8.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:849219c4ca784b7e6e160b41c3bdbba57359b25044df9dff9b044ac66534a8a8", size = 437282, upload-time = "2025-09-21T12:54:23.41Z" }, + { url = "https://files.pythonhosted.org/packages/40/d9/baf67c2e49a244e90ce899e64609067d4b827d660172df47da9e12cf054e/whenever-0.8.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb1e611c4e072c377f29812051f0d3b1ca911ff9f86943b69514755f1ff89a2c", size = 432764, upload-time = "2025-09-21T12:54:37.748Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ac/9eead179a34fc73caaa7b0fa1c1e4584dc58bbbdd13342d680f0f2db5d15/whenever-0.8.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c3e0b52f95d9e0266f04a1cb337ae958c962168ccec957f76d467067dce1f82", size = 451518, upload-time = "2025-09-21T12:54:44.31Z" }, + { url = "https://files.pythonhosted.org/packages/25/b9/df4de152aea628197ae10b3a69a656ec445359d662ee62bb13a72d254c84/whenever-0.8.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de2d393f97fa982c35283546bfadbe6b482f715aa90273950f83a9b3e1093668", size = 412830, upload-time = "2025-09-21T12:55:06.439Z" }, + { url = "https://files.pythonhosted.org/packages/34/bb/2199547f1485e37f35052bab085a0d41d7a5fe4ac40d6325f50461393f96/whenever-0.8.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:059206022a04e27f97524256a6b3c9ed361419bb5f691d382cca4f987c07e0d0", size = 452057, upload-time = "2025-09-21T12:54:51.816Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d7/8cc6e6707680f4e3bfb4bb8a4583db6792839c5ba3d6bc8d0c98266fffc0/whenever-0.8.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ad98973805fc0b75358849f0b428431d071aefa59d1bc716f36c7b187ea49825", size = 575232, upload-time = "2025-09-21T12:54:14.89Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ff/c67304afc02fbc4bacab373d88cb4b3d02a5ba99a341418b011dd89aec6d/whenever-0.8.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ae2f5c57777471b00d2b2ad2b3538e8cf0795439a2ce1cd1996d66d8c090b612", size = 701994, upload-time = "2025-09-21T12:54:30.703Z" }, + { url = "https://files.pythonhosted.org/packages/b2/87/245f8fc3f17036ce5a7a86a58b6c3ab22f44f8727d0f001ed26365cab662/whenever-0.8.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:be3f2b632ceef83de3a2d71156ecb4106fd05723a3d48f390e10c5f00c181e9a", size = 626822, upload-time = "2025-09-21T12:54:59.358Z" }, + { url = "https://files.pythonhosted.org/packages/81/30/e0c91b26c33f6433c4b060ac3f26fff90533626aa935abb65a915eed50d9/whenever-0.8.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cff750598c4fd00774dee1ac0019101c9fd938c5591a999b6d1345c4ac396801", size = 583997, upload-time = "2025-09-21T12:55:13.778Z" }, + { url = "https://files.pythonhosted.org/packages/2f/1d/1195aff62d073e0cccc3b9d9ec692157bc5632e7030adc7c20cb56e1c592/whenever-0.8.9-cp312-cp312-win32.whl", hash = "sha256:2815dbf52316b8865a9e376e1289dd47d8c1da5c07477aab0381bfd8e0e51d0c", size = 329910, upload-time = "2025-09-21T12:55:35.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/2738c8153961e8714bb3751ef25b32d3b0e77d4a086bc0c82e136933b964/whenever-0.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:89e63baf1923507ea82a0c1a02b6181c804f49c558e441d76e5d24e3a10bb690", size = 322293, upload-time = "2025-09-21T12:55:42.468Z" }, + { url = "https://files.pythonhosted.org/packages/cc/76/e7bca69087803db4c4bcc25767964f8ec638d5f3724ee37e3e48e37a3d5c/whenever-0.8.9-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0395da9d9cf0267f50e84c3f5c42e2438d765b96cf7ef5d56ddd801fdb1e1a0b", size = 391227, upload-time = "2025-09-21T12:55:29.683Z" }, + { url = "https://files.pythonhosted.org/packages/31/37/3449a30fc21e84d4221346db7cb69cddf6611b181bc95e1014cb84722b55/whenever-0.8.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:924b778a4e2266c09502fba426ff947271a1a6aafb42be1739cae11e249c895d", size = 375118, upload-time = "2025-09-21T12:55:22.145Z" }, + { url = "https://files.pythonhosted.org/packages/84/f1/dbc972e00fb10a64979a47e9a851bec546daf2d04fa740631c77b24b7613/whenever-0.8.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:210349884bb8b187b8bc9cfb1184136c1f15b59e2076bbefc05919b15a64fe7c", size = 396778, upload-time = "2025-09-21T12:54:08.746Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/0f0f0591ad94569d5ee372295617fae9b715b05952b381837280cd86aea6/whenever-0.8.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a9e747b796101c224dbd3a38280262707fec6bb9fadb103c08f96b3c31f1bef0", size = 437298, upload-time = "2025-09-21T12:54:24.36Z" }, + { url = "https://files.pythonhosted.org/packages/38/1d/20035025e64ddcb79456fbd393f25af03a3aeb0600b302cd5e6295d45fc9/whenever-0.8.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ef3737ad1d65b9e0b1f0471cd3ec92e8df1adf9c32c2749abc9ca4201b165f5", size = 432767, upload-time = "2025-09-21T12:54:38.736Z" }, + { url = "https://files.pythonhosted.org/packages/d5/88/dfe08f0c2df1c05d1d637eff68c25bee67b25286206b01ef4991a2e8d3fa/whenever-0.8.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:048649363722a2d11bf857bcf8d57922722f07d32b1771810f01c2a8b0fc3c5e", size = 451533, upload-time = "2025-09-21T12:54:45.315Z" }, + { url = "https://files.pythonhosted.org/packages/19/4a/98030aacda1af6b10522a6d72e6a72033392c255b474f82e924aa56e3a08/whenever-0.8.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5af3f15e365a123240a337acc6872fefbf0015d79ae40faf02cdf1af0c317bae", size = 412824, upload-time = "2025-09-21T12:55:07.522Z" }, + { url = "https://files.pythonhosted.org/packages/36/0f/30fa4193cd85efd177102363415bc7baf8cd7c4a6d3405386e9ddc404773/whenever-0.8.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b87349db66382ef96683499b493d4d3ba739cb68aa96fad8e6a4ac15c5e5dbef", size = 452063, upload-time = "2025-09-21T12:54:52.822Z" }, + { url = "https://files.pythonhosted.org/packages/0c/13/c4369148f1d9ed96a57780a753a0ba9fc5d18436d7a6af14b44aa1eea398/whenever-0.8.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5587402bc36650613f49eadce2df361e3743fd913d891199a13967538114e3b0", size = 575231, upload-time = "2025-09-21T12:54:16.299Z" }, + { url = "https://files.pythonhosted.org/packages/cc/14/cd587b1a21c9b2d64ce3d089f3bb97988e801fd283ca1767e5f787ee5b19/whenever-0.8.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:0a965613f5fad5880224f4cf164406c0703a5765498c862e565ff80e8413b2b1", size = 702007, upload-time = "2025-09-21T12:54:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/3bebdeda37ceed822cb2a3a2717cc784fb93059cc606115442ffcd29300e/whenever-0.8.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13fa7f0e95424418cd2972f5891f7dbe85dff88ae4dc6d0edfaadea0c9c3fe29", size = 626823, upload-time = "2025-09-21T12:55:00.93Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9c/b847ed6514d3619100f815cd8169ce4c2086640b9b86942ecb15fdb7b911/whenever-0.8.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1ab2e73b5f5becea8408d2c4d46c29bd7af29a5c95194f3c66a64b1a0c8d1eaf", size = 583994, upload-time = "2025-09-21T12:55:14.919Z" }, + { url = "https://files.pythonhosted.org/packages/6d/aa/a575ba6f87174aa3145f7878d07709591586b155ad6da68354fe1705b1bd/whenever-0.8.9-cp313-cp313-win32.whl", hash = "sha256:a9ceafcc60f03c18ed94ee9c0e20ad41b3937d0eacea4bf6b68628baa5263dd5", size = 329910, upload-time = "2025-09-21T12:55:36.733Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3b/fa6ecd7590336a488af0827cfb147dd93a0721a3137f1da1e055948fef93/whenever-0.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:1852dae89e1fa79d629dae3e8f9f5ff7ec06ccb342ce8b4285828fdcda373e7c", size = 322300, upload-time = "2025-09-21T12:55:43.576Z" }, + { url = "https://files.pythonhosted.org/packages/76/9c/7c6ad694e1bfcf12ee1a47e8ebe1a499139ed8372df74d17d2c3854182d6/whenever-0.8.9-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:4dbee1cbef47453bf08aa341aca6e36871ba69f58629cec18f41ab74977d5e5b", size = 392576, upload-time = "2025-09-21T12:55:31.265Z" }, + { url = "https://files.pythonhosted.org/packages/49/65/52e59df11183566ac772995f13d53eaea40a01fff853f947f518ee6e6bbc/whenever-0.8.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3940001d5d2d104b288101ccfd3e298fc9d05884d6cfdf1a98fa5bc47184921f", size = 376915, upload-time = "2025-09-21T12:55:23.803Z" }, + { url = "https://files.pythonhosted.org/packages/e2/16/68bc861c7e8615a487e9ffbe5b122fbe979c22a221f438c1c85d4c55b016/whenever-0.8.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9aca3c617acc2f1f4c3dabd4be6b7791b2ae20795458bcd84a4f1019017827", size = 397481, upload-time = "2025-09-21T12:54:09.973Z" }, + { url = "https://files.pythonhosted.org/packages/c7/3f/cf04a1237efe5b6f886d04e158e34fe67a794d653a3b23a7ff039f6da75c/whenever-0.8.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6791a14098c6c678dfd7e315747a7e67a9b221bae9d10773c6416fd4ac911b69", size = 438608, upload-time = "2025-09-21T12:54:25.345Z" }, + { url = "https://files.pythonhosted.org/packages/91/85/5b184b1d8904c170848c0efb20b209e1a3ef05a98799175fecda16bbd83d/whenever-0.8.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4d9dd4228c2d2fe233973ecf7e6579aa7e4dca1bbcfc6e045aa74fc46c612f7", size = 434458, upload-time = "2025-09-21T12:54:40.12Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/31ee3a05b1caa10879ca4705b4bea0a1fa6ee2d6d9a78df4569494ee03cc/whenever-0.8.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea3f79d6461d3492acbfdd711d2add13609a339825bec955d244a6631a597096", size = 452833, upload-time = "2025-09-21T12:54:46.324Z" }, + { url = "https://files.pythonhosted.org/packages/5d/4b/4697de829f990cbd05765e2e0adef0122b5bbf966b70562de11acbe43aa4/whenever-0.8.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1840420da39c2e4f026959a24b68f5f16c15017de2132eec889ef2a0f7cd37f0", size = 414241, upload-time = "2025-09-21T12:55:08.589Z" }, + { url = "https://files.pythonhosted.org/packages/de/bf/13d5c844052db435b4f86e3de020a0a3f19f59df47d321df82996d5ec27f/whenever-0.8.9-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89289c4d4f9ecec201b782596fbeebb18143443a57e361df54af7810cf2e00c4", size = 453420, upload-time = "2025-09-21T12:54:53.859Z" }, + { url = "https://files.pythonhosted.org/packages/7e/33/df31d23edf931f024b4261c98925d8a834e5b7030fc27f78f23e94c7bad0/whenever-0.8.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e80d2a3f32d02e404498d28cd69d00e7cad545bf0a2a4593a626b45f41dbe7df", size = 575752, upload-time = "2025-09-21T12:54:17.578Z" }, + { url = "https://files.pythonhosted.org/packages/16/59/1b5eacb2559ac7615fd12dd8f05eb496142a545254aef10b8f0b8be4e73e/whenever-0.8.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17e45a79b7f7c21281fc42a27740a34a669d52b5dc852823760f9822f07282f1", size = 703295, upload-time = "2025-09-21T12:54:33.072Z" }, + { url = "https://files.pythonhosted.org/packages/93/49/c7ed43b106c5e2f41fd3095bffc34ac99564c693257995e219554ef3b9b5/whenever-0.8.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f0de5a3ad679abcc2ca63b732a8e3cfb29a5816fb44a36429296df9a46172af9", size = 628684, upload-time = "2025-09-21T12:55:02.133Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b5/d3024a020e92514cea1c1e53bcc5dbfa7060d97b7d2f40799f79df1ae081/whenever-0.8.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:284ac0b1d34d7637d8e22bd2e9945b7150f1fb96a28bbeb222d88efd07a95964", size = 585423, upload-time = "2025-09-21T12:55:16.268Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d2/0cfa031347e9aaf1918d7e5b0401dd0d159483d3cbce1398e7551a016246/whenever-0.8.9-cp314-cp314-win32.whl", hash = "sha256:f4e9e3678f5d4ceabbfa5de20d8284e8c08af8c961ddcccdf1a0c7e28ddb1215", size = 331645, upload-time = "2025-09-21T12:55:37.798Z" }, + { url = "https://files.pythonhosted.org/packages/4a/70/79947e7deaa37f8a1017fb52b27e244f41290a4e7f7db6f16c94f0693c96/whenever-0.8.9-cp314-cp314-win_amd64.whl", hash = "sha256:275f0684c04c79a89ba62c3dda5ff00559fa8fd4121e414199a1b0ded6edcf59", size = 324342, upload-time = "2025-09-21T12:55:44.718Z" }, + { url = "https://files.pythonhosted.org/packages/d3/a1/be7ceae95145e9957b25da01d0c598a5f3fe119ae6c2395d46f42cbb394d/whenever-0.8.9-py3-none-any.whl", hash = "sha256:c9dda1e755f5ac2e0df5e0ef915d7785953cf63bc09838f793316fb26fb18002", size = 53491, upload-time = "2025-09-21T12:55:47.673Z" }, +] + [[package]] name = "wrapt" version = "1.17.2"