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..c153d2d8d0 --- /dev/null +++ b/docs/durable_execution/prefect.md @@ -0,0 +1,286 @@ +# 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. + +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 +* Inspect task execution history and outputs +* 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 + +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..3d81c4fdbc --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/__init__.py @@ -0,0 +1,16 @@ +from ._agent import PrefectAgent +from ._cache_policies import DEFAULT_PYDANTIC_AI_CACHE_POLICY, InputsWithoutTimestamps +from ._function_toolset import PrefectFunctionToolset +from ._mcp_server import PrefectMCPServer +from ._model import PrefectModel +from ._types import TaskConfig + +__all__ = [ + 'PrefectAgent', + 'PrefectModel', + 'PrefectMCPServer', + 'PrefectFunctionToolset', + 'TaskConfig', + 'InputsWithoutTimestamps', + '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..afd7aa1b46 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_agent.py @@ -0,0 +1,609 @@ +from __future__ import annotations + +from collections.abc import AsyncIterable, AsyncIterator, Iterator, Sequence +from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager +from contextvars import ContextVar +from typing import Any, overload + +from prefect import flow, get_run_logger, 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, + _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.exceptions import UserError +from pydantic_ai.models import Model +from pydantic_ai.output import OutputDataT, OutputSpec +from pydantic_ai.settings import ModelSettings +from pydantic_ai.tools import ( + AgentDepsT, + DeferredToolResults, + RunContext, + Tool, + ToolFuncEither, +) + +from ._model import PrefectModel +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, + ): + """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). + """ + 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 + + prefect_toolsets = [toolset.visit_and_replace(self._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 + ) + + def _prefectify_toolset(self, toolset: AbstractToolset[AgentDepsT]) -> AbstractToolset[AgentDepsT]: + """Convert a toolset to its Prefect equivalent.""" + # Replace MCPServer with PrefectMCPServer + 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=self._mcp_task_config, + ) + + # Replace FunctionToolset with PrefectFunctionToolset + from pydantic_ai import FunctionToolset + + from ._function_toolset import PrefectFunctionToolset + + if isinstance(toolset, FunctionToolset): + return PrefectFunctionToolset( + wrapped=toolset, + task_config=self._tool_task_config, + tool_task_config=self._tool_task_config_by_name, + ) + + return toolset + + @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=[]), + self.sequential_tool_calls(), + ): + 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, + 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, + 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, + 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. + + Returns: + The result of the run. + """ + + @flow(name=f'{self._name} Run') + async def wrapped_run_flow() -> AgentRunResult[Any]: + logger = get_run_logger() + prompt_str = str(user_prompt) + logger.info(f'Starting agent run with prompt: {prompt_str}') + + # 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, + ) + logger.info( + f'Agent run completed. Requests: {result.usage().requests}, Tool calls: {result.usage().tool_calls}' + ) + 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, + 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, + 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, + 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. + + Returns: + The result of the run. + """ + + @flow(name=f'{self._name} Sync Run') + def wrapped_run_sync_flow() -> AgentRunResult[Any]: + logger = get_run_logger() + prompt_str = str(user_prompt) + prompt_preview = prompt_str[:100] + '...' if len(prompt_str) > 100 else prompt_str + logger.info(f'Starting sync agent run with prompt: {prompt_preview}') + + # 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, + ) + ) + logger.info( + f'Sync agent run completed. Requests: {result.usage().requests}, Tool calls: {result.usage().tool_calls}' + ) + return result + finally: + self._in_prefect_agent_flow.reset(token) + + return wrapped_run_sync_flow() + + @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, + ) -> 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, + ) -> 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, + ) -> 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. + + 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..84de56a9b2 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_cache_policies.py @@ -0,0 +1,65 @@ +from dataclasses import fields, is_dataclass +from typing import Any, TypeGuard + +from prefect.cache_policies import RUN_ID, TASK_SOURCE, CachePolicy, Inputs +from prefect.context import TaskRunContext + + +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 _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 + + +class InputsWithoutTimestamps(CachePolicy): + """Cache policy that computes a cache key based on inputs, ignoring nested 'timestamp' fields. + + This is similar to the INPUTS cache policy, but recursively removes all 'timestamp' + fields from the messages inputs before computing the hash. This is useful when you want to + cache based on the content of inputs but not their timestamps. + """ + + 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.""" + if not inputs: + return None + + filtered_inputs = _strip_timestamps(inputs) + + # Exclude run_ctx from inputs as it contains non-hashable objects + return Inputs(exclude=['run_ctx']).compute_key(task_ctx, filtered_inputs, flow_parameters, **kwargs) + + +DEFAULT_PYDANTIC_AI_CACHE_POLICY = InputsWithoutTimestamps() + 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..f186c6d6ac --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_function_toolset.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from prefect import get_run_logger, task + +from pydantic_ai import FunctionToolset, ToolsetTool, WrapperToolset +from pydantic_ai.exceptions import ApprovalRequired, CallDeferred, ModelRetry, UserError +from pydantic_ai.tools import AgentDepsT, RunContext + +from ._types import TaskConfig, default_task_config + +if TYPE_CHECKING: + from collections.abc import Callable + + from pydantic_ai import AbstractToolset + + +class PrefectFunctionToolset(WrapperToolset[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 {} + + 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 + + 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 + + @task( + name=f'Call Tool: {name}', + **merged_config, + ) + async def call_tool_task( + tool_name: str, + args: dict[str, Any], + run_ctx: RunContext[AgentDepsT], + ) -> Any: + logger = get_run_logger() + logger.info(f'Calling tool: {tool_name}') + + # Get the tool (need to fetch it again inside the task) + try: + tools = await self.wrapped.get_tools(run_ctx) + tool_instance = tools[tool_name] + except KeyError as e: # pragma: no cover + raise UserError( + f'Tool {tool_name!r} not found in toolset {self.id!r}. ' + 'Removing or renaming tools during an agent run is not supported with Prefect.' + ) from e + + # Call the tool + result = await super(PrefectFunctionToolset, self).call_tool(tool_name, args, run_ctx, tool_instance) + logger.info(f'Tool call completed: {tool_name}') + return result + + try: + return await call_tool_task(name, tool_args, ctx) + except (ApprovalRequired, CallDeferred, ModelRetry): + # Re-raise these exceptions as they're part of the agent control flow + raise 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..0ef40c51ea --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_mcp_server.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from abc import ABC +from collections.abc import Callable +from typing import TYPE_CHECKING, Any + +from prefect import get_run_logger, task +from typing_extensions import Self + +from pydantic_ai import AbstractToolset, ToolsetTool, WrapperToolset +from pydantic_ai.tools import AgentDepsT, RunContext + +from ._types import TaskConfig, default_task_config + +if TYPE_CHECKING: + from pydantic_ai.mcp import MCPServer, ToolResult + + +class PrefectMCPServer(WrapperToolset[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 + + @property + def id(self) -> str | None: + return self.wrapped.id + + 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) + + 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 + + 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.""" + + @task( + name=f'Call MCP Tool: {name}', + **self._task_config, + ) + async def call_tool_task( + tool_name: str, + args: dict[str, Any], + run_ctx: RunContext[AgentDepsT], + ) -> ToolResult: + logger = get_run_logger() + logger.info(f'Calling MCP tool: {tool_name}') + + # Note: We don't include 'tool' parameter as it contains non-serializable objects + result = await super(PrefectMCPServer, self).call_tool(tool_name, args, run_ctx, tool) + logger.info(f'MCP tool call completed: {tool_name}') + return result + + return await call_tool_task(name, tool_args, ctx) 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..fafc8993dd --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/durable_exec/prefect/_model.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager +from datetime import datetime +from typing import Any + +from prefect import get_run_logger, 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 + + 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.""" + # Get model name for task description + model_name = getattr(self.wrapped, 'model_name', 'unknown') + + @task(name=f'Model Request: {model_name}', **self.task_config) + async def wrapped_request( + messages: list[ModelMessage], + model_settings: ModelSettings | None, + model_request_parameters: ModelRequestParameters, + ) -> ModelResponse: + logger = get_run_logger() + logger.info(f'Making model request to {model_name} with {len(messages)} messages') + response = await super(PrefectModel, self).request(messages, model_settings, model_request_parameters) + logger.info(f'Model request completed. Tokens: {response.usage.total_tokens}') + return response + + return await wrapped_request(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 + # Get model name for task description + model_name = getattr(self.wrapped, 'model_name', 'unknown') + + @task( + name=f'Model Request (Streaming): {model_name}', + **self.task_config, + ) + async def request_stream_task(ctx: RunContext[Any] | None) -> ModelResponse: + logger = get_run_logger() + logger.info(f'Making streaming model request to {model_name} with {len(messages)} messages') + + 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() + logger.info(f'Streaming model request completed. Tokens: {response.usage.total_tokens}') + return response + + response = await request_stream_task(run_context) + yield PrefectStreamedResponse(model_request_parameters, response) 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..a3a19f6e2d --- /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.filesystems import WritableFileSystem +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: WritableFileSystem | str | None + """Prefect result storage for the task. Should be a WritableFileSystem 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 de6e164d4b..96fe425a4d 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_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..4f35ea15e6 --- /dev/null +++ b/tests/test_prefect.py @@ -0,0 +1,871 @@ +from __future__ import annotations + +import os +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, + 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, + InputsWithoutTimestamps, + PrefectAgent, + PrefectFunctionToolset, + PrefectMCPServer, + PrefectModel, + ) +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 + +pytestmark = [ + pytest.mark.anyio, + pytest.mark.vcr, + pytest.mark.xdist_group(name='prefect'), + pytest.mark.filterwarnings('ignore:Found propagated trace context.*:RuntimeWarning'), +] + +# 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, scope='module') +def setup_logfire_instrumentation() -> Iterator[None]: + import warnings + + # Set up logfire for the tests. + logfire.configure(metrics=False) + + # Filter out the propagated trace context warning from logfire + # This warning is expected when using Prefect with logfire + warnings.filterwarnings( + 'ignore', message='Found propagated trace context.*', category=RuntimeWarning, module='logfire.*' + ) + + 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 + + +# Wrap event_stream_handler as a Prefect task because it's non-deterministic (uses logfire) +@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 + + 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 + exporter = capfire.exporter + spans = exporter.exported_spans_as_dict() + assert len(spans) > 0, 'No spans were exported' + + +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_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() + + outputs = await run_stream_workflow() + assert outputs == snapshot('The capital of Mexico is Mexico City.') + + +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 InputsWithoutTimestamps works. + Timestamps must be excluded from computed cache keys to avoid + duplicate calls when runs are restarted. + """ + cache_policy = InputsWithoutTimestamps() + + # 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 = InputsWithoutTimestamps() + 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 = InputsWithoutTimestamps() + + 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 548ccfddce..3b6cc6af50 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"