Skip to content

Support for Pydantic Validators #151

@skylarbpayne

Description

@skylarbpayne

Pydantic Validators subtly do not work.

Example

import os
from typing import cast

import logfire
from pydantic import BaseModel, field_validator
from pydantic_ai import Agent
from pydantic_ai.models import KnownModelName

# 'if-token-present' means nothing will be sent (and the example will work) if you don't have logfire configured
logfire.configure(send_to_logfire='if-token-present')


class Location(BaseModel):
    city: str
    country: str

    @field_validator('city')
    @classmethod
    def validate_city(cls, v):
        # Using a validator that always raises a validation error
        raise ValueError("This model will always raise a validation error")

model = cast(KnownModelName, os.getenv('PYDANTIC_AI_MODEL', 'openai:gpt-4o-mini'))
print(f'Using model: {model}')
agent = Agent(model, result_type=Location)

if __name__ == '__main__':
    result = agent.run_sync('barren wasteland?')
    print(result.data)
    print(result.cost())

Error:

Traceback (most recent call last):
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/Users/skylarbpayne/projects/email-reply-agent/minimal_example.py", line 33, in <module>
    result = agent.run_sync('barren wasteland?')
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/agent.py", line 226, in run_sync
    return loop.run_until_complete(self.run(user_prompt, message_history=message_history, model=model, deps=deps))
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/agent.py", line 177, in run
    model_response, request_cost = await agent_model.request(messages)
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/models/openai.py", line 132, in request
    response = await self._completions_create(messages, False)
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/models/openai.py", line 162, in _completions_create
    openai_messages = [self._map_message(m) for m in messages]
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/models/openai.py", line 162, in <listcomp>
    openai_messages = [self._map_message(m) for m in messages]
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/models/openai.py", line 243, in _map_message
    content=message.model_response(),
  File "/Users/skylarbpayne/projects/email-reply-agent/.venv/lib/python3.10/site-packages/pydantic_ai/messages.py", line 112, in model_response
    description = f'{len(self.content)} validation errors: {json.dumps(self.content, indent=2)}'
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 201, in encode
    chunks = list(chunks)
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 429, in _iterencode
    yield from _iterencode_list(o, _current_indent_level)
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 325, in _iterencode_list
    yield from chunks
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 438, in _iterencode
    o = _default(o)
  File "/Users/skylarbpayne/.local/share/uv/python/cpython-3.10.14-macos-aarch64-none/lib/python3.10/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ValueError is not JSON serializable

Environment

python: 3.10.14

uv pip freeze:

aiofiles==24.1.0
aiosqlite==0.20.0
annotated-types==0.7.0
anyio==4.6.2.post1
appnope==0.1.4
asgi-csrf==0.11
asgiref==3.8.1
asttokens==2.4.1
asynciolimiter==1.0.0
cachetools==5.5.0
certifi==2024.8.30
cfgv==3.4.0
chardet==5.2.0
charset-normalizer==3.4.0
click==8.1.7
click-default-group==1.2.4
colorama==0.4.6
comm==0.2.2
datasette==0.65.1
debugpy==1.8.9
decorator==5.1.1
deprecated==1.2.15
devtools==0.12.2
distlib==0.3.9
distro==1.9.0
-e file:///Users/skylarbpayne/projects/email-reply-agent
eval-type-backport==0.2.0
exceptiongroup==1.2.2
executing==2.1.0
filelock==3.16.1
flexcache==0.3
flexparser==0.4
google-api-core==2.23.0
google-api-python-client==2.154.0
google-auth==2.36.0
google-auth-httplib2==0.2.0
google-auth-oauthlib==1.2.1
googleapis-common-protos==1.66.0
greenlet==3.1.1
griffe==1.5.1
groq==0.13.0
h11==0.14.0
html2text==2024.2.26
httpcore==1.0.7
httplib2==0.22.0
httpx==0.28.0
hupper==1.12.1
identify==2.6.3
idna==3.10
importlib-metadata==8.5.0
iniconfig==2.0.0
ipdb==0.13.13
ipykernel==6.29.5
ipython==8.18.1
itsdangerous==2.2.0
janus==1.1.0
jedi==0.19.2
jinja2==3.1.4
jiter==0.8.0
jupyter-client==8.6.3
jupyter-core==5.7.2
logfire==2.6.0
logfire-api==2.6.0
markdown-it-py==3.0.0
markupsafe==3.0.2
matplotlib-inline==0.1.7
mdurl==0.1.2
mergedeep==1.3.4
mypy==1.13.0
mypy-extensions==1.0.0
nest-asyncio==1.6.0
nodeenv==1.9.1
oauthlib==3.2.2
openai==1.56.2
opentelemetry-api==1.28.2
opentelemetry-exporter-otlp-proto-common==1.28.2
opentelemetry-exporter-otlp-proto-http==1.28.2
opentelemetry-instrumentation==0.49b2
opentelemetry-proto==1.28.2
opentelemetry-sdk==1.28.2
opentelemetry-semantic-conventions==0.49b2
packaging==24.2
parso==0.8.4
pexpect==4.9.0
pip==24.3.1
platformdirs==4.3.6
pluggy==1.5.0
pre-commit==4.0.1
prompt-toolkit==3.0.48
proto-plus==1.25.0
protobuf==5.29.1
psutil==6.1.0
ptyprocess==0.7.0
pure-eval==0.2.3
pyasn1==0.6.1
pyasn1-modules==0.4.1
pydantic==2.10.3
pydantic-ai==0.0.9
pydantic-ai-slim==0.0.9
pydantic-core==2.27.1
pydantic-settings==2.6.1
pygments==2.18.0
pyparsing==3.2.0
pyproject-api==1.8.0
pytest==8.3.4
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
python-multipart==0.0.19
pyyaml==6.0.2
pyzmq==26.2.0
requests==2.32.3
requests-oauthlib==2.0.0
rich==13.9.4
rsa==4.9
ruff==0.8.1
setuptools==75.6.0
six==1.17.0
sniffio==1.3.1
sqlalchemy==2.0.36
sqlmodel==0.0.22
stack-data==0.6.3
tomli==2.2.1
tornado==6.4.2
tox==4.23.2
tox-uv==1.16.0
tqdm==4.67.1
traitlets==5.14.3
typing-extensions==4.12.2
uritemplate==4.1.1
urllib3==2.2.3
uv==0.5.6
uvicorn==0.32.1
virtualenv==20.28.0
wcwidth==0.2.13
wrapt==1.17.0
zipp==3.21.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions