pydantic-ai 0.2.19__tar.gz → 0.2.20__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 (264) hide show
  1. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/PKG-INFO +3 -3
  2. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/mcp_server.py +18 -0
  3. pydantic_ai-0.2.20/tests/models/cassettes/test_google/test_google_timeout.yaml +64 -0
  4. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_google.py +12 -1
  5. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_history_processor.py +100 -0
  6. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_mcp.py +49 -5
  7. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/.gitignore +0 -0
  8. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/LICENSE +0 -0
  9. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/Makefile +0 -0
  10. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/README.md +0 -0
  11. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/pyproject.toml +0 -0
  12. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/__init__.py +0 -0
  13. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/assets/dummy.pdf +0 -0
  14. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/assets/kiwi.png +0 -0
  15. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/assets/marcelo.mp3 +0 -0
  16. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/assets/small_video.mp4 +0 -0
  17. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
  18. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_audio_resource.yaml +0 -0
  19. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_dict.yaml +0 -0
  20. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_error.yaml +0 -0
  21. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_image.yaml +0 -0
  22. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_image_resource.yaml +0 -0
  23. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_multiple_items.yaml +0 -0
  24. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_none.yaml +0 -0
  25. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_str.yaml +0 -0
  26. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_mcp/test_tool_returning_text_resource.yaml +0 -0
  27. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[anthropic].yaml +0 -0
  28. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[bedrock].yaml +0 -0
  29. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[cohere].yaml +0 -0
  30. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[gemini].yaml +0 -0
  31. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[google].yaml +0 -0
  32. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[groq].yaml +0 -0
  33. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[mistral].yaml +0 -0
  34. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/cassettes/test_settings/test_stop_settings[openai].yaml +0 -0
  35. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/conftest.py +0 -0
  36. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/__init__.py +0 -0
  37. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_dataset.py +0 -0
  38. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_evaluator_base.py +0 -0
  39. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_evaluator_common.py +0 -0
  40. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_evaluator_context.py +0 -0
  41. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_evaluator_spec.py +0 -0
  42. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_evaluators.py +0 -0
  43. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_llm_as_a_judge.py +0 -0
  44. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_otel.py +0 -0
  45. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_render_numbers.py +0 -0
  46. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_reporting.py +0 -0
  47. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_reports.py +0 -0
  48. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/test_utils.py +0 -0
  49. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/evals/utils.py +0 -0
  50. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/example_modules/README.md +0 -0
  51. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/example_modules/bank_database.py +0 -0
  52. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/example_modules/fake_database.py +0 -0
  53. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/example_modules/weather_service.py +0 -0
  54. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/ext/__init__.py +0 -0
  55. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/ext/test_langchain.py +0 -0
  56. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/fasta2a/__init__.py +0 -0
  57. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/fasta2a/test_applications.py +0 -0
  58. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/__init__.py +0 -0
  59. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/test_file_persistence.py +0 -0
  60. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/test_graph.py +0 -0
  61. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/test_mermaid.py +0 -0
  62. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/test_persistence.py +0 -0
  63. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/test_state.py +0 -0
  64. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/graph/test_utils.py +0 -0
  65. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/import_examples.py +0 -0
  66. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/json_body_serializer.py +0 -0
  67. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/__init__.py +0 -0
  68. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_anthropic_model_empty_message_on_history.yaml +0 -0
  69. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_anthropic_model_instructions.yaml +0 -0
  70. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
  71. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -0
  72. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_extra_headers.yaml +0 -0
  73. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_image_as_binary_content_tool_response.yaml +0 -0
  74. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -0
  75. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
  76. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
  77. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
  78. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_empty_system_prompt.yaml +0 -0
  79. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
  80. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
  81. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_guardrail_config.yaml +0 -0
  82. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_instructions.yaml +0 -0
  83. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
  84. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
  85. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_other_parameters.yaml +0 -0
  86. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_performance_config.yaml +0 -0
  87. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
  88. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
  89. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_response.yaml +0 -0
  90. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
  91. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_bedrock_multiple_documents_in_history.yaml +0 -0
  92. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
  93. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
  94. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
  95. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
  96. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
  97. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_video_as_binary_content_input.yaml +0 -0
  98. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_bedrock/test_video_url_input.yaml +0 -0
  99. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_cohere/test_cohere_model_instructions.yaml +0 -0
  100. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +0 -0
  101. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_download_item/test_download_item_application_octet_stream.yaml +0 -0
  102. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_download_item/test_download_item_no_content_type.yaml +0 -0
  103. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
  104. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_false.yaml +0 -0
  105. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_true.yaml +0 -0
  106. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_drop_exclusive_maximum.yaml +0 -0
  107. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_exclusive_minimum_and_maximum.yaml +0 -0
  108. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_model_instructions.yaml +0 -0
  109. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_tool_config_any_with_tool_without_args.yaml +0 -0
  110. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_gemini_youtube_video_url_input.yaml +0 -0
  111. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
  112. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_image_as_binary_content_tool_response.yaml +0 -0
  113. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
  114. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_labels_are_ignored_with_gla_provider.yaml +0 -0
  115. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_video_as_binary_content_input.yaml +0 -0
  116. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini/test_video_url_input.yaml +0 -0
  117. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_labels.yaml +0 -0
  118. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[AudioUrl (gs)].yaml +0 -0
  119. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[AudioUrl].yaml +0 -0
  120. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[DocumentUrl (gs)].yaml +0 -0
  121. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[DocumentUrl].yaml +0 -0
  122. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[ImageUrl (gs)].yaml +0 -0
  123. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[ImageUrl].yaml +0 -0
  124. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl (YouTube)].yaml +0 -0
  125. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl (gs)].yaml +0 -0
  126. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl].yaml +0 -0
  127. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_gemini_vertex/test_url_input_force_download.yaml +0 -0
  128. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model.yaml +0 -0
  129. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_document_url_input.yaml +0 -0
  130. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_empty_user_prompt.yaml +0 -0
  131. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_image_as_binary_content_input.yaml +0 -0
  132. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_image_url_input.yaml +0 -0
  133. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_instructions.yaml +0 -0
  134. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_iter_stream.yaml +0 -0
  135. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_max_tokens.yaml +0 -0
  136. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_multiple_documents_in_history.yaml +0 -0
  137. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_retry.yaml +0 -0
  138. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_safety_settings.yaml +0 -0
  139. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_stream.yaml +0 -0
  140. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_structured_response.yaml +0 -0
  141. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_text_as_binary_content_input.yaml +0 -0
  142. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_text_document_url_input.yaml +0 -0
  143. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_thinking_config.yaml +0 -0
  144. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_top_p.yaml +0 -0
  145. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_vertex_labels.yaml +0 -0
  146. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_vertex_provider.yaml +0 -0
  147. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_video_as_binary_content_input.yaml +0 -0
  148. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_model_video_url_input.yaml +0 -0
  149. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_tool_config_any_with_tool_without_args.yaml +0 -0
  150. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[AudioUrl (gs)].yaml +0 -0
  151. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[AudioUrl].yaml +0 -0
  152. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[DocumentUrl (gs)].yaml +0 -0
  153. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[DocumentUrl].yaml +0 -0
  154. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[ImageUrl (gs)].yaml +0 -0
  155. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[ImageUrl].yaml +0 -0
  156. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl (YouTube)].yaml +0 -0
  157. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl (gs)].yaml +0 -0
  158. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl].yaml +0 -0
  159. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_google/test_google_url_input_force_download.yaml +0 -0
  160. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_groq/test_extra_headers.yaml +0 -0
  161. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_groq/test_groq_model_instructions.yaml +0 -0
  162. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
  163. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_groq/test_image_as_binary_content_tool_response.yaml +0 -0
  164. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
  165. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_mistral/test_image_as_binary_content_tool_response.yaml +0 -0
  166. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_mistral/test_mistral_model_instructions.yaml +0 -0
  167. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_model_names/test_known_model_names.yaml +0 -0
  168. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
  169. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_compatible_api_with_tool_calls_without_id.yaml +0 -0
  170. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_document_as_binary_content_input.yaml +0 -0
  171. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
  172. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_extra_headers.yaml +0 -0
  173. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
  174. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_image_as_binary_content_tool_response.yaml +0 -0
  175. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_image_url_tool_response.yaml +0 -0
  176. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +0 -0
  177. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +0 -0
  178. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +0 -0
  179. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_multiple_agent_tool_calls.yaml +0 -0
  180. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_openai_audio_url_input.yaml +0 -0
  181. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_openai_instructions.yaml +0 -0
  182. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_openai_instructions_with_tool_calls_keep_instructions.yaml +0 -0
  183. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_openai_model_without_system_prompt.yaml +0 -0
  184. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
  185. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
  186. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_reasoning_model_with_temperature.yaml +0 -0
  187. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai/test_user_id.yaml +0 -0
  188. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_audio_as_binary_content_input.yaml +0 -0
  189. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_input.yaml +0 -0
  190. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_tool_response.yaml +0 -0
  191. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_as_binary_content_input.yaml +0 -0
  192. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_url_input.yaml +0 -0
  193. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_image_url_input.yaml +0 -0
  194. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_builtin_tools.yaml +0 -0
  195. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_http_error.yaml +0 -0
  196. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_instructions.yaml +0 -0
  197. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_retry.yaml +0 -0
  198. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response.yaml +0 -0
  199. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response_with_tool_call.yaml +0 -0
  200. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_output_type.yaml +0 -0
  201. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_effort.yaml +0 -0
  202. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_generate_summary.yaml +0 -0
  203. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_stream.yaml +0 -0
  204. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_system_prompt.yaml +0 -0
  205. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_openai_responses_text_document_url_input.yaml +0 -0
  206. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/cassettes/test_openai_responses/test_reasoning_model_with_temperature.yaml +0 -0
  207. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/mock_async_stream.py +0 -0
  208. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_anthropic.py +0 -0
  209. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_bedrock.py +0 -0
  210. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_cohere.py +0 -0
  211. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_download_item.py +0 -0
  212. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_fallback.py +0 -0
  213. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_gemini.py +0 -0
  214. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_gemini_vertex.py +0 -0
  215. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_groq.py +0 -0
  216. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_instrumented.py +0 -0
  217. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_mistral.py +0 -0
  218. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_model.py +0 -0
  219. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_model_function.py +0 -0
  220. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_model_names.py +0 -0
  221. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_model_request_parameters.py +0 -0
  222. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_model_test.py +0 -0
  223. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_openai.py +0 -0
  224. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/models/test_openai_responses.py +0 -0
  225. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/__init__.py +0 -0
  226. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
  227. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/cassettes/test_google_vertex/test_vertexai_provider.yaml +0 -0
  228. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/cassettes/test_heroku/test_heroku_model_provider_claude_3_7_sonnet.yaml +0 -0
  229. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/cassettes/test_openrouter/test_openrouter_with_google_model.yaml +0 -0
  230. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_anthropic.py +0 -0
  231. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_azure.py +0 -0
  232. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_bedrock.py +0 -0
  233. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_cohere.py +0 -0
  234. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_deepseek.py +0 -0
  235. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_fireworks.py +0 -0
  236. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_google_gla.py +0 -0
  237. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_google_vertex.py +0 -0
  238. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_grok.py +0 -0
  239. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_groq.py +0 -0
  240. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_heroku.py +0 -0
  241. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_mistral.py +0 -0
  242. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_openai.py +0 -0
  243. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_openrouter.py +0 -0
  244. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_provider_names.py +0 -0
  245. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/providers/test_together.py +0 -0
  246. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_a2a.py +0 -0
  247. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_agent.py +0 -0
  248. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_cli.py +0 -0
  249. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_deps.py +0 -0
  250. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_direct.py +0 -0
  251. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_examples.py +0 -0
  252. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_format_as_xml.py +0 -0
  253. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_json_body_serializer.py +0 -0
  254. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_live.py +0 -0
  255. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_logfire.py +0 -0
  256. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_messages.py +0 -0
  257. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_parts_manager.py +0 -0
  258. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_settings.py +0 -0
  259. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_streaming.py +0 -0
  260. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_tools.py +0 -0
  261. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_usage_limits.py +0 -0
  262. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/test_utils.py +0 -0
  263. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/typed_agent.py +0 -0
  264. {pydantic_ai-0.2.19 → pydantic_ai-0.2.20}/tests/typed_graph.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai
