Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
a5ae245
add sanitization to auto-generated output tool name
lionpeloux Sep 22, 2025
6322b70
Update `pyproject.toml` to be PEP639 compliant (#3001)
Kludex Sep 25, 2025
e1784e6
Fix OpenAI docs code example (#3003)
DouweM Sep 25, 2025
60dd3de
Add `operation.cost` metric to instrumented models (#3013)
alexmojaki Sep 26, 2025
86bc16e
docs: replace "customisation" by "customization" (#3019)
Kludex Sep 29, 2025
a19a96d
move `OutputObjectDefinition` from private `_output.py` to public `ou…
moritzwilksch Sep 29, 2025
edf4d2d
Raise error when using Anthropic thinking with output tools (#3025)
DouweM Sep 29, 2025
79a889c
Bump temporalio to 1.18.0 (#3027)
DouweM Sep 29, 2025
329fc47
Bump genai-prices to 0.0.28 (#3030)
DouweM Sep 29, 2025
2f77d3d
Document that Gemini requires native or prompted output mode for stru…
DouweM Sep 29, 2025
c7fdf01
Update Ollama docs instructions (#2993)
slkoo-cc Sep 29, 2025
9804c42
Fix streaming gpt-oss using Ollama (#3035)
DouweM Sep 29, 2025
d0d28c3
Add `claude-sonnet-4-5` to known model names (#3033)
DouweM Sep 29, 2025
e803828
Update docs and tests for DBOS v2.0 (#3004)
qianl15 Sep 29, 2025
f124391
Support callable classes as history processors (#2988)
bitnahian Sep 29, 2025
e92d59b
Support OpenAI image detail on `ImageUrl` and `BinaryContent` via `ve…
moritzwilksch Sep 29, 2025
774e266
Expose `.messages`, `.toolsets` types in top-level `pydantic_ai` (#2957)
moritzwilksch Sep 30, 2025
e0d8321
Add cost metric to the pydantic-evals output (#2984)
dmontagu Sep 30, 2025
78dafbe
Don't error when Gemini returns more than one candidate response (#3043)
DouweM Sep 30, 2025
9b9337f
Add retry args to `pydantic_evals.Dataset.evaluate_sync` (#3022)
m7mdhka Sep 30, 2025
0074eb5
Change type of common_tools to work with agent with any deps type (#3…
lukekh Sep 30, 2025
8cfe803
Support Anthropic built-in memory tool (#3042)
DouweM Sep 30, 2025
8a9e197
Handle Ollama responses without finish_reason (#3045)
DouweM Sep 30, 2025
b09660a
Don't run deploy-docs-preview after a PR is merged to main (#3046)
DouweM Sep 30, 2025
702aab1
Don't run deploy-docs-preview if the event does not have PRs (#3047)
DouweM Sep 30, 2025
50a4aab
Prefer `structuredContent` MCP tool result when present (#2854)
shaheerzaman Sep 30, 2025
4a02de8
Support text, JSON, XML and YAML `DocumentUrl` and `BinaryContent` on…
pulphix Sep 30, 2025
ed10312
Add custom Vertex AI Model Garden example (#2868)
stefannae Sep 30, 2025
444943b
Fix MCP client documentation inconsistencies (#2357)
DouweM Sep 30, 2025
c94f53d
Fix `StructuredDict` with nested JSON schemas using `$ref` (#2570)
ChiaXinLiang Sep 30, 2025
0ce3b36
Raise error when StructuredDict is used with recursive JSON schema re…
DouweM Oct 1, 2025
1613607
feat: Otel instrumentation version 3 (#3021)
bitnahian Oct 1, 2025
39845f0
Don't send strict to HuggingFace API as it's not supported by their t…
DouweM Oct 1, 2025
1504e18
Correctly merge `Model.settings` with `model_settings` in direct mode…
moritzwilksch Oct 1, 2025
f3e0715
Expose `server_info` in `MCPServer` (#3055)
Whadup Oct 1, 2025
06008dc
Support contextually overriding agent instructions (#2926)
mwildehahn Oct 1, 2025
444e7f4
Update evals attributes (#2924)
dmontagu Oct 1, 2025
570a4e1
Remove leftover debug `print` statement (#3070)
KostyaGukish Oct 2, 2025
49bd52e
Fix duplicate output tool return part when concatenating first run me…
DouweM Oct 2, 2025
9429dc9
Support enums in format_as_xml (#3064)
DouweM Oct 3, 2025
7a4d6ba
add latest gemini 2.5 flash(-lite) model names and aliases (#3060)
moritzwilksch Oct 3, 2025
ad2f2da
Fix dataset serialization when inputs have discriminators with defaul…
DouweM Oct 3, 2025
2d0ba24
Support image generation and output with Google and OpenAI (#2970)
DouweM Oct 3, 2025
a9fd3e2
Fix parallel tool call limit enforcement (#2978)
tradeqvest Oct 3, 2025
d51353e
Set `MCPServer` `id` and `tool_prefix` in `load_mcp_servers` (#3052)
DouweM Oct 3, 2025
6c02bc9
Add content (e.g. files) returned by tool to `FunctionToolResultEvent…
DouweM Oct 3, 2025
1b974ea
Add `Agent.run_stream_events()` convenience method wrapping `run(even…
DouweM Oct 3, 2025
a53d87a
Change 'Join Slack' link to direct link (#3085)
DouweM Oct 3, 2025
b445f0b
Fix tool name sanitization to preserve uppercase letters
lionpeloux Oct 14, 2025
3099d13
Merge upstream/main into pr-tool-output-naming
lionpeloux Oct 14, 2025
a854960
Add sanitization to auto-generated output tool name
lionpeloux Oct 23, 2025
f16acbc
Merge remote-tracking branch 'upstream/main' into pr-tool-output-naming
lionpeloux Oct 23, 2025
6315c3b
Refactor test to use inline code instead of create_module
lionpeloux Oct 23, 2025
2a7cdb5
Merge remote-tracking branch 'upstream/main' into pr-tool-output-naming
lionpeloux Oct 23, 2025
32f37cc
Define test classes at top of file instead of inline
lionpeloux Oct 24, 2025
a8d8c78
Merge remote-tracking branch 'upstream/main' into pr-tool-output-naming
lionpeloux Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pydantic_ai_slim/pydantic_ai/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import inspect
import json
import re
from abc import ABC, abstractmethod
from collections.abc import Awaitable, Callable, Sequence
from dataclasses import dataclass, field
Expand Down Expand Up @@ -70,6 +71,7 @@

DEFAULT_OUTPUT_TOOL_NAME = 'final_result'
DEFAULT_OUTPUT_TOOL_DESCRIPTION = 'The final response which ends this conversation'
OUTPUT_TOOL_NAME_SANITIZER = re.compile(r'[^a-zA-Z0-9-_]')


async def execute_traced_output_function(
Expand Down Expand Up @@ -997,7 +999,9 @@ def build(
if name is None:
name = default_name
if multiple:
name += f'_{object_def.name}'
# strip unsupported characters like "[" and "]" from generic class names
safe_name = OUTPUT_TOOL_NAME_SANITIZER.sub('', object_def.name or '')
name += f'_{safe_name}'

i = 1
original_name = name
Expand Down
35 changes: 34 additions & 1 deletion tests/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from collections.abc import AsyncIterable, Callable
from dataclasses import dataclass, replace
from datetime import timezone
from typing import Any, Literal, Union
from typing import Any, Generic, Literal, TypeVar, Union

import httpx
import pytest
Expand Down Expand Up @@ -96,6 +96,21 @@ class Person(BaseModel):
name: str


# Generic classes for testing tool name sanitization with generic types
T = TypeVar('T')


class ResultGeneric(BaseModel, Generic[T]):
"""A generic result class."""

value: T
success: bool


class StringData(BaseModel):
text: str


def test_result_list_of_models_with_stringified_response():
def return_list(_: list[ModelMessage], info: AgentInfo) -> ModelResponse:
assert info.output_tools is not None
Expand Down Expand Up @@ -635,6 +650,24 @@ def validate_output(ctx: RunContext[None], o: Any) -> Any:
assert got_tool_call_name == snapshot('final_result_Bar')


def test_output_type_generic_class_name_sanitization():
"""Test that generic class names with brackets are properly sanitized."""
# This will have a name like "ResultGeneric[StringData]" which needs sanitization
output_type = [ResultGeneric[StringData], ResultGeneric[int]]

m = TestModel()
agent = Agent(m, output_type=output_type)
agent.run_sync('Hello')

# The sanitizer should remove brackets from the generic type name
assert m.last_model_request_parameters is not None
assert m.last_model_request_parameters.output_tools is not None
assert len(m.last_model_request_parameters.output_tools) == 2

tool_names = [tool.name for tool in m.last_model_request_parameters.output_tools]
assert tool_names == snapshot(['final_result_ResultGenericStringData', 'final_result_ResultGenericint'])


def test_output_type_with_two_descriptions():
class MyOutput(BaseModel):
"""Description from docstring"""
Expand Down