- Notifications
You must be signed in to change notification settings - Fork 1.5k
Allow TemporalAgent to switch model at agent.run-time #3537
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
Merged
Merged
Changes from 54 commits
Commits
Show all changes
65 commits Select commit Hold shift + click to select a range
260516d messing with overriding model name at runtime
mattbrandman ef4dcf3 got multi model working
mattbrandman 3441db0 removing docstring that was getting tested
mattbrandman 1cb1c82 fixing types
mattbrandman 6ed7461 more type fixes
mattbrandman a70c360 fixing types
mattbrandman d8d0039 adding coverage
mattbrandman 7904768 allow arbitrary models
mattbrandman c04a14d fixing tests
mattbrandman ce7c5fd fixing lint
mattbrandman 9998a5f remove pandas comment
mattbrandman 0857bbc adding coverage
mattbrandman 02308df lint fixes
mattbrandman 5477868 coverage
mattbrandman eccf3d1 adding is none coverage
mattbrandman a87d09e simplifying registration
mattbrandman 6913faa fixes types
mattbrandman 250606b fixing coverage adding provider factory
mattbrandman 7f354bb adding tests
mattbrandman 0ca186c inlining normalize and setting up set current run context
mattbrandman 6d2474c required kwarg and rename of selection to model_id
mattbrandman ce4b17c rename selection everywhere
mattbrandman 70d2451 reorder arguments
mattbrandman 733f0e3 do not put initial agent through provider factory
mattbrandman a34c7c3 adding docs
mattbrandman 4d1c5a3 fixing tests
mattbrandman 7a8d91b fixing coverage
mattbrandman 9902338 fix
mattbrandman d2930a5 update comments
mattbrandman d2eb6b0 test
mattbrandman eaf4cc3 updates
mattbrandman 748294d updates
mattbrandman 9ca1cdd fixing formatting
mattbrandman 995f0b8 removing unecessary change
mattbrandman 06fe0e1 parametrize tests
mattbrandman 3f71342 fix example
mattbrandman 71d8da3 no cover
mattbrandman 8be2857 fixes comments
mattbrandman 60e2e70 fix more
mattbrandman 44b2aaa fixes
mattbrandman 3fede5a fixing feedback
mattbrandman c921487 fixing tests
mattbrandman b489251 simplify
mattbrandman acee05c remove confusing sentence
mattbrandman 09298da move things into temporal model
mattbrandman f881f8a fixes
mattbrandman df87b41 resolve conflict
mattbrandman 0349a82 updates
mattbrandman f3a8091 first fixes
mattbrandman 4534788 more fixes
mattbrandman 88e90f5 dedupe
mattbrandman f08b057 fixes
mattbrandman 58adb03 error messages
mattbrandman 732c277 fixing tests
mattbrandman 4072fd0 updating feedback
mattbrandman 1dda0de fix tests
mattbrandman 3710b1c fixes
mattbrandman 246a1cc changing everything to id
mattbrandman 85c2a83 merge
mattbrandman 45dc40f merge
mattbrandman 3a37765 merge
mattbrandman 4278e81 new tests
mattbrandman 0ac18fc merge
mattbrandman 61cf85f updates
mattbrandman b4dd880 final test
mattbrandman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -184,6 +184,86 @@ As the streaming model request activity, workflow, and workflow execution call a | |
| - To get data from the workflow call site or workflow to the event stream handler, you can use a [dependencies object](#agent-run-context-and-dependencies). | ||
| - To get data from the event stream handler to the workflow, workflow call site, or a frontend, you need to use an external system that the event stream handler can write to and the event consumer can read from, like a message queue. You can use the dependency object to make sure the same connection string or other unique ID is available in all the places that need it. | ||
| | ||
| ### Model Selection at Runtime | ||
| | ||
| [`Agent.run(model=...)`][pydantic_ai.agent.Agent.run] normally supports both model strings (like `'openai:gpt-5.2'`) and model instances. However, `TemporalAgent` does not support arbitrary model instances because they cannot be serialized for Temporal's replay mechanism. | ||
| | ||
| To use model instances with `TemporalAgent`, you need to pre-register them by passing a dict of model instances to `TemporalAgent(models={...})`. You can then reference them by name or by passing the registered instance directly. If the wrapped agent doesn't have a model set, the first registered model will be used as the default. | ||
| | ||
| Model strings work as expected. For scenarios where you need to customize the provider used by the model string (e.g., inject API keys from deps), you can pass a `provider_factory` to `TemporalAgent`. | ||
| | ||
| Here's an example showing how to pre-register and use multiple models: | ||
| | ||
| ```python {title="multi_model_temporal.py" test="skip"} | ||
| from dataclasses import dataclass | ||
| from typing import Any | ||
| | ||
| from temporalio import workflow | ||
| | ||
| from pydantic_ai import Agent | ||
| from pydantic_ai.durable_exec.temporal import TemporalAgent | ||
| from pydantic_ai.models.anthropic import AnthropicModel | ||
| from pydantic_ai.models.google import GoogleModel | ||
| from pydantic_ai.models.openai import OpenAIResponsesModel | ||
| from pydantic_ai.providers import Provider | ||
| from pydantic_ai.tools import RunContext | ||
| | ||
| | ||
| @dataclass | ||
| class Deps: | ||
| openai_api_key: str | None = None | ||
| anthropic_api_key: str | None = None | ||
| | ||
| | ||
| # Create models from different providers | ||
| default_model = OpenAIResponsesModel('gpt-5.2') | ||
| fast_model = AnthropicModel('claude-sonnet-4-5') | ||
| reasoning_model = GoogleModel('gemini-2.5-pro') | ||
| | ||
| | ||
| # Optional: provider factory for dynamic model configuration | ||
| Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather have a simple example first (showing just the values that are supported for | ||
| def my_provider_factory(run_context: RunContext[Deps], provider_name: str) -> Provider[Any]: | ||
| """Create providers with custom configuration based on run context.""" | ||
| if provider_name == 'openai': | ||
| from pydantic_ai.providers.openai import OpenAIProvider | ||
| | ||
| return OpenAIProvider(api_key=run_context.deps.openai_api_key) | ||
| elif provider_name == 'anthropic': | ||
| from pydantic_ai.providers.anthropic import AnthropicProvider | ||
| | ||
| return AnthropicProvider(api_key=run_context.deps.anthropic_api_key) | ||
| else: | ||
| raise ValueError(f'Unknown provider: {provider_name}') | ||
| | ||
| | ||
| agent = Agent(default_model, name='multi_model_agent', deps_type=Deps) | ||
| | ||
| temporal_agent = TemporalAgent( | ||
| agent, | ||
| models={ | ||
| 'fast': fast_model, | ||
| 'reasoning': reasoning_model, | ||
| }, | ||
| provider_factory=my_provider_factory, # Optional | ||
mattbrandman marked this conversation as resolved. Show resolved Hide resolved | ||
| ) | ||
| | ||
| | ||
| @workflow.defn | ||
| class MultiModelWorkflow: | ||
| @workflow.run | ||
| async def run(self, prompt: str, use_reasoning: bool, use_fast: bool) -> str: | ||
| if use_reasoning: | ||
| # Select by registered name | ||
| result = await temporal_agent.run(prompt, model='reasoning') | ||
| elif use_fast: | ||
| # Or pass the registered instance directly | ||
| result = await temporal_agent.run(prompt, model=fast_model) | ||
| else: | ||
| # Or pass a model string (uses provider_factory if set) | ||
| result = await temporal_agent.run(prompt, model='openai:gpt-4.1-mini') | ||
| return result.output | ||
| ``` | ||
| | ||
| ## Activity Configuration | ||
| | ||
| Temporal activity configuration, like timeouts and retry policies, can be customized by passing [`temporalio.workflow.ActivityConfig`](https://python.temporal.io/temporalio.workflow.ActivityConfig.html) objects to the `TemporalAgent` constructor: | ||
| | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
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.
... which is passed the run context (with a link). I liked the explicit mention you had of getting API keys from deps