3
- Version: 0.2.19
3
+ Version: 0.2.20
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[a2a,anthropic,bedrock,cli,cohere,evals,google,groq,mcp,mistral,openai,vertexai]==0.2.19
31
+ Requires-Dist: pydantic-ai-slim[a2a,anthropic,bedrock,cli,cohere,evals,google,groq,mcp,mistral,openai,vertexai]==0.2.20
32
32
  Provides-Extra: examples
33
- Requires-Dist: pydantic-ai-examples==0.2.19; extra == 'examples'
33
+ Requires-Dist: pydantic-ai-examples==0.2.20; extra == 'examples'
34
34
  Provides-Extra: logfire
35
35
  Requires-Dist: logfire>=3.11.0; extra == 'logfire'
36
36
  Description-Content-Type: text/markdown
@@ -3,6 +3,8 @@ from pathlib import Path
3
3
  from typing import Any
4
4
 
5
5
  from mcp.server.fastmcp import Context, FastMCP, Image
6
+ from mcp.server.session import ServerSessionT
7
+ from mcp.shared.context import LifespanContextT, RequestT
6
8
  from mcp.types import BlobResourceContents, EmbeddedResource, TextResourceContents
7
9
  from pydantic import AnyUrl
8
10
 
@@ -118,6 +120,22 @@ async def get_log_level(ctx: Context) -> str: # type: ignore
118
120
  return log_level
