pydantic-ai 0.0.48__tar.gz → 0.0.49__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 might be problematic. Click here for more details.

Files changed (141) hide show
  1. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/PKG-INFO +3 -3
  2. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_dataset.py +0 -4
  3. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_otel.py +40 -18
  4. pydantic_ai-0.0.49/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +63 -0
  5. pydantic_ai-0.0.49/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +62 -0
  6. pydantic_ai-0.0.49/tests/models/cassettes/test_openai_responses/test_openai_responses_model_builtin_tools.yaml +120 -0
  7. pydantic_ai-0.0.49/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_generate_summary.yaml +105 -0
  8. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_anthropic.py +5 -7
  9. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_openai_responses.py +61 -1
  10. pydantic_ai-0.0.49/tests/test_cli.py +191 -0
  11. pydantic_ai-0.0.48/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -340
  12. pydantic_ai-0.0.48/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -662
  13. pydantic_ai-0.0.48/tests/test_cli.py +0 -78
  14. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/.gitignore +0 -0
  15. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/LICENSE +0 -0
  16. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/Makefile +0 -0
  17. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/README.md +0 -0
  18. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/pyproject.toml +0 -0
  19. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/__init__.py +0 -0
  20. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/assets/dummy.pdf +0 -0
  21. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/assets/kiwi.png +0 -0
  22. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/assets/marcelo.mp3 +0 -0
  23. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
  24. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/conftest.py +0 -0
  25. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/__init__.py +0 -0
  26. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_evaluator_base.py +0 -0
  27. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_evaluator_common.py +0 -0
  28. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_evaluator_context.py +0 -0
  29. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_evaluator_spec.py +0 -0
  30. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_evaluators.py +0 -0
  31. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_llm_as_a_judge.py +0 -0
  32. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_render_numbers.py +0 -0
  33. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_reporting.py +0 -0
  34. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_reports.py +0 -0
  35. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/evals/test_utils.py +0 -0
  36. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/example_modules/README.md +0 -0
  37. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/example_modules/bank_database.py +0 -0
  38. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/example_modules/fake_database.py +0 -0
  39. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/example_modules/weather_service.py +0 -0
  40. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/__init__.py +0 -0
  41. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/test_file_persistence.py +0 -0
  42. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/test_graph.py +0 -0
  43. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/test_mermaid.py +0 -0
  44. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/test_persistence.py +0 -0
  45. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/test_state.py +0 -0
  46. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/graph/test_utils.py +0 -0
  47. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/import_examples.py +0 -0
  48. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/json_body_serializer.py +0 -0
  49. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/mcp_server.py +0 -0
  50. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/__init__.py +0 -0
  51. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
  52. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
  53. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
  54. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
  55. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
  56. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
  57. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
  58. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
  59. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
  60. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
  61. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_response.yaml +0 -0
  62. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
  63. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
  64. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
  65. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
  66. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
  67. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
  68. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +0 -0
  69. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
  70. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
  71. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
  72. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
  73. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
  74. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
  75. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
  76. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
  77. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +0 -0
  78. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +0 -0
  79. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +0 -0
  80. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_multiple_agent_tool_calls.yaml +0 -0
  81. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
  82. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
  83. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai/test_user_id.yaml +0 -0
  84. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_audio_as_binary_content_input.yaml +0 -0
  85. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_input.yaml +0 -0
  86. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_as_binary_content_input.yaml +0 -0
  87. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_url_input.yaml +0 -0
  88. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_image_url_input.yaml +0 -0
  89. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_http_error.yaml +0 -0
  90. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_retry.yaml +0 -0
  91. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response.yaml +0 -0
  92. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response_with_tool_call.yaml +0 -0
  93. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_effort.yaml +0 -0
  94. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_result_type.yaml +0 -0
  95. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_stream.yaml +0 -0
  96. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_system_prompt.yaml +0 -0
  97. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/cassettes/test_openai_responses/test_openai_responses_text_document_url_input.yaml +0 -0
  98. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/mock_async_stream.py +0 -0
  99. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_bedrock.py +0 -0
  100. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_cohere.py +0 -0
  101. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_fallback.py +0 -0
  102. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_gemini.py +0 -0
  103. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_groq.py +0 -0
  104. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_instrumented.py +0 -0
  105. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_mistral.py +0 -0
  106. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_model.py +0 -0
  107. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_model_function.py +0 -0
  108. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_model_names.py +0 -0
  109. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_model_test.py +0 -0
  110. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/models/test_openai.py +0 -0
  111. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/__init__.py +0 -0
  112. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
  113. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/cassettes/test_google_vertex/test_vertexai_provider.yaml +0 -0
  114. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_anthropic.py +0 -0
  115. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_azure.py +0 -0
  116. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_bedrock.py +0 -0
  117. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_cohere.py +0 -0
  118. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_deepseek.py +0 -0
  119. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_google_gla.py +0 -0
  120. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_google_vertex.py +0 -0
  121. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_groq.py +0 -0
  122. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_mistral.py +0 -0
  123. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_openai.py +0 -0
  124. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/providers/test_provider_names.py +0 -0
  125. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_agent.py +0 -0
  126. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_deps.py +0 -0
  127. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_examples.py +0 -0
  128. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_format_as_xml.py +0 -0
  129. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_json_body_serializer.py +0 -0
  130. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_live.py +0 -0
  131. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_logfire.py +0 -0
  132. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_mcp.py +0 -0
  133. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_messages.py +0 -0
  134. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_parts_manager.py +0 -0
  135. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_settings.py +0 -0
  136. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_streaming.py +0 -0
  137. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_tools.py +0 -0
  138. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_usage_limits.py +0 -0
  139. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/test_utils.py +0 -0
  140. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/typed_agent.py +0 -0
  141. {pydantic_ai-0.0.48 → pydantic_ai-0.0.49}/tests/typed_graph.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai
3
- Version: 0.0.48
3
+ Version: 0.0.49
4
4
  Summary: Agent Framework / shim to use Pydantic with LLMs
5
5
  Project-URL: Homepage, https://ai.pydantic.dev
6
6
  Project-URL: Source, https://github.com/pydantic/pydantic-ai
@@ -28,9 +28,9 @@ Classifier: Topic :: Internet
28
28
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
29
29
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
30
  Requires-Python: >=3.9
31
- Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cli,cohere,evals,groq,mcp,mistral,openai,vertexai]==0.0.48
31
+ Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cli,cohere,evals,groq,mcp,mistral,openai,vertexai]==0.0.49
32
32
  Provides-Extra: examples
33
- Requires-Dist: pydantic-ai-examples==0.0.48; extra == 'examples'
33
+ Requires-Dist: pydantic-ai-examples==0.0.49; extra == 'examples'
34
34
  Provides-Extra: logfire
35
35
  Requires-Dist: logfire>=3.11.0; extra == 'logfire'
36
36
  Description-Content-Type: text/markdown
@@ -164,16 +164,12 @@ async def test_add_evaluator(
164
164
  'cases': [
165
165
  {
166
166
  'evaluators': [{'Python': 'ctx.output == 2'}],
167
- 'expected_output': None,
168
167
  'inputs': {'query': 'What is 1+1?'},
169
- 'metadata': None,
170
168
  'name': 'My Case 1',
171
169
  },
172
170
  {
173
171
  'evaluators': [{'Python': 'ctx.output == 4'}],
174
- 'expected_output': None,
175
172
  'inputs': {'query': 'What is 2+2?'},
176
- 'metadata': None,
177
173
  'name': 'My Case 2',
178
174
  },
179
175
  ],
@@ -245,26 +245,26 @@ async def test_span_tree_repr(span_tree: SpanTree):
245
245
  """)
246
246
  assert span_tree.repr_xml(include_span_id=True) == snapshot("""\
247
247
  <SpanTree>
248
- <SpanNode name='root' span_id=0000000000000001 >
249
- <SpanNode name='child1' span_id=0000000000000003 >
250
- <SpanNode name='grandchild1' span_id=0000000000000005 />
251
- <SpanNode name='grandchild2' span_id=0000000000000007 />
248
+ <SpanNode name='root' span_id='0000000000000001' >
249
+ <SpanNode name='child1' span_id='0000000000000003' >
250
+ <SpanNode name='grandchild1' span_id='0000000000000005' />
251
+ <SpanNode name='grandchild2' span_id='0000000000000007' />
252
252
  </SpanNode>
253
- <SpanNode name='child2' span_id=0000000000000009 >
254
- <SpanNode name='grandchild3' span_id=000000000000000b />
253
+ <SpanNode name='child2' span_id='0000000000000009' >
254
+ <SpanNode name='grandchild3' span_id='000000000000000b' />
255
255
  </SpanNode>
256
256
  </SpanNode>
257
257
  </SpanTree>\
258
258
  """)
259
259
  assert span_tree.repr_xml(include_trace_id=True) == snapshot("""\
260
260
  <SpanTree>
261
- <SpanNode name='root' trace_id=00000000000000000000000000000001 >
262
- <SpanNode name='child1' trace_id=00000000000000000000000000000001 >
263
- <SpanNode name='grandchild1' trace_id=00000000000000000000000000000001 />
264
- <SpanNode name='grandchild2' trace_id=00000000000000000000000000000001 />
261
+ <SpanNode name='root' trace_id='00000000000000000000000000000001' >
262
+ <SpanNode name='child1' trace_id='00000000000000000000000000000001' >
263
+ <SpanNode name='grandchild1' trace_id='00000000000000000000000000000001' />
264
+ <SpanNode name='grandchild2' trace_id='00000000000000000000000000000001' />
265
265
  </SpanNode>
266
- <SpanNode name='child2' trace_id=00000000000000000000000000000001 >
267
- <SpanNode name='grandchild3' trace_id=00000000000000000000000000000001 />
266
+ <SpanNode name='child2' trace_id='00000000000000000000000000000001' >
267
+ <SpanNode name='grandchild3' trace_id='00000000000000000000000000000001' />
268
268
  </SpanNode>
269
269
  </SpanNode>
270
270
  </SpanTree>\
@@ -302,9 +302,9 @@ async def test_span_node_repr(span_tree: SpanTree):
302
302
  assert node is not None
303
303
 
304
304
  leaf_node = span_tree.first({'name_equals': 'grandchild1'})
305
- assert str(leaf_node) == snapshot("<SpanNode name='grandchild1' span_id=0000000000000005 />")
305
+ assert str(leaf_node) == snapshot("<SpanNode name='grandchild1' span_id='0000000000000005' />")
306
306
 
307
- assert str(node) == snapshot("<SpanNode name='child2' span_id=0000000000000009>...</SpanNode>")
307
+ assert str(node) == snapshot("<SpanNode name='child2' span_id='0000000000000009'>...</SpanNode>")
308
308
  assert repr(node) == snapshot("""\
309
309
  <SpanNode name='child2' >
310
310
  <SpanNode name='grandchild3' />
@@ -312,13 +312,13 @@ async def test_span_node_repr(span_tree: SpanTree):
312
312
  """)
313
313
  assert node.repr_xml(include_children=False) == snapshot("<SpanNode name='child2' children=... />")
314
314
  assert node.repr_xml(include_span_id=True) == snapshot("""\
315
- <SpanNode name='child2' span_id=0000000000000009 >
316
- <SpanNode name='grandchild3' span_id=000000000000000b />
315
+ <SpanNode name='child2' span_id='0000000000000009' >
316
+ <SpanNode name='grandchild3' span_id='000000000000000b' />
317
317
  </SpanNode>\
318
318
  """)
319
319
  assert node.repr_xml(include_trace_id=True) == snapshot("""\
320
- <SpanNode name='child2' trace_id=00000000000000000000000000000001 >
321
- <SpanNode name='grandchild3' trace_id=00000000000000000000000000000001 />
320
+ <SpanNode name='child2' trace_id='00000000000000000000000000000001' >
321
+ <SpanNode name='grandchild3' trace_id='00000000000000000000000000000001' />
322
322
  </SpanNode>\
323
323
  """)
324
324
  assert node.repr_xml(include_start_timestamp=True) == snapshot("""\
@@ -383,6 +383,17 @@ async def test_span_tree_ancestors_methods():
383
383
  assert not leaf_node.matches({'no_ancestor_has': {'name_matches_regex': 'root'}})
384
384
  assert leaf_node.matches({'no_ancestor_has': {'name_matches_regex': 'abc'}})
385
385
 
386
+ # Test stop_recursing_when:
387
+ assert not leaf_node.matches(
388
+ {'some_ancestor_has': {'name_equals': 'level1'}, 'stop_recursing_when': {'name_equals': 'level2'}}
389
+ )
390
+ assert leaf_node.matches(
391
+ {'all_ancestors_have': {'name_matches_regex': 'level'}, 'stop_recursing_when': {'name_equals': 'level1'}}
392
+ )
393
+ assert leaf_node.matches(
394
+ {'no_ancestor_has': {'name_matches_regex': 'root'}, 'stop_recursing_when': {'name_equals': 'level1'}}
395
+ )
396
+
386
397
 
387
398
  async def test_span_tree_descendants_methods():
388
399
  """Test the descendant traversal methods in SpanNode."""
@@ -462,6 +473,17 @@ async def test_span_tree_descendants_methods():
462
473
  assert leaf_node.matches(negated_descendant_query)
463
474
  assert leaf_node.matches({'no_descendant_has': {'has_attributes': {'depth': 4}}})
464
475
 
476
+ # Test stop_recursing_when:
477
+ assert not root_node.matches(
478
+ {'some_descendant_has': {'name_equals': 'leaf'}, 'stop_recursing_when': {'name_equals': 'level2'}}
479
+ )
480
+ assert root_node.matches(
481
+ {'all_descendants_have': {'has_attribute_keys': ['depth']}, 'stop_recursing_when': {'name_equals': 'level2'}}
482
+ )
483
+ assert root_node.matches(
484
+ {'no_descendant_has': {'name_equals': 'leaf'}, 'stop_recursing_when': {'name_equals': 'level3'}}
485
+ )
486
+
465
487
 
466
488
  async def test_log_levels_and_exceptions():
467
489
  """Test recording different log levels and exceptions in spans."""
@@ -0,0 +1,63 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '267'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.anthropic.com
16
+ method: POST
17
+ parsed_body:
18
+ max_tokens: 1024
19
+ messages:
20
+ - content:
21
+ - text: What is the main content on this document?
22
+ type: text
23
+ - source:
24
+ type: url
25
+ url: https://pdfobject.com/pdf/sample.pdf
26
+ type: document
27
+ role: user
28
+ model: claude-3-5-sonnet-latest
29
+ stream: false
30
+ uri: https://api.anthropic.com/v1/messages
31
+ response:
32
+ headers:
33
+ connection:
34
+ - keep-alive
35
+ content-length:
36
+ - '795'
37
+ content-type:
38
+ - application/json
39
+ transfer-encoding:
40
+ - chunked
41
+ parsed_body:
42
+ content:
43
+ - text: This document appears to be a sample PDF file that primarily contains Lorem ipsum text, which is placeholder
44
+ text commonly used in design and publishing. The document begins with "Sample PDF" and states "This is a simple
45
+ PDF file. Fun fun fun." followed by several paragraphs of Lorem ipsum text. The content doesn't convey any meaningful
46
+ information as Lorem ipsum is essentially dummy text used to demonstrate the visual form of a document without the
47
+ distraction of meaningful content.
48
+ type: text
49
+ id: msg_0146LphUoRKNWvDULHuTfu4H
50
+ model: claude-3-5-sonnet-20241022
51
+ role: assistant
52
+ stop_reason: end_turn
53
+ stop_sequence: null
54
+ type: message
55
+ usage:
56
+ cache_creation_input_tokens: 0
57
+ cache_read_input_tokens: 0
58
+ input_tokens: 2682
59
+ output_tokens: 96
60
+ status:
61
+ code: 200
62
+ message: OK
63
+ version: 1
@@ -0,0 +1,62 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '296'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.anthropic.com
16
+ method: POST
17
+ parsed_body:
18
+ max_tokens: 1024
19
+ messages:
20
+ - content:
21
+ - text: What is this vegetable?
22
+ type: text
23
+ - source:
24
+ type: url
25
+ url: https://t3.ftcdn.net/jpg/00/85/79/92/360_F_85799278_0BBGV9OAdQDTLnKwAPBCcg1J7QtiieJY.jpg
26
+ type: image
27
+ role: user
28
+ model: claude-3-5-haiku-latest
29
+ stream: false
30
+ uri: https://api.anthropic.com/v1/messages
31
+ response:
32
+ headers:
33
+ connection:
34
+ - keep-alive
35
+ content-length:
36
+ - '660'
37
+ content-type:
38
+ - application/json
39
+ transfer-encoding:
40
+ - chunked
41
+ parsed_body:
42
+ content:
43
+ - text: This is a potato. It's a yellow-brown, oblong-shaped potato with a smooth skin and some small eyes or blemishes
44
+ visible on its surface. Potatoes are starchy root vegetables that are a staple food in many cuisines around the
45
+ world. They can be prepared in numerous ways, such as boiling, baking, frying, or mashing, and are rich in carbohydrates
46
+ and nutrients.
47
+ type: text
48
+ id: msg_01WCNHqrhEGCXhoC4G1ojDHN
49
+ model: claude-3-5-haiku-20241022
50
+ role: assistant
51
+ stop_reason: end_turn
52
+ stop_sequence: null
53
+ type: message
54
+ usage:
55
+ cache_creation_input_tokens: 0
56
+ cache_read_input_tokens: 0
57
+ input_tokens: 296
58
+ output_tokens: 93
59
+ status:
60
+ code: 200
61
+ message: OK
62
+ version: 1
@@ -0,0 +1,120 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '217'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.openai.com
16
+ method: POST
17
+ parsed_body:
18
+ input:
19
+ - content: Give me the best news about LLMs from the last 24 hours. Be short.
20
+ role: user
21
+ instructions: ''
22
+ model: gpt-4o
23
+ stream: false
24
+ tool_choice: auto
25
+ tools:
26
+ - type: web_search_preview
27
+ uri: https://api.openai.com/v1/responses
28
+ response:
29
+ headers:
30
+ alt-svc:
31
+ - h3=":443"; ma=86400
32
+ connection:
33
+ - keep-alive
34
+ content-length:
35
+ - '3210'
36
+ content-type:
37
+ - application/json
38
+ openai-organization:
39
+ - pydantic-28gund
40
+ openai-processing-ms:
41
+ - '2843'
42
+ openai-version:
43
+ - '2020-10-01'
44
+ strict-transport-security:
45
+ - max-age=31536000; includeSubDomains; preload
46
+ transfer-encoding:
47
+ - chunked
48
+ parsed_body:
49
+ created_at: 1743506361
50
+ error: null
51
+ id: resp_67ebcbb93728819197f923ff16e98bce04f5055a2a33abc3
52
+ incomplete_details: null
53
+ instructions: ''
54
+ max_output_tokens: null
55
+ metadata: {}
56
+ model: gpt-4o-2024-08-06
57
+ object: response
58
+ output:
59
+ - id: ws_67ebcbb9ab4481918bebf63b66dbb67c04f5055a2a33abc3
60
+ status: completed
61
+ type: web_search_call
62
+ - content:
63
+ - annotations:
64
+ - end_index: 571
65
+ start_index: 404
66
+ title: OpenAI plans to release open-weight language model in coming months
67
+ type: url_citation
68
+ url: https://www.reuters.com/technology/artificial-intelligence/openai-plans-release-open-weight-language-model-coming-months-2025-03-31/?utm_source=openai
69
+ - end_index: 846
70
+ start_index: 625
71
+ title: OpenAI plans to release open-weight language model in coming months
72
+ type: url_citation
73
+ url: https://www.reuters.com/technology/artificial-intelligence/openai-plans-release-open-weight-language-model-coming-months-2025-03-31/?utm_source=openai
74
+ text: "In the past 24 hours, OpenAI announced plans to release its first open-weight language model with reasoning
75
+ capabilities since GPT-2. This model will allow developers to fine-tune it for specific applications without needing
76
+ the original training data. To gather feedback and refine the model, OpenAI will host developer events starting
77
+ in San Francisco and expanding to Europe and Asia-Pacific regions. ([reuters.com](https://www.reuters.com/technology/artificial-intelligence/openai-plans-release-open-weight-language-model-coming-months-2025-03-31/?utm_source=openai))\n\n\n##
78
+ OpenAI to Release Open-Weight Language Model:\n- [OpenAI plans to release open-weight language model in coming
79
+ months](https://www.reuters.com/technology/artificial-intelligence/openai-plans-release-open-weight-language-model-coming-months-2025-03-31/?utm_source=openai) "
80
+ type: output_text
81
+ id: msg_67ebcbbaf988819192d44919020b82e704f5055a2a33abc3
82
+ role: assistant
83
+ status: completed
84
+ type: message
85
+ parallel_tool_calls: true
86
+ previous_response_id: null
87
+ reasoning:
88
+ effort: null
89
+ generate_summary: null
90
+ status: completed
91
+ store: true
92
+ temperature: 1.0
93
+ text:
94
+ format:
95
+ type: text
96
+ tool_choice: auto
97
+ tools:
98
+ - search_context_size: medium
99
+ type: web_search_preview
100
+ user_location:
101
+ city: null
102
+ country: US
103
+ region: null
104
+ timezone: null
105
+ type: approximate
106
+ top_p: 1.0
107
+ truncation: disabled
108
+ usage:
109
+ input_tokens: 320
110
+ input_tokens_details:
111
+ cached_tokens: 0
112
+ output_tokens: 200
113
+ output_tokens_details:
114
+ reasoning_tokens: 0
115
+ total_tokens: 520
116
+ user: null
117
+ status:
118
+ code: 200
119
+ message: OK
120
+ version: 1
@@ -0,0 +1,105 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '218'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.openai.com
16
+ method: POST
17
+ parsed_body:
18
+ input:
19
+ - content: What should I do to cross the street?
20
+ role: user
21
+ instructions: ''
22
+ model: computer-use-preview
23
+ reasoning:
24
+ effort: null
25
+ generate_summary: concise
26
+ stream: false
27
+ truncation: auto
28
+ uri: https://api.openai.com/v1/responses
29
+ response:
30
+ headers:
31
+ alt-svc:
32
+ - h3=":443"; ma=86400
33
+ connection:
34
+ - keep-alive
35
+ content-length:
36
+ - '1976'
37
+ content-type:
38
+ - application/json
39
+ openai-organization:
40
+ - pydantic-28gund
41
+ openai-processing-ms:
42
+ - '2793'
43
+ openai-version:
44
+ - '2020-10-01'
45
+ strict-transport-security:
46
+ - max-age=31536000; includeSubDomains; preload
47
+ transfer-encoding:
48
+ - chunked
49
+ parsed_body:
50
+ created_at: 1743514924
51
+ error: null
52
+ id: resp_67ebed2c92cc81919d2fe37992a6a78b0e0766e7260ad9b9
53
+ incomplete_details: null
54
+ instructions: ''
55
+ max_output_tokens: null
56
+ metadata: {}
57
+ model: computer-use-preview-2025-03-11
58
+ object: response
59
+ output:
60
+ - content:
61
+ - annotations: []
62
+ text: |-
63
+ To cross the street safely, follow these steps:
64
+
65
+ 1. **Use a Crosswalk**: Always use a designated crosswalk or pedestrian crossing whenever available.
66
+ 2. **Press the Button**: If there is a pedestrian signal button, press it and wait for the signal.
67
+ 3. **Look Both Ways**: Look left, right, and left again before stepping off the curb.
68
+ 4. **Wait for the Signal**: Cross only when the pedestrian signal indicates it is safe to do so or when there is a clear gap in traffic.
69
+ 5. **Stay Alert**: Be mindful of turning vehicles and stay attentive while crossing.
70
+ 6. **Walk, Don't Run**: Walk across the street; running can increase the risk of falling or not noticing an oncoming vehicle.
71
+
72
+ Always follow local traffic rules and be cautious, even when crossing at a crosswalk. Safety is the priority.
73
+ type: output_text
74
+ id: msg_67ebed2db814819195e77f3dd1f057640e0766e7260ad9b9
75
+ role: assistant
76
+ status: completed
77
+ type: message
78
+ parallel_tool_calls: true
79
+ previous_response_id: null
80
+ reasoning:
81
+ effort: medium
82
+ generate_summary: concise
83
+ status: completed
84
+ store: true
85
+ temperature: 1.0
86
+ text:
87
+ format:
88
+ type: text
89
+ tool_choice: auto
90
+ tools: []
91
+ top_p: 1.0
92
+ truncation: auto
93
+ usage:
94
+ input_tokens: 15
95
+ input_tokens_details:
96
+ cached_tokens: 0
97
+ output_tokens: 180
98
+ output_tokens_details:
99
+ reasoning_tokens: 0
100
+ total_tokens: 195
101
+ user: null
102
+ status:
103
+ code: 200
104
+ message: OK
105
+ version: 1
@@ -566,11 +566,9 @@ async def test_image_url_input(allow_model_requests: None, anthropic_api_key: st
566
566
  ImageUrl(url='https://t3.ftcdn.net/jpg/00/85/79/92/360_F_85799278_0BBGV9OAdQDTLnKwAPBCcg1J7QtiieJY.jpg'),
567
567
  ]
568
568
  )
569
- assert result.data == snapshot("""\
570
- This is a potato. It's a yellow-skinned potato with a somewhat oblong or oval shape. The surface is covered in small eyes or dimples, which is typical of potato skin. The color is a golden-yellow, and the potato appears to be clean and fresh, photographed against a white background.
571
-
572
- Potatoes are root vegetables that are staple foods in many cuisines around the world. They can be prepared in numerous ways such as boiling, baking, roasting, frying, or mashing. This particular potato looks like it could be a Yukon Gold or a similar yellow-fleshed variety.\
573
- """)
569
+ assert result.data == snapshot(
570
+ "This is a potato. It's a yellow-brown, oblong-shaped potato with a smooth skin and some small eyes or blemishes visible on its surface. Potatoes are starchy root vegetables that are a staple food in many cuisines around the world. They can be prepared in numerous ways, such as boiling, baking, frying, or mashing, and are rich in carbohydrates and nutrients."
571
+ )
574
572
 
575
573
 
576
574
  @pytest.mark.vcr()
@@ -639,11 +637,11 @@ async def test_document_url_input(allow_model_requests: None, anthropic_api_key:
639
637
  m = AnthropicModel('claude-3-5-sonnet-latest', provider=AnthropicProvider(api_key=anthropic_api_key))
640
638
  agent = Agent(m)
641
639
 
642
- document_url = DocumentUrl(url='https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf')
640
+ document_url = DocumentUrl(url='https://pdfobject.com/pdf/sample.pdf')
643
641
 
644
642
  result = await agent.run(['What is the main content on this document?', document_url])
645
643
  assert result.data == snapshot(
646
- 'The document appears to be a simple PDF file with only the text "Dummy PDF file" displayed at the top. It seems to be a blank or template document with minimal content.'
644
+ 'This document appears to be a sample PDF file that primarily contains Lorem ipsum text, which is placeholder text commonly used in design and publishing. The document begins with "Sample PDF" and states "This is a simple PDF file. Fun fun fun." followed by several paragraphs of Lorem ipsum text. The content doesn\'t convey any meaningful information as Lorem ipsum is essentially dummy text used to demonstrate the visual form of a document without the distraction of meaningful content.'
647
645
  )
648
646
 
649
647
 
@@ -22,7 +22,7 @@ from pydantic_ai.messages import (
22
22
  from ..conftest import IsDatetime, IsStr, TestEnv, try_import
23
23
 
24
24
  with try_import() as imports_successful:
25
- from pydantic_ai.models.openai import OpenAIModelSettings, OpenAIResponsesModel
25
+ from pydantic_ai.models.openai import OpenAIModelSettings, OpenAIResponsesModel, OpenAIResponsesModelSettings
26
26
  from pydantic_ai.providers.openai import OpenAIProvider
27
27
 
28
28
  pytestmark = [
@@ -118,6 +118,30 @@ async def test_openai_responses_reasoning_effort(allow_model_requests: None, ope
118
118
  )
119
119
 
120
120
 
121
+ async def test_openai_responses_reasoning_generate_summary(allow_model_requests: None, openai_api_key: str):
122
+ model = OpenAIResponsesModel('computer-use-preview', provider=OpenAIProvider(api_key=openai_api_key))
123
+ agent = Agent(
124
+ model=model,
125
+ model_settings=OpenAIResponsesModelSettings(
126
+ openai_reasoning_generate_summary='concise',
127
+ openai_truncation='auto',
128
+ ),
129
+ )
130
+ result = await agent.run('What should I do to cross the street?')
131
+ assert result.data == snapshot("""\
132
+ To cross the street safely, follow these steps:
133
+
134
+ 1. **Use a Crosswalk**: Always use a designated crosswalk or pedestrian crossing whenever available.
135
+ 2. **Press the Button**: If there is a pedestrian signal button, press it and wait for the signal.
136
+ 3. **Look Both Ways**: Look left, right, and left again before stepping off the curb.
137
+ 4. **Wait for the Signal**: Cross only when the pedestrian signal indicates it is safe to do so or when there is a clear gap in traffic.
138
+ 5. **Stay Alert**: Be mindful of turning vehicles and stay attentive while crossing.
139
+ 6. **Walk, Don't Run**: Walk across the street; running can increase the risk of falling or not noticing an oncoming vehicle.
140
+
141
+ Always follow local traffic rules and be cautious, even when crossing at a crosswalk. Safety is the priority.\
142
+ """)
143
+
144
+
121
145
  async def test_openai_responses_system_prompt(allow_model_requests: None, openai_api_key: str):
122
146
  model = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
123
147
  agent = Agent(model=model, system_prompt='You are a helpful assistant.')
@@ -286,3 +310,39 @@ async def test_openai_responses_model_http_error(allow_model_requests: None, ope
286
310
  with pytest.raises(ModelHTTPError):
287
311
  async with agent.run_stream('What is the capital of France?'):
288
312
  ...
313
+
314
+
315
+ async def test_openai_responses_model_builtin_tools(allow_model_requests: None, openai_api_key: str):
316
+ model = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
317
+ settings = OpenAIResponsesModelSettings(openai_builtin_tools=[{'type': 'web_search_preview'}])
318
+ agent = Agent(model=model, model_settings=settings)
319
+ result = await agent.run('Give me the best news about LLMs from the last 24 hours. Be short.')
320
+
321
+ # NOTE: We don't have the tool call because OpenAI calls the tool internally.
322
+ assert result.all_messages() == snapshot(
323
+ [
324
+ ModelRequest(
325
+ parts=[
326
+ UserPromptPart(
327
+ content='Give me the best news about LLMs from the last 24 hours. Be short.',
328
+ timestamp=IsDatetime(),
329
+ )
330
+ ]
331
+ ),
332
+ ModelResponse(
333
+ parts=[
334
+ TextPart(
335
+ content="""\
336
+ In the past 24 hours, OpenAI announced plans to release its first open-weight language model with reasoning capabilities since GPT-2. This model will allow developers to fine-tune it for specific applications without needing the original training data. To gather feedback and refine the model, OpenAI will host developer events starting in San Francisco and expanding to Europe and Asia-Pacific regions. ([reuters.com](https://www.reuters.com/technology/artificial-intelligence/openai-plans-release-open-weight-language-model-coming-months-2025-03-31/?utm_source=openai))
337
+
338
+
339
+ ## OpenAI to Release Open-Weight Language Model:
340
+ - [OpenAI plans to release open-weight language model in coming months](https://www.reuters.com/technology/artificial-intelligence/openai-plans-release-open-weight-language-model-coming-months-2025-03-31/?utm_source=openai) \
341
+ """
342
+ )
343
+ ],
344
+ model_name='gpt-4o-2024-08-06',
345
+ timestamp=IsDatetime(),
346
+ ),
347
+ ]
348
+ )