pydantic-ai 0.2.10__tar.gz → 0.2.12__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 (229) hide show
  1. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/PKG-INFO +3 -3
  2. pydantic_ai-0.2.12/tests/models/cassettes/test_anthropic/test_anthropic_model_empty_message_on_history.yaml +73 -0
  3. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_anthropic.py +28 -0
  4. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_bedrock.py +95 -0
  5. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_gemini.py +52 -9
  6. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_google.py +60 -8
  7. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_openai.py +125 -16
  8. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_openai_responses.py +43 -0
  9. pydantic_ai-0.2.12/tests/providers/test_azure.py +141 -0
  10. pydantic_ai-0.2.12/tests/providers/test_bedrock.py +95 -0
  11. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_deepseek.py +8 -1
  12. pydantic_ai-0.2.12/tests/providers/test_fireworks.py +104 -0
  13. pydantic_ai-0.2.12/tests/providers/test_grok.py +57 -0
  14. pydantic_ai-0.2.12/tests/providers/test_groq.py +107 -0
  15. pydantic_ai-0.2.12/tests/providers/test_openrouter.py +154 -0
  16. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_provider_names.py +6 -0
  17. pydantic_ai-0.2.12/tests/providers/test_together.py +100 -0
  18. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_agent.py +601 -3
  19. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_examples.py +94 -1
  20. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_tools.py +1 -1
  21. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/typed_agent.py +50 -22
  22. pydantic_ai-0.2.10/tests/providers/test_azure.py +0 -72
  23. pydantic_ai-0.2.10/tests/providers/test_bedrock.py +0 -34
  24. pydantic_ai-0.2.10/tests/providers/test_groq.py +0 -57
  25. pydantic_ai-0.2.10/tests/providers/test_openrouter.py +0 -67
  26. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/.gitignore +0 -0
  27. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/LICENSE +0 -0
  28. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/Makefile +0 -0
  29. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/README.md +0 -0
  30. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/pyproject.toml +0 -0
  31. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/__init__.py +0 -0
  32. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/assets/dummy.pdf +0 -0
  33. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/assets/kiwi.png +0 -0
  34. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/assets/marcelo.mp3 +0 -0
  35. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/assets/small_video.mp4 +0 -0
  36. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
  37. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_dict.yaml +0 -0
  38. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_error.yaml +0 -0
  39. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_image.yaml +0 -0
  40. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_image_resource.yaml +0 -0
  41. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_multiple_items.yaml +0 -0
  42. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_none.yaml +0 -0
  43. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_str.yaml +0 -0
  44. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_text_resource.yaml +0 -0
  45. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[anthropic].yaml +0 -0
  46. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[bedrock].yaml +0 -0
  47. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[cohere].yaml +0 -0
  48. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[gemini].yaml +0 -0
  49. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[groq].yaml +0 -0
  50. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[mistral].yaml +0 -0
  51. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[openai].yaml +0 -0
  52. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/conftest.py +0 -0
  53. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/__init__.py +0 -0
  54. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_dataset.py +0 -0
  55. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_base.py +0 -0
  56. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_common.py +0 -0
  57. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_context.py +0 -0
  58. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_spec.py +0 -0
  59. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_evaluators.py +0 -0
  60. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_llm_as_a_judge.py +0 -0
  61. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_otel.py +0 -0
  62. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_render_numbers.py +0 -0
  63. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_reporting.py +0 -0
  64. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_reports.py +0 -0
  65. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/test_utils.py +0 -0
  66. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/evals/utils.py +0 -0
  67. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/example_modules/README.md +0 -0
  68. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/example_modules/bank_database.py +0 -0
  69. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/example_modules/fake_database.py +0 -0
  70. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/example_modules/weather_service.py +0 -0
  71. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/fasta2a/__init__.py +0 -0
  72. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/fasta2a/test_applications.py +0 -0
  73. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/__init__.py +0 -0
  74. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/test_file_persistence.py +0 -0
  75. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/test_graph.py +0 -0
  76. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/test_mermaid.py +0 -0
  77. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/test_persistence.py +0 -0
  78. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/test_state.py +0 -0
  79. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/graph/test_utils.py +0 -0
  80. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/import_examples.py +0 -0
  81. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/json_body_serializer.py +0 -0
  82. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/mcp_server.py +0 -0
  83. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/__init__.py +0 -0
  84. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_anthropic_model_instructions.yaml +0 -0
  85. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
  86. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -0
  87. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_extra_headers.yaml +0 -0
  88. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_image_as_binary_content_tool_response.yaml +0 -0
  89. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -0
  90. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
  91. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
  92. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
  93. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_empty_system_prompt.yaml +0 -0
  94. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
  95. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
  96. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_guardrail_config.yaml +0 -0
  97. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_instructions.yaml +0 -0
  98. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
  99. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
  100. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_other_parameters.yaml +0 -0
  101. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_performance_config.yaml +0 -0
  102. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
  103. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
  104. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_response.yaml +0 -0
  105. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
  106. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_multiple_documents_in_history.yaml +0 -0
  107. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
  108. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
  109. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
  110. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
  111. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
  112. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_video_as_binary_content_input.yaml +0 -0
  113. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_video_url_input.yaml +0 -0
  114. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_cohere/test_cohere_model_instructions.yaml +0 -0
  115. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +0 -0
  116. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
  117. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_false.yaml +0 -0
  118. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_true.yaml +0 -0
  119. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_drop_exclusive_maximum.yaml +0 -0
  120. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_exclusive_minimum_and_maximum.yaml +0 -0
  121. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_model_instructions.yaml +0 -0
  122. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
  123. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_image_as_binary_content_tool_response.yaml +0 -0
  124. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
  125. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_labels_are_ignored_with_gla_provider.yaml +0 -0
  126. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_video_as_binary_content_input.yaml +0 -0
  127. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_video_url_input.yaml +0 -0
  128. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini_vertexai/test_labels.yaml +0 -0
  129. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model.yaml +0 -0
  130. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_document_url_input.yaml +0 -0
  131. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_image_as_binary_content_input.yaml +0 -0
  132. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_image_url_input.yaml +0 -0
  133. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_instructions.yaml +0 -0
  134. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_iter_stream.yaml +0 -0
  135. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_max_tokens.yaml +0 -0
  136. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_multiple_documents_in_history.yaml +0 -0
  137. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_retry.yaml +0 -0
  138. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_safety_settings.yaml +0 -0
  139. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_stream.yaml +0 -0
  140. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_structured_response.yaml +0 -0
  141. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_text_as_binary_content_input.yaml +0 -0
  142. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_text_document_url_input.yaml +0 -0
  143. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_thinking_config.yaml +0 -0
  144. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_top_p.yaml +0 -0
  145. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_vertex_labels.yaml +0 -0
  146. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_vertex_provider.yaml +0 -0
  147. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_video_as_binary_content_input.yaml +0 -0
  148. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_video_url_input.yaml +0 -0
  149. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_extra_headers.yaml +0 -0
  150. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_groq_model_instructions.yaml +0 -0
  151. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
  152. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_image_as_binary_content_tool_response.yaml +0 -0
  153. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
  154. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_mistral/test_image_as_binary_content_tool_response.yaml +0 -0
  155. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_mistral/test_mistral_model_instructions.yaml +0 -0
  156. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
  157. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_document_as_binary_content_input.yaml +0 -0
  158. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
  159. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_extra_headers.yaml +0 -0
  160. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
  161. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_image_as_binary_content_tool_response.yaml +0 -0
  162. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_image_url_tool_response.yaml +0 -0
  163. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +0 -0
  164. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +0 -0
  165. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +0 -0
  166. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_multiple_agent_tool_calls.yaml +0 -0
  167. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_audio_url_input.yaml +0 -0
  168. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_instructions.yaml +0 -0
  169. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_instructions_with_tool_calls_keep_instructions.yaml +0 -0
  170. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_model_without_system_prompt.yaml +0 -0
  171. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
  172. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
  173. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_user_id.yaml +0 -0
  174. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_audio_as_binary_content_input.yaml +0 -0
  175. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_input.yaml +0 -0
  176. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_tool_response.yaml +0 -0
  177. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_as_binary_content_input.yaml +0 -0
  178. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_url_input.yaml +0 -0
  179. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_image_url_input.yaml +0 -0
  180. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_builtin_tools.yaml +0 -0
  181. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_http_error.yaml +0 -0
  182. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_instructions.yaml +0 -0
  183. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_retry.yaml +0 -0
  184. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response.yaml +0 -0
  185. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response_with_tool_call.yaml +0 -0
  186. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_output_type.yaml +0 -0
  187. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_effort.yaml +0 -0
  188. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_generate_summary.yaml +0 -0
  189. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_stream.yaml +0 -0
  190. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_system_prompt.yaml +0 -0
  191. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_text_document_url_input.yaml +0 -0
  192. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/mock_async_stream.py +0 -0
  193. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_cohere.py +0 -0
  194. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_fallback.py +0 -0
  195. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_gemini_vertexai.py +0 -0
  196. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_groq.py +0 -0
  197. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_instrumented.py +0 -0
  198. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_mistral.py +0 -0
  199. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_model.py +0 -0
  200. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_model_function.py +0 -0
  201. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_model_names.py +0 -0
  202. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_model_request_parameters.py +0 -0
  203. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/models/test_model_test.py +0 -0
  204. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/__init__.py +0 -0
  205. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
  206. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/cassettes/test_google_vertex/test_vertexai_provider.yaml +0 -0
  207. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/cassettes/test_openrouter/test_openrouter_with_google_model.yaml +0 -0
  208. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_anthropic.py +0 -0
  209. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_cohere.py +0 -0
  210. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_google_gla.py +0 -0
  211. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_google_vertex.py +0 -0
  212. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_mistral.py +0 -0
  213. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/providers/test_openai.py +0 -0
  214. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_a2a.py +0 -0
  215. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_cli.py +0 -0
  216. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_deps.py +0 -0
  217. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_direct.py +0 -0
  218. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_format_as_xml.py +0 -0
  219. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_json_body_serializer.py +0 -0
  220. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_live.py +0 -0
  221. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_logfire.py +0 -0
  222. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_mcp.py +0 -0
  223. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_messages.py +0 -0
  224. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_parts_manager.py +0 -0
  225. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_settings.py +0 -0
  226. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_streaming.py +0 -0
  227. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_usage_limits.py +0 -0
  228. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/tests/test_utils.py +0 -0
  229. {pydantic_ai-0.2.10 → pydantic_ai-0.2.12}/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.10
3
+ Version: 0.2.12
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.10
31
+ Requires-Dist: pydantic-ai-slim[a2a,anthropic,bedrock,cli,cohere,evals,google,groq,mcp,mistral,openai,vertexai]==0.2.12
32
32
  Provides-Extra: examples
33
- Requires-Dist: pydantic-ai-examples==0.2.10; extra == 'examples'
33
+ Requires-Dist: pydantic-ai-examples==0.2.12; extra == 'examples'
34
34
  Provides-Extra: logfire
35
35
  Requires-Dist: logfire>=3.11.0; extra == 'logfire'
36
36
  Description-Content-Type: text/markdown
@@ -0,0 +1,73 @@
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
+ - '281'
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: Hello, how can I help you?
22
+ type: text
23
+ role: assistant
24
+ - content:
25
+ - text: I need a potato!
26
+ type: text
27
+ role: user
28
+ model: claude-3-5-sonnet-latest
29
+ stream: false
30
+ system: |+
31
+ You are a helpful assistant.
32
+
33
+ uri: https://api.anthropic.com/v1/messages?beta=true
34
+ response:
35
+ headers:
36
+ connection:
37
+ - keep-alive
38
+ content-length:
39
+ - '671'
40
+ content-type:
41
+ - application/json
42
+ strict-transport-security:
43
+ - max-age=31536000; includeSubDomains; preload
44
+ transfer-encoding:
45
+ - chunked
46
+ parsed_body:
47
+ content:
48
+ - text: |-
49
+ I can't physically give you a potato since I'm a computer program. However, I can:
50
+
51
+ 1. Help you find recipes that use potatoes
52
+ 2. Give you tips on how to select, store, or cook potatoes
53
+ 3. Share information about different potato varieties
54
+ 4. Provide guidance on growing potatoes
55
+
56
+ What specifically would you like to know about potatoes?
57
+ type: text
58
+ id: msg_01UjnDmX3B57Drosu49sMteT
59
+ model: claude-3-5-sonnet-20241022
60
+ role: assistant
61
+ stop_reason: end_turn
62
+ stop_sequence: null
63
+ type: message
64
+ usage:
65
+ cache_creation_input_tokens: 0
66
+ cache_read_input_tokens: 0
67
+ input_tokens: 41
68
+ output_tokens: 82
69
+ service_tier: standard
70
+ status:
71
+ code: 200
72
+ message: OK
73
+ version: 1
@@ -1035,3 +1035,31 @@ def anth_msg(usage: BetaUsage) -> BetaMessage:
1035
1035
  )