119
121
 
120
122
 
123
+ @mcp.tool()
124
+ async def echo_deps(ctx: Context[ServerSessionT, LifespanContextT, RequestT]) -> dict[str, Any]:
125
+ """Echo the run context.
126
+
127
+ Args:
128
+ ctx: Context object containing request and session information.
129
+
130
+ Returns:
131
+ Dictionary with an echo message and the deps.
132
+ """
133
+ await ctx.info('This is an info message')
134
+
135
+ deps: Any = getattr(ctx.request_context.meta, 'deps')
136
+ return {'echo': 'This is an echo message', 'deps': deps}
137
+
138
+
121
139
  @mcp._mcp_server.set_logging_level() # pyright: ignore[reportPrivateUsage]
122
140
  async def set_logging_level(level: str) -> None:
123
141
  global log_level
@@ -0,0 +1,64 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - '*/*'
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '87'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - generativelanguage.googleapis.com
16
+ method: POST
17
+ parsed_body:
18
+ contents:
19
+ - parts:
20
+ - text: Hello!
21
+ role: user
22
+ generationConfig: {}
23
+ uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent
24
+ response:
25
+ headers:
26
+ alt-svc:
27
+ - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
28
+ content-length:
29
+ - '687'
30
+ content-type:
31
+ - application/json; charset=UTF-8
32
+ server-timing:
33
+ - gfet4t7; dur=304
34
+ transfer-encoding:
35
+ - chunked
36
+ vary:
37
+ - Origin
38
+ - X-Origin
39
+ - Referer
40
+ parsed_body:
41
+ candidates:
42
+ - avgLogprobs: -0.0009780019860376012
43
+ content:
44
+ parts:
45
+ - text: |
46
+ Hello there! How can I help you today?
47
+ role: model
48
+ finishReason: STOP
49
+ modelVersion: gemini-1.5-flash
50
+ responseId: KKRRaJyZNJiFm9IPzMfNiAk
51
+ usageMetadata:
52
+ candidatesTokenCount: 11
53
+ candidatesTokensDetails:
54
+ - modality: TEXT
55
+ tokenCount: 11
56
+ promptTokenCount: 2
57
+ promptTokensDetails:
58
+ - modality: TEXT
59
+ tokenCount: 2
60
+ totalTokenCount: 13
61
+ status:
62
+ code: 200
63
+ message: OK
64
+ version: 1
@@ -6,7 +6,7 @@ from dataclasses import dataclass
6
6
  from typing import Any, Union
