pydantic-ai-slim 0.7.5__tar.gz → 0.7.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/PKG-INFO +3 -3
  2. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/agent/__init__.py +11 -4
  3. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/__init__.py +20 -9
  4. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/anthropic.py +2 -2
  5. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/bedrock.py +1 -1
  6. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/openai.py +62 -36
  7. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/__init__.py +1 -1
  8. pydantic_ai_slim-0.7.6/pydantic_ai/profiles/harmony.py +13 -0
  9. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/openai.py +6 -1
  10. pydantic_ai_slim-0.7.6/pydantic_ai/profiles/qwen.py +19 -0
  11. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/__init__.py +5 -1
  12. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/azure.py +1 -1
  13. pydantic_ai_slim-0.7.6/pydantic_ai/providers/cerebras.py +96 -0
  14. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/cohere.py +2 -2
  15. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/deepseek.py +4 -4
  16. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/fireworks.py +3 -3
  17. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/github.py +4 -4
  18. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/grok.py +3 -3
  19. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/groq.py +3 -3
  20. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/heroku.py +3 -3
  21. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/mistral.py +3 -3
  22. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/moonshotai.py +3 -6
  23. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/ollama.py +1 -1
  24. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/openrouter.py +4 -4
  25. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/together.py +3 -3
  26. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/vercel.py +4 -4
  27. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/retries.py +154 -42
  28. pydantic_ai_slim-0.7.5/pydantic_ai/profiles/qwen.py +0 -11
  29. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/.gitignore +0 -0
  30. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/LICENSE +0 -0
  31. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/README.md +0 -0
  32. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/__init__.py +0 -0
  33. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/__main__.py +0 -0
  34. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_a2a.py +0 -0
  35. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_agent_graph.py +0 -0
  36. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_cli.py +0 -0
  37. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_function_schema.py +0 -0
  38. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_griffe.py +0 -0
  39. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_mcp.py +0 -0
  40. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_otel_messages.py +0 -0
  41. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_output.py +0 -0
  42. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_parts_manager.py +0 -0
  43. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_run_context.py +0 -0
  44. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_system_prompt.py +0 -0
  45. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_thinking_part.py +0 -0
  46. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_tool_manager.py +0 -0
  47. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/_utils.py +0 -0
  48. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/ag_ui.py +0 -0
  49. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/agent/abstract.py +0 -0
  50. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/agent/wrapper.py +0 -0
  51. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/builtin_tools.py +0 -0
  52. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/common_tools/__init__.py +0 -0
  53. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/common_tools/duckduckgo.py +0 -0
  54. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/common_tools/tavily.py +0 -0
  55. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/direct.py +0 -0
  56. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/__init__.py +0 -0
  57. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/__init__.py +0 -0
  58. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_agent.py +0 -0
  59. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_function_toolset.py +0 -0
  60. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_logfire.py +0 -0
  61. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_mcp_server.py +0 -0
  62. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_model.py +0 -0
  63. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_run_context.py +0 -0
  64. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/durable_exec/temporal/_toolset.py +0 -0
  65. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/exceptions.py +0 -0
  66. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/ext/__init__.py +0 -0
  67. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/ext/aci.py +0 -0
  68. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/ext/langchain.py +0 -0
  69. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/format_prompt.py +0 -0
  70. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/mcp.py +0 -0
  71. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/messages.py +0 -0
  72. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/cohere.py +0 -0
  73. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/fallback.py +0 -0
  74. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/function.py +0 -0
  75. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/gemini.py +0 -0
  76. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/google.py +0 -0
  77. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/groq.py +0 -0
  78. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/huggingface.py +0 -0
  79. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/instrumented.py +0 -0
  80. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/mcp_sampling.py +0 -0
  81. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/mistral.py +0 -0
  82. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/test.py +0 -0
  83. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/models/wrapper.py +0 -0
  84. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/output.py +0 -0
  85. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/_json_schema.py +0 -0
  86. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/amazon.py +0 -0
  87. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/anthropic.py +0 -0
  88. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/cohere.py +0 -0
  89. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/deepseek.py +0 -0
  90. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/google.py +0 -0
  91. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/grok.py +0 -0
  92. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/groq.py +0 -0
  93. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/meta.py +0 -0
  94. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/mistral.py +0 -0
  95. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/profiles/moonshotai.py +0 -0
  96. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/anthropic.py +0 -0
  97. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/bedrock.py +0 -0
  98. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/google.py +0 -0
  99. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/google_gla.py +0 -0
  100. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/google_vertex.py +0 -0
  101. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/huggingface.py +0 -0
  102. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/providers/openai.py +0 -0
  103. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/py.typed +0 -0
  104. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/result.py +0 -0
  105. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/run.py +0 -0
  106. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/settings.py +0 -0
  107. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/tools.py +0 -0
  108. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/__init__.py +0 -0
  109. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/_dynamic.py +0 -0
  110. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/abstract.py +0 -0
  111. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/combined.py +0 -0
  112. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/deferred.py +0 -0
  113. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/filtered.py +0 -0
  114. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/function.py +0 -0
  115. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/prefixed.py +0 -0
  116. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/prepared.py +0 -0
  117. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/renamed.py +0 -0
  118. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/toolsets/wrapper.py +0 -0
  119. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pydantic_ai/usage.py +0 -0
  120. {pydantic_ai_slim-0.7.5 → pydantic_ai_slim-0.7.6}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai-slim