1036
1036
  def test_usage(message_callback: Callable[[], BetaMessage | BetaRawMessageStreamEvent], usage: Usage):
1037
1037
  assert _map_usage(message_callback()) == usage
1038
+
1039
+
1040
+ @pytest.mark.vcr()
1041
+ async def test_anthropic_model_empty_message_on_history(allow_model_requests: None, anthropic_api_key: str):
1042
+ """The Anthropic API will error if you send an empty message on the history.
1043
+
1044
+ Check <https://github.com/pydantic/pydantic-ai/pull/1027> for more details.
1045
+ """
1046
+ m = AnthropicModel('claude-3-5-sonnet-latest', provider=AnthropicProvider(api_key=anthropic_api_key))
1047
+ agent = Agent(m, instructions='You are a helpful assistant.')
1048
+
1049
+ result = await agent.run(
1050
+ 'I need a potato!',
1051
+ message_history=[
1052
+ ModelRequest(parts=[], instructions='You are a helpful assistant.', kind='request'),
1053
+ ModelResponse(parts=[TextPart(content='Hello, how can I help you?')], kind='response'),
1054
+ ],
1055
+ )
1056
+ assert result.output == snapshot("""\
1057
+ I can't physically give you a potato since I'm a computer program. However, I can:
1058
+
1059
+ 1. Help you find recipes that use potatoes
1060
+ 2. Give you tips on how to select, store, or cook potatoes
1061
+ 3. Share information about different potato varieties
1062
+ 4. Provide guidance on growing potatoes
1063
+
1064
+ What specifically would you like to know about potatoes?\
1065
+ """)
@@ -31,6 +31,8 @@ from pydantic_ai.messages import (
31
31
  UserPromptPart,
32
32
  VideoUrl,
33
33
  )