7
7
 
8
8
  import pytest
9
- from httpx import Request
9
+ from httpx import Request, Timeout
10
10
  from inline_snapshot import Is, snapshot
11
11
  from pytest_mock import MockerFixture
12
12
  from typing_extensions import TypedDict
@@ -806,3 +806,14 @@ async def test_google_tool_config_any_with_tool_without_args(
806
806
  ),
807
807
  ]
808
808
  )
809
+
810
+
811
+ async def test_google_timeout(allow_model_requests: None, google_provider: GoogleProvider):
812
+ model = GoogleModel('gemini-1.5-flash', provider=google_provider)
813
+ agent = Agent(model=model)
814
+
815
+ result = await agent.run('Hello!', model_settings={'timeout': 10})
816
+ assert result.output == snapshot('Hello there! How can I help you today?\n')
817
+
818
+ with pytest.raises(UserError, match='Google does not support setting ModelSettings.timeout to a httpx.Timeout'):
819
+ await agent.run('Hello!', model_settings={'timeout': Timeout(10)})
@@ -1,4 +1,5 @@
1
1
  from collections.abc import AsyncIterator
2
+ from typing import Any, cast
2
3
 
3
4
  import pytest
4
5
  from inline_snapshot import snapshot
@@ -6,6 +7,7 @@ from inline_snapshot import snapshot
6
7
  from pydantic_ai import Agent