3
- Version: 0.7.5
3
+ Version: 0.7.6
4
4
  Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
5
5
  Project-URL: Homepage, https://github.com/pydantic/pydantic-ai/tree/main/pydantic_ai_slim
6
6
  Project-URL: Source, https://github.com/pydantic/pydantic-ai/tree/main/pydantic_ai_slim
@@ -35,7 +35,7 @@ Requires-Dist: genai-prices>=0.0.22
35
35
  Requires-Dist: griffe>=1.3.2
36
36
  Requires-Dist: httpx>=0.27
37
37
  Requires-Dist: opentelemetry-api>=1.28.0
38
- Requires-Dist: pydantic-graph==0.7.5
38
+ Requires-Dist: pydantic-graph==0.7.6
39
39
  Requires-Dist: pydantic>=2.10
40
40
  Requires-Dist: typing-inspection>=0.4.0
41
41
  Provides-Extra: a2a
@@ -57,7 +57,7 @@ Requires-Dist: cohere>=5.16.0; (platform_system != 'Emscripten') and extra == 'c
57
57
  Provides-Extra: duckduckgo
58
58
  Requires-Dist: ddgs>=9.0.0; extra == 'duckduckgo'
59
59
  Provides-Extra: evals
60
- Requires-Dist: pydantic-evals==0.7.5; extra == 'evals'
60
+ Requires-Dist: pydantic-evals==0.7.6; extra == 'evals'
61
61
  Provides-Extra: google
62
62
  Requires-Dist: google-genai>=1.31.0; extra == 'google'
63
63
  Provides-Extra: groq
@@ -677,16 +677,23 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
677
677
  def _run_span_end_attributes(
678
678
  self, state: _agent_graph.GraphAgentState, usage: _usage.RunUsage, settings: InstrumentationSettings
679
679
  ):
