pydantic-ai 0.4.8__tar.gz → 0.4.10__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.
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/PKG-INFO +3 -3
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_gemini.py +24 -8
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_ag_ui.py +110 -12
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_agent.py +42 -1
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_streaming.py +36 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_tools.py +100 -2
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_toolsets.py +133 -1
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/.gitignore +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/LICENSE +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/Makefile +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/README.md +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/pyproject.toml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/__init__.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/assets/dummy.pdf +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/assets/kiwi.png +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/assets/marcelo.mp3 +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/assets/product_name.txt +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/assets/small_video.mp4 +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_agent_with_server_not_running.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_audio_resource.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_audio_resource_link.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_dict.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_error.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_image.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_image_resource.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_image_resource_link.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_multiple_items.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_none.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_str.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_text_resource.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_mcp/test_tool_returning_text_resource_link.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[anthropic].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[bedrock].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[cohere].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[gemini].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[google].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[groq].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[mistral].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/cassettes/test_settings/test_stop_settings[openai].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/conftest.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/__init__.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_dataset.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_evaluator_base.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_evaluator_common.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_evaluator_context.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_evaluator_spec.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_evaluators.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_llm_as_a_judge.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_otel.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_render_numbers.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_reporting.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_reports.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/test_utils.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/evals/utils.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/example_modules/README.md +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/example_modules/bank_database.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/example_modules/fake_database.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/example_modules/mcp_server.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/example_modules/weather_service.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/ext/__init__.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/ext/test_langchain.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/__init__.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/test_file_persistence.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/test_graph.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/test_mermaid.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/test_persistence.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/test_state.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/graph/test_utils.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/import_examples.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/json_body_serializer.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/mcp_server.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/__init__.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_model_empty_message_on_history.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_model_thinking_part_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_prompted_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_prompted_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_text_output_function.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_tool_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_anthropic_tool_with_thinking.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_empty_system_prompt.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_guardrail_config.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_other_parameters.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_performance_config.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_thinking_part_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_bedrock_multiple_documents_in_history.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_bedrock/test_video_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_cohere/test_cohere_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_cohere/test_cohere_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_deepseek/test_deepseek_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_deepseek/test_deepseek_model_thinking_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_download_item/test_download_item_application_octet_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_download_item/test_download_item_audio_mpeg.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_download_item/test_download_item_no_content_type.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_false.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_true.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_drop_exclusive_maximum.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_exclusive_minimum_and_maximum.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_native_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_native_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_prompted_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_prompted_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_prompted_output_with_tools.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_text_output_function.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_tool_config_any_with_tool_without_args.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_tool_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_gemini_youtube_video_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_labels_are_ignored_with_gla_provider.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini/test_video_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_labels.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[AudioUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[AudioUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[DocumentUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[DocumentUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[ImageUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[ImageUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl (YouTube)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_gemini_vertex/test_url_input_force_download.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_empty_assistant_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_empty_user_prompt.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_iter_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_max_tokens.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_multiple_documents_in_history.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_retry.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_safety_settings.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_structured_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_text_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_thinking_config.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_thinking_part_iter.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_top_p.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_vertex_labels.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_vertex_provider.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_video_as_binary_content_input_with_vendor_metadata.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_video_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_model_youtube_video_url_input_with_vendor_metadata.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_native_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_native_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_prompted_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_prompted_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_prompted_output_with_tools.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_text_output_function.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_timeout.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_tool_config_any_with_tool_without_args.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_tool_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[AudioUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[AudioUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[DocumentUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[DocumentUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[ImageUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[ImageUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl (YouTube)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl (gs)].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_google/test_google_url_input_force_download.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_groq_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_groq_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_groq_model_thinking_part_iter.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_hf_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_hf_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_hf_model_thinking_part_iter.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_max_completion_tokens[Qwen-Qwen2.5-72B-Instruct].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_max_completion_tokens[deepseek-ai-DeepSeek-R1-0528].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_max_completion_tokens[meta-llama-Llama-3.3-70B-Instruct].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_request_simple_success_with_vcr.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_request_simple_usage.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_simple_completion.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_huggingface/test_stream_completion.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_mistral/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_mistral/test_mistral_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_mistral/test_mistral_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_model_names/test_known_model_names.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_compatible_api_with_tool_calls_without_id.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_document_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_image_url_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_invalid_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_multiple_agent_tool_calls.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_audio_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_instructions_with_tool_calls_keep_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_model_thinking_part_iter.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_model_without_system_prompt.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_native_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_native_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_prompted_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_prompted_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_responses_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_text_output_function.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_openai_tool_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_reasoning_model_with_temperature.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_text_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_user_id.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai/test_valid_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_audio_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_native_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_native_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_image_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_builtin_tools.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_http_error.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_instructions.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_retry.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response_with_tool_call.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_output_type.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_effort.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_generate_summary.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_stream.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_system_prompt.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_openai_responses_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_prompted_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_prompted_output_multiple.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_reasoning_model_with_temperature.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_text_output_function.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/cassettes/test_openai_responses/test_tool_output.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/mock_async_stream.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_anthropic.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_bedrock.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_cohere.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_deepseek.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_download_item.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_fallback.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_gemini_vertex.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_google.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_groq.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_huggingface.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_instrumented.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_mcp_sampling.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_mistral.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_model.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_model_function.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_model_names.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_model_request_parameters.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_model_settings.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_model_test.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_openai.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/models/test_openai_responses.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/__init__.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/cassettes/test_google_vertex/test_vertexai_provider.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/cassettes/test_heroku/test_heroku_model_provider_claude_3_7_sonnet.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/cassettes/test_openrouter/test_openrouter_with_google_model.yaml +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_anthropic.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_azure.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_bedrock.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_cohere.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_deepseek.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_fireworks.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_github.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_google_gla.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_google_vertex.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_grok.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_groq.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_heroku.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_huggingface.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_mistral.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_moonshotai.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_openai.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_openrouter.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_provider_names.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_together.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/providers/test_vercel.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_a2a.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_cli.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_deps.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_direct.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_examples.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_format_as_xml.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_history_processor.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_json_body_serializer.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_live.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_logfire.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_mcp.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_messages.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_parts_manager.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_settings.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_tenacity.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_thinking_part.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_usage_limits.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/test_utils.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/typed_agent.py +0 -0
- {pydantic_ai-0.4.8 → pydantic_ai-0.4.10}/tests/typed_graph.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.10
|
|
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,11 +28,11 @@ 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[ag-ui,anthropic,bedrock,cli,cohere,evals,google,groq,huggingface,mcp,mistral,openai,retries,vertexai]==0.4.
|
|
31
|
+
Requires-Dist: pydantic-ai-slim[ag-ui,anthropic,bedrock,cli,cohere,evals,google,groq,huggingface,mcp,mistral,openai,retries,vertexai]==0.4.10
|
|
32
32
|
Provides-Extra: a2a
|
|
33
33
|
Requires-Dist: fasta2a>=0.4.1; extra == 'a2a'
|
|
34
34
|
Provides-Extra: examples
|
|
35
|
-
Requires-Dist: pydantic-ai-examples==0.4.
|
|
35
|
+
Requires-Dist: pydantic-ai-examples==0.4.10; extra == 'examples'
|
|
36
36
|
Provides-Extra: logfire
|
|
37
37
|
Requires-Dist: logfire>=3.11.0; extra == 'logfire'
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
@@ -194,7 +194,7 @@ async def test_require_response_tool(allow_model_requests: None):
|
|
|
194
194
|
|
|
195
195
|
async def test_json_def_replaced(allow_model_requests: None):
|
|
196
196
|
class Axis(BaseModel):
|
|
197
|
-
label: str
|
|
197
|
+
label: str = Field(default='<unlabeled axis>', description='The label of the axis')
|
|
198
198
|
|
|
199
199
|
class Chart(BaseModel):
|
|
200
200
|
x_axis: Axis
|
|
@@ -213,8 +213,14 @@ async def test_json_def_replaced(allow_model_requests: None):
|
|
|
213
213
|
{
|
|
214
214
|
'$defs': {
|
|
215
215
|
'Axis': {
|
|
216
|
-
'properties': {
|
|
217
|
-
|
|
216
|
+
'properties': {
|
|
217
|
+
'label': {
|
|
218
|
+
'default': '<unlabeled axis>',
|
|
219
|
+
'description': 'The label of the axis',
|
|
220
|
+
'title': 'Label',
|
|
221
|
+
'type': 'string',
|
|
222
|
+
}
|
|
223
|
+
},
|
|
218
224
|
'title': 'Axis',
|
|
219
225
|
'type': 'object',
|
|
220
226
|
},
|
|
@@ -268,17 +274,27 @@ async def test_json_def_replaced(allow_model_requests: None):
|
|
|
268
274
|
'items': {
|
|
269
275
|
'properties': {
|
|
270
276
|
'lat': {'type': 'number'},
|
|
271
|
-
'lng': {'type': 'number'},
|
|
277
|
+
'lng': {'default': 1.1, 'type': 'number'},
|
|
272
278
|
'chart': {
|
|
273
279
|
'properties': {
|
|
274
280
|
'x_axis': {
|
|
275
|
-
'properties': {
|
|
276
|
-
|
|
281
|
+
'properties': {
|
|
282
|
+
'label': {
|
|
283
|
+
'default': '<unlabeled axis>',
|
|
284
|
+
'description': 'The label of the axis',
|
|
285
|
+
'type': 'string',
|
|
286
|
+
}
|
|
287
|
+
},
|
|
277
288
|
'type': 'object',
|
|
278
289
|
},
|
|
279
290
|
'y_axis': {
|
|
280
|
-
'properties': {
|
|
281
|
-
|
|
291
|
+
'properties': {
|
|
292
|
+
'label': {
|
|
293
|
+
'default': '<unlabeled axis>',
|
|
294
|
+
'description': 'The label of the axis',
|
|
295
|
+
'type': 'string',
|
|
296
|
+
}
|
|
297
|
+
},
|
|
282
298
|
'type': 'object',
|
|
283
299
|
},
|
|
284
300
|
},
|
|
@@ -7,6 +7,7 @@ import contextlib
|
|
|
7
7
|
import json
|
|
8
8
|
import uuid
|
|
9
9
|
from collections.abc import AsyncIterator
|
|
10
|
+
from dataclasses import dataclass
|
|
10
11
|
from http import HTTPStatus
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
@@ -17,7 +18,9 @@ from dirty_equals import IsStr
|
|
|
17
18
|
from inline_snapshot import snapshot
|
|
18
19
|
from pydantic import BaseModel
|
|
19
20
|
|
|
21
|
+
from pydantic_ai._run_context import RunContext
|
|
20
22
|
from pydantic_ai.agent import Agent
|
|
23
|
+
from pydantic_ai.exceptions import UserError
|
|
21
24
|
from pydantic_ai.messages import ModelMessage
|
|
22
25
|
from pydantic_ai.models.function import (
|
|
23
26
|
AgentInfo,
|
|
@@ -27,8 +30,9 @@ from pydantic_ai.models.function import (
|
|
|
27
30
|
DeltaToolCalls,
|
|
28
31
|
FunctionModel,
|
|
29
32
|
)
|
|
33
|
+
from pydantic_ai.models.test import TestModel
|
|
30
34
|
from pydantic_ai.output import OutputDataT
|
|
31
|
-
from pydantic_ai.tools import AgentDepsT
|
|
35
|
+
from pydantic_ai.tools import AgentDepsT, ToolDefinition
|
|
32
36
|
|
|
33
37
|
from .conftest import IsSameStr
|
|
34
38
|
|
|
@@ -180,7 +184,7 @@ def create_input(
|
|
|
180
184
|
thread_id=thread_id,
|
|
181
185
|
run_id=uuid_str(),
|
|
182
186
|
messages=list(messages),
|
|
183
|
-
state=state,
|
|
187
|
+
state=dict(state) if state else {},
|
|
184
188
|
context=[],
|
|
185
189
|
tools=tools or [],
|
|
186
190
|
forwarded_props=None,
|
|
@@ -1050,9 +1054,19 @@ async def test_tool_local_then_ag_ui() -> None:
|
|
|
1050
1054
|
async def test_request_with_state() -> None:
|
|
1051
1055
|
"""Test request with state modification."""
|
|
1052
1056
|
|
|
1057
|
+
seen_states: list[int] = []
|
|
1058
|
+
|
|
1059
|
+
async def store_state(
|
|
1060
|
+
ctx: RunContext[StateDeps[StateInt]], tool_defs: list[ToolDefinition]
|
|
1061
|
+
) -> list[ToolDefinition]:
|
|
1062
|
+
seen_states.append(ctx.deps.state.value)
|
|
1063
|
+
ctx.deps.state.value += 1
|
|
1064
|
+
return tool_defs
|
|
1065
|
+
|
|
1053
1066
|
agent: Agent[StateDeps[StateInt], str] = Agent(
|
|
1054
1067
|
model=FunctionModel(stream_function=simple_stream),
|
|
1055
1068
|
deps_type=StateDeps[StateInt], # type: ignore[reportUnknownArgumentType]
|
|
1069
|
+
prepare_tools=store_state,
|
|
1056
1070
|
)
|
|
1057
1071
|
adapter = _Adapter(agent=agent)
|
|
1058
1072
|
run_inputs = [
|
|
@@ -1074,32 +1088,101 @@ async def test_request_with_state() -> None:
|
|
|
1074
1088
|
id='msg_3',
|
|
1075
1089
|
content='Hello, how are you?',
|
|
1076
1090
|
),
|
|
1091
|
+
),
|
|
1092
|
+
create_input(
|
|
1093
|
+
UserMessage(
|
|
1094
|
+
id='msg_4',
|
|
1095
|
+
content='Hello, how are you?',
|
|
1096
|
+
),
|
|
1077
1097
|
state=StateInt(value=42),
|
|
1078
1098
|
),
|
|
1079
1099
|
]
|
|
1080
1100
|
|
|
1081
|
-
deps = StateDeps(StateInt())
|
|
1101
|
+
deps = StateDeps(StateInt(value=0))
|
|
1082
1102
|
|
|
1083
|
-
last_value = deps.state.value
|
|
1084
1103
|
for run_input in run_inputs:
|
|
1085
1104
|
events = list[dict[str, Any]]()
|
|
1086
1105
|
async for event in adapter.run(run_input, deps=deps):
|
|
1087
1106
|
events.append(json.loads(event.removeprefix('data: ')))
|
|
1088
1107
|
|
|
1089
1108
|
assert events == simple_result()
|
|
1090
|
-
|
|
1091
|
-
|
|
1109
|
+
assert seen_states == snapshot(
|
|
1110
|
+
[
|
|
1111
|
+
41, # run msg_1, prepare_tools call 1
|
|
1112
|
+
42, # run msg_1, prepare_tools call 2
|
|
1113
|
+
0, # run msg_2, prepare_tools call 1
|
|
1114
|
+
1, # run msg_2, prepare_tools call 2
|
|
1115
|
+
0, # run msg_3, prepare_tools call 1
|
|
1116
|
+
1, # run msg_3, prepare_tools call 2
|
|
1117
|
+
42, # run msg_4, prepare_tools call 1
|
|
1118
|
+
43, # run msg_4, prepare_tools call 2
|
|
1119
|
+
]
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
async def test_request_with_state_without_handler() -> None:
|
|
1124
|
+
agent = Agent(model=FunctionModel(stream_function=simple_stream))
|
|
1125
|
+
adapter = _Adapter(agent=agent)
|
|
1126
|
+
run_input = create_input(
|
|
1127
|
+
UserMessage(
|
|
1128
|
+
id='msg_1',
|
|
1129
|
+
content='Hello, how are you?',
|
|
1130
|
+
),
|
|
1131
|
+
state=StateInt(value=41),
|
|
1132
|
+
)
|
|
1133
|
+
|
|
1134
|
+
with pytest.raises(
|
|
1135
|
+
UserError,
|
|
1136
|
+
match='AG-UI state is provided but `deps` of type `NoneType` does not implement the `StateHandler` protocol: it needs to be a dataclass with a non-optional `state` field.',
|
|
1137
|
+
):
|
|
1138
|
+
async for _ in adapter.run(run_input):
|
|
1139
|
+
pass
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
async def test_request_with_state_with_custom_handler() -> None:
|
|
1143
|
+
@dataclass
|
|
1144
|
+
class CustomStateDeps:
|
|
1145
|
+
state: dict[str, Any]
|
|
1146
|
+
|
|
1147
|
+
seen_states: list[dict[str, Any]] = []
|
|
1148
|
+
|
|
1149
|
+
async def store_state(ctx: RunContext[CustomStateDeps], tool_defs: list[ToolDefinition]) -> list[ToolDefinition]:
|
|
1150
|
+
seen_states.append(ctx.deps.state)
|
|
1151
|
+
return tool_defs
|
|
1152
|
+
|
|
1153
|
+
agent: Agent[CustomStateDeps, str] = Agent(
|
|
1154
|
+
model=FunctionModel(stream_function=simple_stream),
|
|
1155
|
+
deps_type=CustomStateDeps,
|
|
1156
|
+
prepare_tools=store_state,
|
|
1157
|
+
)
|
|
1158
|
+
adapter = _Adapter(agent=agent)
|
|
1159
|
+
run_input = create_input(
|
|
1160
|
+
UserMessage(
|
|
1161
|
+
id='msg_1',
|
|
1162
|
+
content='Hello, how are you?',
|
|
1163
|
+
),
|
|
1164
|
+
state={'value': 42},
|
|
1165
|
+
)
|
|
1166
|
+
|
|
1167
|
+
async for _ in adapter.run(run_input, deps=CustomStateDeps(state={'value': 0})):
|
|
1168
|
+
pass
|
|
1092
1169
|
|
|
1093
|
-
assert
|
|
1170
|
+
assert seen_states[-1] == {'value': 42}
|
|
1094
1171
|
|
|
1095
1172
|
|
|
1096
1173
|
async def test_concurrent_runs() -> None:
|
|
1097
1174
|
"""Test concurrent execution of multiple runs."""
|
|
1098
1175
|
import asyncio
|
|
1099
1176
|
|
|
1100
|
-
agent = Agent(
|
|
1101
|
-
model=
|
|
1177
|
+
agent: Agent[StateDeps[StateInt], str] = Agent(
|
|
1178
|
+
model=TestModel(),
|
|
1179
|
+
deps_type=StateDeps[StateInt], # type: ignore[reportUnknownArgumentType]
|
|
1102
1180
|
)
|
|
1181
|
+
|
|
1182
|
+
@agent.tool
|
|
1183
|
+
async def get_state(ctx: RunContext[StateDeps[StateInt]]) -> int:
|
|
1184
|
+
return ctx.deps.state.value
|
|
1185
|
+
|
|
1103
1186
|
adapter = _Adapter(agent=agent)
|
|
1104
1187
|
concurrent_tasks: list[asyncio.Task[list[dict[str, Any]]]] = []
|
|
1105
1188
|
|
|
@@ -1109,10 +1192,11 @@ async def test_concurrent_runs() -> None:
|
|
|
1109
1192
|
id=f'msg_{i}',
|
|
1110
1193
|
content=f'Message {i}',
|
|
1111
1194
|
),
|
|
1195
|
+
state=StateInt(value=i),
|
|
1112
1196
|
thread_id=f'test_thread_{i}',
|
|
1113
1197
|
)
|
|
1114
1198
|
|
|
1115
|
-
task = asyncio.create_task(collect_events_from_adapter(adapter, run_input))
|
|
1199
|
+
task = asyncio.create_task(collect_events_from_adapter(adapter, run_input, deps=StateDeps(StateInt())))
|
|
1116
1200
|
concurrent_tasks.append(task)
|
|
1117
1201
|
|
|
1118
1202
|
results = await asyncio.gather(*concurrent_tasks)
|
|
@@ -1121,9 +1205,23 @@ async def test_concurrent_runs() -> None:
|
|
|
1121
1205
|
for i, events in enumerate(results):
|
|
1122
1206
|
assert events == [
|
|
1123
1207
|
{'type': 'RUN_STARTED', 'threadId': f'test_thread_{i}', 'runId': (run_id := IsSameStr())},
|
|
1208
|
+
{
|
|
1209
|
+
'type': 'TOOL_CALL_START',
|
|
1210
|
+
'toolCallId': (tool_call_id := IsSameStr()),
|
|
1211
|
+
'toolCallName': 'get_state',
|
|
1212
|
+
'parentMessageId': IsStr(),
|
|
1213
|
+
},
|
|
1214
|
+
{'type': 'TOOL_CALL_END', 'toolCallId': tool_call_id},
|
|
1215
|
+
{
|
|
1216
|
+
'type': 'TOOL_CALL_RESULT',
|
|
1217
|
+
'messageId': IsStr(),
|
|
1218
|
+
'toolCallId': tool_call_id,
|
|
1219
|
+
'content': str(i),
|
|
1220
|
+
'role': 'tool',
|
|
1221
|
+
},
|
|
1124
1222
|
{'type': 'TEXT_MESSAGE_START', 'messageId': (message_id := IsSameStr()), 'role': 'assistant'},
|
|
1125
|
-
{'type': 'TEXT_MESSAGE_CONTENT', 'messageId': message_id, 'delta': '
|
|
1126
|
-
{'type': 'TEXT_MESSAGE_CONTENT', 'messageId': message_id, 'delta': '(
|
|
1223
|
+
{'type': 'TEXT_MESSAGE_CONTENT', 'messageId': message_id, 'delta': '{"get_s'},
|
|
1224
|
+
{'type': 'TEXT_MESSAGE_CONTENT', 'messageId': message_id, 'delta': 'tate":' + str(i) + '}'},
|
|
1127
1225
|
{'type': 'TEXT_MESSAGE_END', 'messageId': message_id},
|
|
1128
1226
|
{'type': 'RUN_FINISHED', 'threadId': f'test_thread_{i}', 'runId': run_id},
|
|
1129
1227
|
]
|
|
@@ -3866,7 +3866,7 @@ def test_prepare_output_tools():
|
|
|
3866
3866
|
)
|
|
3867
3867
|
|
|
3868
3868
|
|
|
3869
|
-
async def
|
|
3869
|
+
async def test_explicit_context_manager():
|
|
3870
3870
|
try:
|
|
3871
3871
|
from pydantic_ai.mcp import MCPServerStdio
|
|
3872
3872
|
except ImportError: # pragma: lax no cover
|
|
@@ -3886,6 +3886,47 @@ async def test_context_manager():
|
|
|
3886
3886
|
assert server2.is_running
|
|
3887
3887
|
|
|
3888
3888
|
|
|
3889
|
+
async def test_implicit_context_manager():
|
|
3890
|
+
try:
|
|
3891
|
+
from pydantic_ai.mcp import MCPServerStdio
|
|
3892
|
+
except ImportError: # pragma: lax no cover
|
|
3893
|
+
pytest.skip('mcp is not installed')
|
|
3894
|
+
|
|
3895
|
+
server1 = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
3896
|
+
server2 = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
3897
|
+
toolset = CombinedToolset([server1, PrefixedToolset(server2, 'prefix')])
|
|
3898
|
+
agent = Agent('test', toolsets=[toolset])
|
|
3899
|
+
|
|
3900
|
+
async with agent.iter(
|
|
3901
|
+
user_prompt='Hello',
|
|
3902
|
+
):
|
|
3903
|
+
assert server1.is_running
|
|
3904
|
+
assert server2.is_running
|
|
3905
|
+
|
|
3906
|
+
|
|
3907
|
+
def test_parallel_mcp_calls():
|
|
3908
|
+
try:
|
|
3909
|
+
from pydantic_ai.mcp import MCPServerStdio
|
|
3910
|
+
except ImportError: # pragma: lax no cover
|
|
3911
|
+
pytest.skip('mcp is not installed')
|
|
3912
|
+
|
|
3913
|
+
async def call_tools_parallel(messages: list[ModelMessage], info: AgentInfo) -> ModelResponse:
|
|
3914
|
+
if len(messages) == 1:
|
|
3915
|
+
return ModelResponse(
|
|
3916
|
+
parts=[
|
|
3917
|
+
ToolCallPart(tool_name='get_none'),
|
|
3918
|
+
ToolCallPart(tool_name='get_multiple_items'),
|
|
3919
|
+
]
|
|
3920
|
+
)
|
|
3921
|
+
else:
|
|
3922
|
+
return ModelResponse(parts=[TextPart('finished')])
|
|
3923
|
+
|
|
3924
|
+
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
3925
|
+
agent = Agent(FunctionModel(call_tools_parallel), toolsets=[server])
|
|
3926
|
+
result = agent.run_sync()
|
|
3927
|
+
assert result.output == snapshot('finished')
|
|
3928
|
+
|
|
3929
|
+
|
|
3889
3930
|
def test_set_mcp_sampling_model():
|
|
3890
3931
|
try:
|
|
3891
3932
|
from pydantic_ai.mcp import MCPServerStdio
|
|
@@ -1108,6 +1108,42 @@ async def test_iter_stream_structured_output():
|
|
|
1108
1108
|
)
|
|
1109
1109
|
|
|
1110
1110
|
|
|
1111
|
+
async def test_iter_stream_output_tool_dont_hit_retry_limit():
|
|
1112
|
+
class CityLocation(BaseModel):
|
|
1113
|
+
city: str
|
|
1114
|
+
country: str | None = None
|
|
1115
|
+
|
|
1116
|
+
async def text_stream(_messages: list[ModelMessage], agent_info: AgentInfo) -> AsyncIterator[DeltaToolCalls]:
|
|
1117
|
+
"""Stream partial JSON data that will initially fail validation."""
|
|
1118
|
+
assert agent_info.output_tools is not None
|
|
1119
|
+
assert len(agent_info.output_tools) == 1
|
|
1120
|
+
name = agent_info.output_tools[0].name
|
|
1121
|
+
|
|
1122
|
+
yield {0: DeltaToolCall(name=name)}
|
|
1123
|
+
yield {0: DeltaToolCall(json_args='{"c')}
|
|
1124
|
+
yield {0: DeltaToolCall(json_args='ity":')}
|
|
1125
|
+
yield {0: DeltaToolCall(json_args=' "Mex')}
|
|
1126
|
+
yield {0: DeltaToolCall(json_args='ico City",')}
|
|
1127
|
+
yield {0: DeltaToolCall(json_args=' "cou')}
|
|
1128
|
+
yield {0: DeltaToolCall(json_args='ntry": "Mexico"}')}
|
|
1129
|
+
|
|
1130
|
+
agent = Agent(FunctionModel(stream_function=text_stream), output_type=CityLocation)
|
|
1131
|
+
|
|
1132
|
+
async with agent.iter('Generate city info') as run:
|
|
1133
|
+
async for node in run:
|
|
1134
|
+
if agent.is_model_request_node(node):
|
|
1135
|
+
async with node.stream(run.ctx) as stream:
|
|
1136
|
+
assert [c async for c in stream.stream_output(debounce_by=None)] == snapshot(
|
|
1137
|
+
[
|
|
1138
|
+
CityLocation(city='Mex'),
|
|
1139
|
+
CityLocation(city='Mexico City'),
|
|
1140
|
+
CityLocation(city='Mexico City'),
|
|
1141
|
+
CityLocation(city='Mexico City', country='Mexico'),
|
|
1142
|
+
CityLocation(city='Mexico City', country='Mexico'),
|
|
1143
|
+
]
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
|
|
1111
1147
|
def test_function_tool_event_tool_call_id_properties():
|
|
1112
1148
|
"""Ensure that the `tool_call_id` property on function tool events mirrors the underlying part's ID."""
|
|
1113
1149
|
# Prepare a ToolCallPart with a fixed ID
|
|
@@ -13,7 +13,16 @@ from typing_extensions import TypedDict
|
|
|
13
13
|
|
|
14
14
|
from pydantic_ai import Agent, RunContext, Tool, UserError
|
|
15
15
|
from pydantic_ai.exceptions import ModelRetry, UnexpectedModelBehavior
|
|
16
|
-
from pydantic_ai.messages import
|
|
16
|
+
from pydantic_ai.messages import (
|
|
17
|
+
ModelMessage,
|
|
18
|
+
ModelRequest,
|
|
19
|
+
ModelResponse,
|
|
20
|
+
TextPart,
|
|
21
|
+
ToolCallPart,
|
|
22
|
+
ToolReturn,
|
|
23
|
+
ToolReturnPart,
|
|
24
|
+
UserPromptPart,
|
|
25
|
+
)
|
|
17
26
|
from pydantic_ai.models.function import AgentInfo, FunctionModel
|
|
18
27
|
from pydantic_ai.models.test import TestModel
|
|
19
28
|
from pydantic_ai.output import DeferredToolCalls, ToolOutput
|
|
@@ -21,8 +30,9 @@ from pydantic_ai.tools import ToolDefinition
|
|
|
21
30
|
from pydantic_ai.toolsets.deferred import DeferredToolset
|
|
22
31
|
from pydantic_ai.toolsets.function import FunctionToolset
|
|
23
32
|
from pydantic_ai.toolsets.prefixed import PrefixedToolset
|
|
33
|
+
from pydantic_ai.usage import Usage
|
|
24
34
|
|
|
25
|
-
from .conftest import IsStr
|
|
35
|
+
from .conftest import IsDatetime, IsStr
|
|
26
36
|
|
|
27
37
|
|
|
28
38
|
def test_tool_no_ctx():
|
|
@@ -1321,3 +1331,91 @@ def test_output_type_deferred_tool_calls_by_itself():
|
|
|
1321
1331
|
def test_output_type_empty():
|
|
1322
1332
|
with pytest.raises(UserError, match='At least one output type must be provided.'):
|
|
1323
1333
|
Agent(TestModel(), output_type=[])
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
def test_parallel_tool_return():
|
|
1337
|
+
def llm(messages: list[ModelMessage], info: AgentInfo) -> ModelResponse:
|
|
1338
|
+
if len(messages) == 1:
|
|
1339
|
+
return ModelResponse(
|
|
1340
|
+
parts=[ToolCallPart('get_price', {'fruit': 'apple'}), ToolCallPart('get_price', {'fruit': 'banana'})]
|
|
1341
|
+
)
|
|
1342
|
+
else:
|
|
1343
|
+
return ModelResponse(
|
|
1344
|
+
parts=[
|
|
1345
|
+
TextPart('Done!'),
|
|
1346
|
+
]
|
|
1347
|
+
)
|
|
1348
|
+
|
|
1349
|
+
agent = Agent(FunctionModel(llm))
|
|
1350
|
+
|
|
1351
|
+
@agent.tool_plain
|
|
1352
|
+
def get_price(fruit: str) -> ToolReturn:
|
|
1353
|
+
return ToolReturn(
|
|
1354
|
+
return_value=10.0,
|
|
1355
|
+
content=f'The price of {fruit} is 10.0',
|
|
1356
|
+
metadata={'foo': 'bar'},
|
|
1357
|
+
)
|
|
1358
|
+
|
|
1359
|
+
result = agent.run_sync('What do an apple and a banana cost?')
|
|
1360
|
+
|
|
1361
|
+
assert result.all_messages() == snapshot(
|
|
1362
|
+
[
|
|
1363
|
+
ModelRequest(
|
|
1364
|
+
parts=[
|
|
1365
|
+
UserPromptPart(
|
|
1366
|
+
content='What do an apple and a banana cost?',
|
|
1367
|
+
timestamp=IsDatetime(),
|
|
1368
|
+
)
|
|
1369
|
+
]
|
|
1370
|
+
),
|
|
1371
|
+
ModelResponse(
|
|
1372
|
+
parts=[
|
|
1373
|
+
ToolCallPart(
|
|
1374
|
+
tool_name='get_price',
|
|
1375
|
+
args={'fruit': 'apple'},
|
|
1376
|
+
tool_call_id=IsStr(),
|
|
1377
|
+
),
|
|
1378
|
+
ToolCallPart(
|
|
1379
|
+
tool_name='get_price',
|
|
1380
|
+
args={'fruit': 'banana'},
|
|
1381
|
+
tool_call_id=IsStr(),
|
|
1382
|
+
),
|
|
1383
|
+
],
|
|
1384
|
+
usage=Usage(requests=1, request_tokens=58, response_tokens=10, total_tokens=68),
|
|
1385
|
+
model_name='function:llm:',
|
|
1386
|
+
timestamp=IsDatetime(),
|
|
1387
|
+
),
|
|
1388
|
+
ModelRequest(
|
|
1389
|
+
parts=[
|
|
1390
|
+
ToolReturnPart(
|
|
1391
|
+
tool_name='get_price',
|
|
1392
|
+
content=10.0,
|
|
1393
|
+
tool_call_id=IsStr(),
|
|
1394
|
+
metadata={'foo': 'bar'},
|
|
1395
|
+
timestamp=IsDatetime(),
|
|
1396
|
+
),
|
|
1397
|
+
ToolReturnPart(
|
|
1398
|
+
tool_name='get_price',
|
|
1399
|
+
content=10.0,
|
|
1400
|
+
tool_call_id=IsStr(),
|
|
1401
|
+
metadata={'foo': 'bar'},
|
|
1402
|
+
timestamp=IsDatetime(),
|
|
1403
|
+
),
|
|
1404
|
+
UserPromptPart(
|
|
1405
|
+
content='The price of apple is 10.0',
|
|
1406
|
+
timestamp=IsDatetime(),
|
|
1407
|
+
),
|
|
1408
|
+
UserPromptPart(
|
|
1409
|
+
content='The price of banana is 10.0',
|
|
1410
|
+
timestamp=IsDatetime(),
|
|
1411
|
+
),
|
|
1412
|
+
]
|
|
1413
|
+
),
|
|
1414
|
+
ModelResponse(
|
|
1415
|
+
parts=[TextPart(content='Done!')],
|
|
1416
|
+
usage=Usage(requests=1, request_tokens=76, response_tokens=11, total_tokens=87),
|
|
1417
|
+
model_name='function:llm:',
|
|
1418
|
+
timestamp=IsDatetime(),
|
|
1419
|
+
),
|
|
1420
|
+
]
|
|
1421
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
+
from collections import defaultdict
|
|
4
5
|
from dataclasses import dataclass, replace
|
|
5
6
|
from typing import TypeVar
|
|
6
7
|
from unittest.mock import AsyncMock
|
|
@@ -10,7 +11,7 @@ from inline_snapshot import snapshot
|
|
|
10
11
|
|
|
11
12
|
from pydantic_ai._run_context import RunContext
|
|
12
13
|
from pydantic_ai._tool_manager import ToolManager
|
|
13
|
-
from pydantic_ai.exceptions import UserError
|
|
14
|
+
from pydantic_ai.exceptions import ModelRetry, ToolRetryError, UnexpectedModelBehavior, UserError
|
|
14
15
|
from pydantic_ai.messages import ToolCallPart
|
|
15
16
|
from pydantic_ai.models.test import TestModel
|
|
16
17
|
from pydantic_ai.tools import ToolDefinition
|
|
@@ -494,3 +495,134 @@ async def test_context_manager_failed_initialization():
|
|
|
494
495
|
pass
|
|
495
496
|
|
|
496
497
|
assert server1.is_running is False
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
async def test_tool_manager_retry_logic():
|
|
501
|
+
"""Test the retry logic with failed_tools and for_run_step method."""
|
|
502
|
+
|
|
503
|
+
@dataclass
|
|
504
|
+
class TestDeps:
|
|
505
|
+
pass
|
|
506
|
+
|
|
507
|
+
# Create a toolset with tools that can fail
|
|
508
|
+
toolset = FunctionToolset[TestDeps](max_retries=2)
|
|
509
|
+
call_count: defaultdict[str, int] = defaultdict(int)
|
|
510
|
+
|
|
511
|
+
@toolset.tool
|
|
512
|
+
def failing_tool(x: int) -> int:
|
|
513
|
+
"""A tool that always fails"""
|
|
514
|
+
call_count['failing_tool'] += 1
|
|
515
|
+
raise ModelRetry('This tool always fails')
|
|
516
|
+
|
|
517
|
+
@toolset.tool
|
|
518
|
+
def other_tool(x: int) -> int:
|
|
519
|
+
"""A tool that works"""
|
|
520
|
+
call_count['other_tool'] += 1
|
|
521
|
+
return x * 2
|
|
522
|
+
|
|
523
|
+
# Create initial context and tool manager
|
|
524
|
+
initial_context = build_run_context(TestDeps())
|
|
525
|
+
tool_manager = await ToolManager[TestDeps].build(toolset, initial_context)
|
|
526
|
+
|
|
527
|
+
# Initially no failed tools
|
|
528
|
+
assert tool_manager.failed_tools == set()
|
|
529
|
+
assert initial_context.retries == {}
|
|
530
|
+
|
|
531
|
+
# Call the failing tool - should add to failed_tools
|
|
532
|
+
with pytest.raises(ToolRetryError):
|
|
533
|
+
await tool_manager.handle_call(ToolCallPart(tool_name='failing_tool', args={'x': 1}))
|
|
534
|
+
|
|
535
|
+
assert tool_manager.failed_tools == {'failing_tool'}
|
|
536
|
+
assert call_count['failing_tool'] == 1
|
|
537
|
+
|
|
538
|
+
# Call the working tool - should not add to failed_tools
|
|
539
|
+
result = await tool_manager.handle_call(ToolCallPart(tool_name='other_tool', args={'x': 3}))
|
|
540
|
+
assert result == 6
|
|
541
|
+
assert tool_manager.failed_tools == {'failing_tool'} # unchanged
|
|
542
|
+
assert call_count['other_tool'] == 1
|
|
543
|
+
|
|
544
|
+
# Test for_run_step - should create new tool manager with updated retry counts
|
|
545
|
+
new_context = build_run_context(TestDeps())
|
|
546
|
+
new_tool_manager = await tool_manager.for_run_step(new_context)
|
|
547
|
+
|
|
548
|
+
# The new tool manager should have retry count for the failed tool
|
|
549
|
+
assert new_tool_manager.ctx.retries == {'failing_tool': 1}
|
|
550
|
+
assert new_tool_manager.failed_tools == set() # reset for new run step
|
|
551
|
+
|
|
552
|
+
# Call the failing tool again in the new manager - should have retry=1
|
|
553
|
+
with pytest.raises(ToolRetryError):
|
|
554
|
+
await new_tool_manager.handle_call(ToolCallPart(tool_name='failing_tool', args={'x': 1}))
|
|
555
|
+
|
|
556
|
+
# Call the failing tool another time in the new manager
|
|
557
|
+
with pytest.raises(ToolRetryError):
|
|
558
|
+
await new_tool_manager.handle_call(ToolCallPart(tool_name='failing_tool', args={'x': 1}))
|
|
559
|
+
|
|
560
|
+
# Call the failing tool a third time in the new manager
|
|
561
|
+
with pytest.raises(ToolRetryError):
|
|
562
|
+
await new_tool_manager.handle_call(ToolCallPart(tool_name='failing_tool', args={'x': 1}))
|
|
563
|
+
|
|
564
|
+
assert new_tool_manager.failed_tools == {'failing_tool'}
|
|
565
|
+
assert call_count['failing_tool'] == 4
|
|
566
|
+
|
|
567
|
+
# Create another run step
|
|
568
|
+
another_context = build_run_context(TestDeps())
|
|
569
|
+
another_tool_manager = await new_tool_manager.for_run_step(another_context)
|
|
570
|
+
|
|
571
|
+
# Should now have retry count of 2 for failing_tool
|
|
572
|
+
assert another_tool_manager.ctx.retries == {'failing_tool': 2}
|
|
573
|
+
assert another_tool_manager.failed_tools == set()
|
|
574
|
+
|
|
575
|
+
# Call the failing tool _again_, now we should finally hit the limit
|
|
576
|
+
with pytest.raises(UnexpectedModelBehavior, match="Tool 'failing_tool' exceeded max retries count of 2"):
|
|
577
|
+
await another_tool_manager.handle_call(ToolCallPart(tool_name='failing_tool', args={'x': 1}))
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
async def test_tool_manager_multiple_failed_tools():
|
|
581
|
+
"""Test retry logic when multiple tools fail in the same run step."""
|
|
582
|
+
|
|
583
|
+
@dataclass
|
|
584
|
+
class TestDeps:
|
|
585
|
+
pass
|
|
586
|
+
|
|
587
|
+
toolset = FunctionToolset[TestDeps]()
|
|
588
|
+
|
|
589
|
+
@toolset.tool
|
|
590
|
+
def tool_a(x: int) -> int:
|
|
591
|
+
"""Tool A that fails"""
|
|
592
|
+
raise ModelRetry('Tool A fails')
|
|
593
|
+
|
|
594
|
+
@toolset.tool
|
|
595
|
+
def tool_b(x: int) -> int:
|
|
596
|
+
"""Tool B that fails"""
|
|
597
|
+
raise ModelRetry('Tool B fails')
|
|
598
|
+
|
|
599
|
+
@toolset.tool
|
|
600
|
+
def tool_c(x: int) -> int:
|
|
601
|
+
"""Tool C that works"""
|
|
602
|
+
return x * 3
|
|
603
|
+
|
|
604
|
+
# Create tool manager
|
|
605
|
+
context = build_run_context(TestDeps())
|
|
606
|
+
tool_manager = await ToolManager[TestDeps].build(toolset, context)
|
|
607
|
+
|
|
608
|
+
# Call tool_a - should fail and be added to failed_tools
|
|
609
|
+
with pytest.raises(ToolRetryError):
|
|
610
|
+
await tool_manager.handle_call(ToolCallPart(tool_name='tool_a', args={'x': 1}))
|
|
611
|
+
assert tool_manager.failed_tools == {'tool_a'}
|
|
612
|
+
|
|
613
|
+
# Call tool_b - should also fail and be added to failed_tools
|
|
614
|
+
with pytest.raises(ToolRetryError):
|
|
615
|
+
await tool_manager.handle_call(ToolCallPart(tool_name='tool_b', args={'x': 1}))
|
|
616
|
+
assert tool_manager.failed_tools == {'tool_a', 'tool_b'}
|
|
617
|
+
|
|
618
|
+
# Call tool_c - should succeed and not be added to failed_tools
|
|
619
|
+
result = await tool_manager.handle_call(ToolCallPart(tool_name='tool_c', args={'x': 2}))
|
|
620
|
+
assert result == 6
|
|
621
|
+
assert tool_manager.failed_tools == {'tool_a', 'tool_b'} # unchanged
|
|
622
|
+
|
|
623
|
+
# Create next run step - should have retry counts for both failed tools
|
|
624
|
+
new_context = build_run_context(TestDeps())
|
|
625
|
+
new_tool_manager = await tool_manager.for_run_step(new_context)
|
|
626
|
+
|
|
627
|
+
assert new_tool_manager.ctx.retries == {'tool_a': 1, 'tool_b': 1}
|
|
628
|
+
assert new_tool_manager.failed_tools == set() # reset for new run step
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|