7
8
  from pydantic_ai.messages import ModelMessage, ModelRequest, ModelRequestPart, ModelResponse, TextPart, UserPromptPart
8
9
  from pydantic_ai.models.function import AgentInfo, FunctionModel
10
+ from pydantic_ai.tools import RunContext
9
11
  from pydantic_ai.usage import Usage
10
12
 
11
13
  from .conftest import IsDatetime
@@ -201,3 +203,101 @@ async def test_history_processor_on_streamed_run(function_model: FunctionModel,
201
203
  ModelRequest(parts=[UserPromptPart(content='Question 2', timestamp=IsDatetime())]),
202
204
  ]
203
205
  )
206
+
207
+
208
+ async def test_history_processor_with_context(function_model: FunctionModel, received_messages: list[ModelMessage]):
209
+ """Test history processor that takes RunContext."""
210
+
211
+ def context_processor(ctx: RunContext[str], messages: list[ModelMessage]) -> list[ModelMessage]:
212
+ # Access deps from context
213
+ prefix = ctx.deps
214
+ processed: list[ModelMessage] = []
215
+ for msg in messages:
216
+ if isinstance(msg, ModelRequest):
217
+ new_parts: list[ModelRequestPart] = []
218
+ for part in msg.parts:
219
+ if isinstance(part, UserPromptPart):
220
+ new_parts.append(UserPromptPart(content=f'{prefix}: {part.content}'))
221
+ else:
222
+ new_parts.append(part) # pragma: no cover
223
+ processed.append(ModelRequest(parts=new_parts))
224
+ else:
225
+ processed.append(msg) # pragma: no cover
226
+ return processed
227
+
228
+ agent = Agent(function_model, history_processors=[context_processor], deps_type=str)
229
+ await agent.run('test', deps='PREFIX')
230
+
231
+ # Verify the prefix was added
232
+ assert len(received_messages) == 1
233
+ assert isinstance(received_messages[0], ModelRequest)
234
+ user_part = received_messages[0].parts[0]
235
+ assert isinstance(user_part, UserPromptPart)
236
+ assert user_part.content == 'PREFIX: test'
237
+
238
+
239
+ async def test_history_processor_with_context_async(
240
+ function_model: FunctionModel, received_messages: list[ModelMessage]
241
+ ):
242
+ """Test async history processor that takes RunContext."""
243
+
244
+ async def async_context_processor(ctx: RunContext[Any], messages: list[ModelMessage]) -> list[ModelMessage]:
245
+ return messages[-1:] # Keep only the last message
246
+
247
+ message_history = [
248
+ ModelRequest(parts=[UserPromptPart(content='Question 1')]),
249
+ ModelResponse(parts=[TextPart(content='Answer 1')]),
250
+ ModelRequest(parts=[UserPromptPart(content='Question 2')]),
251
+ ModelResponse(parts=[TextPart(content='Answer 2')]),
252
+ ]
253
+
254
+ agent = Agent(function_model, history_processors=[async_context_processor])
255
+ await agent.run('Question 3', message_history=message_history)
256
+
257
+ # Should have filtered to only recent messages
258
+ assert len(received_messages) <= 2 # Last message from history + new message
259
+
260
+
261
+ async def test_history_processor_mixed_signatures(function_model: FunctionModel, received_messages: list[ModelMessage]):
262
+ """Test mixing processors with and without context."""
263
+
264
+ def simple_processor(messages: list[ModelMessage]) -> list[ModelMessage]:
265
+ # Filter out responses
266
+ return [msg for msg in messages if isinstance(msg, ModelRequest)]
267
+
268
+ def context_processor(ctx: RunContext[Any], messages: list[ModelMessage]) -> list[ModelMessage]:
269
+ # Add prefix based on deps
270
+ prefix = getattr(ctx.deps, 'prefix', 'DEFAULT')
271
+ processed: list[ModelMessage] = []
272
+ for msg in messages:
273
+ if isinstance(msg, ModelRequest):
274
+ new_parts: list[ModelRequestPart] = []
275
+ for part in msg.parts:
276
+ if isinstance(part, UserPromptPart):
277
+ new_parts.append(UserPromptPart(content=f'{prefix}: {part.content}'))
278
+ else:
279
+ new_parts.append(part) # pragma: no cover
280
+ processed.append(ModelRequest(parts=new_parts))
281
+ else:
282
+ processed.append(msg) # pragma: no cover
283
+ return processed
284
+
285
+ message_history = [
286
+ ModelRequest(parts=[UserPromptPart(content='Question 1')]),
287
+ ModelResponse(parts=[TextPart(content='Answer 1')]),
288
+ ]
289
+
290
+ # Create deps with prefix attribute
291
+ class Deps:
292
+ prefix = 'TEST'
293
+
294
+ agent = Agent(function_model, history_processors=[simple_processor, context_processor], deps_type=Deps)
295
+ await agent.run('Question 2', message_history=message_history, deps=Deps())
296
+
297
+ # Should have filtered responses and added prefix
298
+ assert len(received_messages) == 2
299
+ for msg in received_messages:
300
+ assert isinstance(msg, ModelRequest)
301
+ user_part = msg.parts[0]
302
+ assert isinstance(user_part, UserPromptPart)
303
+ assert cast(str, user_part.content).startswith('TEST: ')
@@ -2,12 +2,14 @@
2
2
 
