-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Let Agent be run in a Temporal workflow by moving model requests, tool calls, and MCP to Temporal activities #2225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Docs Preview
|
I'm so excited for this to be natively supported |
Thank you for taking the time to look into native temporal support - this is a topic I’m really interested in seeing progress on. One key feature I’d love to see is support for HITL (Human-in-the-Loop) during tool calls when using temporal and agents. My suggestion on how to achieve that (and not being to specific for HITL) is to pause the workflow before a tool call waiting on a condition for a signal. This will allow, among other things, to wait for human input. Currently, tool calls are handled like this: async def call_tool(
self, name: str, tool_args: dict[str, Any], ctx: RunContext[AgentDepsT], tool: ToolsetTool[AgentDepsT]
) -> Any:
serialized_run_context = self.serialize_run_context(ctx)
return await workflow.execute_activity(
activity=self.call_tool_activity,
arg=FunctionCallToolParams(name=name, tool_args=tool_args, serialized_run_context=serialized_run_context),
**self.temporal_settings.__dict__,
) To add the wait support, we can do something like:
async def call_tool(
self, name: str, tool_args: dict[str, Any], ctx: RunContext[AgentDepsT], tool: ToolsetTool[AgentDepsT]
) -> Any:
# --- ADDED: tool call pause using signal and wait_condition ---
await workflow.wait_condition(lambda: self.allow_tool_call)
# --- END ADDED CODE ---
serialized_run_context = self.serialize_run_context(ctx)
activity_result = await workflow.execute_activity(
activity=self.call_tool_activity,
arg=FunctionCallToolParams(name=name, tool_args=tool_args, serialized_run_context=serialized_run_context),
**self.temporal_settings.__dict__,
)
# --- ADDED: tool call reset state ---
self.allow_tool_call = False
return activity_result
# --- END ADDED CODE --- This will allow users to define their workflow something like: @workflow.signal
def approve_tool_call(self):
... # some code to determine the tool is OK to run
self.allow_tool_call = True This way, the workflow will pause at the tool call and only continue once the signal is approved. of course this would be optional and the default ,that can be overwritten by the user, will be to approve the request Appreciate the ongoing work! |
2abccad
to
a0b934f
Compare
PR Change SummaryIntroduced the Temporal Agent feature by enhancing toolset definitions and adding optional IDs for better identification in error messages.
Modified Files
How can I customize these reviews?Check out the Hyperlint AI Reviewer docs for more information on how to customize the review. If you just want to ignore it on this PR, you can add the Note specifically for link checks, we only check the first 30 links in a file and we cache the results for several hours (for instance, if you just added a page, you might experience this). Our recommendation is to add |
6e8c76a
to
5f6cfa7
Compare
@@ -0,0 +1,229 @@ | |||
# Durable Execution with Temporal | |||
|
|||
Pydantic AI allows you to build durable agents that never lose their progress 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pydantic AI allows you to build durable agents that never lose their progress 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 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. |
Imo this feels like it better-represents the benefits, though a bit wordy..
Temporal relies primarily on a replay mechanism to recover from failures. | ||
As the program makes progress, Temporal saves key inputs and decisions, allowing a re-started program to pick up right where it left off. | ||
|
||
The key to making this work is to separate the applications repeatable (deterministic) and non-repeatable (non-deterministic) parts: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key to making this work is to separate the applications repeatable (deterministic) and non-repeatable (non-deterministic) parts: | |
The key to making this work is to separate the application's repeatable (deterministic) and non-repeatable (non-deterministic) parts: |
1. Deterministic pieces, termed [**workflows**](https://docs.temporal.io/workflow-definition), execute the same way when re-run with the same inputs. | ||
2. Non-deterministic pieces, termed [**activities**](https://docs.temporal.io/activities), can run arbitrary code, performing I/O and any other operations. | ||
|
||
Workflow code can run for extended periods and, if interrupted, resume exactly where it left off. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you explicitly mention anywhere that workflows cannot incorporate I/O (at least, nowhere up to this point?). This might be implicit in saying "deterministic" but I feel like when most people read "deterministic" they think non-random, rather than not-including-I/O. I think it's worth making the point explicitly quite early:
Workflow code can run for extended periods and, if interrupted, resume exactly where it left off. | |
Workflow code can run for extended periods and, if interrupted, resume exactly where it left off. Critically, workflow code _cannot_ include any kind of I/O, over network, terminal, etc. |
or similar
2. Non-deterministic pieces, termed [**activities**](https://docs.temporal.io/activities), can run arbitrary code, performing I/O and any other operations. | ||
|
||
Workflow code can run for extended periods and, if interrupted, resume exactly where it left off. | ||
Activity code faces no restrictions on I/O or external interactions, but if it fails part-way through it restarts from the beginning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it restarts from the beginning.
This feels a bit ambiguous if you don't understand what an activity is. What is "the beginning"? (I don't think we've really explained that activities are triggered via function calls, and that "restarting" essentially amounts to calling that function from the top.)
I think it's maybe worth explaining something like — "if you are familiar with distributed execution frameworks like celery, you can think of calling activity code like executing a celery task, except where you wait for the response and continue executing once it is received". (You can add caveats about how this isn't a perfect analogy or whatever, but I think it would help people unfamiliar with temporal understand what was going on.)
Workflow code can run for extended periods and, if interrupted, resume exactly where it left off. | ||
Activity code faces no restrictions on I/O or external interactions, but if it fails part-way through it restarts from the beginning. | ||
|
||
In the case of Pydantic AI agents, this means that [model requests](models/index.md), [tool calls](tools.md) that may require I/O, and [MCP server communication](mcp/client.md) all need to be offloaded to Temporal activities, while the logic that coordinates them (i.e. the agent run) lives in the workflow. Code that handles a scheduled job or web request can then execute the workflow, which will in turn execute the activities as needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of Pydantic AI agents, this means that [model requests](models/index.md), [tool calls](tools.md) that may require I/O, and [MCP server communication](mcp/client.md) all need to be offloaded to Temporal activities, while the logic that coordinates them (i.e. the agent run) lives in the workflow. Code that handles a scheduled job or web request can then execute the workflow, which will in turn execute the activities as needed. | |
In the case of Pydantic AI agents, integration with Temporal means that [model requests](models/index.md), [tool calls](tools.md) that may require I/O, and [MCP server communication](mcp/client.md) all need to be offloaded to Temporal activities due to their I/O requirements, while the logic that coordinates them (i.e. the agent run) can live in the workflow. Code that should _trigger_ the agent run, such as scheduled jobs or web requests, can execute the workflow, which will in turn execute these activities as needed. |
In the case of Pydantic AI agents, this means that [model requests](models/index.md), [tool calls](tools.md) that may require I/O, and [MCP server communication](mcp/client.md) all need to be offloaded to Temporal activities, while the logic that coordinates them (i.e. the agent run) lives in the workflow. Code that handles a scheduled job or web request can then execute the workflow, which will in turn execute the activities as needed. | ||
|
||
The diagram below shows the overall architecture of an agentic application in Temporal. | ||
The Temporal Server is responsible to tracking program execution and making sure associated state is preserved reliably (i.e., stored to a database, possibly replicated across cloud regions). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Temporal Server is responsible to tracking program execution and making sure associated state is preserved reliably (i.e., stored to a database, possibly replicated across cloud regions). | |
The Temporal Server is responsible for tracking program execution and making sure the associated state is preserved reliably (i.e., stored to an internal database, and possibly replicated across cloud regions). |
[External APIs, services, databases, etc.] | ||
``` | ||
|
||
See the [Temporal documentation](https://docs.temporal.io/evaluate/understanding-temporal#temporal-application-the-building-blocks) for more information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the [Temporal documentation](https://docs.temporal.io/evaluate/understanding-temporal#temporal-application-the-building-blocks) for more information. | |
See the [Temporal documentation](https://docs.temporal.io/evaluate/understanding-temporal#temporal-application-the-building-blocks) for more information. |
|
||
## Durable Agent | ||
|
||
Any agent can be wrapped in a [`TemporalAgent`][pydantic_ai.durable_exec.temporal.TemporalAgent] to get a durable agent that can be used inside a deterministic Temporal workflow, by automatically offloading all work that requires IO (namely model requests, tool calls, and MCP server communication) to non-deterministic activities. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any agent can be wrapped in a [`TemporalAgent`][pydantic_ai.durable_exec.temporal.TemporalAgent] to get a durable agent that can be used inside a deterministic Temporal workflow, by automatically offloading all work that requires IO (namely model requests, tool calls, and MCP server communication) to non-deterministic activities. | |
Any agent can be wrapped in a [`TemporalAgent`][pydantic_ai.durable_exec.temporal.TemporalAgent] to get a durable agent that can be used inside a deterministic Temporal workflow, by automatically offloading all work that requires I/O (namely model requests, tool calls, and MCP server communication) to non-deterministic activities. |
Let's try to be consistent on "IO" vs. "I/O". I don't care which we use, but I think I've seen both already.
|
||
Any agent can be wrapped in a [`TemporalAgent`][pydantic_ai.durable_exec.temporal.TemporalAgent] to get a durable agent that can be used inside a deterministic Temporal workflow, by automatically offloading all work that requires IO (namely model requests, tool calls, and MCP server communication) to non-deterministic activities. | ||
|
||
At the time of wrapping, the agent's [model](models/index.md) and [toolsets](toolsets.md) (including function tools registered on the agent and MCP servers) are frozen, activities are dynamically created for each, and the original model and toolsets are wrapped to call on the worker to execute the corresponding activity instead of directly performing the action inside the workflow. The original agent can still be used as normal outside of the Temporal workflow, but any changes to its model or toolsets after wrapping will not be reflected in the durable agent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the time of wrapping, the agent's [model](models/index.md) and [toolsets](toolsets.md) (including function tools registered on the agent and MCP servers) are frozen, activities are dynamically created for each, and the original model and toolsets are wrapped to call on the worker to execute the corresponding activity instead of directly performing the action inside the workflow. The original agent can still be used as normal outside of the Temporal workflow, but any changes to its model or toolsets after wrapping will not be reflected in the durable agent. | |
At the time of wrapping, the agent's [model](models/index.md) and [toolsets](toolsets.md) (including function tools registered on the agent and MCP servers) are frozen, activities are dynamically created for each, and the original model and toolsets are wrapped to call on the worker to execute the corresponding activities instead of directly performing the actions inside the workflow. The original agent can still be used as normal outside of the Temporal workflow, but any changes to its model or toolsets after wrapping will not be reflected in the durable agent. |
|
||
At the time of wrapping, the agent's [model](models/index.md) and [toolsets](toolsets.md) (including function tools registered on the agent and MCP servers) are frozen, activities are dynamically created for each, and the original model and toolsets are wrapped to call on the worker to execute the corresponding activity instead of directly performing the action inside the workflow. The original agent can still be used as normal outside of the Temporal workflow, but any changes to its model or toolsets after wrapping will not be reflected in the durable agent. | ||
|
||
This is a simple but complete example of wrapping an agent for durable execution, creating a Temporal workflow with durable execution logic, connecting to a Temporal server, and running the workflow from non-durable code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a simple but complete example of wrapping an agent for durable execution, creating a Temporal workflow with durable execution logic, connecting to a Temporal server, and running the workflow from non-durable code. | |
### Simple example | |
Here is a simple but complete example of wrapping an agent for durable execution, creating a Temporal workflow with durable execution logic, connecting to a Temporal server, and running the workflow from non-durable code. |
Feels like it's worth a bit heavior of a markdown transition here.
|
||
This is a simple but complete example of wrapping an agent for durable execution, creating a Temporal workflow with durable execution logic, connecting to a Temporal server, and running the workflow from non-durable code. | ||
|
||
All it requires is a Temporal server to be [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All it requires is a Temporal server to be [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally): | |
All it requires is the Temporal dev server to be [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally) on the default port: |
4. We connect to the Temporal server which keeps track of workflow and activity execution. | ||
5. This assumes the Temporal server is [running locally](https://github.com/temporalio/temporal#download-and-start-temporal-server-locally). | ||
6. The [`PydanticAIPlugin`][pydantic_ai.durable_exec.temporal.PydanticAIPlugin] tells Temporal to use Pydantic for serialization and deserialization, and to treat [`UserError`][pydantic_ai.exceptions.UserError] exceptions as non-retryable. | ||
7. We start the worker process that will listen on the specified task queue and run workflows and activities. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really a worker process? If it's not a distinct process, but just something else running on the same async event loop, I'd probably drop the word process here.
Separately, I would add here that in a production deployment, you would typically run the worker in a different code path than you'd use to execute a workflow. Basically just more clearly making the point that this structure is designed for making the example convenient to run and not what "real world" temporal code would look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you made this point below:
the agent, workflow, and worker are typically defined separately from the code that calls for a workflow to be executed.
but I still feel it's also worth making this point here, where the code is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separately, I would add here that in a production deployment, you would typically run the worker in a different code path than you'd use to execute a workflow. Basically just more clearly making the point that this structure is designed for making the example convenient to run and not what "real world" temporal code would look like.
I mentioned that below, but feel free to rephrase:
In a real world application, the agent, workflow, and worker are typically defined separately from the code that calls for a workflow to be executed.
9. We call on the server to execute the workflow on a worker that's listening on the specified task queue. | ||
10. The agent's `name` is used to uniquely identify its activities. | ||
|
||
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_ | |
_(This example is complete, it can be run "as is" — you just need to add `asyncio.run(main())` to run `main`)_ |
if we are already using the existing phrasing elsewhere its' fine.
In a real world application, the agent, workflow, and worker are typically defined separately from the code that calls for a workflow to be executed. | ||
Because Temporal workflows need to be defined at the top level of the file and the `TemporalAgent` instance is needed inside the workflow and when starting the worker (to register the activities), it needs to be defined at the top level of the file as well. | ||
|
||
For more information on how to use Temporal in Python applications, see their [Python SDK guide](https://docs.temporal.io/develop/python). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For more information on how to use Temporal in Python applications, see their [Python SDK guide](https://docs.temporal.io/develop/python). | |
Note that temporal workflows can be used for more than just handling failures, including .... For more information on how to use Temporal in Python applications, see their [Python SDK guide](https://docs.temporal.io/develop/python). |
(the "..." here might talk about how you can use signals to achieve human-in-the-loop, how you can "sleep" for days without a problem, etc., I just think it's worth pointing out that temporal is more than just error handling, and helps motivate why someone might want to read more. Sorry I didn't flesh it out for you, maybe AI can help lol.)
|
||
When `TemporalAgent` dynamically creates activities for the wrapped agent's model requests and toolsets (specifically those that implement their own tool listing and calling, i.e. [`FunctionToolset`][pydantic_ai.toolsets.FunctionToolset] and [`MCPServer`][pydantic_ai.mcp.MCPServer]), their names are derived from the agent's [`name`][pydantic_ai.agent.AbstractAgent.name] and the toolsets' [`id`s][pydantic_ai.toolsets.AbstractToolset.id]. These fields are normally optional, but are required to be set when using Temporal. They should not be changed once the temporal agent has been deployed to production as this would break active workflows. | ||
|
||
Other than that, any agent and toolset will just work! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than that, any agent and toolset will just work! | |
Other than that, using any agent and toolset with `TemporalAgent` should "just work"! |
|
||
For more information on how to use Temporal in Python applications, see their [Python SDK guide](https://docs.temporal.io/develop/python). | ||
|
||
## Agent and Toolset Requirements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should convert this and the next 3 sections into subsections of a parent section called something like "Requirements on durable agent usage" or similar, and drop the point about "Other than that, any agent ... will just work!", given it's actually three caveats here. But for the most part I think these sections can stay the same.
Outstanding docs feedback will be addressed by @dmontagu in a follow-up PR tonight or tomorrow. |
* Add `priority` `service_tier` to `OpenAIModelSettings` and respect it in `OpenAIResponsesModel` (pydantic#2368) * Add an example of using RunContext to pass data among tools (pydantic#2316) Co-authored-by: Douwe Maan <[email protected]> * Rename gemini-2.5-flash-lite-preview-06-17 to gemini-2.5-flash-lite as it's out of preview (pydantic#2387) * Fix toggleable toolset example so toolset state is not shared across agent runs (pydantic#2396) * Support custom thinking tags specified on the model profile (pydantic#2364) Co-authored-by: jescudero <[email protected]> Co-authored-by: Douwe Maan <[email protected]> * Add convenience functions to handle AG-UI requests with request-specific deps (pydantic#2397) * docs: add missing optional packages in `install.md` (pydantic#2412) * Include default values in tool arguments JSON schema (pydantic#2418) * Fix "test_download_item_no_content_type test fails on macOS" (pydantic#2404) * Allow string format, pattern and others in OpenAI strict JSON mode (pydantic#2420) * Let more `BaseModel`s use OpenAI strict JSON mode by defaulting to `additionalProperties=False` (pydantic#2419) * BREAKING CHANGE: Change type of 'source' field on EvaluationResult (pydantic#2388) Co-authored-by: Douwe Maan <[email protected]> * Fix ImageUrl, VideoUrl, AudioUrl and DocumentUrl not being serializable (pydantic#2422) * BREAKING CHANGE: Support printing reasons in the console output for pydantic-evals (pydantic#2163) * Document performance implications of async vs sync tools (pydantic#2298) Co-authored-by: Douwe Maan <[email protected]> * Mention that tools become toolset internally (pydantic#2395) Co-authored-by: Douwe Maan <[email protected]> * Fix tests for Logfire>=3.22.0 (pydantic#2346) * tests: speed up the test suite (pydantic#2414) * google: add more information about schema on union (pydantic#2426) * typo in output docs (pydantic#2427) * Deprecate `GeminiModel` in favor of `GoogleModel` (pydantic#2416) * Use `httpx` on `GoogleProvider` (pydantic#2438) * Remove older deprecated models and add new model of Anthropic (pydantic#2435) * Remove `next()` method from `Graph` (pydantic#2440) * BREAKING CHANGE: Remove `data` from `FinalResult` (pydantic#2443) * BREAKING CHANGE: Remove `get_data` and `validate_structured_result` from `StreamedRunResult` (pydantic#2445) * docs: add `griffe_warnings_deprecated` (pydantic#2444) * BREAKING CHANGE: Remove `format_as_xml` module (pydantic#2446) * BREAKING CHANGE: Remove `result_type` parameter and similar from `Agent` (pydantic#2441) * Deprecate `GoogleGLAProvider` and `GoogleVertexProvider` (pydantic#2450) * BREAKING CHANGE: drop 4 months old deprecation warnings (pydantic#2451) * Automatically use OpenAI strict mode for strict-compatible native output types (pydantic#2447) * Make `InlineDefsJsonSchemaTransformer` public (pydantic#2455) * Send `ThinkingPart`s back to Anthropic used through Bedrock (pydantic#2454) * Bump boto3 to support `AWS_BEARER_TOKEN_BEDROCK` API key env var (pydantic#2456) * Add new Heroku models (pydantic#2459) * Add `builtin_tools` to `Agent` (pydantic#2102) Co-authored-by: Marcelo Trylesinski <[email protected]> Co-authored-by: Douwe Maan <[email protected]> * Bump mcp-run-python (pydantic#2470) * Remove fail_under from top-level coverage config so <100% html-coverage step doesn't end CI run (pydantic#2475) * Add AbstractAgent, WrapperAgent, Agent.event_stream_handler, Toolset.id, Agent.override(tools=...) in preparation for Temporal (pydantic#2458) * Let toolsets be built dynamically based on run context (pydantic#2366) Co-authored-by: Douwe Maan <[email protected]> * Add ToolsetFunc to API docs (fix CI) (pydantic#2486) * tests: change time of evals example (pydantic#2501) * ci: remove html and xml reports (pydantic#2491) * fix: Add gpt-5 models to reasoning model detection for temperature parameter handling (pydantic#2483) Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Douwe Maan <[email protected]> Co-authored-by: Marcelo Trylesinski <[email protected]> * History processor replaces message history (pydantic#2324) Co-authored-by: Marcelo Trylesinski <[email protected]> * ci: split test suite (pydantic#2436) Co-authored-by: Douwe Maan <[email protected]> * ci: use the right install command (pydantic#2506) * Update config.yaml (pydantic#2514) * Skip testing flaky evals example (pydantic#2518) * Fix error when parsing usage details for video without audio track in Google models (pydantic#2507) * Make OpenAIResponsesModelSettings.openai_builtin_tools work again (pydantic#2520) * Let Agent be run in a Temporal workflow by moving model requests, tool calls, and MCP to Temporal activities (pydantic#2225) * Install only dev in CI (pydantic#2523) * Improve CLAUDE.md (pydantic#2524) * Add best practices regarding to coverage to CLAUDE.md (pydantic#2527) * Add support for `"openai-responses"` model inference string (pydantic#2528) Co-authored-by: Claude <[email protected]> * docs: Confident AI (pydantic#2529) * chore: mention what to do with the documentation when deprecating a class (pydantic#2530) * chore: drop hyperlint (pydantic#2531) * ci: improve matrix readability (pydantic#2532) * Add pip to dev deps for PyCharm (pydantic#2533) Co-authored-by: Marcelo Trylesinski <[email protected]> * Add genai-prices to dev deps and a basic test (pydantic#2537) * Add `--durations=100` to all pytest calls in CI (pydantic#2534) * Cleanup snapshot in test_evaluate_async_logfire (pydantic#2538) * Make some minor tweaks to the temporal docs (pydantic#2522) Co-authored-by: Douwe Maan <[email protected]> * Add new OpenAI GPT-5 models (pydantic#2503) * Fix `FallbackModel` to respect each model's model settings (pydantic#2540) * Add support for OpenAI verbosity parameter in Responses API (pydantic#2493) Co-authored-by: Claude <[email protected]> Co-authored-by: Douwe Maan <[email protected]> * Add `UsageLimits.count_tokens_before_request` using Gemini `count_tokens` API (pydantic#2137) Co-authored-by: Douwe Maan <[email protected]> * chore: Fix uv.lock (pydantic#2546) * Stop calling MCP server `get_tools` ahead of `agent run` span (pydantic#2545) * Disable instrumentation by default in tests (pydantic#2535) Co-authored-by: Marcelo Trylesinski <[email protected]> * Only wrap necessary parts of type aliases in forward annotations (pydantic#2548) * Remove anthropic-beta default header set in `AnthropicModel` (pydantic#2544) Co-authored-by: Marcelo Trylesinski <[email protected]> * docs: Clarify why AG-UI example links are on localhost (pydantic#2549) * chore: Fix path to agent class in CLAUDE.md (pydantic#2550) * Ignore leading whitespace when streaming from Qwen or DeepSeek (pydantic#2554) * Ask model to try again if it produced a response without text or tool calls, only thinking (pydantic#2556) Co-authored-by: Douwe Maan <[email protected]> * chore: Improve Temporal test to check trace as tree instead of list (pydantic#2559) * Fix: Forward max_uses parameter to Anthropic WebSearchTool (pydantic#2561) * Let message history end on ModelResponse and execute pending tool calls (pydantic#2562) * Fix type issues * skip tests requiring API keys * add `google-genai` dependency * add other provider deps * add pragma: no cover for untested logic --------- Co-authored-by: akenar <[email protected]> Co-authored-by: Tony Woland <[email protected]> Co-authored-by: Douwe Maan <[email protected]> Co-authored-by: Yi-Chen Lin <[email protected]> Co-authored-by: José I. Escudero <[email protected]> Co-authored-by: jescudero <[email protected]> Co-authored-by: Marcelo Trylesinski <[email protected]> Co-authored-by: William Easton <[email protected]> Co-authored-by: David Montague <[email protected]> Co-authored-by: Guillermo <[email protected]> Co-authored-by: Hamza Farhan <[email protected]> Co-authored-by: Mohamed Amine Zghal <[email protected]> Co-authored-by: Yinon Ehrlich <[email protected]> Co-authored-by: Matthew Brandman <[email protected]> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Douwe Maan <[email protected]> Co-authored-by: Alex Enrique <[email protected]> Co-authored-by: Jerry Yan <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: Mayank <[email protected]> Co-authored-by: Alex Hall <[email protected]> Co-authored-by: Jerry Lin <[email protected]> Co-authored-by: Raymond Xu <[email protected]> Co-authored-by: kauabh <[email protected]> Co-authored-by: Victorien <[email protected]> Co-authored-by: Ethan Brooks <[email protected]> Co-authored-by: eballesteros <[email protected]>
To do: