Skip to content

Commit b6f213b

Browse files
committed
fix validation errors serialization
1 parent 6201af4 commit b6f213b

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

pydantic_ai_slim/pydantic_ai/messages.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations as _annotations
22

3-
import json
43
from dataclasses import dataclass, field
54
from datetime import datetime
65
from typing import Annotated, Any, Literal, Union
@@ -74,6 +73,9 @@ def model_response_object(self) -> dict[str, Any]:
7473
return {'return_value': tool_return_ta.dump_python(self.content, mode='json')}
7574

7675

76+
ErrorDetailsTa = _pydantic.LazyTypeAdapter(list[pydantic_core.ErrorDetails])
77+
78+
7779
@dataclass
7880
class RetryPrompt:
7981
"""A message back to a model asking it to try again.
@@ -109,7 +111,8 @@ def model_response(self) -> str:
109111
if isinstance(self.content, str):
110112
description = self.content
111113
else:
112-
description = f'{len(self.content)} validation errors: {json.dumps(self.content, indent=2)}'
114+
json_errors = ErrorDetailsTa.dump_json(self.content, exclude={'__all__': {'ctx'}}, indent=2)
115+
description = f'{len(self.content)} validation errors: {json_errors.decode()}'
113116
return f'{description}\n\nFix the errors and try again.'
114117

115118

tests/test_agent.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66
from inline_snapshot import snapshot
7-
from pydantic import BaseModel
7+
from pydantic import BaseModel, field_validator
88

99
from pydantic_ai import Agent, ModelRetry, RunContext, UnexpectedModelBehavior, UserError
1010
from pydantic_ai.messages import (
@@ -105,6 +105,50 @@ def return_model(messages: list[Message], info: AgentInfo) -> ModelAnyResponse:
105105
assert result.all_messages_json().startswith(b'[{"content":"Hello"')
106106

107107

108+
def test_result_pydantic_model_validation_error(set_event_loop: None):
109+
def return_model(messages: list[Message], info: AgentInfo) -> ModelAnyResponse:
110+
assert info.result_tools is not None
111+
if len(messages) == 1:
112+
args_json = '{"a": 1, "b": "foo"}'
113+
else:
114+
args_json = '{"a": 1, "b": "bar"}'
115+
return ModelStructuredResponse(calls=[ToolCall.from_json(info.result_tools[0].name, args_json)])
116+
117+
class Bar(BaseModel):
118+
a: int
119+
b: str
120+
121+
@field_validator('b')
122+
def check_b(cls, v: str) -> str:
123+
if v == 'foo':
124+
raise ValueError('must not be foo')
125+
return v
126+
127+
agent = Agent(FunctionModel(return_model), result_type=Bar)
128+
129+
result = agent.run_sync('Hello')
130+
assert isinstance(result.data, Bar)
131+
assert result.data.model_dump() == snapshot({'a': 1, 'b': 'bar'})
132+
message_roles = [m.role for m in result.all_messages()]
133+
assert message_roles == snapshot(['user', 'model-structured-response', 'retry-prompt', 'model-structured-response'])
134+
135+
retry_prompt = result.all_messages()[2]
136+
assert isinstance(retry_prompt, RetryPrompt)
137+
assert retry_prompt.model_response() == snapshot("""\
138+
1 validation errors: [
139+
{
140+
"type": "value_error",
141+
"loc": [
142+
"b"
143+
],
144+
"msg": "Value error, must not be foo",
145+
"input": "foo"
146+
}
147+
]
148+
149+
Fix the errors and try again.""")
150+
151+
108152
def test_result_validator(set_event_loop: None):
109153
def return_model(messages: list[Message], info: AgentInfo) -> ModelAnyResponse:
110154
assert info.result_tools is not None

0 commit comments

Comments
 (0)