680
+ if settings.version == 1:
681
+ attr_name = 'all_messages_events'
682
+ value = [
683
+ InstrumentedModel.event_to_dict(e) for e in settings.messages_to_otel_events(state.message_history)
684
+ ]
685
+ else:
686
+ attr_name = 'pydantic_ai.all_messages'
687
+ value = settings.messages_to_otel_messages(state.message_history)
688
+
680
689
  return {
681
690
  **usage.opentelemetry_attributes(),
682
- 'all_messages_events': json.dumps(
683
- [InstrumentedModel.event_to_dict(e) for e in settings.messages_to_otel_events(state.message_history)]
684
- ),
691
+ attr_name: json.dumps(value),
685
692
  'logfire.json_schema': json.dumps(
686
693
  {
687
694
  'type': 'object',
688
695
  'properties': {
689
- 'all_messages_events': {'type': 'array'},
696
+ attr_name: {'type': 'array'},
690
697
  'final_result': {'type': 'object'},
691
698
  },
692
699
  }
@@ -111,6 +111,15 @@ KnownModelName = TypeAliasType(
111
111
  'bedrock:mistral.mixtral-8x7b-instruct-v0:1',
112
112
  'bedrock:mistral.mistral-large-2402-v1:0',
113
113
  'bedrock:mistral.mistral-large-2407-v1:0',
114
+ 'cerebras:gpt-oss-120b',
115
+ 'cerebras:llama3.1-8b',
116
+ 'cerebras:llama-3.3-70b',
117
+ 'cerebras:llama-4-scout-17b-16e-instruct',
118
+ 'cerebras:llama-4-maverick-17b-128e-instruct',
119
+ 'cerebras:qwen-3-235b-a22b-instruct-2507',
120
+ 'cerebras:qwen-3-32b',
121
+ 'cerebras:qwen-3-coder-480b',
122
+ 'cerebras:qwen-3-235b-a22b-thinking-2507',
114
123
  'claude-3-5-haiku-20241022',
115
124
  'claude-3-5-haiku-latest',
116
125
  'claude-3-5-sonnet-20240620',
@@ -695,21 +704,23 @@ def infer_model(model: Model | KnownModelName | str) -> Model: # noqa: C901
695
704
 
696
705
  return CohereModel(model_name, provider=provider)
697
706
  elif provider in (
698
- 'openai',
699
- 'deepseek',
700
707
  'azure',
701
- 'openrouter',
702
- 'vercel',
708
+ 'deepseek',
709
+ 'cerebras',
710
+ 'fireworks',
711
+ 'github',
703
712
  'grok',
713
+ 'heroku',
704
714
  'moonshotai',
705
- 'fireworks',
715
+ 'openai',
716
+ 'openai-chat',
717
+ 'openrouter',
706
718
  'together',
707
- 'heroku',
708
- 'github',
719
+ 'vercel',
709
720
  ):
710
- from .openai import OpenAIModel
721
+ from .openai import OpenAIChatModel
711
722
 
712
- return OpenAIModel(model_name, provider=provider)
723
+ return OpenAIChatModel(model_name, provider=provider)
713
724
  elif provider == 'openai-responses':
714
725
  from .openai import OpenAIResponsesModel
715
726
 
@@ -475,9 +475,9 @@ class AnthropicModel(Model):
475
475
  anthropic_messages.append(BetaMessageParam(role='assistant', content=assistant_content_params))
476
476
  else:
477
477
  assert_never(m)
478
- system_prompt = '\n\n'.join(system_prompt_parts)
479
478
  if instructions := self._get_instructions(messages):
480
- system_prompt = f'{instructions}\n\n{system_prompt}'
479
+ system_prompt_parts.insert(0, instructions)
480
+ system_prompt = '\n\n'.join(system_prompt_parts)
481
481
  return system_prompt, anthropic_messages
482
482
 
483
483
  @staticmethod
@@ -423,7 +423,7 @@ class BedrockConverseModel(Model):
423
423
  for message in messages:
424
424
  if isinstance(message, ModelRequest):
425
425
  for part in message.parts:
426
- if isinstance(part, SystemPromptPart):
426
+ if isinstance(part, SystemPromptPart) and part.content:
427
427
  system_prompt.append({'text': part.content})
428
428
  elif isinstance(part, UserPromptPart):
429
429
  bedrock_messages.extend(await self._map_user_prompt(part, document_count))
@@ -82,8 +82,10 @@ except ImportError as _import_error:
82
82
 
83
83
  __all__ = (
84
84
  'OpenAIModel',
85
+ 'OpenAIChatModel',
85
86
  'OpenAIResponsesModel',
86
87
  'OpenAIModelSettings',
88
+ 'OpenAIChatModelSettings',
87
89
  'OpenAIResponsesModelSettings',
88
90
  'OpenAIModelName',
89
91
  )
@@ -101,7 +103,7 @@ allows this model to be used more easily with other model types (ie, Ollama, Dee
101
103
  """
102
104
 
103
105
 
104
- class OpenAIModelSettings(ModelSettings, total=False):
106
+ class OpenAIChatModelSettings(ModelSettings, total=False):
105
107
  """Settings used for an OpenAI model request."""
106
108
 
107
109
  # ALL FIELDS MUST BE `openai_` PREFIXED SO YOU CAN MERGE THEM WITH OTHER MODELS.
@@ -139,7 +141,12 @@ class OpenAIModelSettings(ModelSettings, total=False):
139
141
  """
140
142
 
141
143
 
142
- class OpenAIResponsesModelSettings(OpenAIModelSettings, total=False):
144
+ @deprecated('Use `OpenAIChatModelSettings` instead.')
145
+ class OpenAIModelSettings(OpenAIChatModelSettings, total=False):
146
+ """Deprecated alias for `OpenAIChatModelSettings`."""
147
+
148
+
149
+ class OpenAIResponsesModelSettings(OpenAIChatModelSettings, total=False):
143
150
  """Settings used for an OpenAI Responses model request.
144
151
 
145
152
  ALL FIELDS MUST BE `openai_` PREFIXED SO YOU CAN MERGE THEM WITH OTHER MODELS.
@@ -185,7 +192,7 @@ class OpenAIResponsesModelSettings(OpenAIModelSettings, total=False):
185
192
 
186
193
 
187
194
  @dataclass(init=False)
188
- class OpenAIModel(Model):
195
+ class OpenAIChatModel(Model):
189
196
  """A model that uses the OpenAI API.
190
197
 
191
198
  Internally, this uses the [OpenAI Python client](https://github.com/openai/openai-python) to interact with the API.
@@ -204,18 +211,20 @@ class OpenAIModel(Model):
204
211
  model_name: OpenAIModelName,
205
212
  *,
206
213
  provider: Literal[
207
- 'openai',
208
- 'deepseek',
209
214
  'azure',
210
- 'openrouter',
211
- 'moonshotai',
212
- 'vercel',
213
- 'grok',
215
+ 'deepseek',
216
+ 'cerebras',
214
217
  'fireworks',
215
- 'together',
216
- 'heroku',
217
218
  'github',
219
+ 'grok',
220
+ 'heroku',
221
+ 'moonshotai',
218
222
  'ollama',
223
+ 'openai',
224
+ 'openai-chat',
225
+ 'openrouter',
226
+ 'together',
227
+ 'vercel',
219
228
  ]
220
229
  | Provider[AsyncOpenAI] = 'openai',
221
230
  profile: ModelProfileSpec | None = None,
@@ -229,18 +238,20 @@ class OpenAIModel(Model):
229
238
  model_name: OpenAIModelName,
230
239
  *,
231
240
  provider: Literal[
232
- 'openai',
233
- 'deepseek',
234
241
  'azure',
235
- 'openrouter',
236
- 'moonshotai',
237
- 'vercel',
238
- 'grok',
242
+ 'deepseek',
243
+ 'cerebras',
239
244
  'fireworks',
240
- 'together',
241
- 'heroku',
242
245
  'github',
246
+ 'grok',
247
+ 'heroku',
248
+ 'moonshotai',
243
249
  'ollama',
250
+ 'openai',
251
+ 'openai-chat',
252
+ 'openrouter',
253
+ 'together',
254
+ 'vercel',
244
255
  ]
245
256
  | Provider[AsyncOpenAI] = 'openai',
246
257
  profile: ModelProfileSpec | None = None,
@@ -253,18 +264,20 @@ class OpenAIModel(Model):
253
264
  model_name: OpenAIModelName,
254
265
  *,
255
266
  provider: Literal[
256
- 'openai',
257
- 'deepseek',
258
267
  'azure',
259
- 'openrouter',
260
- 'moonshotai',
261
- 'vercel',
262
- 'grok',
268
+ 'deepseek',
269
+ 'cerebras',
263
270
  'fireworks',
264
- 'together',
265
- 'heroku',
266
271
  'github',
272
+ 'grok',
273
+ 'heroku',
274
+ 'moonshotai',
267
275
  'ollama',
276
+ 'openai',
277
+ 'openai-chat',
278
+ 'openrouter',
279
+ 'together',
280
+ 'vercel',
268
281
  ]
269
282
  | Provider[AsyncOpenAI] = 'openai',
270
283
  profile: ModelProfileSpec | None = None,
@@ -322,7 +335,7 @@ class OpenAIModel(Model):
322
335
  ) -> ModelResponse:
323
336
  check_allow_model_requests()
324
337
  response = await self._completions_create(
325
- messages, False, cast(OpenAIModelSettings, model_settings or {}), model_request_parameters
338
+ messages, False, cast(OpenAIChatModelSettings, model_settings or {}), model_request_parameters
326
339
  )
327
340
  model_response = self._process_response(response)
328
341
  return model_response
@@ -337,7 +350,7 @@ class OpenAIModel(Model):
337
350
  ) -> AsyncIterator[StreamedResponse]:
338
351
  check_allow_model_requests()
339
352
  response = await self._completions_create(
340
- messages, True, cast(OpenAIModelSettings, model_settings or {}), model_request_parameters
353
+ messages, True, cast(OpenAIChatModelSettings, model_settings or {}), model_request_parameters
341
354
  )
342
355
  async with response:
343
356
  yield await self._process_streamed_response(response, model_request_parameters)
@@ -347,7 +360,7 @@ class OpenAIModel(Model):
347
360
  self,
348
361
  messages: list[ModelMessage],
349
362
  stream: Literal[True],
350
- model_settings: OpenAIModelSettings,
363
+ model_settings: OpenAIChatModelSettings,
351
364
  model_request_parameters: ModelRequestParameters,
352
365
  ) -> AsyncStream[ChatCompletionChunk]: ...
353
366
 
@@ -356,7 +369,7 @@ class OpenAIModel(Model):
356
369
  self,
357
370
  messages: list[ModelMessage],
358
371
  stream: Literal[False],
359
- model_settings: OpenAIModelSettings,
372
+ model_settings: OpenAIChatModelSettings,
360
373
  model_request_parameters: ModelRequestParameters,
361
374
  ) -> chat.ChatCompletion: ...
362
375
 
@@ -364,7 +377,7 @@ class OpenAIModel(Model):
364
377
  self,
365
378
  messages: list[ModelMessage],
366
379
  stream: bool,
367
- model_settings: OpenAIModelSettings,
380
+ model_settings: OpenAIChatModelSettings,
368
381
  model_request_parameters: ModelRequestParameters,
369
382
  ) -> chat.ChatCompletion | AsyncStream[ChatCompletionChunk]:
370
383
  tools = self._get_tools(model_request_parameters)
@@ -392,10 +405,15 @@ class OpenAIModel(Model):
392
405
  ): # pragma: no branch
393
406
  response_format = {'type': 'json_object'}
394
407
 
408
+ unsupported_model_settings = OpenAIModelProfile.from_profile(self.profile).openai_unsupported_model_settings
409
+ for setting in unsupported_model_settings:
410
+ model_settings.pop(setting, None)
411
+
412
+ # TODO(Marcelo): Deprecate this in favor of `openai_unsupported_model_settings`.
395
413
  sampling_settings = (
396
414
  model_settings
397
415
  if OpenAIModelProfile.from_profile(self.profile).openai_supports_sampling_settings
398
- else OpenAIModelSettings()
416
+ else OpenAIChatModelSettings()
399
417
  )
400
418
 
401
419
  try:
@@ -636,9 +654,7 @@ class OpenAIModel(Model):
636
654
  )
637
655
  elif isinstance(part, RetryPromptPart):
638
656
  if part.tool_name is None:
639
- yield chat.ChatCompletionUserMessageParam( # pragma: no cover
640
- role='user', content=part.model_response()
641
- )
657
+ yield chat.ChatCompletionUserMessageParam(role='user', content=part.model_response())
642
658
  else:
643
659
  yield chat.ChatCompletionToolMessageParam(
644
660
  role='tool',
@@ -706,6 +722,16 @@ class OpenAIModel(Model):
706
722
  return chat.ChatCompletionUserMessageParam(role='user', content=content)
707
723
 
708
724
 
725
+ @deprecated(
726
+ '`OpenAIModel` was renamed to `OpenAIChatModel` to clearly distinguish it from `OpenAIResponsesModel` which '
727
+ "uses OpenAI's newer Responses API. Use that unless you're using an OpenAI Chat Completions-compatible API, or "
728
+ "require a feature that the Responses API doesn't support yet like audio."
729
+ )
730
+ @dataclass(init=False)
731
+ class OpenAIModel(OpenAIChatModel):
732
+ """Deprecated alias for `OpenAIChatModel`."""
733
+
734
+
709
735
  @dataclass(init=False)
710
736
  class OpenAIResponsesModel(Model):
711
737
  """A model that uses the OpenAI Responses API.
@@ -52,7 +52,7 @@ class ModelProfile:
52
52
  This is a workaround for models that emit `<think>\n</think>\n\n` or an empty text part ahead of tool calls (e.g. Ollama + Qwen3),
53
53
  which we don't want to end up treating as a final result when using `run_stream` with `str` a valid `output_type`.
54
54
 
55
- This is currently only used by `OpenAIModel`, `HuggingFaceModel`, and `GroqModel`.
55
+ This is currently only used by `OpenAIChatModel`, `HuggingFaceModel`, and `GroqModel`.
56
56
  """
57
57
 
58
58
  @classmethod
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations as _annotations
2
+
3
+ from . import ModelProfile
4
+ from .openai import OpenAIModelProfile, openai_model_profile
5
+
6
+
7
+ def harmony_model_profile(model_name: str) -> ModelProfile | None:
8
+ """The model profile for the OpenAI Harmony Response format.
9
+
10
+ See <https://cookbook.openai.com/articles/openai-harmony> for more details.
11
+ """
12
+ profile = openai_model_profile(model_name)
13
+ return OpenAIModelProfile(openai_supports_tool_choice_required=False).update(profile)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
3
  import re
4
+ from collections.abc import Sequence
4
5
  from dataclasses import dataclass
5
6
  from typing import Any, Literal
6
7
 
@@ -12,7 +13,7 @@ OpenAISystemPromptRole = Literal['system', 'developer', 'user']
12
13
 
13
14
  @dataclass
14
15
  class OpenAIModelProfile(ModelProfile):
15
- """Profile for models used with OpenAIModel.
16
+ """Profile for models used with `OpenAIChatModel`.
16
17
 
17
18
  ALL FIELDS MUST BE `openai_` PREFIXED SO YOU CAN MERGE THEM WITH OTHER MODELS.
18
19
  """
@@ -20,9 +21,13 @@ class OpenAIModelProfile(ModelProfile):
20
21
  openai_supports_strict_tool_definition: bool = True
21
22
  """This can be set by a provider or user if the OpenAI-"compatible" API doesn't support strict tool definitions."""
22
23
 
24
+ # TODO(Marcelo): Deprecate this in favor of `openai_unsupported_model_settings`.
23
25
  openai_supports_sampling_settings: bool = True
24
26
  """Turn off to don't send sampling settings like `temperature` and `top_p` to models that don't support them, like OpenAI's o-series reasoning models."""
25
27
 
28
+ openai_unsupported_model_settings: Sequence[str] = ()
29
+ """A list of model settings that are not supported by the model."""
30
+
26
31
  # Some OpenAI-compatible providers (e.g. MoonshotAI) currently do **not** accept
27
32
  # `tool_choice="required"`. This flag lets the calling model know whether it's
28
33
  # safe to pass that value along. Default is `True` to preserve existing
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations as _annotations
2
+
3
+ from ..profiles.openai import OpenAIModelProfile
4
+ from . import InlineDefsJsonSchemaTransformer, ModelProfile
5
+
6
+
7
+ def qwen_model_profile(model_name: str) -> ModelProfile | None:
8
+ """Get the model profile for a Qwen model."""
9
+ if model_name.startswith('qwen-3-coder'):
10
+ return OpenAIModelProfile(
11
+ json_schema_transformer=InlineDefsJsonSchemaTransformer,
12
+ openai_supports_tool_choice_required=False,
13
+ openai_supports_strict_tool_definition=False,
14
+ ignore_streamed_leading_whitespace=True,
15
+ )
16
+ return ModelProfile(
17
+ json_schema_transformer=InlineDefsJsonSchemaTransformer,
18
+ ignore_streamed_leading_whitespace=True,
19
+ )
@@ -20,7 +20,7 @@ class Provider(ABC, Generic[InterfaceClient]):
20
20
 
21
21
  Each provider only supports a specific interface. A interface can be supported by multiple providers.
22
22
 
23
- For example, the OpenAIModel interface can be supported by the OpenAIProvider and the DeepSeekProvider.
23
+ For example, the `OpenAIChatModel` interface can be supported by the `OpenAIProvider` and the `DeepSeekProvider`.
24
24
  """
25
25
 
26
26
  _client: InterfaceClient
@@ -95,6 +95,10 @@ def infer_provider_class(provider: str) -> type[Provider[Any]]: # noqa: C901
95
95
  from .mistral import MistralProvider
96
96
 
97
97
  return MistralProvider
98
+ elif provider == 'cerebras':
99
+ from .cerebras import CerebrasProvider
100
+
101
+ return CerebrasProvider
98
102
  elif provider == 'cohere':
99
103
  from .cohere import CohereProvider
100
104
 
@@ -65,7 +65,7 @@ class AzureProvider(Provider[AsyncOpenAI]):
65
65
 
66
66
  profile = profile_func(model_name)
67
67
 
68
- # As AzureProvider is always used with OpenAIModel, which used to unconditionally use OpenAIJsonSchemaTransformer,
68
+ # As AzureProvider is always used with OpenAIChatModel, which used to unconditionally use OpenAIJsonSchemaTransformer,
69
69
  # we need to maintain that behavior unless json_schema_transformer is set explicitly
70
70
  return OpenAIModelProfile(json_schema_transformer=OpenAIJsonSchemaTransformer).update(profile)
71
71
 
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations as _annotations
2
+
3
+ import os
4
+ from typing import overload
5
+
6
+ import httpx
7
+
8
+ from pydantic_ai.exceptions import UserError
9
+ from pydantic_ai.models import cached_async_http_client
10
+ from pydantic_ai.profiles import ModelProfile
11
+ from pydantic_ai.profiles.harmony import harmony_model_profile
12
+ from pydantic_ai.profiles.meta import meta_model_profile
13
+ from pydantic_ai.profiles.openai import OpenAIJsonSchemaTransformer, OpenAIModelProfile
14
+ from pydantic_ai.profiles.qwen import qwen_model_profile
15
+ from pydantic_ai.providers import Provider
16
+
17
+ try:
18
+ from openai import AsyncOpenAI
19
+ except ImportError as _import_error: # pragma: no cover
20
+ raise ImportError(
21
+ 'Please install the `openai` package to use the Cerebras provider, '
22
+ 'you can use the `openai` optional group — `pip install "pydantic-ai-slim[openai]"`'
23
+ ) from _import_error
24
+
25
+
26
+ class CerebrasProvider(Provider[AsyncOpenAI]):
27
+ """Provider for Cerebras API."""
28
+
29
+ @property
30
+ def name(self) -> str:
31
+ return 'cerebras'
32
+
33
+ @property
34
+ def base_url(self) -> str:
35
+ return 'https://api.cerebras.ai/v1'
36
+
37
+ @property
38
+ def client(self) -> AsyncOpenAI:
39
+ return self._client
40
+
41
+ def model_profile(self, model_name: str) -> ModelProfile | None:
42
+ prefix_to_profile = {'llama': meta_model_profile, 'qwen': qwen_model_profile, 'gpt-oss': harmony_model_profile}
43
+
44
+ profile = None
45
+ for prefix, profile_func in prefix_to_profile.items():
46
+ model_name = model_name.lower()
47
+ if model_name.startswith(prefix):
48
+ profile = profile_func(model_name)
49
+
50
+ # According to https://inference-docs.cerebras.ai/resources/openai#currently-unsupported-openai-features,
51
+ # Cerebras doesn't support some model settings.
52
+ unsupported_model_settings = (
53
+ 'frequency_penalty',
54
+ 'logit_bias',
55
+ 'presence_penalty',
56
+ 'parallel_tool_calls',
57
+ 'service_tier',
58
+ )
59
+ return OpenAIModelProfile(
60
+ json_schema_transformer=OpenAIJsonSchemaTransformer,
61
+ openai_unsupported_model_settings=unsupported_model_settings,
62
+ ).update(profile)
63
+
64
+ @overload
65
+ def __init__(self) -> None: ...
66
+
67
+ @overload
68
+ def __init__(self, *, api_key: str) -> None: ...
69
+
70
+ @overload
71
+ def __init__(self, *, api_key: str, http_client: httpx.AsyncClient) -> None: ...
72
+
73
+ @overload
74
+ def __init__(self, *, openai_client: AsyncOpenAI | None = None) -> None: ...
75
+
76
+ def __init__(
77
+ self,
78
+ *,
79
+ api_key: str | None = None,
80
+ openai_client: AsyncOpenAI | None = None,
81
+ http_client: httpx.AsyncClient | None = None,
82
+ ) -> None:
83
+ api_key = api_key or os.getenv('CEREBRAS_API_KEY')
84
+ if not api_key and openai_client is None:
85
+ raise UserError(
86
+ 'Set the `CEREBRAS_API_KEY` environment variable or pass it via `CerebrasProvider(api_key=...)` '
87
+ 'to use the Cerebras provider.'
88
+ )
89
+
90
+ if openai_client is not None:
91
+ self._client = openai_client
92
+ elif http_client is not None:
93
+ self._client = AsyncOpenAI(base_url=self.base_url, api_key=api_key, http_client=http_client)
94
+ else:
95
+ http_client = cached_async_http_client(provider='cerebras')
96
+ self._client = AsyncOpenAI(base_url=self.base_url, api_key=api_key, http_client=http_client)
@@ -2,7 +2,7 @@ from __future__ import annotations as _annotations
2
2
 
3
3
  import os
4
4
 
5
- from httpx import AsyncClient as AsyncHTTPClient
5
+ import httpx
6
6
 
7
7
  from pydantic_ai.exceptions import UserError
8
8
  from pydantic_ai.models import cached_async_http_client
@@ -43,7 +43,7 @@ class CohereProvider(Provider[AsyncClientV2]):
43
43
  *,
44
44
  api_key: str | None = None,
45
45
  cohere_client: AsyncClientV2 | None = None,
46
- http_client: AsyncHTTPClient | None = None,
46
+ http_client: httpx.AsyncClient | None = None,
47
47
  ) -> None:
48
48
  """Create a new Cohere provider.
49
49
 
@@ -3,7 +3,7 @@ from __future__ import annotations as _annotations
3
3
  import os
4
4
  from typing import overload
5
5
 
6
- from httpx import AsyncClient as AsyncHTTPClient
6
+ import httpx
7
7
  from openai import AsyncOpenAI
8
8
 
9
9
  from pydantic_ai.exceptions import UserError
@@ -40,7 +40,7 @@ class DeepSeekProvider(Provider[AsyncOpenAI]):
40
40
  def model_profile(self, model_name: str) -> ModelProfile | None:
41
41
  profile = deepseek_model_profile(model_name)
42
42
 
43
- # As DeepSeekProvider is always used with OpenAIModel, which used to unconditionally use OpenAIJsonSchemaTransformer,
43
+ # As DeepSeekProvider is always used with OpenAIChatModel, which used to unconditionally use OpenAIJsonSchemaTransformer,
44
44
  # we need to maintain that behavior unless json_schema_transformer is set explicitly.
45
45
  # This was not the case when using a DeepSeek model with another model class (e.g. BedrockConverseModel or GroqModel),
46
46
  # so we won't do this in `deepseek_model_profile` unless we learn it's always needed.
@@ -53,7 +53,7 @@ class DeepSeekProvider(Provider[AsyncOpenAI]):
53
53
  def __init__(self, *, api_key: str) -> None: ...
54
54
 
55
55
  @overload
56
- def __init__(self, *, api_key: str, http_client: AsyncHTTPClient) -> None: ...
56
+ def __init__(self, *, api_key: str, http_client: httpx.AsyncClient) -> None: ...
57
57
 
58
58
  @overload
59
59
  def __init__(self, *, openai_client: AsyncOpenAI | None = None) -> None: ...
@@ -63,7 +63,7 @@ class DeepSeekProvider(Provider[AsyncOpenAI]):
63
63
  *,
64
64
  api_key: str | None = None,
65
65
  openai_client: AsyncOpenAI | None = None,
66
- http_client: AsyncHTTPClient | None = None,
66
+ http_client: httpx.AsyncClient | None = None,
67
67
  ) -> None:
68
68
  api_key = api_key or os.getenv('DEEPSEEK_API_KEY')
69
69
  if not api_key and openai_client is None:
@@ -3,7 +3,7 @@ from __future__ import annotations as _annotations
3
3
  import os
4
4
  from typing import overload
5
5
 
6
- from httpx import AsyncClient as AsyncHTTPClient
6
+ import httpx
7
7
  from openai import AsyncOpenAI
8
8
 
9
9
  from pydantic_ai.exceptions import UserError
@@ -71,7 +71,7 @@ class FireworksProvider(Provider[AsyncOpenAI]):
71
71
  def __init__(self, *, api_key: str) -> None: ...
72
72
 
73
73
  @overload
74
- def __init__(self, *, api_key: str, http_client: AsyncHTTPClient) -> None: ...
74
+ def __init__(self, *, api_key: str, http_client: httpx.AsyncClient) -> None: ...
75
75
 
76
76
  @overload
77
77
  def __init__(self, *, openai_client: AsyncOpenAI | None = None) -> None: ...
@@ -81,7 +81,7 @@ class FireworksProvider(Provider[AsyncOpenAI]):
81
81
  *,
82
82
  api_key: str | None = None,
83
83
  openai_client: AsyncOpenAI | None = None,
84
- http_client: AsyncHTTPClient | None = None,
84
+ http_client: httpx.AsyncClient | None = None,
85
85
  ) -> None:
86
86
  api_key = api_key or os.getenv('FIREWORKS_API_KEY')
87
87
  if not api_key and openai_client is None:
@@ -3,7 +3,7 @@ from __future__ import annotations as _annotations
3
3
  import os
4
4
  from typing import overload
5
5
 
6
- from httpx import AsyncClient as AsyncHTTPClient
6
+ import httpx
7
7
 
8
8
  from pydantic_ai.exceptions import UserError
9
9
  from pydantic_ai.models import cached_async_http_client
@@ -65,7 +65,7 @@ class GitHubProvider(Provider[AsyncOpenAI]):
65
65
  model_name, *_ = model_name.split(':', 1) # drop tags
66
66
  profile = provider_to_profile[provider](model_name)
67
67
 
68
- # As GitHubProvider is always used with OpenAIModel, which used to unconditionally use OpenAIJsonSchemaTransformer,
68
+ # As GitHubProvider is always used with OpenAIChatModel, which used to unconditionally use OpenAIJsonSchemaTransformer,
69
69
  # we need to maintain that behavior unless json_schema_transformer is set explicitly
70
70
  return OpenAIModelProfile(json_schema_transformer=OpenAIJsonSchemaTransformer).update(profile)
71
71
 
@@ -76,7 +76,7 @@ class GitHubProvider(Provider[AsyncOpenAI]):
76
76
  def __init__(self, *, api_key: str) -> None: ...
77
77
 
78
78
  @overload
79
- def __init__(self, *, api_key: str, http_client: AsyncHTTPClient) -> None: ...
79
+ def __init__(self, *, api_key: str, http_client: httpx.AsyncClient) -> None: ...
80
80
 
81
81
  @overload
82
82
  def __init__(self, *, openai_client: AsyncOpenAI | None = None) -> None: ...
@@ -86,7 +86,7 @@ class GitHubProvider(Provider[AsyncOpenAI]):
86
86
  *,
87
87
  api_key: str | None = None,
88
88
  openai_client: AsyncOpenAI | None = None,
89
- http_client: AsyncHTTPClient | None = None,
89
+ http_client: httpx.AsyncClient | None = None,
90
90
  ) -> None:
91
91
  """Create a new GitHub Models provider.
92
92