34
+ from pydantic_ai.models import ModelRequestParameters
35
+ from pydantic_ai.tools import ToolDefinition
34
36
  from pydantic_ai.usage import Usage
35
37
 
36
38
  from ..conftest import IsDatetime, try_import
@@ -631,3 +633,96 @@ async def test_bedrock_group_consecutive_tool_return_parts(bedrock_provider: Bed
631
633
  },
632
634
  ]
633
635
  )
636
+
637
+
638
+ async def test_bedrock_mistral_tool_result_format(bedrock_provider: BedrockProvider):
639
+ now = datetime.datetime.now()
640
+ req = [
641
+ ModelRequest(
642
+ parts=[
643
+ ToolReturnPart(tool_name='tool1', content={'foo': 'bar'}, tool_call_id='id1', timestamp=now),
644
+ ]
645
+ ),
646
+ ]
647
+
648
+ # Models other than Mistral support toolResult.content with text, not json
649
+ model = BedrockConverseModel('us.amazon.nova-micro-v1:0', provider=bedrock_provider)
650
+ # Call the mapping function directly
651
+ _, bedrock_messages = await model._map_messages(req) # type: ignore[reportPrivateUsage]
652
+
653
+ assert bedrock_messages == snapshot(
654
+ [
655
+ {
656
+ 'role': 'user',
657
+ 'content': [
658
+ {'toolResult': {'toolUseId': 'id1', 'content': [{'text': '{"foo":"bar"}'}], 'status': 'success'}},
659
+ ],
660
+ },
661
+ ]
662
+ )
663
+
664
+ # Mistral requires toolResult.content to hold json, not text
665
+ model = BedrockConverseModel('mistral.mistral-7b-instruct-v0:2', provider=bedrock_provider)
666
+ # Call the mapping function directly
667
+ _, bedrock_messages = await model._map_messages(req) # type: ignore[reportPrivateUsage]
668
+
669
+ assert bedrock_messages == snapshot(
670
+ [
671
+ {
672
+ 'role': 'user',
673
+ 'content': [
674
+ {'toolResult': {'toolUseId': 'id1', 'content': [{'json': {'foo': 'bar'}}], 'status': 'success'}},
675
+ ],
676
+ },
677
+ ]
678
+ )
679
+
680
+
681
+ async def test_bedrock_anthropic_no_tool_choice(bedrock_provider: BedrockProvider):
682
+ my_tool = ToolDefinition(
683
+ 'my_tool',
684
+ 'This is my tool',
685
+ {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
686
+ )
687
+ mrp = ModelRequestParameters(function_tools=[my_tool], allow_text_output=False, output_tools=[])
688
+
689
+ # Models other than Anthropic support tool_choice
690
+ model = BedrockConverseModel('us.amazon.nova-micro-v1:0', provider=bedrock_provider)
691
+ tool_config = model._map_tool_config(mrp) # type: ignore[reportPrivateUsage]
692
+
693
+ assert tool_config == snapshot(
694
+ {
695
+ 'tools': [
696
+ {
697
+ 'toolSpec': {
698
+ 'name': 'my_tool',
699
+ 'description': 'This is my tool',
700
+ 'inputSchema': {
701
+ 'json': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}}
702
+ },
703
+ }
704
+ }
705
+ ],
706
+ 'toolChoice': {'any': {}},
707
+ }
708
+ )
709
+
710
+ # Anthropic models don't support tool_choice
711
+ model = BedrockConverseModel('us.anthropic.claude-3-7-sonnet-20250219-v1:0', provider=bedrock_provider)
712
+ tool_config = model._map_tool_config(mrp) # type: ignore[reportPrivateUsage]
713
+
714
+ assert tool_config == snapshot(
715
+ {
716
+ 'tools': [
717
+ {
718
+ 'toolSpec': {
719
+ 'name': 'my_tool',
720
+ 'description': 'This is my tool',
721
+ 'inputSchema': {
722
+ 'json': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}}
723
+ },
724
+ }
725
+ }
726
+ ]
727
+ }
728
+ )
@@ -540,6 +540,7 @@ async def test_text_success(get_gemini_client: GetGeminiClient):
540
540
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
541
541
  model_name='gemini-1.5-flash-123',
