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