pydantic-ai 0.2.11__tar.gz → 0.2.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pydantic-ai might be problematic. Click here for more details.
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/PKG-INFO +3 -3
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_bedrock.py +95 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_gemini.py +52 -9
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_google.py +60 -8
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_openai.py +125 -16
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_openai_responses.py +43 -0
- pydantic_ai-0.2.12/tests/providers/test_azure.py +141 -0
- pydantic_ai-0.2.12/tests/providers/test_bedrock.py +95 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_deepseek.py +8 -1
- pydantic_ai-0.2.12/tests/providers/test_fireworks.py +104 -0
- pydantic_ai-0.2.12/tests/providers/test_grok.py +57 -0
- pydantic_ai-0.2.12/tests/providers/test_groq.py +107 -0
- pydantic_ai-0.2.12/tests/providers/test_openrouter.py +154 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_provider_names.py +6 -0
- pydantic_ai-0.2.12/tests/providers/test_together.py +100 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_agent.py +601 -3
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_examples.py +94 -1
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_tools.py +1 -1
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/typed_agent.py +50 -22
- pydantic_ai-0.2.11/tests/providers/test_azure.py +0 -72
- pydantic_ai-0.2.11/tests/providers/test_bedrock.py +0 -34
- pydantic_ai-0.2.11/tests/providers/test_groq.py +0 -57
- pydantic_ai-0.2.11/tests/providers/test_openrouter.py +0 -67
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/.gitignore +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/LICENSE +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/Makefile +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/README.md +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/pyproject.toml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/__init__.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/assets/dummy.pdf +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/assets/kiwi.png +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/assets/marcelo.mp3 +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/assets/small_video.mp4 +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_dict.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_error.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_image.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_image_resource.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_multiple_items.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_none.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_str.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_mcp/test_tool_returning_text_resource.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[anthropic].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[bedrock].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[cohere].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[gemini].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[groq].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[mistral].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/cassettes/test_settings/test_stop_settings[openai].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/conftest.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/__init__.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_dataset.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_base.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_common.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_context.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_evaluator_spec.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_evaluators.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_llm_as_a_judge.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_otel.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_render_numbers.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_reporting.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_reports.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/test_utils.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/evals/utils.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/example_modules/README.md +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/example_modules/bank_database.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/example_modules/fake_database.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/example_modules/weather_service.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/fasta2a/__init__.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/fasta2a/test_applications.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/__init__.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/test_file_persistence.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/test_graph.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/test_mermaid.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/test_persistence.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/test_state.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/graph/test_utils.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/import_examples.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/json_body_serializer.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/mcp_server.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/__init__.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_anthropic_model_empty_message_on_history.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_anthropic_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_empty_system_prompt.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_guardrail_config.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_other_parameters.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_performance_config.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_bedrock_multiple_documents_in_history.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_bedrock/test_video_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_cohere/test_cohere_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_false.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_additional_properties_is_true.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_drop_exclusive_maximum.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_exclusive_minimum_and_maximum.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_gemini_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_labels_are_ignored_with_gla_provider.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini/test_video_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_gemini_vertexai/test_labels.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_image_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_iter_stream.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_max_tokens.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_multiple_documents_in_history.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_retry.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_safety_settings.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_stream.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_structured_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_text_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_thinking_config.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_top_p.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_vertex_labels.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_vertex_provider.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_video_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_google/test_google_model_video_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_groq_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_mistral/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_mistral/test_mistral_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_document_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_extra_headers.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_image_url_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_multiple_agent_tool_calls.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_audio_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_instructions_with_tool_calls_keep_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_model_without_system_prompt.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai/test_user_id.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_audio_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_image_as_binary_content_tool_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_as_binary_content_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_image_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_builtin_tools.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_http_error.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_instructions.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_retry.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_model_simple_response_with_tool_call.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_output_type.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_effort.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_reasoning_generate_summary.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_stream.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_system_prompt.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/cassettes/test_openai_responses/test_openai_responses_text_document_url_input.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/mock_async_stream.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_anthropic.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_cohere.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_fallback.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_gemini_vertexai.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_groq.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_instrumented.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_mistral.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_model.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_model_function.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_model_names.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_model_request_parameters.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/models/test_model_test.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/__init__.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/cassettes/test_google_vertex/test_vertexai_provider.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/cassettes/test_openrouter/test_openrouter_with_google_model.yaml +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_anthropic.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_cohere.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_google_gla.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_google_vertex.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_mistral.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/providers/test_openai.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_a2a.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_cli.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_deps.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_direct.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_format_as_xml.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_json_body_serializer.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_live.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_logfire.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_mcp.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_messages.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_parts_manager.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_settings.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_streaming.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_usage_limits.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/test_utils.py +0 -0
- {pydantic_ai-0.2.11 → pydantic_ai-0.2.12}/tests/typed_graph.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Summary: Agent Framework / shim to use Pydantic with LLMs
|
|
5
5
|
Project-URL: Homepage, https://ai.pydantic.dev
|
|
6
6
|
Project-URL: Source, https://github.com/pydantic/pydantic-ai
|
|
@@ -28,9 +28,9 @@ Classifier: Topic :: Internet
|
|
|
28
28
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
29
29
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
30
|
Requires-Python: >=3.9
|
|
31
|
-
Requires-Dist: pydantic-ai-slim[a2a,anthropic,bedrock,cli,cohere,evals,google,groq,mcp,mistral,openai,vertexai]==0.2.
|
|
31
|
+
Requires-Dist: pydantic-ai-slim[a2a,anthropic,bedrock,cli,cohere,evals,google,groq,mcp,mistral,openai,vertexai]==0.2.12
|
|
32
32
|
Provides-Extra: examples
|
|
33
|
-
Requires-Dist: pydantic-ai-examples==0.2.
|
|
33
|
+
Requires-Dist: pydantic-ai-examples==0.2.12; extra == 'examples'
|
|
34
34
|
Provides-Extra: logfire
|
|
35
35
|
Requires-Dist: logfire>=3.11.0; extra == 'logfire'
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
@@ -31,6 +31,8 @@ from pydantic_ai.messages import (
|
|
|
31
31
|
UserPromptPart,
|
|
32
32
|
VideoUrl,
|
|
33
33
|
)
|
|
34
|
+
from pydantic_ai.models import ModelRequestParameters
|
|
35
|
+
from pydantic_ai.tools import ToolDefinition
|
|
34
36
|
from pydantic_ai.usage import Usage
|
|
35
37
|
|
|
36
38
|
from ..conftest import IsDatetime, try_import
|
|
@@ -631,3 +633,96 @@ async def test_bedrock_group_consecutive_tool_return_parts(bedrock_provider: Bed
|
|
|
631
633
|
},
|
|
632
634
|
]
|
|
633
635
|
)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
async def test_bedrock_mistral_tool_result_format(bedrock_provider: BedrockProvider):
|
|
639
|
+
now = datetime.datetime.now()
|
|
640
|
+
req = [
|
|
641
|
+
ModelRequest(
|
|
642
|
+
parts=[
|
|
643
|
+
ToolReturnPart(tool_name='tool1', content={'foo': 'bar'}, tool_call_id='id1', timestamp=now),
|
|
644
|
+
]
|
|
645
|
+
),
|
|
646
|
+
]
|
|
647
|
+
|
|
648
|
+
# Models other than Mistral support toolResult.content with text, not json
|
|
649
|
+
model = BedrockConverseModel('us.amazon.nova-micro-v1:0', provider=bedrock_provider)
|
|
650
|
+
# Call the mapping function directly
|
|
651
|
+
_, bedrock_messages = await model._map_messages(req) # type: ignore[reportPrivateUsage]
|
|
652
|
+
|
|
653
|
+
assert bedrock_messages == snapshot(
|
|
654
|
+
[
|
|
655
|
+
{
|
|
656
|
+
'role': 'user',
|
|
657
|
+
'content': [
|
|
658
|
+
{'toolResult': {'toolUseId': 'id1', 'content': [{'text': '{"foo":"bar"}'}], 'status': 'success'}},
|
|
659
|
+
],
|
|
660
|
+
},
|
|
661
|
+
]
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
# Mistral requires toolResult.content to hold json, not text
|
|
665
|
+
model = BedrockConverseModel('mistral.mistral-7b-instruct-v0:2', provider=bedrock_provider)
|
|
666
|
+
# Call the mapping function directly
|
|
667
|
+
_, bedrock_messages = await model._map_messages(req) # type: ignore[reportPrivateUsage]
|
|
668
|
+
|
|
669
|
+
assert bedrock_messages == snapshot(
|
|
670
|
+
[
|
|
671
|
+
{
|
|
672
|
+
'role': 'user',
|
|
673
|
+
'content': [
|
|
674
|
+
{'toolResult': {'toolUseId': 'id1', 'content': [{'json': {'foo': 'bar'}}], 'status': 'success'}},
|
|
675
|
+
],
|
|
676
|
+
},
|
|
677
|
+
]
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
async def test_bedrock_anthropic_no_tool_choice(bedrock_provider: BedrockProvider):
|
|
682
|
+
my_tool = ToolDefinition(
|
|
683
|
+
'my_tool',
|
|
684
|
+
'This is my tool',
|
|
685
|
+
{'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
686
|
+
)
|
|
687
|
+
mrp = ModelRequestParameters(function_tools=[my_tool], allow_text_output=False, output_tools=[])
|
|
688
|
+
|
|
689
|
+
# Models other than Anthropic support tool_choice
|
|
690
|
+
model = BedrockConverseModel('us.amazon.nova-micro-v1:0', provider=bedrock_provider)
|
|
691
|
+
tool_config = model._map_tool_config(mrp) # type: ignore[reportPrivateUsage]
|
|
692
|
+
|
|
693
|
+
assert tool_config == snapshot(
|
|
694
|
+
{
|
|
695
|
+
'tools': [
|
|
696
|
+
{
|
|
697
|
+
'toolSpec': {
|
|
698
|
+
'name': 'my_tool',
|
|
699
|
+
'description': 'This is my tool',
|
|
700
|
+
'inputSchema': {
|
|
701
|
+
'json': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}}
|
|
702
|
+
},
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
],
|
|
706
|
+
'toolChoice': {'any': {}},
|
|
707
|
+
}
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
# Anthropic models don't support tool_choice
|
|
711
|
+
model = BedrockConverseModel('us.anthropic.claude-3-7-sonnet-20250219-v1:0', provider=bedrock_provider)
|
|
712
|
+
tool_config = model._map_tool_config(mrp) # type: ignore[reportPrivateUsage]
|
|
713
|
+
|
|
714
|
+
assert tool_config == snapshot(
|
|
715
|
+
{
|
|
716
|
+
'tools': [
|
|
717
|
+
{
|
|
718
|
+
'toolSpec': {
|
|
719
|
+
'name': 'my_tool',
|
|
720
|
+
'description': 'This is my tool',
|
|
721
|
+
'inputSchema': {
|
|
722
|
+
'json': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}}
|
|
723
|
+
},
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
}
|
|
728
|
+
)
|
|
@@ -540,6 +540,7 @@ async def test_text_success(get_gemini_client: GetGeminiClient):
|
|
|
540
540
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
541
541
|
model_name='gemini-1.5-flash-123',
|
|
542
542
|
timestamp=IsNow(tz=timezone.utc),
|
|
543
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
543
544
|
),
|
|
544
545
|
]
|
|
545
546
|
)
|
|
@@ -555,6 +556,7 @@ async def test_text_success(get_gemini_client: GetGeminiClient):
|
|
|
555
556
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
556
557
|
model_name='gemini-1.5-flash-123',
|
|
557
558
|
timestamp=IsNow(tz=timezone.utc),
|
|
559
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
558
560
|
),
|
|
559
561
|
ModelRequest(parts=[UserPromptPart(content='Hello', timestamp=IsNow(tz=timezone.utc))]),
|
|
560
562
|
ModelResponse(
|
|
@@ -562,6 +564,7 @@ async def test_text_success(get_gemini_client: GetGeminiClient):
|
|
|
562
564
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
563
565
|
model_name='gemini-1.5-flash-123',
|
|
564
566
|
timestamp=IsNow(tz=timezone.utc),
|
|
567
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
565
568
|
),
|
|
566
569
|
]
|
|
567
570
|
)
|
|
@@ -585,6 +588,7 @@ async def test_request_structured_response(get_gemini_client: GetGeminiClient):
|
|
|
585
588
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
586
589
|
model_name='gemini-1.5-flash-123',
|
|
587
590
|
timestamp=IsNow(tz=timezone.utc),
|
|
591
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
588
592
|
),
|
|
589
593
|
ModelRequest(
|
|
590
594
|
parts=[
|
|
@@ -647,6 +651,7 @@ async def test_request_tool_call(get_gemini_client: GetGeminiClient):
|
|
|
647
651
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
648
652
|
model_name='gemini-1.5-flash-123',
|
|
649
653
|
timestamp=IsNow(tz=timezone.utc),
|
|
654
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
650
655
|
),
|
|
651
656
|
ModelRequest(
|
|
652
657
|
parts=[
|
|
@@ -666,6 +671,7 @@ async def test_request_tool_call(get_gemini_client: GetGeminiClient):
|
|
|
666
671
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
667
672
|
model_name='gemini-1.5-flash-123',
|
|
668
673
|
timestamp=IsNow(tz=timezone.utc),
|
|
674
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
669
675
|
),
|
|
670
676
|
ModelRequest(
|
|
671
677
|
parts=[
|
|
@@ -688,6 +694,7 @@ async def test_request_tool_call(get_gemini_client: GetGeminiClient):
|
|
|
688
694
|
usage=Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
689
695
|
model_name='gemini-1.5-flash-123',
|
|
690
696
|
timestamp=IsNow(tz=timezone.utc),
|
|
697
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
691
698
|
),
|
|
692
699
|
]
|
|
693
700
|
)
|
|
@@ -732,12 +739,12 @@ async def test_stream_text(get_gemini_client: GetGeminiClient):
|
|
|
732
739
|
'Hello world',
|
|
733
740
|
]
|
|
734
741
|
)
|
|
735
|
-
assert result.usage() == snapshot(Usage(requests=1, request_tokens=
|
|
742
|
+
assert result.usage() == snapshot(Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3))
|
|
736
743
|
|
|
737
744
|
async with agent.run_stream('Hello') as result:
|
|
738
745
|
chunks = [chunk async for chunk in result.stream_text(delta=True, debounce_by=None)]
|
|
739
746
|
assert chunks == snapshot(['Hello ', 'world'])
|
|
740
|
-
assert result.usage() == snapshot(Usage(requests=1, request_tokens=
|
|
747
|
+
assert result.usage() == snapshot(Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3))
|
|
741
748
|
|
|
742
749
|
|
|
743
750
|
async def test_stream_invalid_unicode_text(get_gemini_client: GetGeminiClient):
|
|
@@ -769,7 +776,7 @@ async def test_stream_invalid_unicode_text(get_gemini_client: GetGeminiClient):
|
|
|
769
776
|
async with agent.run_stream('Hello') as result:
|
|
770
777
|
chunks = [chunk async for chunk in result.stream(debounce_by=None)]
|
|
771
778
|
assert chunks == snapshot(['abc', 'abc€def', 'abc€def'])
|
|
772
|
-
assert result.usage() == snapshot(Usage(requests=1, request_tokens=
|
|
779
|
+
assert result.usage() == snapshot(Usage(requests=1, request_tokens=1, response_tokens=2, total_tokens=3))
|
|
773
780
|
|
|
774
781
|
|
|
775
782
|
async def test_stream_text_no_data(get_gemini_client: GetGeminiClient):
|
|
@@ -840,7 +847,7 @@ async def test_stream_structured_tool_calls(get_gemini_client: GetGeminiClient):
|
|
|
840
847
|
async with agent.run_stream('Hello') as result:
|
|
841
848
|
response = await result.get_output()
|
|
842
849
|
assert response == snapshot((1, 2))
|
|
843
|
-
assert result.usage() == snapshot(Usage(requests=2, request_tokens=
|
|
850
|
+
assert result.usage() == snapshot(Usage(requests=2, request_tokens=2, response_tokens=4, total_tokens=6))
|
|
844
851
|
assert result.all_messages() == snapshot(
|
|
845
852
|
[
|
|
846
853
|
ModelRequest(parts=[UserPromptPart(content='Hello', timestamp=IsNow(tz=timezone.utc))]),
|
|
@@ -849,7 +856,7 @@ async def test_stream_structured_tool_calls(get_gemini_client: GetGeminiClient):
|
|
|
849
856
|
ToolCallPart(tool_name='foo', args={'x': 'a'}, tool_call_id=IsStr()),
|
|
850
857
|
ToolCallPart(tool_name='bar', args={'y': 'b'}, tool_call_id=IsStr()),
|
|
851
858
|
],
|
|
852
|
-
usage=Usage(request_tokens=
|
|
859
|
+
usage=Usage(request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
853
860
|
model_name='gemini-1.5-flash',
|
|
854
861
|
timestamp=IsNow(tz=timezone.utc),
|
|
855
862
|
),
|
|
@@ -865,7 +872,7 @@ async def test_stream_structured_tool_calls(get_gemini_client: GetGeminiClient):
|
|
|
865
872
|
),
|
|
866
873
|
ModelResponse(
|
|
867
874
|
parts=[ToolCallPart(tool_name='final_result', args={'response': [1, 2]}, tool_call_id=IsStr())],
|
|
868
|
-
usage=Usage(request_tokens=1, response_tokens=2, total_tokens=3),
|
|
875
|
+
usage=Usage(request_tokens=1, response_tokens=2, total_tokens=3, details={}),
|
|
869
876
|
model_name='gemini-1.5-flash',
|
|
870
877
|
timestamp=IsNow(tz=timezone.utc),
|
|
871
878
|
),
|
|
@@ -1096,9 +1103,16 @@ I need to use the `get_image` tool to see the image first.
|
|
|
1096
1103
|
),
|
|
1097
1104
|
ToolCallPart(tool_name='get_image', args={}, tool_call_id=IsStr()),
|
|
1098
1105
|
],
|
|
1099
|
-
usage=Usage(
|
|
1106
|
+
usage=Usage(
|
|
1107
|
+
requests=1,
|
|
1108
|
+
request_tokens=38,
|
|
1109
|
+
response_tokens=28,
|
|
1110
|
+
total_tokens=427,
|
|
1111
|
+
details={'thoughts_tokens': 361, 'text_prompt_tokens': 38},
|
|
1112
|
+
),
|
|
1100
1113
|
model_name='gemini-2.5-pro-preview-03-25',
|
|
1101
1114
|
timestamp=IsDatetime(),
|
|
1115
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
1102
1116
|
),
|
|
1103
1117
|
ModelRequest(
|
|
1104
1118
|
parts=[
|
|
@@ -1119,9 +1133,16 @@ I need to use the `get_image` tool to see the image first.
|
|
|
1119
1133
|
),
|
|
1120
1134
|
ModelResponse(
|
|
1121
1135
|
parts=[TextPart(content='The image shows a kiwi fruit, sliced in half.')],
|
|
1122
|
-
usage=Usage(
|
|
1136
|
+
usage=Usage(
|
|
1137
|
+
requests=1,
|
|
1138
|
+
request_tokens=360,
|
|
1139
|
+
response_tokens=11,
|
|
1140
|
+
total_tokens=572,
|
|
1141
|
+
details={'thoughts_tokens': 201, 'text_prompt_tokens': 102, 'image_prompt_tokens': 258},
|
|
1142
|
+
),
|
|
1123
1143
|
model_name='gemini-2.5-pro-preview-03-25',
|
|
1124
1144
|
timestamp=IsDatetime(),
|
|
1145
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
1125
1146
|
),
|
|
1126
1147
|
]
|
|
1127
1148
|
)
|
|
@@ -1241,9 +1262,16 @@ async def test_gemini_model_instructions(allow_model_requests: None, gemini_api_
|
|
|
1241
1262
|
),
|
|
1242
1263
|
ModelResponse(
|
|
1243
1264
|
parts=[TextPart(content='The capital of France is Paris.\n')],
|
|
1244
|
-
usage=Usage(
|
|
1265
|
+
usage=Usage(
|
|
1266
|
+
requests=1,
|
|
1267
|
+
request_tokens=13,
|
|
1268
|
+
response_tokens=8,
|
|
1269
|
+
total_tokens=21,
|
|
1270
|
+
details={'text_prompt_tokens': 13, 'text_candidates_tokens': 8},
|
|
1271
|
+
),
|
|
1245
1272
|
model_name='gemini-1.5-flash',
|
|
1246
1273
|
timestamp=IsDatetime(),
|
|
1274
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
1247
1275
|
),
|
|
1248
1276
|
]
|
|
1249
1277
|
)
|
|
@@ -1284,3 +1312,18 @@ async def test_gemini_additional_properties_is_true(allow_model_requests: None,
|
|
|
1284
1312
|
assert result.output == snapshot(
|
|
1285
1313
|
'I need a location dictionary to use the `get_temperature` function. I cannot provide the temperature in Tokyo without more information.\n'
|
|
1286
1314
|
)
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
async def test_gemini_no_finish_reason(get_gemini_client: GetGeminiClient):
|
|
1318
|
+
response = gemini_response(
|
|
1319
|
+
_content_model_response(ModelResponse(parts=[TextPart('Hello world')])), finish_reason=None
|
|
1320
|
+
)
|
|
1321
|
+
gemini_client = get_gemini_client(response)
|
|
1322
|
+
m = GeminiModel('gemini-1.5-flash', provider=GoogleGLAProvider(http_client=gemini_client))
|
|
1323
|
+
agent = Agent(m)
|
|
1324
|
+
|
|
1325
|
+
result = await agent.run('Hello World')
|
|
1326
|
+
|
|
1327
|
+
for message in result.all_messages():
|
|
1328
|
+
if isinstance(message, ModelResponse):
|
|
1329
|
+
assert message.vendor_details is None
|
|
@@ -65,7 +65,15 @@ async def test_google_model(allow_model_requests: None, google_provider: GoogleP
|
|
|
65
65
|
|
|
66
66
|
result = await agent.run('Hello!')
|
|
67
67
|
assert result.output == snapshot('Hello there! How can I help you today?\n')
|
|
68
|
-
assert result.usage() == snapshot(
|
|
68
|
+
assert result.usage() == snapshot(
|
|
69
|
+
Usage(
|
|
70
|
+
requests=1,
|
|
71
|
+
request_tokens=7,
|
|
72
|
+
response_tokens=11,
|
|
73
|
+
total_tokens=18,
|
|
74
|
+
details={'text_prompt_tokens': 7, 'text_candidates_tokens': 11},
|
|
75
|
+
)
|
|
76
|
+
)
|
|
69
77
|
assert result.all_messages() == snapshot(
|
|
70
78
|
[
|
|
71
79
|
ModelRequest(
|
|
@@ -82,9 +90,16 @@ async def test_google_model(allow_model_requests: None, google_provider: GoogleP
|
|
|
82
90
|
),
|
|
83
91
|
ModelResponse(
|
|
84
92
|
parts=[TextPart(content='Hello there! How can I help you today?\n')],
|
|
85
|
-
usage=Usage(
|
|
93
|
+
usage=Usage(
|
|
94
|
+
requests=1,
|
|
95
|
+
request_tokens=7,
|
|
96
|
+
response_tokens=11,
|
|
97
|
+
total_tokens=18,
|
|
98
|
+
details={'text_prompt_tokens': 7, 'text_candidates_tokens': 11},
|
|
99
|
+
),
|
|
86
100
|
model_name='gemini-1.5-flash',
|
|
87
101
|
timestamp=IsDatetime(),
|
|
102
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
88
103
|
),
|
|
89
104
|
]
|
|
90
105
|
)
|
|
@@ -114,7 +129,15 @@ async def test_google_model_structured_response(allow_model_requests: None, goog
|
|
|
114
129
|
|
|
115
130
|
result = await agent.run('What was the temperature in London 1st January 2022?', output_type=Response)
|
|
116
131
|
assert result.output == snapshot({'temperature': '30°C', 'date': datetime.date(2022, 1, 1), 'city': 'London'})
|
|
117
|
-
assert result.usage() == snapshot(
|
|
132
|
+
assert result.usage() == snapshot(
|
|
133
|
+
Usage(
|
|
134
|
+
requests=2,
|
|
135
|
+
request_tokens=224,
|
|
136
|
+
response_tokens=35,
|
|
137
|
+
total_tokens=259,
|
|
138
|
+
details={'text_prompt_tokens': 224, 'text_candidates_tokens': 35},
|
|
139
|
+
)
|
|
140
|
+
)
|
|
118
141
|
assert result.all_messages() == snapshot(
|
|
119
142
|
[
|
|
120
143
|
ModelRequest(
|
|
@@ -135,9 +158,16 @@ async def test_google_model_structured_response(allow_model_requests: None, goog
|
|
|
135
158
|
tool_name='temperature', args={'date': '2022-01-01', 'city': 'London'}, tool_call_id=IsStr()
|
|
136
159
|
)
|
|
137
160
|
],
|
|
138
|
-
usage=Usage(
|
|
161
|
+
usage=Usage(
|
|
162
|
+
requests=1,
|
|
163
|
+
request_tokens=101,
|
|
164
|
+
response_tokens=14,
|
|
165
|
+
total_tokens=115,
|
|
166
|
+
details={'text_prompt_tokens': 101, 'text_candidates_tokens': 14},
|
|
167
|
+
),
|
|
139
168
|
model_name='gemini-1.5-flash',
|
|
140
169
|
timestamp=IsDatetime(),
|
|
170
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
141
171
|
),
|
|
142
172
|
ModelRequest(
|
|
143
173
|
parts=[
|
|
@@ -154,9 +184,16 @@ async def test_google_model_structured_response(allow_model_requests: None, goog
|
|
|
154
184
|
tool_call_id=IsStr(),
|
|
155
185
|
)
|
|
156
186
|
],
|
|
157
|
-
usage=Usage(
|
|
187
|
+
usage=Usage(
|
|
188
|
+
requests=1,
|
|
189
|
+
request_tokens=123,
|
|
190
|
+
response_tokens=21,
|
|
191
|
+
total_tokens=144,
|
|
192
|
+
details={'text_prompt_tokens': 123, 'text_candidates_tokens': 21},
|
|
193
|
+
),
|
|
158
194
|
model_name='gemini-1.5-flash',
|
|
159
195
|
timestamp=IsDatetime(),
|
|
196
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
160
197
|
),
|
|
161
198
|
ModelRequest(
|
|
162
199
|
parts=[
|
|
@@ -211,10 +248,11 @@ async def test_google_model_retry(allow_model_requests: None, google_provider: G
|
|
|
211
248
|
request_tokens=57,
|
|
212
249
|
response_tokens=15,
|
|
213
250
|
total_tokens=173,
|
|
214
|
-
details={'
|
|
251
|
+
details={'thoughts_tokens': 101, 'text_prompt_tokens': 57},
|
|
215
252
|
),
|
|
216
253
|
model_name='models/gemini-2.5-pro-preview-05-06',
|
|
217
254
|
timestamp=IsDatetime(),
|
|
255
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
218
256
|
),
|
|
219
257
|
ModelRequest(
|
|
220
258
|
parts=[
|
|
@@ -232,9 +270,16 @@ async def test_google_model_retry(allow_model_requests: None, google_provider: G
|
|
|
232
270
|
content='I am sorry, I cannot fulfill this request. The country you provided is not supported.'
|
|
233
271
|
)
|
|
234
272
|
],
|
|
235
|
-
usage=Usage(
|
|
273
|
+
usage=Usage(
|
|
274
|
+
requests=1,
|
|
275
|
+
request_tokens=104,
|
|
276
|
+
response_tokens=18,
|
|
277
|
+
total_tokens=122,
|
|
278
|
+
details={'text_prompt_tokens': 104},
|
|
279
|
+
),
|
|
236
280
|
model_name='models/gemini-2.5-pro-preview-05-06',
|
|
237
281
|
timestamp=IsDatetime(),
|
|
282
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
238
283
|
),
|
|
239
284
|
]
|
|
240
285
|
)
|
|
@@ -488,9 +533,16 @@ async def test_google_model_instructions(allow_model_requests: None, google_prov
|
|
|
488
533
|
),
|
|
489
534
|
ModelResponse(
|
|
490
535
|
parts=[TextPart(content='The capital of France is Paris.\n')],
|
|
491
|
-
usage=Usage(
|
|
536
|
+
usage=Usage(
|
|
537
|
+
requests=1,
|
|
538
|
+
request_tokens=13,
|
|
539
|
+
response_tokens=8,
|
|
540
|
+
total_tokens=21,
|
|
541
|
+
details={'text_prompt_tokens': 13, 'text_candidates_tokens': 8},
|
|
542
|
+
),
|
|
492
543
|
model_name='gemini-2.0-flash',
|
|
493
544
|
timestamp=IsDatetime(),
|
|
545
|
+
vendor_details={'finish_reason': 'STOP'},
|
|
494
546
|
),
|
|
495
547
|
]
|
|
496
548
|
)
|
|
@@ -30,9 +30,13 @@ from pydantic_ai.messages import (
|
|
|
30
30
|
UserPromptPart,
|
|
31
31
|
)
|
|
32
32
|
from pydantic_ai.models.gemini import GeminiModel
|
|
33
|
+
from pydantic_ai.profiles import ModelProfile
|
|
34
|
+
from pydantic_ai.profiles._json_schema import InlineDefsJsonSchemaTransformer
|
|
35
|
+
from pydantic_ai.profiles.openai import OpenAIModelProfile, openai_model_profile
|
|
33
36
|
from pydantic_ai.providers.google_gla import GoogleGLAProvider
|
|
34
37
|
from pydantic_ai.result import Usage
|
|
35
38
|
from pydantic_ai.settings import ModelSettings
|
|
39
|
+
from pydantic_ai.tools import ToolDefinition
|
|
36
40
|
|
|
37
41
|
from ..conftest import IsDatetime, IsNow, IsStr, raise_if_exception, try_import
|
|
38
42
|
from .mock_async_stream import MockAsyncStream
|
|
@@ -56,8 +60,8 @@ with try_import() as imports_successful:
|
|
|
56
60
|
OpenAIModel,
|
|
57
61
|
OpenAIModelSettings,
|
|
58
62
|
OpenAISystemPromptRole,
|
|
59
|
-
_OpenAIJsonSchema, # pyright: ignore[reportPrivateUsage]
|
|
60
63
|
)
|
|
64
|
+
from pydantic_ai.profiles.openai import OpenAIJsonSchemaTransformer
|
|
61
65
|
from pydantic_ai.providers.openai import OpenAIProvider
|
|
62
66
|
|
|
63
67
|
# note: we use Union here so that casting works with Python 3.9
|
|
@@ -1356,23 +1360,33 @@ async def test_strict_mode_cannot_infer_strict(
|
|
|
1356
1360
|
# Create a mock completion for testing
|
|
1357
1361
|
c = completion_message(ChatCompletionMessage(content='world', role='assistant'))
|
|
1358
1362
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
+
async def assert_strict(expected_strict: bool | None, profile: ModelProfile | None = None):
|
|
1364
|
+
mock_client = MockOpenAI.create_mock(c)
|
|
1365
|
+
m = OpenAIModel('gpt-4o', provider=OpenAIProvider(openai_client=mock_client), profile=profile)
|
|
1366
|
+
agent = Agent(m)
|
|
1363
1367
|
|
|
1364
|
-
|
|
1368
|
+
agent.tool_plain(strict=tool_strict)(tool)
|
|
1365
1369
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1370
|
+
await agent.run('hello')
|
|
1371
|
+
kwargs = get_mock_chat_completion_kwargs(mock_client)[0]
|
|
1372
|
+
assert 'tools' in kwargs, kwargs
|
|
1373
|
+
|
|
1374
|
+
assert kwargs['tools'][0]['function']['parameters'] == expected_params
|
|
1375
|
+
actual_strict = kwargs['tools'][0]['function'].get('strict')
|
|
1376
|
+
assert actual_strict == expected_strict
|
|
1377
|
+
if actual_strict is None:
|
|
1378
|
+
# If strict is included, it should be non-None
|
|
1379
|
+
assert 'strict' not in kwargs['tools'][0]['function']
|
|
1380
|
+
|
|
1381
|
+
await assert_strict(expected_strict)
|
|
1369
1382
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1383
|
+
# If the model profile says strict is not supported, we never pass strict
|
|
1384
|
+
await assert_strict(
|
|
1385
|
+
None,
|
|
1386
|
+
profile=OpenAIModelProfile(openai_supports_strict_tool_definition=False).update(
|
|
1387
|
+
openai_model_profile('test-model')
|
|
1388
|
+
),
|
|
1389
|
+
)
|
|
1376
1390
|
|
|
1377
1391
|
|
|
1378
1392
|
def test_strict_schema():
|
|
@@ -1390,7 +1404,7 @@ def test_strict_schema():
|
|
|
1390
1404
|
my_list: list[float]
|
|
1391
1405
|
my_discriminated_union: Annotated[Apple | Banana, Discriminator('kind')]
|
|
1392
1406
|
|
|
1393
|
-
assert
|
|
1407
|
+
assert OpenAIJsonSchemaTransformer(MyModel.model_json_schema(), strict=True).walk() == snapshot(
|
|
1394
1408
|
{
|
|
1395
1409
|
'$defs': {
|
|
1396
1410
|
'Apple': {
|
|
@@ -1601,3 +1615,98 @@ async def test_openai_instructions_with_logprobs(allow_model_requests: None):
|
|
|
1601
1615
|
'top_logprobs': [],
|
|
1602
1616
|
}
|
|
1603
1617
|
]
|
|
1618
|
+
|
|
1619
|
+
|
|
1620
|
+
def test_openai_model_profile():
|
|
1621
|
+
m = OpenAIModel('gpt-4o', provider=OpenAIProvider(api_key='foobar'))
|
|
1622
|
+
assert isinstance(m.profile, OpenAIModelProfile)
|
|
1623
|
+
|
|
1624
|
+
|
|
1625
|
+
def test_openai_model_profile_custom():
|
|
1626
|
+
m = OpenAIModel(
|
|
1627
|
+
'gpt-4o',
|
|
1628
|
+
provider=OpenAIProvider(api_key='foobar'),
|
|
1629
|
+
profile=ModelProfile(json_schema_transformer=InlineDefsJsonSchemaTransformer),
|
|
1630
|
+
)
|
|
1631
|
+
assert isinstance(m.profile, ModelProfile)
|
|
1632
|
+
assert m.profile.json_schema_transformer is InlineDefsJsonSchemaTransformer
|
|
1633
|
+
|
|
1634
|
+
m = OpenAIModel(
|
|
1635
|
+
'gpt-4o',
|
|
1636
|
+
provider=OpenAIProvider(api_key='foobar'),
|
|
1637
|
+
profile=OpenAIModelProfile(openai_supports_strict_tool_definition=False),
|
|
1638
|
+
)
|
|
1639
|
+
assert isinstance(m.profile, OpenAIModelProfile)
|
|
1640
|
+
assert m.profile.openai_supports_strict_tool_definition is False
|
|
1641
|
+
|
|
1642
|
+
|
|
1643
|
+
def test_openai_model_profile_function():
|
|
1644
|
+
def model_profile(model_name: str) -> ModelProfile:
|
|
1645
|
+
return ModelProfile(json_schema_transformer=InlineDefsJsonSchemaTransformer if model_name == 'gpt-4o' else None)
|
|
1646
|
+
|
|
1647
|
+
m = OpenAIModel('gpt-4o', provider=OpenAIProvider(api_key='foobar'), profile=model_profile)
|
|
1648
|
+
assert isinstance(m.profile, ModelProfile)
|
|
1649
|
+
assert m.profile.json_schema_transformer is InlineDefsJsonSchemaTransformer
|
|
1650
|
+
|
|
1651
|
+
m = OpenAIModel('gpt-4o-mini', provider=OpenAIProvider(api_key='foobar'), profile=model_profile)
|
|
1652
|
+
assert isinstance(m.profile, ModelProfile)
|
|
1653
|
+
assert m.profile.json_schema_transformer is None
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
def test_openai_model_profile_from_provider():
|
|
1657
|
+
class CustomProvider(OpenAIProvider):
|
|
1658
|
+
def model_profile(self, model_name: str) -> ModelProfile:
|
|
1659
|
+
return ModelProfile(
|
|
1660
|
+
json_schema_transformer=InlineDefsJsonSchemaTransformer if model_name == 'gpt-4o' else None
|
|
1661
|
+
)
|
|
1662
|
+
|
|
1663
|
+
m = OpenAIModel('gpt-4o', provider=CustomProvider(api_key='foobar'))
|
|
1664
|
+
assert isinstance(m.profile, ModelProfile)
|
|
1665
|
+
assert m.profile.json_schema_transformer is InlineDefsJsonSchemaTransformer
|
|
1666
|
+
|
|
1667
|
+
m = OpenAIModel('gpt-4o-mini', provider=CustomProvider(api_key='foobar'))
|
|
1668
|
+
assert isinstance(m.profile, ModelProfile)
|
|
1669
|
+
assert m.profile.json_schema_transformer is None
|
|
1670
|
+
|
|
1671
|
+
|
|
1672
|
+
def test_model_profile_strict_not_supported():
|
|
1673
|
+
my_tool = ToolDefinition(
|
|
1674
|
+
'my_tool',
|
|
1675
|
+
'This is my tool',
|
|
1676
|
+
{'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
1677
|
+
strict=True,
|
|
1678
|
+
)
|
|
1679
|
+
|
|
1680
|
+
m = OpenAIModel('gpt-4o', provider=OpenAIProvider(api_key='foobar'))
|
|
1681
|
+
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
|
|
1682
|
+
|
|
1683
|
+
assert tool_param == snapshot(
|
|
1684
|
+
{
|
|
1685
|
+
'type': 'function',
|
|
1686
|
+
'function': {
|
|
1687
|
+
'name': 'my_tool',
|
|
1688
|
+
'description': 'This is my tool',
|
|
1689
|
+
'parameters': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
1690
|
+
'strict': True,
|
|
1691
|
+
},
|
|
1692
|
+
}
|
|
1693
|
+
)
|
|
1694
|
+
|
|
1695
|
+
# Some models don't support strict tool definitions
|
|
1696
|
+
m = OpenAIModel(
|
|
1697
|
+
'gpt-4o',
|
|
1698
|
+
provider=OpenAIProvider(api_key='foobar'),
|
|
1699
|
+
profile=OpenAIModelProfile(openai_supports_strict_tool_definition=False).update(openai_model_profile('gpt-4o')),
|
|
1700
|
+
)
|
|
1701
|
+
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
|
|
1702
|
+
|
|
1703
|
+
assert tool_param == snapshot(
|
|
1704
|
+
{
|
|
1705
|
+
'type': 'function',
|
|
1706
|
+
'function': {
|
|
1707
|
+
'name': 'my_tool',
|
|
1708
|
+
'description': 'This is my tool',
|
|
1709
|
+
'parameters': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
1710
|
+
},
|
|
1711
|
+
}
|
|
1712
|
+
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from dataclasses import replace
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
4
5
|
from inline_snapshot import snapshot
|
|
@@ -18,6 +19,8 @@ from pydantic_ai.messages import (
|
|
|
18
19
|
ToolReturnPart,
|
|
19
20
|
UserPromptPart,
|
|
20
21
|
)
|
|
22
|
+
from pydantic_ai.profiles.openai import openai_model_profile
|
|
23
|
+
from pydantic_ai.tools import ToolDefinition
|
|
21
24
|
from pydantic_ai.usage import Usage
|
|
22
25
|
|
|
23
26
|
from ..conftest import IsDatetime, IsStr, TestEnv, try_import
|
|
@@ -462,3 +465,43 @@ async def test_openai_responses_model_instructions(allow_model_requests: None, o
|
|
|
462
465
|
),
|
|
463
466
|
]
|
|
464
467
|
)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def test_model_profile_strict_not_supported():
|
|
471
|
+
my_tool = ToolDefinition(
|
|
472
|
+
'my_tool',
|
|
473
|
+
'This is my tool',
|
|
474
|
+
{'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
475
|
+
strict=True,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
m = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(api_key='foobar'))
|
|
479
|
+
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
|
|
480
|
+
|
|
481
|
+
assert tool_param == snapshot(
|
|
482
|
+
{
|
|
483
|
+
'name': 'my_tool',
|
|
484
|
+
'parameters': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
485
|
+
'type': 'function',
|
|
486
|
+
'description': 'This is my tool',
|
|
487
|
+
'strict': True,
|
|
488
|
+
}
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
# Some models don't support strict tool definitions
|
|
492
|
+
m = OpenAIResponsesModel(
|
|
493
|
+
'gpt-4o',
|
|
494
|
+
provider=OpenAIProvider(api_key='foobar'),
|
|
495
|
+
profile=replace(openai_model_profile('gpt-4o'), openai_supports_strict_tool_definition=False),
|
|
496
|
+
)
|
|
497
|
+
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
|
|
498
|
+
|
|
499
|
+
assert tool_param == snapshot(
|
|
500
|
+
{
|
|
501
|
+
'name': 'my_tool',
|
|
502
|
+
'parameters': {'type': 'object', 'title': 'Result', 'properties': {'spam': {'type': 'number'}}},
|
|
503
|
+
'type': 'function',
|
|
504
|
+
'description': 'This is my tool',
|
|
505
|
+
'strict': False,
|
|
506
|
+
}
|
|
507
|
+
)
|