pydantic-ai 0.3.1__tar.gz → 0.3.2__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.3.1 → pydantic_ai-0.3.2}/.gitignore +1 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/Makefile +3 -3
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/PKG-INFO +3 -3
- pydantic_ai-0.3.2/tests/example_modules/mcp_server.py +26 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/mcp_server.py +18 -1
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_cohere/test_cohere_model_thinking_part.yaml +1 -1
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_cohere.py +1 -1
- pydantic_ai-0.3.2/tests/models/test_mcp_sampling.py +135 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_examples.py +41 -34
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_mcp.py +96 -13
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/LICENSE +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/README.md +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/pyproject.toml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/assets/dummy.pdf +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/assets/kiwi.png +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/assets/marcelo.mp3 +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/assets/small_video.mp4 +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_audio_resource.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_dict.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_error.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_image.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_image_resource.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_multiple_items.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_none.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_str.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_text_resource.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[anthropic].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[bedrock].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[cohere].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[gemini].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[google].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[groq].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[mistral].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_settings/test_stop_settings[openai].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/conftest.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_dataset.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_evaluator_base.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_evaluator_common.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_evaluator_context.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_evaluator_spec.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_evaluators.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_llm_as_a_judge.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_otel.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_render_numbers.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_reporting.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_reports.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/test_utils.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/evals/utils.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/example_modules/README.md +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/example_modules/bank_database.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/example_modules/fake_database.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/example_modules/weather_service.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/ext/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/ext/test_langchain.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/fasta2a/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/fasta2a/test_applications.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/test_file_persistence.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/test_graph.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/test_mermaid.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/test_persistence.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/test_state.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/graph/test_utils.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/import_examples.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/json_body_serializer.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_anthropic_model_empty_message_on_history.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_anthropic_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_anthropic_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_anthropic_model_thinking_part_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_empty_system_prompt.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_guardrail_config.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_other_parameters.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_performance_config.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_thinking_part_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_bedrock_multiple_documents_in_history.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_bedrock/test_video_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_cohere/test_cohere_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_deepseek/test_deepseek_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_download_item/test_download_item_application_octet_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_download_item/test_download_item_no_content_type.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_false.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_true.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_drop_exclusive_maximum.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_exclusive_minimum_and_maximum.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_tool_config_any_with_tool_without_args.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_gemini_youtube_video_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_labels_are_ignored_with_gla_provider.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini/test_video_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_labels.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[AudioUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[AudioUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[DocumentUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[DocumentUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[ImageUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[ImageUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl (YouTube)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input[VideoUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_gemini_vertex/test_url_input_force_download.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_empty_user_prompt.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_image_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_iter_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_max_tokens.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_multiple_documents_in_history.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_retry.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_safety_settings.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_structured_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_text_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_thinking_config.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_thinking_part_iter.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_top_p.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_vertex_labels.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_vertex_provider.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_model_video_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_timeout.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_tool_config_any_with_tool_without_args.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[AudioUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[AudioUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[DocumentUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[DocumentUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[ImageUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[ImageUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl (YouTube)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl (gs)].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input[VideoUrl].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_google/test_google_url_input_force_download.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_groq/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_groq/test_groq_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_groq/test_groq_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_groq/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_mistral/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_mistral/test_mistral_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_mistral/test_mistral_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_model_names/test_known_model_names.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_compatible_api_with_tool_calls_without_id.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_document_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_image_url_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_multiple_agent_tool_calls.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_audio_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_instructions_with_tool_calls_keep_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_model_thinking_part_iter.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_model_without_system_prompt.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_openai_responses_model_thinking_part.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_reasoning_model_with_temperature.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai/test_user_id.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_audio_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_image_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_builtin_tools.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_http_error.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_instructions.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_retry.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response_with_tool_call.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_output_type.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_effort.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_generate_summary.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_stream.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_system_prompt.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_openai_responses_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/cassettes/test_openai_responses/test_reasoning_model_with_temperature.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/mock_async_stream.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_anthropic.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_bedrock.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_deepseek.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_download_item.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_fallback.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_gemini.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_gemini_vertex.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_google.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_groq.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_instrumented.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_mistral.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_model.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_model_function.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_model_names.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_model_request_parameters.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_model_test.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_openai.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/models/test_openai_responses.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/__init__.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/cassettes/test_google_vertex/test_vertexai_provider.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/cassettes/test_heroku/test_heroku_model_provider_claude_3_7_sonnet.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/cassettes/test_openrouter/test_openrouter_with_google_model.yaml +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_anthropic.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_azure.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_bedrock.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_cohere.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_deepseek.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_fireworks.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_google_gla.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_google_vertex.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_grok.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_groq.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_heroku.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_mistral.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_openai.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_openrouter.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_provider_names.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/providers/test_together.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_a2a.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_agent.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_cli.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_deps.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_direct.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_format_as_xml.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_history_processor.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_json_body_serializer.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_live.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_logfire.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_messages.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_parts_manager.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_settings.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_streaming.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_thinking_part.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_tools.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_usage_limits.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/test_utils.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/typed_agent.py +0 -0
- {pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/typed_graph.py +0 -0
|
@@ -64,7 +64,7 @@ test: ## Run tests and collect coverage data
|
|
|
64
64
|
@uv run coverage report
|
|
65
65
|
|
|
66
66
|
.PHONY: test-fast
|
|
67
|
-
test-fast: ## Same as test except no coverage
|
|
67
|
+
test-fast: ## Same as test except no coverage and 4x faster depending on hardware
|
|
68
68
|
uv run pytest -n auto --dist=loadgroup
|
|
69
69
|
|
|
70
70
|
.PHONY: test-all-python
|
|
@@ -78,12 +78,12 @@ test-all-python: ## Run tests on Python 3.9 to 3.13
|
|
|
78
78
|
@uv run coverage report
|
|
79
79
|
|
|
80
80
|
.PHONY: testcov
|
|
81
|
-
testcov: test ## Run tests and generate
|
|
81
|
+
testcov: test ## Run tests and generate an HTML coverage report
|
|
82
82
|
@echo "building coverage html"
|
|
83
83
|
@uv run coverage html
|
|
84
84
|
|
|
85
85
|
.PHONY: test-mrp
|
|
86
|
-
test-mrp: ## Build and
|
|
86
|
+
test-mrp: ## Build and tests of mcp-run-python
|
|
87
87
|
cd mcp-run-python && deno task build
|
|
88
88
|
uv run --package mcp-run-python pytest mcp-run-python -v
|
|
89
89
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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.3.
|
|
31
|
+
Requires-Dist: pydantic-ai-slim[a2a,anthropic,bedrock,cli,cohere,evals,google,groq,mcp,mistral,openai,vertexai]==0.3.2
|
|
32
32
|
Provides-Extra: examples
|
|
33
|
-
Requires-Dist: pydantic-ai-examples==0.3.
|
|
33
|
+
Requires-Dist: pydantic-ai-examples==0.3.2; extra == 'examples'
|
|
34
34
|
Provides-Extra: logfire
|
|
35
35
|
Requires-Dist: logfire>=3.11.0; extra == 'logfire'
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import Context, FastMCP
|
|
4
|
+
from mcp.server.session import ServerSessionT
|
|
5
|
+
from mcp.shared.context import LifespanContextT, RequestT
|
|
6
|
+
|
|
7
|
+
mcp = FastMCP('PydanticAI MCP Server')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@mcp.tool()
|
|
11
|
+
async def echo_deps(ctx: Context[ServerSessionT, LifespanContextT, RequestT]) -> dict[str, Any]:
|
|
12
|
+
"""Echo the run context.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
ctx: Context object containing request and session information.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
Dictionary with an echo message and the deps.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
deps: Any = getattr(ctx.request_context.meta, 'deps')
|
|
22
|
+
return {'echo': 'This is an echo message', 'deps': deps}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == '__main__':
|
|
26
|
+
mcp.run()
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
from mcp.server.fastmcp import Context, FastMCP, Image
|
|
6
6
|
from mcp.server.session import ServerSessionT
|
|
7
7
|
from mcp.shared.context import LifespanContextT, RequestT
|
|
8
|
-
from mcp.types import BlobResourceContents, EmbeddedResource, TextResourceContents
|
|
8
|
+
from mcp.types import BlobResourceContents, EmbeddedResource, SamplingMessage, TextContent, TextResourceContents
|
|
9
9
|
from pydantic import AnyUrl
|
|
10
10
|
|
|
11
11
|
mcp = FastMCP('PydanticAI MCP Server')
|
|
@@ -136,6 +136,23 @@ async def echo_deps(ctx: Context[ServerSessionT, LifespanContextT, RequestT]) ->
|
|
|
136
136
|
return {'echo': 'This is an echo message', 'deps': deps}
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
@mcp.tool()
|
|
140
|
+
async def use_sampling(ctx: Context, foo: str) -> str: # type: ignore
|
|
141
|
+
"""Use sampling callback."""
|
|
142
|
+
|
|
143
|
+
result = await ctx.session.create_message(
|
|
144
|
+
[
|
|
145
|
+
SamplingMessage(role='assistant', content=TextContent(type='text', text='')),
|
|
146
|
+
SamplingMessage(role='user', content=TextContent(type='text', text=foo)),
|
|
147
|
+
],
|
|
148
|
+
max_tokens=1_024,
|
|
149
|
+
system_prompt='this is a test of MCP sampling',
|
|
150
|
+
temperature=0.5,
|
|
151
|
+
stop_sequences=['potato'],
|
|
152
|
+
)
|
|
153
|
+
return result.model_dump_json(indent=2)
|
|
154
|
+
|
|
155
|
+
|
|
139
156
|
@mcp._mcp_server.set_logging_level() # pyright: ignore[reportPrivateUsage]
|
|
140
157
|
async def set_logging_level(level: str) -> None:
|
|
141
158
|
global log_level
|
|
@@ -258,7 +258,7 @@ interactions:
|
|
|
258
258
|
- **Bridge or Ferry:** If available, use a bridge or a ferry service. These are typically the safest and most reliable methods for crossing a river.
|
|
259
259
|
|
|
260
260
|
3. **Prepare and Pack Essential Items:**
|
|
261
|
-
- **Life Jacket/Personal
|
|
261
|
+
- **Life Jacket/Personal Flotation Device (PFD):** Always wear a life jacket or PFD when crossing a river, especially if swimming or using a boat.
|
|
262
262
|
- **First-Aid Kit:** Carry a basic first-aid kit to handle any minor injuries that might occur during the crossing.
|
|
263
263
|
- **Map and Compass:** Navigate the river and its surroundings with the help of a map and compass, especially if you're in an unfamiliar area.
|
|
264
264
|
- **Communication Device:** Have a means of communication, such as a satellite phone or a personal locator beacon, especially in remote areas.
|
|
@@ -512,7 +512,7 @@ Crossing a river can be a different challenge compared to crossing a street, and
|
|
|
512
512
|
- **Bridge or Ferry:** If available, use a bridge or a ferry service. These are typically the safest and most reliable methods for crossing a river.
|
|
513
513
|
|
|
514
514
|
3. **Prepare and Pack Essential Items:**
|
|
515
|
-
- **Life Jacket/Personal
|
|
515
|
+
- **Life Jacket/Personal Flotation Device (PFD):** Always wear a life jacket or PFD when crossing a river, especially if swimming or using a boat.
|
|
516
516
|
- **First-Aid Kit:** Carry a basic first-aid kit to handle any minor injuries that might occur during the crossing.
|
|
517
517
|
- **Map and Compass:** Navigate the river and its surroundings with the help of a map and compass, especially if you're in an unfamiliar area.
|
|
518
518
|
- **Communication Device:** Have a means of communication, such as a satellite phone or a personal locator beacon, especially in remote areas.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from datetime import timezone
|
|
4
|
+
from typing import Any
|
|
5
|
+
from unittest.mock import AsyncMock
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from inline_snapshot import snapshot
|
|
9
|
+
|
|
10
|
+
from pydantic_ai.agent import Agent
|
|
11
|
+
from pydantic_ai.exceptions import UnexpectedModelBehavior
|
|
12
|
+
from pydantic_ai.messages import BinaryContent, ModelRequest, ModelResponse, SystemPromptPart, TextPart, UserPromptPart
|
|
13
|
+
from pydantic_ai.usage import Usage
|
|
14
|
+
|
|
15
|
+
from ..conftest import IsNow, try_import
|
|
16
|
+
|
|
17
|
+
with try_import() as imports_successful:
|
|
18
|
+
from mcp import CreateMessageResult
|
|
19
|
+
from mcp.types import TextContent
|
|
20
|
+
|
|
21
|
+
from pydantic_ai.models.mcp_sampling import MCPSamplingModel
|
|
22
|
+
|
|
23
|
+
pytestmark = pytest.mark.skipif(not imports_successful(), reason='mcp package not installed')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class FakeSession:
|
|
28
|
+
create_message: Any
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def fake_session(create_message: Any) -> Any:
|
|
32
|
+
return FakeSession(create_message)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_mcp_sampling_model():
|
|
36
|
+
model = MCPSamplingModel(fake_session(AsyncMock()))
|
|
37
|
+
assert model.model_name == 'mcp-sampling'
|
|
38
|
+
assert model.system == 'MCP'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_assistant_text():
|
|
42
|
+
result = CreateMessageResult(
|
|
43
|
+
role='assistant', content=TextContent(type='text', text='text content'), model='test-model'
|
|
44
|
+
)
|
|
45
|
+
create_message = AsyncMock(return_value=result)
|
|
46
|
+
agent = Agent(model=MCPSamplingModel(fake_session(create_message)))
|
|
47
|
+
|
|
48
|
+
result = agent.run_sync('Hello')
|
|
49
|
+
assert result.output == snapshot('text content')
|
|
50
|
+
assert result.all_messages() == snapshot(
|
|
51
|
+
[
|
|
52
|
+
ModelRequest(
|
|
53
|
+
parts=[
|
|
54
|
+
UserPromptPart(
|
|
55
|
+
content='Hello',
|
|
56
|
+
timestamp=IsNow(tz=timezone.utc),
|
|
57
|
+
)
|
|
58
|
+
]
|
|
59
|
+
),
|
|
60
|
+
ModelResponse(
|
|
61
|
+
parts=[TextPart(content='text content')],
|
|
62
|
+
usage=Usage(requests=1),
|
|
63
|
+
model_name='test-model',
|
|
64
|
+
timestamp=IsNow(tz=timezone.utc),
|
|
65
|
+
),
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_user_text():
|
|
71
|
+
result = CreateMessageResult(role='user', content=TextContent(type='text', text='text content'), model='test-model')
|
|
72
|
+
create_message = AsyncMock(return_value=result)
|
|
73
|
+
agent = Agent(model=MCPSamplingModel(fake_session(create_message)))
|
|
74
|
+
|
|
75
|
+
expected_match = 'Unexpected result from MCP sampling, expected "assistant" role, got user.'
|
|
76
|
+
with pytest.raises(UnexpectedModelBehavior, match=expected_match):
|
|
77
|
+
agent.run_sync('Hello')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_assistant_text_history():
|
|
81
|
+
result = CreateMessageResult(
|
|
82
|
+
role='assistant', content=TextContent(type='text', text='text content'), model='test-model'
|
|
83
|
+
)
|
|
84
|
+
create_message = AsyncMock(return_value=result)
|
|
85
|
+
agent = Agent(model=MCPSamplingModel(fake_session(create_message)), instructions='testing')
|
|
86
|
+
|
|
87
|
+
result = agent.run_sync('1')
|
|
88
|
+
result = agent.run_sync('2', message_history=result.all_messages())
|
|
89
|
+
|
|
90
|
+
assert result.output == snapshot('text content')
|
|
91
|
+
assert result.all_messages() == snapshot(
|
|
92
|
+
[
|
|
93
|
+
ModelRequest(parts=[UserPromptPart(content='1', timestamp=IsNow(tz=timezone.utc))], instructions='testing'),
|
|
94
|
+
ModelResponse(
|
|
95
|
+
parts=[TextPart(content='text content')],
|
|
96
|
+
usage=Usage(requests=1),
|
|
97
|
+
model_name='test-model',
|
|
98
|
+
timestamp=IsNow(tz=timezone.utc),
|
|
99
|
+
),
|
|
100
|
+
ModelRequest(parts=[UserPromptPart(content='2', timestamp=IsNow(tz=timezone.utc))], instructions='testing'),
|
|
101
|
+
ModelResponse(
|
|
102
|
+
parts=[TextPart(content='text content')],
|
|
103
|
+
usage=Usage(requests=1),
|
|
104
|
+
model_name='test-model',
|
|
105
|
+
timestamp=IsNow(tz=timezone.utc),
|
|
106
|
+
),
|
|
107
|
+
]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_assistant_text_history_complex():
|
|
112
|
+
history = [
|
|
113
|
+
ModelRequest(
|
|
114
|
+
parts=[
|
|
115
|
+
UserPromptPart(content='1'),
|
|
116
|
+
UserPromptPart(
|
|
117
|
+
content=['a string', BinaryContent(data=base64.b64encode(b'data'), media_type='image/jpeg')]
|
|
118
|
+
),
|
|
119
|
+
SystemPromptPart(content='system content'),
|
|
120
|
+
]
|
|
121
|
+
),
|
|
122
|
+
ModelResponse(
|
|
123
|
+
parts=[TextPart(content='text content')],
|
|
124
|
+
usage=Usage(requests=1),
|
|
125
|
+
model_name='test-model',
|
|
126
|
+
),
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
result = CreateMessageResult(
|
|
130
|
+
role='assistant', content=TextContent(type='text', text='text content'), model='test-model'
|
|
131
|
+
)
|
|
132
|
+
create_message = AsyncMock(return_value=result)
|
|
133
|
+
agent = Agent(model=MCPSamplingModel(fake_session(create_message)))
|
|
134
|
+
result = agent.run_sync('1', message_history=history)
|
|
135
|
+
assert result.output == snapshot('text content')
|
|
@@ -3,13 +3,13 @@ from __future__ import annotations as _annotations
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
|
+
import shutil
|
|
6
7
|
import sys
|
|
7
8
|
from collections.abc import AsyncIterator, Iterable, Sequence
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from inspect import FrameInfo
|
|
10
11
|
from io import StringIO
|
|
11
12
|
from pathlib import Path
|
|
12
|
-
from types import ModuleType
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
15
|
import httpx
|
|
@@ -64,32 +64,44 @@ pytestmark = [
|
|
|
64
64
|
reason='google-auth or logfire or google-provider not installed',
|
|
65
65
|
),
|
|
66
66
|
]
|
|
67
|
+
code_examples: dict[str, CodeExample] = {}
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
def find_filter_examples() -> Iterable[ParameterSet]:
|
|
70
71
|
# Ensure this is run from the package root regardless of where/how the tests are run
|
|
71
|
-
|
|
72
|
+
root_dir = Path(__file__).parent.parent
|
|
73
|
+
os.chdir(root_dir)
|
|
72
74
|
|
|
73
75
|
for ex in find_examples('docs', 'pydantic_ai_slim', 'pydantic_graph', 'pydantic_evals'):
|
|
74
76
|
if ex.path.name != '_utils.py':
|
|
75
77
|
try:
|
|
76
|
-
path = ex.path.relative_to(
|
|
78
|
+
path = ex.path.relative_to(root_dir)
|
|
77
79
|
except ValueError:
|
|
78
80
|
path = ex.path
|
|
79
81
|
test_id = f'{path}:{ex.start_line}'
|
|
80
82
|
prefix_settings = ex.prefix_settings()
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
+
if title := prefix_settings.get('title'):
|
|
84
|
+
if title.endswith('.py'):
|
|
85
|
+
code_examples[title] = ex
|
|
86
|
+
test_id += f':{title}'
|
|
83
87
|
yield pytest.param(ex, id=test_id)
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
@pytest.fixture
|
|
87
|
-
def
|
|
88
|
-
|
|
91
|
+
def tmp_path_cwd(tmp_path: Path):
|
|
92
|
+
cwd = os.getcwd()
|
|
93
|
+
|
|
94
|
+
root_dir = Path(__file__).parent.parent
|
|
95
|
+
for file in (root_dir / 'tests' / 'example_modules').glob('*.py'):
|
|
96
|
+
shutil.copy(file, tmp_path)
|
|
97
|
+
sys.path.append(str(tmp_path))
|
|
98
|
+
os.chdir(tmp_path)
|
|
99
|
+
|
|
89
100
|
try:
|
|
90
|
-
yield
|
|
101
|
+
yield tmp_path
|
|
91
102
|
finally:
|
|
92
|
-
os.chdir(
|
|
103
|
+
os.chdir(cwd)
|
|
104
|
+
sys.path.remove(str(tmp_path))
|
|
93
105
|
|
|
94
106
|
|
|
95
107
|
@pytest.mark.xdist_group(name='doc_tests')
|
|
@@ -101,8 +113,7 @@ def test_docs_examples( # noqa: C901
|
|
|
101
113
|
client_with_handler: ClientWithHandler,
|
|
102
114
|
allow_model_requests: None,
|
|
103
115
|
env: TestEnv,
|
|
104
|
-
|
|
105
|
-
reset_cwd: None,
|
|
116
|
+
tmp_path_cwd: Path,
|
|
106
117
|
):
|
|
107
118
|
mocker.patch('pydantic_ai.agent.models.infer_model', side_effect=mock_infer_model)
|
|
108
119
|
mocker.patch('pydantic_ai._utils.group_by_temporal', side_effect=mock_group_by_temporal)
|
|
@@ -142,14 +153,13 @@ def test_docs_examples( # noqa: C901
|
|
|
142
153
|
env.set('AWS_SECRET_ACCESS_KEY', 'testing')
|
|
143
154
|
env.set('AWS_DEFAULT_REGION', 'us-east-1')
|
|
144
155
|
|
|
145
|
-
sys.path.append('tests/example_modules')
|
|
146
|
-
|
|
147
156
|
prefix_settings = example.prefix_settings()
|
|
148
|
-
opt_title = prefix_settings.get('title')
|
|
149
157
|
opt_test = prefix_settings.get('test', '')
|
|
150
158
|
opt_lint = prefix_settings.get('lint', '')
|
|
151
159
|
noqa = prefix_settings.get('noqa', '')
|
|
152
|
-
python_version = prefix_settings.get('py'
|
|
160
|
+
python_version = prefix_settings.get('py')
|
|
161
|
+
dunder_name = prefix_settings.get('dunder_name', '__main__')
|
|
162
|
+
requires = prefix_settings.get('requires')
|
|
153
163
|
|
|
154
164
|
if python_version:
|
|
155
165
|
python_version_info = tuple(int(v) for v in python_version.split('.'))
|
|
@@ -159,14 +169,12 @@ def test_docs_examples( # noqa: C901
|
|
|
159
169
|
if opt_test.startswith('skip') and opt_lint.startswith('skip'):
|
|
160
170
|
pytest.skip('both running code and lint skipped')
|
|
161
171
|
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}:
|
|
169
|
-
os.chdir(tmp_path)
|
|
172
|
+
if requires:
|
|
173
|
+
for req in requires.split(','):
|
|
174
|
+
if ex := code_examples.get(req):
|
|
175
|
+
(tmp_path_cwd / req).write_text(ex.source)
|
|
176
|
+
else: # pragma: no cover
|
|
177
|
+
raise KeyError(f'Example {req} not found, check the `requires` header of this example.')
|
|
170
178
|
|
|
171
179
|
ruff_ignore: list[str] = ['D', 'Q001']
|
|
172
180
|
# `from bank_database import DatabaseConn` wrongly sorted in imports
|
|
@@ -196,19 +204,12 @@ def test_docs_examples( # noqa: C901
|
|
|
196
204
|
if opt_test.startswith('skip'):
|
|
197
205
|
print(opt_test[4:].lstrip(' -') or 'running code skipped')
|
|
198
206
|
else:
|
|
199
|
-
test_globals: dict[str, str] = {}
|
|
200
|
-
|
|
201
|
-
test_globals['__name__'] = '__test__'
|
|
207
|
+
test_globals: dict[str, str] = {'__name__': dunder_name}
|
|
208
|
+
|
|
202
209
|
if eval_example.update_examples: # pragma: lax no cover
|
|
203
|
-
|
|
210
|
+
eval_example.run_print_update(example, call=call_name, module_globals=test_globals)
|
|
204
211
|
else:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if title := opt_title:
|
|
208
|
-
if title.endswith('.py'):
|
|
209
|
-
module_name = title[:-3]
|
|
210
|
-
sys.modules[module_name] = module = ModuleType(module_name)
|
|
211
|
-
module.__dict__.update(module_dict)
|
|
212
|
+
eval_example.run_print_check(example, call=call_name, module_globals=test_globals)
|
|
212
213
|
|
|
213
214
|
|
|
214
215
|
def print_callback(s: str) -> str:
|
|
@@ -432,6 +433,10 @@ text_responses: dict[str, str | ToolCallPart] = {
|
|
|
432
433
|
'explanation': 'I am not equipped to provide travel information, such as flights from Amsterdam to Mexico City.'
|
|
433
434
|
},
|
|
434
435
|
),
|
|
436
|
+
'Create an image of a robot in a punk style.': ToolCallPart(
|
|
437
|
+
tool_name='image_generator', args={'subject': 'robot', 'style': 'punk'}, tool_call_id='0001'
|
|
438
|
+
),
|
|
439
|
+
"subject='robot' style='punk'": '<svg/>',
|
|
435
440
|
}
|
|
436
441
|
|
|
437
442
|
tool_responses: dict[tuple[str, str], str] = {
|
|
@@ -676,6 +681,8 @@ async def model_logic( # noqa: C901
|
|
|
676
681
|
)
|
|
677
682
|
]
|
|
678
683
|
)
|
|
684
|
+
elif isinstance(m, ToolReturnPart) and m.tool_name == 'image_generator':
|
|
685
|
+
return ModelResponse(parts=[TextPart('Image file written to robot_punk.svg.')])
|
|
679
686
|
else:
|
|
680
687
|
sys.stdout.write(str(debug.format(messages, info)))
|
|
681
688
|
raise RuntimeError(f'Unexpected message: {m}')
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
"""Tests for the MCP (Model Context Protocol) server implementation."""
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
import re
|
|
5
|
+
from datetime import timezone
|
|
4
6
|
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
6
8
|
from unittest.mock import AsyncMock, patch
|
|
7
9
|
|
|
8
10
|
import pytest
|
|
9
11
|
from inline_snapshot import snapshot
|
|
10
12
|
|
|
11
13
|
from pydantic_ai.agent import Agent
|
|
12
|
-
from pydantic_ai.exceptions import ModelRetry, UserError
|
|
14
|
+
from pydantic_ai.exceptions import ModelRetry, UnexpectedModelBehavior, UserError
|
|
13
15
|
from pydantic_ai.messages import (
|
|
14
16
|
BinaryContent,
|
|
15
17
|
ModelRequest,
|
|
16
18
|
ModelResponse,
|
|
17
19
|
RetryPromptPart,
|
|
18
20
|
TextPart,
|
|
21
|
+
ThinkingPart,
|
|
19
22
|
ToolCallPart,
|
|
20
23
|
ToolReturnPart,
|
|
21
24
|
UserPromptPart,
|
|
@@ -24,19 +27,19 @@ from pydantic_ai.models.test import TestModel
|
|
|
24
27
|
from pydantic_ai.tools import RunContext
|
|
25
28
|
from pydantic_ai.usage import Usage
|
|
26
29
|
|
|
27
|
-
from .conftest import IsDatetime, IsStr, try_import
|
|
30
|
+
from .conftest import IsDatetime, IsNow, IsStr, try_import
|
|
28
31
|
|
|
29
32
|
with try_import() as imports_successful:
|
|
30
|
-
from mcp import ErrorData, McpError
|
|
33
|
+
from mcp import ErrorData, McpError, SamplingMessage
|
|
34
|
+
from mcp.types import CreateMessageRequestParams, ImageContent, TextContent
|
|
31
35
|
|
|
36
|
+
from pydantic_ai._mcp import map_from_mcp_params, map_from_model_response
|
|
32
37
|
from pydantic_ai.mcp import CallToolFunc, MCPServerSSE, MCPServerStdio, ToolResult
|
|
33
38
|
from pydantic_ai.models.google import GoogleModel
|
|
34
39
|
from pydantic_ai.models.openai import OpenAIModel
|
|
35
40
|
from pydantic_ai.providers.google import GoogleProvider
|
|
36
41
|
from pydantic_ai.providers.openai import OpenAIProvider
|
|
37
42
|
|
|
38
|
-
TOOL_COUNT: Final[int] = 12
|
|
39
|
-
|
|
40
43
|
pytestmark = [
|
|
41
44
|
pytest.mark.skipif(not imports_successful(), reason='mcp and openai not installed'),
|
|
42
45
|
pytest.mark.anyio,
|
|
@@ -55,7 +58,7 @@ async def test_stdio_server():
|
|
|
55
58
|
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
56
59
|
async with server:
|
|
57
60
|
tools = await server.list_tools()
|
|
58
|
-
assert len(tools) ==
|
|
61
|
+
assert len(tools) == snapshot(13)
|
|
59
62
|
assert tools[0].name == 'celsius_to_fahrenheit'
|
|
60
63
|
assert tools[0].description.startswith('Convert Celsius to Fahrenheit.')
|
|
61
64
|
|
|
@@ -64,6 +67,13 @@ async def test_stdio_server():
|
|
|
64
67
|
assert result == snapshot('32.0')
|
|
65
68
|
|
|
66
69
|
|
|
70
|
+
async def test_reentrant_context_manager():
|
|
71
|
+
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
72
|
+
async with server:
|
|
73
|
+
async with server:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
67
77
|
async def test_stdio_server_with_tool_prefix():
|
|
68
78
|
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'], tool_prefix='foo')
|
|
69
79
|
async with server:
|
|
@@ -76,7 +86,7 @@ async def test_stdio_server_with_cwd():
|
|
|
76
86
|
server = MCPServerStdio('python', ['mcp_server.py'], cwd=test_dir)
|
|
77
87
|
async with server:
|
|
78
88
|
tools = await server.list_tools()
|
|
79
|
-
assert len(tools) ==
|
|
89
|
+
assert len(tools) == snapshot(13)
|
|
80
90
|
|
|
81
91
|
|
|
82
92
|
async def test_process_tool_call() -> None:
|
|
@@ -104,7 +114,7 @@ async def test_process_tool_call() -> None:
|
|
|
104
114
|
def test_sse_server():
|
|
105
115
|
sse_server = MCPServerSSE(url='http://localhost:8000/sse')
|
|
106
116
|
assert sse_server.url == 'http://localhost:8000/sse'
|
|
107
|
-
assert sse_server.
|
|
117
|
+
assert sse_server.log_level is None
|
|
108
118
|
|
|
109
119
|
|
|
110
120
|
def test_sse_server_with_header_and_timeout():
|
|
@@ -119,7 +129,7 @@ def test_sse_server_with_header_and_timeout():
|
|
|
119
129
|
assert sse_server.headers is not None and sse_server.headers['my-custom-header'] == 'my-header-value'
|
|
120
130
|
assert sse_server.timeout == 10
|
|
121
131
|
assert sse_server.sse_read_timeout == 100
|
|
122
|
-
assert sse_server.
|
|
132
|
+
assert sse_server.log_level == 'info'
|
|
123
133
|
|
|
124
134
|
|
|
125
135
|
@pytest.mark.vcr()
|
|
@@ -240,10 +250,10 @@ async def test_agent_with_server_not_running(openai_api_key: str):
|
|
|
240
250
|
|
|
241
251
|
async def test_log_level_unset():
|
|
242
252
|
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
243
|
-
assert server.
|
|
253
|
+
assert server.log_level is None
|
|
244
254
|
async with server:
|
|
245
255
|
tools = await server.list_tools()
|
|
246
|
-
assert len(tools) ==
|
|
256
|
+
assert len(tools) == snapshot(13)
|
|
247
257
|
assert tools[10].name == 'get_log_level'
|
|
248
258
|
|
|
249
259
|
result = await server.call_tool('get_log_level', {})
|
|
@@ -252,7 +262,7 @@ async def test_log_level_unset():
|
|
|
252
262
|
|
|
253
263
|
async def test_log_level_set():
|
|
254
264
|
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'], log_level='info')
|
|
255
|
-
assert server.
|
|
265
|
+
assert server.log_level == 'info'
|
|
256
266
|
async with server:
|
|
257
267
|
result = await server.call_tool('get_log_level', {})
|
|
258
268
|
assert result == snapshot('info')
|
|
@@ -963,6 +973,30 @@ async def test_tool_returning_multiple_items(allow_model_requests: None, agent:
|
|
|
963
973
|
)
|
|
964
974
|
|
|
965
975
|
|
|
976
|
+
async def test_client_sampling():
|
|
977
|
+
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'])
|
|
978
|
+
server.sampling_model = TestModel(custom_output_text='sampling model response')
|
|
979
|
+
async with server:
|
|
980
|
+
result = await server.call_tool('use_sampling', {'foo': 'bar'})
|
|
981
|
+
assert result == snapshot(
|
|
982
|
+
{
|
|
983
|
+
'meta': None,
|
|
984
|
+
'role': 'assistant',
|
|
985
|
+
'content': {'type': 'text', 'text': 'sampling model response', 'annotations': None},
|
|
986
|
+
'model': 'test',
|
|
987
|
+
'stopReason': None,
|
|
988
|
+
}
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
async def test_client_sampling_disabled():
|
|
993
|
+
server = MCPServerStdio('python', ['-m', 'tests.mcp_server'], allow_sampling=False)
|
|
994
|
+
server.sampling_model = TestModel(custom_output_text='sampling model response')
|
|
995
|
+
async with server:
|
|
996
|
+
with pytest.raises(ModelRetry, match='Error executing tool use_sampling: Sampling not supported'):
|
|
997
|
+
await server.call_tool('use_sampling', {'foo': 'bar'})
|
|
998
|
+
|
|
999
|
+
|
|
966
1000
|
async def test_mcp_server_raises_mcp_error(allow_model_requests: None, agent: Agent) -> None:
|
|
967
1001
|
server = agent._mcp_servers[0] # pyright: ignore[reportPrivateUsage]
|
|
968
1002
|
|
|
@@ -976,3 +1010,52 @@ async def test_mcp_server_raises_mcp_error(allow_model_requests: None, agent: Ag
|
|
|
976
1010
|
):
|
|
977
1011
|
with pytest.raises(ModelRetry, match='Test MCP error conversion'):
|
|
978
1012
|
await server.call_tool('test_tool', {})
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def test_map_from_mcp_params_model_request():
|
|
1016
|
+
params = CreateMessageRequestParams(
|
|
1017
|
+
messages=[
|
|
1018
|
+
SamplingMessage(role='user', content=TextContent(type='text', text='xx')),
|
|
1019
|
+
SamplingMessage(
|
|
1020
|
+
role='user',
|
|
1021
|
+
content=ImageContent(type='image', data=base64.b64encode(b'img').decode(), mimeType='image/png'),
|
|
1022
|
+
),
|
|
1023
|
+
],
|
|
1024
|
+
maxTokens=8,
|
|
1025
|
+
)
|
|
1026
|
+
pai_messages = map_from_mcp_params(params)
|
|
1027
|
+
assert pai_messages == snapshot(
|
|
1028
|
+
[
|
|
1029
|
+
ModelRequest(
|
|
1030
|
+
parts=[
|
|
1031
|
+
UserPromptPart(content='xx', timestamp=IsNow(tz=timezone.utc)),
|
|
1032
|
+
UserPromptPart(
|
|
1033
|
+
content=[BinaryContent(data=b'img', media_type='image/png')], timestamp=IsNow(tz=timezone.utc)
|
|
1034
|
+
),
|
|
1035
|
+
]
|
|
1036
|
+
)
|
|
1037
|
+
]
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
def test_map_from_mcp_params_model_response():
|
|
1042
|
+
params = CreateMessageRequestParams(
|
|
1043
|
+
messages=[
|
|
1044
|
+
SamplingMessage(role='assistant', content=TextContent(type='text', text='xx')),
|
|
1045
|
+
],
|
|
1046
|
+
maxTokens=8,
|
|
1047
|
+
)
|
|
1048
|
+
pai_messages = map_from_mcp_params(params)
|
|
1049
|
+
assert pai_messages == snapshot(
|
|
1050
|
+
[
|
|
1051
|
+
ModelResponse(
|
|
1052
|
+
parts=[TextPart(content='xx')],
|
|
1053
|
+
timestamp=IsNow(tz=timezone.utc),
|
|
1054
|
+
)
|
|
1055
|
+
]
|
|
1056
|
+
)
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
def test_map_from_model_response():
|
|
1060
|
+
with pytest.raises(UnexpectedModelBehavior, match='Unexpected part type: ThinkingPart, expected TextPart'):
|
|
1061
|
+
map_from_model_response(ModelResponse(parts=[ThinkingPart(content='Thinking...')]))
|
|
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.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_dict.yaml
RENAMED
|
File without changes
|
{pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_error.yaml
RENAMED
|
File without changes
|
{pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_image.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_ai-0.3.1 → pydantic_ai-0.3.2}/tests/cassettes/test_mcp/test_tool_returning_none.yaml
RENAMED
|
File without changes
|