pydantic-ai-slim 0.7.0__tar.gz → 0.7.1__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.
Potentially problematic release.
This version of pydantic-ai-slim might be problematic. Click here for more details.
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/PKG-INFO +4 -4
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_agent_graph.py +12 -5
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/__init__.py +28 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/fallback.py +7 -2
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/google.py +66 -6
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/openai.py +28 -4
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/usage.py +17 -1
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pyproject.toml +1 -1
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/.gitignore +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/LICENSE +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/README.md +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/__main__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_a2a.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_cli.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_function_schema.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_griffe.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_mcp.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_output.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_parts_manager.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_run_context.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_system_prompt.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_thinking_part.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_tool_manager.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/_utils.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/ag_ui.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/agent/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/agent/abstract.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/agent/wrapper.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/builtin_tools.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/common_tools/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/common_tools/duckduckgo.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/common_tools/tavily.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/direct.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_agent.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_function_toolset.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_logfire.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_mcp_server.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_model.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_run_context.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_toolset.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/exceptions.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/ext/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/ext/aci.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/ext/langchain.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/format_prompt.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/mcp.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/messages.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/anthropic.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/bedrock.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/cohere.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/function.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/gemini.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/groq.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/huggingface.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/instrumented.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/mcp_sampling.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/mistral.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/test.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/models/wrapper.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/output.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/_json_schema.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/amazon.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/anthropic.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/cohere.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/deepseek.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/google.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/grok.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/groq.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/meta.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/mistral.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/moonshotai.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/openai.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/profiles/qwen.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/anthropic.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/azure.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/bedrock.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/cohere.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/deepseek.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/fireworks.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/github.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/google.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/google_gla.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/google_vertex.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/grok.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/groq.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/heroku.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/huggingface.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/mistral.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/moonshotai.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/openai.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/openrouter.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/together.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/providers/vercel.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/py.typed +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/result.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/retries.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/run.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/settings.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/tools.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/__init__.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/_dynamic.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/abstract.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/combined.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/deferred.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/filtered.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/function.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/prefixed.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/prepared.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/renamed.py +0 -0
- {pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/toolsets/wrapper.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai-slim
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
|
|
5
5
|
Author-email: Samuel Colvin <samuel@pydantic.dev>, Marcelo Trylesinski <marcelotryle@gmail.com>, David Montague <david@pydantic.dev>, Alex Hall <alex@pydantic.dev>, Douwe Maan <douwe@pydantic.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,7 +30,7 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
|
|
|
30
30
|
Requires-Dist: griffe>=1.3.2
|
|
31
31
|
Requires-Dist: httpx>=0.27
|
|
32
32
|
Requires-Dist: opentelemetry-api>=1.28.0
|
|
33
|
-
Requires-Dist: pydantic-graph==0.7.
|
|
33
|
+
Requires-Dist: pydantic-graph==0.7.1
|
|
34
34
|
Requires-Dist: pydantic>=2.10
|
|
35
35
|
Requires-Dist: typing-inspection>=0.4.0
|
|
36
36
|
Provides-Extra: a2a
|
|
@@ -51,7 +51,7 @@ Requires-Dist: cohere>=5.16.0; (platform_system != 'Emscripten') and extra == 'c
|
|
|
51
51
|
Provides-Extra: duckduckgo
|
|
52
52
|
Requires-Dist: ddgs>=9.0.0; extra == 'duckduckgo'
|
|
53
53
|
Provides-Extra: evals
|
|
54
|
-
Requires-Dist: pydantic-evals==0.7.
|
|
54
|
+
Requires-Dist: pydantic-evals==0.7.1; extra == 'evals'
|
|
55
55
|
Provides-Extra: google
|
|
56
56
|
Requires-Dist: google-genai>=1.28.0; extra == 'google'
|
|
57
57
|
Provides-Extra: groq
|
|
@@ -65,7 +65,7 @@ Requires-Dist: mcp>=1.10.0; (python_version >= '3.10') and extra == 'mcp'
|
|
|
65
65
|
Provides-Extra: mistral
|
|
66
66
|
Requires-Dist: mistralai>=1.9.2; extra == 'mistral'
|
|
67
67
|
Provides-Extra: openai
|
|
68
|
-
Requires-Dist: openai>=1.
|
|
68
|
+
Requires-Dist: openai>=1.99.9; extra == 'openai'
|
|
69
69
|
Provides-Extra: retries
|
|
70
70
|
Requires-Dist: tenacity>=8.2.3; extra == 'retries'
|
|
71
71
|
Provides-Extra: tavily
|
|
@@ -351,11 +351,6 @@ class ModelRequestNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
351
351
|
) -> tuple[ModelSettings | None, models.ModelRequestParameters, list[_messages.ModelMessage], RunContext[DepsT]]:
|
|
352
352
|
ctx.state.message_history.append(self.request)
|
|
353
353
|
|
|
354
|
-
# Check usage
|
|
355
|
-
if ctx.deps.usage_limits: # pragma: no branch
|
|
356
|
-
ctx.deps.usage_limits.check_before_request(ctx.state.usage)
|
|
357
|
-
|
|
358
|
-
# Increment run_step
|
|
359
354
|
ctx.state.run_step += 1
|
|
360
355
|
|
|
361
356
|
run_context = build_run_context(ctx)
|
|
@@ -367,6 +362,18 @@ class ModelRequestNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
367
362
|
|
|
368
363
|
message_history = await _process_message_history(ctx.state, ctx.deps.history_processors, run_context)
|
|
369
364
|
|
|
365
|
+
usage = ctx.state.usage
|
|
366
|
+
if ctx.deps.usage_limits.count_tokens_before_request:
|
|
367
|
+
# Copy to avoid modifying the original usage object with the counted usage
|
|
368
|
+
usage = dataclasses.replace(usage)
|
|
369
|
+
|
|
370
|
+
counted_usage = await ctx.deps.model.count_tokens(
|
|
371
|
+
message_history, ctx.deps.model_settings, model_request_parameters
|
|
372
|
+
)
|
|
373
|
+
usage.incr(counted_usage)
|
|
374
|
+
|
|
375
|
+
ctx.deps.usage_limits.check_before_request(usage)
|
|
376
|
+
|
|
370
377
|
return model_settings, model_request_parameters, message_history, run_context
|
|
371
378
|
|
|
372
379
|
def _finish_handling(
|
|
@@ -194,6 +194,13 @@ KnownModelName = TypeAliasType(
|
|
|
194
194
|
'gpt-4o-mini-search-preview-2025-03-11',
|
|
195
195
|
'gpt-4o-search-preview',
|
|
196
196
|
'gpt-4o-search-preview-2025-03-11',
|
|
197
|
+
'gpt-5',
|
|
198
|
+
'gpt-5-2025-08-07',
|
|
199
|
+
'gpt-5-chat-latest',
|
|
200
|
+
'gpt-5-mini',
|
|
201
|
+
'gpt-5-mini-2025-08-07',
|
|
202
|
+
'gpt-5-nano',
|
|
203
|
+
'gpt-5-nano-2025-08-07',
|
|
197
204
|
'grok:grok-4',
|
|
198
205
|
'grok:grok-4-0709',
|
|
199
206
|
'grok:grok-3',
|
|
@@ -313,11 +320,18 @@ KnownModelName = TypeAliasType(
|
|
|
313
320
|
'openai:gpt-4o-mini-search-preview-2025-03-11',
|
|
314
321
|
'openai:gpt-4o-search-preview',
|
|
315
322
|
'openai:gpt-4o-search-preview-2025-03-11',
|
|
323
|
+
'openai:gpt-5',
|
|
324
|
+
'openai:gpt-5-2025-08-07',
|
|
316
325
|
'openai:o1',
|
|
326
|
+
'openai:gpt-5-chat-latest',
|
|
317
327
|
'openai:o1-2024-12-17',
|
|
328
|
+
'openai:gpt-5-mini',
|
|
318
329
|
'openai:o1-mini',
|
|
330
|
+
'openai:gpt-5-mini-2025-08-07',
|
|
319
331
|
'openai:o1-mini-2024-09-12',
|
|
332
|
+
'openai:gpt-5-nano',
|
|
320
333
|
'openai:o1-preview',
|
|
334
|
+
'openai:gpt-5-nano-2025-08-07',
|
|
321
335
|
'openai:o1-preview-2024-09-12',
|
|
322
336
|
'openai:o1-pro',
|
|
323
337
|
'openai:o1-pro-2025-03-19',
|
|
@@ -399,6 +413,16 @@ class Model(ABC):
|
|
|
399
413
|
"""Make a request to the model."""
|
|
400
414
|
raise NotImplementedError()
|
|
401
415
|
|
|
416
|
+
async def count_tokens(
|
|
417
|
+
self,
|
|
418
|
+
messages: list[ModelMessage],
|
|
419
|
+
model_settings: ModelSettings | None,
|
|
420
|
+
model_request_parameters: ModelRequestParameters,
|
|
421
|
+
) -> Usage:
|
|
422
|
+
"""Make a request to the model for counting tokens."""
|
|
423
|
+
# This method is not required, but you need to implement it if you want to support `UsageLimits.count_tokens_before_request`.
|
|
424
|
+
raise NotImplementedError(f'Token counting ahead of the request is not supported by {self.__class__.__name__}')
|
|
425
|
+
|
|
402
426
|
@asynccontextmanager
|
|
403
427
|
async def request_stream(
|
|
404
428
|
self,
|
|
@@ -679,6 +703,10 @@ def infer_model(model: Model | KnownModelName | str) -> Model: # noqa: C901
|
|
|
679
703
|
from .openai import OpenAIModel
|
|
680
704
|
|
|
681
705
|
return OpenAIModel(model_name, provider=provider)
|
|
706
|
+
elif provider == 'openai-responses':
|
|
707
|
+
from .openai import OpenAIResponsesModel
|
|
708
|
+
|
|
709
|
+
return OpenAIResponsesModel(model_name, provider='openai')
|
|
682
710
|
elif provider in ('google-gla', 'google-vertex'):
|
|
683
711
|
from .google import GoogleModel
|
|
684
712
|
|
|
@@ -11,6 +11,7 @@ from pydantic_ai._run_context import RunContext
|
|
|
11
11
|
from pydantic_ai.models.instrumented import InstrumentedModel
|
|
12
12
|
|
|
13
13
|
from ..exceptions import FallbackExceptionGroup, ModelHTTPError
|
|
14
|
+
from ..settings import merge_model_settings
|
|
14
15
|
from . import KnownModelName, Model, ModelRequestParameters, StreamedResponse, infer_model
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
@@ -65,8 +66,9 @@ class FallbackModel(Model):
|
|
|
65
66
|
|
|
66
67
|
for model in self.models:
|
|
67
68
|
customized_model_request_parameters = model.customize_request_parameters(model_request_parameters)
|
|
69
|
+
merged_settings = merge_model_settings(model.settings, model_settings)
|
|
68
70
|
try:
|
|
69
|
-
response = await model.request(messages,
|
|
71
|
+
response = await model.request(messages, merged_settings, customized_model_request_parameters)
|
|
70
72
|
except Exception as exc:
|
|
71
73
|
if self._fallback_on(exc):
|
|
72
74
|
exceptions.append(exc)
|
|
@@ -91,10 +93,13 @@ class FallbackModel(Model):
|
|
|
91
93
|
|
|
92
94
|
for model in self.models:
|
|
93
95
|
customized_model_request_parameters = model.customize_request_parameters(model_request_parameters)
|
|
96
|
+
merged_settings = merge_model_settings(model.settings, model_settings)
|
|
94
97
|
async with AsyncExitStack() as stack:
|
|
95
98
|
try:
|
|
96
99
|
response = await stack.enter_async_context(
|
|
97
|
-
model.request_stream(
|
|
100
|
+
model.request_stream(
|
|
101
|
+
messages, merged_settings, customized_model_request_parameters, run_context
|
|
102
|
+
)
|
|
98
103
|
)
|
|
99
104
|
except Exception as exc:
|
|
100
105
|
if self._fallback_on(exc):
|
|
@@ -52,6 +52,7 @@ try:
|
|
|
52
52
|
from google.genai.types import (
|
|
53
53
|
ContentDict,
|
|
54
54
|
ContentUnionDict,
|
|
55
|
+
CountTokensConfigDict,
|
|
55
56
|
ExecutableCodeDict,
|
|
56
57
|
FunctionCallDict,
|
|
57
58
|
FunctionCallingConfigDict,
|
|
@@ -59,6 +60,7 @@ try:
|
|
|
59
60
|
FunctionDeclarationDict,
|
|
60
61
|
GenerateContentConfigDict,
|
|
61
62
|
GenerateContentResponse,
|
|
63
|
+
GenerationConfigDict,
|
|
62
64
|
GoogleSearchDict,
|
|
63
65
|
HttpOptionsDict,
|
|
64
66
|
MediaResolution,
|
|
@@ -188,6 +190,59 @@ class GoogleModel(Model):
|
|
|
188
190
|
response = await self._generate_content(messages, False, model_settings, model_request_parameters)
|
|
189
191
|
return self._process_response(response)
|
|
190
192
|
|
|
193
|
+
async def count_tokens(
|
|
194
|
+
self,
|
|
195
|
+
messages: list[ModelMessage],
|
|
196
|
+
model_settings: ModelSettings | None,
|
|
197
|
+
model_request_parameters: ModelRequestParameters,
|
|
198
|
+
) -> usage.Usage:
|
|
199
|
+
check_allow_model_requests()
|
|
200
|
+
model_settings = cast(GoogleModelSettings, model_settings or {})
|
|
201
|
+
contents, generation_config = await self._build_content_and_config(
|
|
202
|
+
messages, model_settings, model_request_parameters
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Annoyingly, the type of `GenerateContentConfigDict.get` is "partially `Unknown`" because `response_schema` includes `typing._UnionGenericAlias`,
|
|
206
|
+
# so without this we'd need `pyright: ignore[reportUnknownMemberType]` on every line and wouldn't get type checking anyway.
|
|
207
|
+
generation_config = cast(dict[str, Any], generation_config)
|
|
208
|
+
|
|
209
|
+
config = CountTokensConfigDict(
|
|
210
|
+
http_options=generation_config.get('http_options'),
|
|
211
|
+
)
|
|
212
|
+
if self.system != 'google-gla':
|
|
213
|
+
# The fields are not supported by the Gemini API per https://github.com/googleapis/python-genai/blob/7e4ec284dc6e521949626f3ed54028163ef9121d/google/genai/models.py#L1195-L1214
|
|
214
|
+
config.update(
|
|
215
|
+
system_instruction=generation_config.get('system_instruction'),
|
|
216
|
+
tools=cast(list[ToolDict], generation_config.get('tools')),
|
|
217
|
+
# Annoyingly, GenerationConfigDict has fewer fields than GenerateContentConfigDict, and no extra fields are allowed.
|
|
218
|
+
generation_config=GenerationConfigDict(
|
|
219
|
+
temperature=generation_config.get('temperature'),
|
|
220
|
+
top_p=generation_config.get('top_p'),
|
|
221
|
+
max_output_tokens=generation_config.get('max_output_tokens'),
|
|
222
|
+
stop_sequences=generation_config.get('stop_sequences'),
|
|
223
|
+
presence_penalty=generation_config.get('presence_penalty'),
|
|
224
|
+
frequency_penalty=generation_config.get('frequency_penalty'),
|
|
225
|
+
thinking_config=generation_config.get('thinking_config'),
|
|
226
|
+
media_resolution=generation_config.get('media_resolution'),
|
|
227
|
+
response_mime_type=generation_config.get('response_mime_type'),
|
|
228
|
+
response_schema=generation_config.get('response_schema'),
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
response = await self.client.aio.models.count_tokens(
|
|
233
|
+
model=self._model_name,
|
|
234
|
+
contents=contents,
|
|
235
|
+
config=config,
|
|
236
|
+
)
|
|
237
|
+
if response.total_tokens is None:
|
|
238
|
+
raise UnexpectedModelBehavior( # pragma: no cover
|
|
239
|
+
'Total tokens missing from Gemini response', str(response)
|
|
240
|
+
)
|
|
241
|
+
return usage.Usage(
|
|
242
|
+
request_tokens=response.total_tokens,
|
|
243
|
+
total_tokens=response.total_tokens,
|
|
244
|
+
)
|
|
245
|
+
|
|
191
246
|
@asynccontextmanager
|
|
192
247
|
async def request_stream(
|
|
193
248
|
self,
|
|
@@ -265,16 +320,23 @@ class GoogleModel(Model):
|
|
|
265
320
|
model_settings: GoogleModelSettings,
|
|
266
321
|
model_request_parameters: ModelRequestParameters,
|
|
267
322
|
) -> GenerateContentResponse | Awaitable[AsyncIterator[GenerateContentResponse]]:
|
|
268
|
-
|
|
323
|
+
contents, config = await self._build_content_and_config(messages, model_settings, model_request_parameters)
|
|
324
|
+
func = self.client.aio.models.generate_content_stream if stream else self.client.aio.models.generate_content
|
|
325
|
+
return await func(model=self._model_name, contents=contents, config=config) # type: ignore
|
|
269
326
|
|
|
327
|
+
async def _build_content_and_config(
|
|
328
|
+
self,
|
|
329
|
+
messages: list[ModelMessage],
|
|
330
|
+
model_settings: GoogleModelSettings,
|
|
331
|
+
model_request_parameters: ModelRequestParameters,
|
|
332
|
+
) -> tuple[list[ContentUnionDict], GenerateContentConfigDict]:
|
|
333
|
+
tools = self._get_tools(model_request_parameters)
|
|
270
334
|
response_mime_type = None
|
|
271
335
|
response_schema = None
|
|
272
336
|
if model_request_parameters.output_mode == 'native':
|
|
273
337
|
if tools:
|
|
274
338
|
raise UserError('Gemini does not support structured output and tools at the same time.')
|
|
275
|
-
|
|
276
339
|
response_mime_type = 'application/json'
|
|
277
|
-
|
|
278
340
|
output_object = model_request_parameters.output_object
|
|
279
341
|
assert output_object is not None
|
|
280
342
|
response_schema = self._map_response_schema(output_object)
|
|
@@ -311,9 +373,7 @@ class GoogleModel(Model):
|
|
|
311
373
|
response_mime_type=response_mime_type,
|
|
312
374
|
response_schema=response_schema,
|
|
313
375
|
)
|
|
314
|
-
|
|
315
|
-
func = self.client.aio.models.generate_content_stream if stream else self.client.aio.models.generate_content
|
|
316
|
-
return await func(model=self._model_name, contents=contents, config=config) # type: ignore
|
|
376
|
+
return contents, config
|
|
317
377
|
|
|
318
378
|
def _process_response(self, response: GenerateContentResponse) -> ModelResponse:
|
|
319
379
|
if not response.candidates or len(response.candidates) != 1:
|
|
@@ -59,6 +59,11 @@ try:
|
|
|
59
59
|
from openai.types.chat.chat_completion_content_part_image_param import ImageURL
|
|
60
60
|
from openai.types.chat.chat_completion_content_part_input_audio_param import InputAudio
|
|
61
61
|
from openai.types.chat.chat_completion_content_part_param import File, FileFile
|
|
62
|
+
from openai.types.chat.chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall
|
|
63
|
+
from openai.types.chat.chat_completion_message_function_tool_call import ChatCompletionMessageFunctionToolCall
|
|
64
|
+
from openai.types.chat.chat_completion_message_function_tool_call_param import (
|
|
65
|
+
ChatCompletionMessageFunctionToolCallParam,
|
|
66
|
+
)
|
|
62
67
|
from openai.types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam
|
|
63
68
|
from openai.types.chat.completion_create_params import (
|
|
64
69
|
WebSearchOptions,
|
|
@@ -172,6 +177,14 @@ class OpenAIResponsesModelSettings(OpenAIModelSettings, total=False):
|
|
|
172
177
|
middle of the conversation.
|
|
173
178
|
"""
|
|
174
179
|
|
|
180
|
+
openai_text_verbosity: Literal['low', 'medium', 'high']
|
|
181
|
+
"""Constrains the verbosity of the model's text response.
|
|
182
|
+
|
|
183
|
+
Lower values will result in more concise responses, while higher values will
|
|
184
|
+
result in more verbose responses. Currently supported values are `low`,
|
|
185
|
+
`medium`, and `high`.
|
|
186
|
+
"""
|
|
187
|
+
|
|
175
188
|
|
|
176
189
|
@dataclass(init=False)
|
|
177
190
|
class OpenAIModel(Model):
|
|
@@ -416,7 +429,14 @@ class OpenAIModel(Model):
|
|
|
416
429
|
items.extend(split_content_into_text_and_thinking(choice.message.content, self.profile.thinking_tags))
|
|
417
430
|
if choice.message.tool_calls is not None:
|
|
418
431
|
for c in choice.message.tool_calls:
|
|
419
|
-
|
|
432
|
+
if isinstance(c, ChatCompletionMessageFunctionToolCall):
|
|
433
|
+
part = ToolCallPart(c.function.name, c.function.arguments, tool_call_id=c.id)
|
|
434
|
+
elif isinstance(c, ChatCompletionMessageCustomToolCall): # pragma: no cover
|
|
435
|
+
# NOTE: Custom tool calls are not supported.
|
|
436
|
+
# See <https://github.com/pydantic/pydantic-ai/issues/2513> for more details.
|
|
437
|
+
raise RuntimeError('Custom tool calls are not supported')
|
|
438
|
+
else:
|
|
439
|
+
assert_never(c)
|
|
420
440
|
part.tool_call_id = _guard_tool_call_id(part)
|
|
421
441
|
items.append(part)
|
|
422
442
|
return ModelResponse(
|
|
@@ -476,7 +496,7 @@ class OpenAIModel(Model):
|
|
|
476
496
|
openai_messages.append(item)
|
|
477
497
|
elif isinstance(message, ModelResponse):
|
|
478
498
|
texts: list[str] = []
|
|
479
|
-
tool_calls: list[
|
|
499
|
+
tool_calls: list[ChatCompletionMessageFunctionToolCallParam] = []
|
|
480
500
|
for item in message.parts:
|
|
481
501
|
if isinstance(item, TextPart):
|
|
482
502
|
texts.append(item.content)
|
|
@@ -507,8 +527,8 @@ class OpenAIModel(Model):
|
|
|
507
527
|
return openai_messages
|
|
508
528
|
|
|
509
529
|
@staticmethod
|
|
510
|
-
def _map_tool_call(t: ToolCallPart) ->
|
|
511
|
-
return
|
|
530
|
+
def _map_tool_call(t: ToolCallPart) -> ChatCompletionMessageFunctionToolCallParam:
|
|
531
|
+
return ChatCompletionMessageFunctionToolCallParam(
|
|
512
532
|
id=_guard_tool_call_id(t=t),
|
|
513
533
|
type='function',
|
|
514
534
|
function={'name': t.tool_name, 'arguments': t.args_as_json_str()},
|
|
@@ -807,6 +827,10 @@ class OpenAIResponsesModel(Model):
|
|
|
807
827
|
openai_messages.insert(0, responses.EasyInputMessageParam(role='system', content=instructions))
|
|
808
828
|
instructions = NOT_GIVEN
|
|
809
829
|
|
|
830
|
+
if verbosity := model_settings.get('openai_text_verbosity'):
|
|
831
|
+
text = text or {}
|
|
832
|
+
text['verbosity'] = verbosity
|
|
833
|
+
|
|
810
834
|
sampling_settings = (
|
|
811
835
|
model_settings
|
|
812
836
|
if OpenAIModelProfile.from_profile(self.profile).openai_supports_sampling_settings
|
|
@@ -96,6 +96,10 @@ class UsageLimits:
|
|
|
96
96
|
"""The maximum number of tokens allowed in responses from the model."""
|
|
97
97
|
total_tokens_limit: int | None = None
|
|
98
98
|
"""The maximum number of tokens allowed in requests and responses combined."""
|
|
99
|
+
count_tokens_before_request: bool = False
|
|
100
|
+
"""If True, perform a token counting pass before sending the request to the model,
|
|
101
|
+
to enforce `request_tokens_limit` ahead of time. This may incur additional overhead
|
|
102
|
+
(from calling the model's `count_tokens` API before making the actual request) and is disabled by default."""
|
|
99
103
|
|
|
100
104
|
def has_token_limits(self) -> bool:
|
|
101
105
|
"""Returns `True` if this instance places any limits on token counts.
|
|
@@ -111,11 +115,23 @@ class UsageLimits:
|
|
|
111
115
|
)
|
|
112
116
|
|
|
113
117
|
def check_before_request(self, usage: Usage) -> None:
|
|
114
|
-
"""Raises a `UsageLimitExceeded` exception if the next request would exceed the
|
|
118
|
+
"""Raises a `UsageLimitExceeded` exception if the next request would exceed any of the limits."""
|
|
115
119
|
request_limit = self.request_limit
|
|
116
120
|
if request_limit is not None and usage.requests >= request_limit:
|
|
117
121
|
raise UsageLimitExceeded(f'The next request would exceed the request_limit of {request_limit}')
|
|
118
122
|
|
|
123
|
+
request_tokens = usage.request_tokens or 0
|
|
124
|
+
if self.request_tokens_limit is not None and request_tokens > self.request_tokens_limit:
|
|
125
|
+
raise UsageLimitExceeded(
|
|
126
|
+
f'The next request would exceed the request_tokens_limit of {self.request_tokens_limit} ({request_tokens=})'
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
total_tokens = usage.total_tokens or 0
|
|
130
|
+
if self.total_tokens_limit is not None and total_tokens > self.total_tokens_limit:
|
|
131
|
+
raise UsageLimitExceeded(
|
|
132
|
+
f'The next request would exceed the total_tokens_limit of {self.total_tokens_limit} ({total_tokens=})'
|
|
133
|
+
)
|
|
134
|
+
|
|
119
135
|
def check_tokens(self, usage: Usage) -> None:
|
|
120
136
|
"""Raises a `UsageLimitExceeded` exception if the usage exceeds any of the token limits."""
|
|
121
137
|
request_tokens = usage.request_tokens or 0
|
|
@@ -62,7 +62,7 @@ dependencies = [
|
|
|
62
62
|
# WARNING if you add optional groups, please update docs/install.md
|
|
63
63
|
logfire = ["logfire>=3.14.1"]
|
|
64
64
|
# Models
|
|
65
|
-
openai = ["openai>=1.
|
|
65
|
+
openai = ["openai>=1.99.9"]
|
|
66
66
|
cohere = ["cohere>=5.16.0; platform_system != 'Emscripten'"]
|
|
67
67
|
vertexai = ["google-auth>=2.36.0", "requests>=2.32.2"]
|
|
68
68
|
google = ["google-genai>=1.28.0"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/__init__.py
RENAMED
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_logfire.py
RENAMED
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_mcp_server.py
RENAMED
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_model.py
RENAMED
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_run_context.py
RENAMED
|
File without changes
|
{pydantic_ai_slim-0.7.0 → pydantic_ai_slim-0.7.1}/pydantic_ai/durable_exec/temporal/_toolset.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|