542
542
  timestamp=IsNow(tz=timezone.utc),
543
+ vendor_details={'finish_reason': 'STOP'},
543
544
  ),
544
545
  ]
545
546
  )
@@ -555,6 +556,7 @@ async def test_text_success(get_gemini_client: GetGeminiClient):
555
556
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
556
557
  model_name='gemini-1.5-flash-123',
557
558
  timestamp=IsNow(tz=timezone.utc),
559
+ vendor_details={'finish_reason': 'STOP'},
558
560
  ),
559
561
  ModelRequest(parts=[UserPromptPart(content='Hello', timestamp=IsNow(tz=timezone.utc))]),
560
562
  ModelResponse(
@@ -562,6 +564,7 @@ async def test_text_success(get_gemini_client: GetGeminiClient):
562
564
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
563
565
  model_name='gemini-1.5-flash-123',
564
566
  timestamp=IsNow(tz=timezone.utc),
567
+ vendor_details={'finish_reason': 'STOP'},
565
568
  ),
566
569
  ]
567
570
  )
@@ -585,6 +588,7 @@ async def test_request_structured_response(get_gemini_client: GetGeminiClient):
585
588
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
586
589
  model_name='gemini-1.5-flash-123',
587
590
  timestamp=IsNow(tz=timezone.utc),
591
+ vendor_details={'finish_reason': 'STOP'},
588
592
  ),
589
593
  ModelRequest(
590
594
  parts=[
@@ -647,6 +651,7 @@ async def test_request_tool_call(get_gemini_client: GetGeminiClient):
647
651
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
648
652
  model_name='gemini-1.5-flash-123',
649
653
  timestamp=IsNow(tz=timezone.utc),
654
+ vendor_details={'finish_reason': 'STOP'},
650
655
  ),
651
656
  ModelRequest(
652
657
  parts=[
@@ -666,6 +671,7 @@ async def test_request_tool_call(get_gemini_client: GetGeminiClient):
666
671
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
667
672
  model_name='gemini-1.5-flash-123',
668
673
  timestamp=IsNow(tz=timezone.utc),
674
+ vendor_details={'finish_reason': 'STOP'},
669
675
  ),
670
676
  ModelRequest(
671
677
  parts=[
@@ -688,6 +694,7 @@ async def test_request_tool_call(get_gemini_client: GetGeminiClient):
688
694
  usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
689
695
  model_name='gemini-1.5-flash-123',
690
696
  timestamp=IsNow(tz=timezone.utc),
697
+ vendor_details={'finish_reason': 'STOP'},
691
698
  ),
692
699
  ]
693
700
  )
@@ -732,12 +739,12 @@ async def test_stream_text(get_gemini_client: GetGeminiClient):
732
739
  'Hello world',
733
740
  ]
734
741
  )
735
- assert result.usage() == snapshot(Usage(requests=1, request_tokens=2, response_tokens=4, total_tokens=6))
742
+ assert result.usage() == snapshot(Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3))
736
743
 
737
744
  async with agent.run_stream('Hello') as result:
738
745
  chunks = [chunk async for chunk in result.stream_text(delta=True, debounce_by=None)]
739
746
  assert chunks == snapshot(['Hello ', 'world'])
740
- assert result.usage() == snapshot(Usage(requests=1, request_tokens=2, response_tokens=4, total_tokens=6))
747
+ assert result.usage() == snapshot(Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3))
741
748
 
742
749
 
743
750
  async def test_stream_invalid_unicode_text(get_gemini_client: GetGeminiClient):
@@ -769,7 +776,7 @@ async def test_stream_invalid_unicode_text(get_gemini_client: GetGeminiClient):
769
776
  async with agent.run_stream('Hello') as result:
770
777
  chunks = [chunk async for chunk in result.stream(debounce_by=None)]
771
778
  assert chunks == snapshot(['abc', 'abc€def', 'abc€def'])
772
- assert result.usage() == snapshot(Usage(requests=1, request_tokens=2, response_tokens=4, total_tokens=6))
779
+ assert result.usage() == snapshot(Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3))
773
780
 
774
781
 
775
782
  async def test_stream_text_no_data(get_gemini_client: GetGeminiClient):
@@ -840,7 +847,7 @@ async def test_stream_structured_tool_calls(get_gemini_client: GetGeminiClient):
840
847
  async with agent.run_stream('Hello') as result:
841
848
  response = await result.get_output()
842
849
  assert response == snapshot((1, 2))
843
- assert result.usage() == snapshot(Usage(requests=2, request_tokens=3, response_tokens=6, total_tokens=9))
850
+ assert result.usage() == snapshot(Usage(requests=2, request_tokens=2, response_tokens=4, total_tokens=6))
844
851
  assert result.all_messages() == snapshot(
845
852
  [
846
853
  ModelRequest(parts=[UserPromptPart(content='Hello', timestamp=IsNow(tz=timezone.utc))]),
@@ -849,7 +856,7 @@ async def test_stream_structured_tool_calls(get_gemini_client: GetGeminiClient):
849
856
  ToolCallPart(tool_name='foo', args={'x': 'a'}, tool_call_id=IsStr()),
850
857
  ToolCallPart(tool_name='bar', args={'y': 'b'}, tool_call_id=IsStr()),
851
858
  ],
852
- usage=Usage(request_tokens=2, response_tokens=4, total_tokens=6),
859
+ usage=Usage(request_tokens=1, response_tokens=2, total_tokens=3, details={}),
853
860
  model_name='gemini-1.5-flash',
854
861
  timestamp=IsNow(tz=timezone.utc),
855
862
  ),
@@ -865,7 +872,7 @@ async def test_stream_structured_tool_calls(get_gemini_client: GetGeminiClient):
865
872
  ),
866
873
  ModelResponse(
867
874
  parts=[ToolCallPart(tool_name='final_result', args={'response': [1, 2]}, tool_call_id=IsStr())],
868
- usage=Usage(request_tokens=1, response_tokens=2, total_tokens=3),
875
+ usage=Usage(request_tokens=1, response_tokens=2, total_tokens=3, details={}),
869
876
  model_name='gemini-1.5-flash',
870
877
  timestamp=IsNow(tz=timezone.utc),
871
878
  ),
@@ -1096,9 +1103,16 @@ I need to use the `get_image` tool to see the image first.
1096
1103
  ),
1097
1104
  ToolCallPart(tool_name='get_image', args={}, tool_call_id=IsStr()),
1098
1105
  ],
1099
- usage=Usage(requests=1, request_tokens=38, response_tokens=28, total_tokens=427, details={}),
1106
+ usage=Usage(
1107
+ requests=1,
1108
+ request_tokens=38,
1109
+ response_tokens=28,
1110
+ total_tokens=427,
1111
+ details={'thoughts_tokens': 361, 'text_prompt_tokens': 38},
1112
+ ),
1100
1113
  model_name='gemini-2.5-pro-preview-03-25',
1101
1114
  timestamp=IsDatetime(),
1115
+ vendor_details={'finish_reason': 'STOP'},
1102
1116
  ),
1103
1117
  ModelRequest(
1104
1118
  parts=[
@@ -1119,9 +1133,16 @@ I need to use the `get_image` tool to see the image first.
1119
1133
  ),
1120
1134
  ModelResponse(
1121
1135
  parts=[TextPart(content='The image shows a kiwi fruit, sliced in half.')],
1122
- usage=Usage(requests=1, request_tokens=360, response_tokens=11, total_tokens=572, details={}),
1136
+ usage=Usage(
1137
+ requests=1,
1138
+ request_tokens=360,
1139
+ response_tokens=11,
1140
+ total_tokens=572,
1141
+ details={'thoughts_tokens': 201, 'text_prompt_tokens': 102, 'image_prompt_tokens': 258},
1142
+ ),
1123
1143
  model_name='gemini-2.5-pro-preview-03-25',
1124
1144
  timestamp=IsDatetime(),
1145
+ vendor_details={'finish_reason': 'STOP'},
1125
1146
  ),
1126
1147
  ]