3
3
  import re
4
4
  from pathlib import Path
5
+ from typing import Any, Final
6
+ from unittest.mock import AsyncMock, patch
5
7
 
6
8
  import pytest
7
9
  from inline_snapshot import snapshot
8
10
 
9
11
  from pydantic_ai.agent import Agent
10
- from pydantic_ai.exceptions import UserError
12
+ from pydantic_ai.exceptions import ModelRetry, UserError
11
13
  from pydantic_ai.messages import (
12
14
  BinaryContent,
13
15
  ModelRequest,
@@ -18,17 +20,22 @@ from pydantic_ai.messages import (
18
20
  ToolReturnPart,
19
21
  UserPromptPart,
20
22
  )
23
+ from pydantic_ai.models.test import TestModel
24
+ from pydantic_ai.tools import RunContext
21
25
  from pydantic_ai.usage import Usage
22
26
 
23
27
  from .conftest import IsDatetime, IsStr, try_import
24
28
 
25
29
  with try_import() as imports_successful:
26
- from pydantic_ai.mcp import MCPServerSSE, MCPServerStdio
30
+ from mcp import ErrorData, McpError
31
+
32
+ from pydantic_ai.mcp import CallToolFunc, MCPServerSSE, MCPServerStdio, ToolResult
27
33
  from pydantic_ai.models.google import GoogleModel
28
34
  from pydantic_ai.models.openai import OpenAIModel
29
35
  from pydantic_ai.providers.google import GoogleProvider
30
36
  from pydantic_ai.providers.openai import OpenAIProvider
31
37
 
38
+ TOOL_COUNT: Final[int] = 12
32
39
 
33
40
  pytestmark = [
34
41
  pytest.mark.skipif(not imports_successful(), reason='mcp and openai not installed'),
@@ -48,7 +55,7 @@ async def test_stdio_server():
48
55
  server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
49
56
  async with server:
50
57
  tools = await server.list_tools()
51
- assert len(tools) == 11
58
+ assert len(tools) == TOOL_COUNT
52
59
  assert tools[0].name == 'celsius_to_fahrenheit'
53
60
  assert tools[0].description.startswith('Convert Celsius to Fahrenheit.')
54
61
 
@@ -69,7 +76,29 @@ async def test_stdio_server_with_cwd():
69
76
  server = MCPServerStdio('python', ['mcp_server.py'], cwd=test_dir)
70
77
  async with server:
71
78
  tools = await server.list_tools()
72
- assert len(tools) == snapshot(11)
79
+ assert len(tools) == TOOL_COUNT
80
+
81
+
82
+ async def test_process_tool_call() -> None:
83
+ called: bool = False
84
+
85
+ async def process_tool_call(
86
+ ctx: RunContext[int],
87
+ call_tool: CallToolFunc,
88
+ tool_name: str,
89
+ args: dict[str, Any],
90
+ ) -> ToolResult:
91
+ """A process_tool_call that sets a flag and sends deps as metadata."""
92
+ nonlocal called
93
+ called = True
94
+ return await call_tool(tool_name, args, {'deps': ctx.deps})
95
+
96
+ server = MCPServerStdio('python', ['-m', 'tests.mcp_server'], process_tool_call=process_tool_call)
97
+ async with server:
98
+ agent = Agent(deps_type=int, model=TestModel(call_tools=['echo_deps']), mcp_servers=[server])
99
+ result = await agent.run('Echo with deps set to 42', deps=42)
100
+ assert result.output == snapshot('{"echo_deps":{"echo":"This is an echo message","deps":42}}')
101
+ assert called, 'process_tool_call should have been called'
73
102
 
74
103
 
75
104
  def test_sse_server():
@@ -214,7 +243,7 @@ async def test_log_level_unset():
214
243
  assert server._get_log_level() is None # pyright: ignore[reportPrivateUsage]
215
244
  async with server:
216
245
  tools = await server.list_tools()
217
- assert len(tools) == snapshot(11)
246
+ assert len(tools) == TOOL_COUNT
218
247
  assert tools[10].name == 'get_log_level'
219
248
 
220
249
  result = await server.call_tool('get_log_level', {})
@@ -932,3 +961,18 @@ async def test_tool_returning_multiple_items(allow_model_requests: None, agent:
932
961
  ),
933
962
  ]
934
963
  )
964
+
965
+
966
+ async def test_mcp_server_raises_mcp_error(allow_model_requests: None, agent: Agent) -> None:
967
+ server = agent._mcp_servers[0] # pyright: ignore[reportPrivateUsage]
968
+
969
+ mcp_error = McpError(error=ErrorData(code=400, message='Test MCP error conversion'))
970
+
971
+ async with agent.run_mcp_servers():
972
+ with patch.object(
973
+ server._client, # pyright: ignore[reportPrivateUsage]
974
+ 'send_request',
975
+ new=AsyncMock(side_effect=mcp_error),
976
+ ):
977
+ with pytest.raises(ModelRetry, match='Test MCP error conversion'):
978
+ await server.call_tool('test_tool', {})
File without changes
File without changes
File without changes
File without changes