1127
1148
  )
@@ -1241,9 +1262,16 @@ async def test_gemini_model_instructions(allow_model_requests: None, gemini_api_
1241
1262
  ),
1242
1263
  ModelResponse(
1243
1264
  parts=[TextPart(content='The capital of France is Paris.\n')],
1244
- usage=Usage(requests=1, request_tokens=13, response_tokens=8, total_tokens=21, details={}),
1265
+ usage=Usage(
1266
+ requests=1,
1267
+ request_tokens=13,
1268
+ response_tokens=8,
1269
+ total_tokens=21,
1270
+ details={'text_prompt_tokens': 13, 'text_candidates_tokens': 8},
1271
+ ),
1245
1272
  model_name='gemini-1.5-flash',
1246
1273
  timestamp=IsDatetime(),
1274
+ vendor_details={'finish_reason': 'STOP'},
1247
1275
  ),
1248
1276
  ]
1249
1277
  )
@@ -1284,3 +1312,18 @@ async def test_gemini_additional_properties_is_true(allow_model_requests: None,
1284
1312
  assert result.output == snapshot(
1285
1313
  'I need a location dictionary to use the `get_temperature` function. I cannot provide the temperature in Tokyo without more information.\n'
1286
1314
  )
1315
+
1316
+
1317
+ async def test_gemini_no_finish_reason(get_gemini_client: GetGeminiClient):
1318
+ response = gemini_response(
1319
+ _content_model_response(ModelResponse(parts=[TextPart('Hello world')])), finish_reason=None
1320
+ )
1321
+ gemini_client = get_gemini_client(response)
1322
+ m = GeminiModel('gemini-1.5-flash', provider=GoogleGLAProvider(http_client=gemini_client))
1323
+ agent = Agent(m)
1324
+
1325
+ result = await agent.run('Hello World')
1326
+
1327
+ for message in result.all_messages():
1328
+ if isinstance(message, ModelResponse):
1329
+ assert message.vendor_details is None
@@ -65,7 +65,15 @@ async def test_google_model(allow_model_requests: None, google_provider: GoogleP
65
65
 
66
66
  result = await agent.run('Hello!')
67
67
  assert result.output == snapshot('Hello there! How can I help you today?\n')
68
- assert result.usage() == snapshot(Usage(requests=1, request_tokens=7, response_tokens=11, total_tokens=18))
68
+ assert result.usage() == snapshot(
69
+ Usage(
70
+ requests=1,
71
+ request_tokens=7,
72
+ response_tokens=11,
73
+ total_tokens=18,
74
+ details={'text_prompt_tokens': 7, 'text_candidates_tokens': 11},
75
+ )
76
+ )
69
77
  assert result.all_messages() == snapshot(
70
78
  [
71
79
  ModelRequest(
@@ -82,9 +90,16 @@ async def test_google_model(allow_model_requests: None, google_provider: GoogleP
82
90
  ),
83
91
  ModelResponse(
84
92
  parts=[TextPart(content='Hello there! How can I help you today?\n')],
85
- usage=Usage(requests=1, request_tokens=7, response_tokens=11, total_tokens=18, details={}),
93
+ usage=Usage(
94
+ requests=1,
95
+ request_tokens=7,
96
+ response_tokens=11,
97
+ total_tokens=18,
98
+ details={'text_prompt_tokens': 7, 'text_candidates_tokens': 11},
99
+ ),
86
100
  model_name='gemini-1.5-flash',
87
101
  timestamp=IsDatetime(),
102
+ vendor_details={'finish_reason': 'STOP'},
88
103
  ),
89
104
  ]
90
105
  )
@@ -114,7 +129,15 @@ async def test_google_model_structured_response(allow_model_requests: None, goog
114
129
 
115
130
  result = await agent.run('What was the temperature in London 1st January 2022?', output_type=Response)
116
131
  assert result.output == snapshot({'temperature': '30°C', 'date': datetime.date(2022, 1, 1), 'city': 'London'})
117
- assert result.usage() == snapshot(Usage(requests=2, request_tokens=224, response_tokens=35, total_tokens=259))
132
+ assert result.usage() == snapshot(
133
+ Usage(
134
+ requests=2,
135
+ request_tokens=224,
136
+ response_tokens=35,
137
+ total_tokens=259,
138
+ details={'text_prompt_tokens': 224, 'text_candidates_tokens': 35},
139
+ )
140
+ )
118
141
  assert result.all_messages() == snapshot(
119
142
  [
120
143
  ModelRequest(
@@ -135,9 +158,16 @@ async def test_google_model_structured_response(allow_model_requests: None, goog
135
158
  tool_name='temperature', args={'date': '2022-01-01', 'city': 'London'}, tool_call_id=IsStr()
136
159
  )
137
160
  ],
138
- usage=Usage(requests=1, request_tokens=101, response_tokens=14, total_tokens=115, details={}),
161
+ usage=Usage(
162
+ requests=1,
163
+ request_tokens=101,
164
+ response_tokens=14,
165
+ total_tokens=115,
166
+ details={'text_prompt_tokens': 101, 'text_candidates_tokens': 14},
167
+ ),
139
168
  model_name='gemini-1.5-flash',
140
169
  timestamp=IsDatetime(),
170
+ vendor_details={'finish_reason': 'STOP'},
141
171
  ),
142
172
  ModelRequest(
143
173
  parts=[
@@ -154,9 +184,16 @@ async def test_google_model_structured_response(allow_model_requests: None, goog
154
184
  tool_call_id=IsStr(),
155
185
  )
156
186
  ],
157
- usage=Usage(requests=1, request_tokens=123, response_tokens=21, total_tokens=144, details={}),
187
+ usage=Usage(
188
+ requests=1,
189
+ request_tokens=123,
190
+ response_tokens=21,
191
+ total_tokens=144,
192
+ details={'text_prompt_tokens': 123, 'text_candidates_tokens': 21},
193
+ ),
158
194
  model_name='gemini-1.5-flash',
159
195
  timestamp=IsDatetime(),
196
+ vendor_details={'finish_reason': 'STOP'},
160
197
  ),
161
198
  ModelRequest(
162
199
  parts=[
@@ -211,10 +248,11 @@ async def test_google_model_retry(allow_model_requests: None, google_provider: G
211
248
  request_tokens=57,
212
249
  response_tokens=15,
213
250
  total_tokens=173,
214
- details={'thoughts_token_count': 101},
251
+ details={'thoughts_tokens': 101, 'text_prompt_tokens': 57},
215
252
  ),
216
253
  model_name='models/gemini-2.5-pro-preview-05-06',
217
254
  timestamp=IsDatetime(),
255
+ vendor_details={'finish_reason': 'STOP'},
218
256
  ),
219
257
  ModelRequest(
220
258
  parts=[
@@ -232,9 +270,16 @@ async def test_google_model_retry(allow_model_requests: None, google_provider: G
232
270
  content='I am sorry, I cannot fulfill this request. The country you provided is not supported.'
233
271
  )
234
272
  ],
235
- usage=Usage(requests=1, request_tokens=104, response_tokens=18, total_tokens=122, details={}),
273
+ usage=Usage(
274
+ requests=1,
275
+ request_tokens=104,
276
+ response_tokens=18,
277
+ total_tokens=122,
278
+ details={'text_prompt_tokens': 104},
279
+ ),
236
280
  model_name='models/gemini-2.5-pro-preview-05-06',
237
281
  timestamp=IsDatetime(),
282
+ vendor_details={'finish_reason': 'STOP'},
238
283
  ),
239
284
  ]
240
285
  )
@@ -488,9 +533,16 @@ async def test_google_model_instructions(allow_model_requests: None, google_prov
488
533
  ),
489
534
  ModelResponse(
490
535
  parts=[TextPart(content='The capital of France is Paris.\n')],
491
- usage=Usage(requests=1, request_tokens=13, response_tokens=8, total_tokens=21, details={}),
536
+ usage=Usage(
537
+ requests=1,
538
+ request_tokens=13,
539
+ response_tokens=8,
540
+ total_tokens=21,
541
+ details={'text_prompt_tokens': 13, 'text_candidates_tokens': 8},
542
+ ),
492
543
  model_name='gemini-2.0-flash',
493
544
  timestamp=IsDatetime(),
545
+ vendor_details={'finish_reason': 'STOP'},
494
546
  ),
495
547
  ]
496
548
  )