pydantic-ai 0.0.43__tar.gz → 0.0.44__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pydantic-ai might be problematic. Click here for more details.

Files changed (105) hide show
  1. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/PKG-INFO +3 -3
  2. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/pyproject.toml +3 -3
  3. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/conftest.py +5 -0
  4. pydantic_ai-0.0.44/tests/graph/test_utils.py +20 -0
  5. pydantic_ai-0.0.44/tests/models/cassettes/test_cohere/test_request_simple_success_with_vcr.yaml +64 -0
  6. pydantic_ai-0.0.44/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4.5-preview].yaml +78 -0
  7. pydantic_ai-0.0.44/tests/models/cassettes/test_openai/test_max_completion_tokens[gpt-4o-mini].yaml +79 -0
  8. pydantic_ai-0.0.44/tests/models/cassettes/test_openai/test_max_completion_tokens[o3-mini].yaml +78 -0
  9. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_cohere.py +16 -7
  10. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_fallback.py +4 -2
  11. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_instrumented.py +8 -1
  12. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_openai.py +11 -1
  13. pydantic_ai-0.0.44/tests/providers/test_cohere.py +54 -0
  14. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_provider_names.py +2 -0
  15. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_examples.py +2 -0
  16. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_logfire.py +23 -2
  17. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_tools.py +148 -8
  18. pydantic_ai-0.0.43/tests/graph/test_utils.py +0 -13
  19. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/.gitignore +0 -0
  20. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/LICENSE +0 -0
  21. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/Makefile +0 -0
  22. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/README.md +0 -0
  23. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/__init__.py +0 -0
  24. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/assets/dummy.pdf +0 -0
  25. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/assets/kiwi.png +0 -0
  26. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/assets/marcelo.mp3 +0 -0
  27. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/cassettes/test_mcp/test_agent_with_stdio_server.yaml +0 -0
  28. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/example_modules/README.md +0 -0
  29. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/example_modules/bank_database.py +0 -0
  30. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/example_modules/fake_database.py +0 -0
  31. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/example_modules/weather_service.py +0 -0
  32. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/graph/__init__.py +0 -0
  33. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/graph/test_file_persistence.py +0 -0
  34. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/graph/test_graph.py +0 -0
  35. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/graph/test_mermaid.py +0 -0
  36. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/graph/test_persistence.py +0 -0
  37. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/graph/test_state.py +0 -0
  38. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/import_examples.py +0 -0
  39. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/json_body_serializer.py +0 -0
  40. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/mcp_server.py +0 -0
  41. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/__init__.py +0 -0
  42. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_anthropic/test_document_binary_content_input.yaml +0 -0
  43. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_anthropic/test_document_url_input.yaml +0 -0
  44. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_anthropic/test_image_url_input.yaml +0 -0
  45. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_anthropic/test_image_url_input_invalid_mime_type.yaml +0 -0
  46. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_anthropic/test_multiple_parallel_tool_calls.yaml +0 -0
  47. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_anthropic/test_text_document_url_input.yaml +0 -0
  48. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model.yaml +0 -0
  49. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_anthropic_model_without_tools.yaml +0 -0
  50. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_iter_stream.yaml +0 -0
  51. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_max_tokens.yaml +0 -0
  52. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_retry.yaml +0 -0
  53. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_stream.yaml +0 -0
  54. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_structured_response.yaml +0 -0
  55. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_bedrock_model_top_p.yaml +0 -0
  56. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_document_url_input.yaml +0 -0
  57. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_image_as_binary_content_input.yaml +0 -0
  58. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_image_url_input.yaml +0 -0
  59. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_text_as_binary_content_input.yaml +0 -0
  60. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_bedrock/test_text_document_url_input.yaml +0 -0
  61. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_gemini/test_document_url_input.yaml +0 -0
  62. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_gemini/test_image_as_binary_content_input.yaml +0 -0
  63. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_gemini/test_image_url_input.yaml +0 -0
  64. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_groq/test_image_as_binary_content_input.yaml +0 -0
  65. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_groq/test_image_url_input.yaml +0 -0
  66. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_openai/test_audio_as_binary_content_input.yaml +0 -0
  67. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_openai/test_document_url_input.yaml +0 -0
  68. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_openai/test_image_as_binary_content_input.yaml +0 -0
  69. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[developer].yaml +0 -0
  70. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/cassettes/test_openai/test_openai_o1_mini_system_role[system].yaml +0 -0
  71. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/mock_async_stream.py +0 -0
  72. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_anthropic.py +0 -0
  73. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_bedrock.py +0 -0
  74. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_gemini.py +0 -0
  75. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_groq.py +0 -0
  76. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_mistral.py +0 -0
  77. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_model.py +0 -0
  78. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_model_function.py +0 -0
  79. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_model_names.py +0 -0
  80. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_model_test.py +0 -0
  81. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/models/test_vertexai.py +0 -0
  82. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/__init__.py +0 -0
  83. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/cassettes/test_azure/test_azure_provider_call.yaml +0 -0
  84. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_anthropic.py +0 -0
  85. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_azure.py +0 -0
  86. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_bedrock.py +0 -0
  87. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_deepseek.py +0 -0
  88. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_google_gla.py +0 -0
  89. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_google_vertex.py +0 -0
  90. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_groq.py +0 -0
  91. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/providers/test_mistral.py +0 -0
  92. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_agent.py +0 -0
  93. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_cli.py +0 -0
  94. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_deps.py +0 -0
  95. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_format_as_xml.py +0 -0
  96. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_json_body_serializer.py +0 -0
  97. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_live.py +0 -0
  98. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_mcp.py +0 -0
  99. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_messages.py +0 -0
  100. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_parts_manager.py +0 -0
  101. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_streaming.py +0 -0
  102. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_usage_limits.py +0 -0
  103. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/test_utils.py +0 -0
  104. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/typed_agent.py +0 -0
  105. {pydantic_ai-0.0.43 → pydantic_ai-0.0.44}/tests/typed_graph.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai
3
- Version: 0.0.43
3
+ Version: 0.0.44
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[anthropic,bedrock,cli,cohere,groq,mcp,mistral,openai,vertexai]==0.0.43
31
+ Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cli,cohere,groq,mcp,mistral,openai,vertexai]==0.0.44
32
32
  Provides-Extra: examples
33
- Requires-Dist: pydantic-ai-examples==0.0.43; extra == 'examples'
33
+ Requires-Dist: pydantic-ai-examples==0.0.44; extra == 'examples'
34
34
  Provides-Extra: logfire
35
35
  Requires-Dist: logfire>=2.3; extra == 'logfire'
36
36
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pydantic-ai"
7
- version = "0.0.43"
7
+ version = "0.0.44"
8
8
  description = "Agent Framework / shim to use Pydantic with LLMs"
9
9
  authors = [
10
10
  { name = "Samuel Colvin", email = "samuel@pydantic.dev" },
@@ -36,7 +36,7 @@ classifiers = [
36
36
  ]
37
37
  requires-python = ">=3.9"
38
38
  dependencies = [
39
- "pydantic-ai-slim[openai,vertexai,groq,anthropic,mistral,cohere,bedrock,cli,mcp]==0.0.43",
39
+ "pydantic-ai-slim[openai,vertexai,groq,anthropic,mistral,cohere,bedrock,cli,mcp]==0.0.44",
40
40
  ]
41
41
 
42
42
  [project.urls]
@@ -46,7 +46,7 @@ Documentation = "https://ai.pydantic.dev"
46
46
  Changelog = "https://github.com/pydantic/pydantic-ai/releases"
47
47
 
48
48
  [project.optional-dependencies]
49
- examples = ["pydantic-ai-examples==0.0.43"]
49
+ examples = ["pydantic-ai-examples==0.0.44"]
50
50
  logfire = ["logfire>=2.3"]
51
51
 
52
52
  [tool.uv.sources]
@@ -246,6 +246,11 @@ def anthropic_api_key() -> str:
246
246
  return os.getenv('ANTHROPIC_API_KEY', 'mock-api-key')
247
247
 
248
248
 
249
+ @pytest.fixture(scope='session')
250
+ def co_api_key() -> str:
251
+ return os.getenv('CO_API_KEY', 'mock-api-key')
252
+
253
+
249
254
  @pytest.fixture
250
255
  def mock_snapshot_id(mocker: MockerFixture):
251
256
  i = 0
@@ -0,0 +1,20 @@
1
+ from threading import Thread
2
+
3
+ from pydantic_graph._utils import run_until_complete
4
+
5
+
6
+ def test_run_until_complete_in_main_thread():
7
+ async def run(): ...
8
+
9
+ run_until_complete(run())
10
+
11
+
12
+ def test_run_until_complete_in_thread():
13
+ async def run(): ...
14
+
15
+ def get_and_close_event_loop():
16
+ run_until_complete(run())
17
+
18
+ thread = Thread(target=get_and_close_event_loop)
19
+ thread.start()
20
+ thread.join()
@@ -0,0 +1,64 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - '*/*'
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '93'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.cohere.com
16
+ method: POST
17
+ parsed_body:
18
+ messages:
19
+ - content: hello
20
+ role: user
21
+ model: command-r7b-12-2024
22
+ stream: false
23
+ uri: https://api.cohere.com/v2/chat
24
+ response:
25
+ headers:
26
+ access-control-expose-headers:
27
+ - X-Debug-Trace-ID
28
+ alt-svc:
29
+ - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
30
+ cache-control:
31
+ - no-cache, no-store, no-transform, must-revalidate, private, max-age=0
32
+ content-length:
33
+ - '286'
34
+ content-type:
35
+ - application/json
36
+ expires:
37
+ - Thu, 01 Jan 1970 00:00:00 UTC
38
+ num_chars:
39
+ - '2583'
40
+ num_tokens:
41
+ - '10'
42
+ pragma:
43
+ - no-cache
44
+ vary:
45
+ - Origin
46
+ parsed_body:
47
+ finish_reason: COMPLETE
48
+ id: f17a5f6c-1734-4098-bd0d-733ef000ac7b
49
+ message:
50
+ content:
51
+ - text: Hello! How can I assist you today?
52
+ type: text
53
+ role: assistant
54
+ usage:
55
+ billed_units:
56
+ input_tokens: 1
57
+ output_tokens: 9
58
+ tokens:
59
+ input_tokens: 496
60
+ output_tokens: 11
61
+ status:
62
+ code: 200
63
+ message: OK
64
+ version: 1
@@ -0,0 +1,78 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '123'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.openai.com
16
+ method: POST
17
+ parsed_body:
18
+ max_completion_tokens: 100
19
+ messages:
20
+ - content: hello
21
+ role: user
22
+ model: gpt-4.5-preview
23
+ n: 1
24
+ stream: false
25
+ uri: https://api.openai.com/v1/chat/completions
26
+ response:
27
+ headers:
28
+ access-control-expose-headers:
29
+ - X-Request-ID
30
+ alt-svc:
31
+ - h3=":443"; ma=86400
32
+ connection:
33
+ - keep-alive
34
+ content-length:
35
+ - '807'
36
+ content-type:
37
+ - application/json
38
+ openai-organization:
39
+ - pydantic-28gund
40
+ openai-processing-ms:
41
+ - '1408'
42
+ openai-version:
43
+ - '2020-10-01'
44
+ strict-transport-security:
45
+ - max-age=31536000; includeSubDomains; preload
46
+ transfer-encoding:
47
+ - chunked
48
+ parsed_body:
49
+ choices:
50
+ - finish_reason: stop
51
+ index: 0
52
+ message:
53
+ annotations: []
54
+ content: Hello! How can I help you today?
55
+ refusal: null
56
+ role: assistant
57
+ created: 1742636225
58
+ id: chatcmpl-BDpZplWguNLn40wA5mIpCR3OIzvYP
59
+ model: gpt-4.5-preview-2025-02-27
60
+ object: chat.completion
61
+ service_tier: default
62
+ system_fingerprint: null
63
+ usage:
64
+ completion_tokens: 10
65
+ completion_tokens_details:
66
+ accepted_prediction_tokens: 0
67
+ audio_tokens: 0
68
+ reasoning_tokens: 0
69
+ rejected_prediction_tokens: 0
70
+ prompt_tokens: 8
71
+ prompt_tokens_details:
72
+ audio_tokens: 0
73
+ cached_tokens: 0
74
+ total_tokens: 18
75
+ status:
76
+ code: 200
77
+ message: OK
78
+ version: 1
@@ -0,0 +1,79 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '119'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.openai.com
16
+ method: POST
17
+ parsed_body:
18
+ max_completion_tokens: 100
19
+ messages:
20
+ - content: hello
21
+ role: user
22
+ model: gpt-4o-mini
23
+ n: 1
24
+ stream: false
25
+ uri: https://api.openai.com/v1/chat/completions
26
+ response:
27
+ headers:
28
+ access-control-expose-headers:
29
+ - X-Request-ID
30
+ alt-svc:
31
+ - h3=":443"; ma=86400
32
+ connection:
33
+ - keep-alive
34
+ content-length:
35
+ - '840'
36
+ content-type:
37
+ - application/json
38
+ openai-organization:
39
+ - pydantic-28gund
40
+ openai-processing-ms:
41
+ - '278'
42
+ openai-version:
43
+ - '2020-10-01'
44
+ strict-transport-security:
45
+ - max-age=31536000; includeSubDomains; preload
46
+ transfer-encoding:
47
+ - chunked
48
+ parsed_body:
49
+ choices:
50
+ - finish_reason: stop
51
+ index: 0
52
+ logprobs: null
53
+ message:
54
+ annotations: []
55
+ content: Hello! How can I assist you today?
56
+ refusal: null
57
+ role: assistant
58
+ created: 1742636224
59
+ id: chatcmpl-BDpZoxw4i90ZesN8iyrwLmGWRJ5lz
60
+ model: gpt-4o-mini-2024-07-18
61
+ object: chat.completion
62
+ service_tier: default
63
+ system_fingerprint: fp_b8bc95a0ac
64
+ usage:
65
+ completion_tokens: 10
66
+ completion_tokens_details:
67
+ accepted_prediction_tokens: 0
68
+ audio_tokens: 0
69
+ reasoning_tokens: 0
70
+ rejected_prediction_tokens: 0
71
+ prompt_tokens: 8
72
+ prompt_tokens_details:
73
+ audio_tokens: 0
74
+ cached_tokens: 0
75
+ total_tokens: 18
76
+ status:
77
+ code: 200
78
+ message: OK
79
+ version: 1
@@ -0,0 +1,78 @@
1
+ interactions:
2
+ - request:
3
+ headers:
4
+ accept:
5
+ - application/json
6
+ accept-encoding:
7
+ - gzip, deflate
8
+ connection:
9
+ - keep-alive
10
+ content-length:
11
+ - '115'
12
+ content-type:
13
+ - application/json
14
+ host:
15
+ - api.openai.com
16
+ method: POST
17
+ parsed_body:
18
+ max_completion_tokens: 100
19
+ messages:
20
+ - content: hello
21
+ role: user
22
+ model: o3-mini
23
+ n: 1
24
+ stream: false
25
+ uri: https://api.openai.com/v1/chat/completions
26
+ response:
27
+ headers:
28
+ access-control-expose-headers:
29
+ - X-Request-ID
30
+ alt-svc:
31
+ - h3=":443"; ma=86400
32
+ connection:
33
+ - keep-alive
34
+ content-length:
35
+ - '817'
36
+ content-type:
37
+ - application/json
38
+ openai-organization:
39
+ - pydantic-28gund
40
+ openai-processing-ms:
41
+ - '1895'
42
+ openai-version:
43
+ - '2020-10-01'
44
+ strict-transport-security:
45
+ - max-age=31536000; includeSubDomains; preload
46
+ transfer-encoding:
47
+ - chunked
48
+ parsed_body:
49
+ choices:
50
+ - finish_reason: stop
51
+ index: 0
52
+ message:
53
+ annotations: []
54
+ content: Hello there! How can I help you today?
55
+ refusal: null
56
+ role: assistant
57
+ created: 1742636222
58
+ id: chatcmpl-BDpZm1SiItIXIcDA0xRV9QGZhD97e
59
+ model: o3-mini-2025-01-31
60
+ object: chat.completion
61
+ service_tier: default
62
+ system_fingerprint: fp_617f206dd9
63
+ usage:
64
+ completion_tokens: 85
65
+ completion_tokens_details:
66
+ accepted_prediction_tokens: 0
67
+ audio_tokens: 0
68
+ reasoning_tokens: 64
69
+ rejected_prediction_tokens: 0
70
+ prompt_tokens: 7
71
+ prompt_tokens_details:
72
+ audio_tokens: 0
73
+ cached_tokens: 0
74
+ total_tokens: 92
75
+ status:
76
+ code: 200
77
+ message: OK
78
+ version: 1
@@ -38,6 +38,7 @@ with try_import() as imports_successful:
38
38
  from cohere.core.api_error import ApiError
39
39
 
40
40
  from pydantic_ai.models.cohere import CohereModel
41
+ from pydantic_ai.providers.cohere import CohereProvider
41
42
 
42
43
  # note: we use Union here for compatibility with Python 3.9
43
44
  MockChatResponse = Union[ChatResponse, Exception]
@@ -49,7 +50,7 @@ pytestmark = [
49
50
 
50
51
 
51
52
  def test_init():
52
- m = CohereModel('command-r7b-12-2024', api_key='foobar')
53
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(api_key='foobar'))
53
54
  assert m.model_name == 'command-r7b-12-2024'
54
55
  assert m.system == 'cohere'
55
56
  assert m.base_url == 'https://api.cohere.com'
@@ -96,7 +97,7 @@ async def test_request_simple_success(allow_model_requests: None):
96
97
  )
97
98
  )
98
99
  mock_client = MockAsyncClientV2.create_mock(c)
99
- m = CohereModel('command-r7b-12-2024', cohere_client=mock_client)
100
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(cohere_client=mock_client))
100
101
  agent = Agent(m)
101
102
 
102
103
  result = await agent.run('hello')
@@ -135,7 +136,7 @@ async def test_request_simple_usage(allow_model_requests: None):
135
136
  ),
136
137
  )
137
138
  mock_client = MockAsyncClientV2.create_mock(c)
138
- m = CohereModel('command-r7b-12-2024', cohere_client=mock_client)
139
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(cohere_client=mock_client))
139
140
  agent = Agent(m)
140
141
 
141
142
  result = await agent.run('Hello')
@@ -169,7 +170,7 @@ async def test_request_structured_response(allow_model_requests: None):
169
170
  )
170
171
  )
171
172
  mock_client = MockAsyncClientV2.create_mock(c)
172
- m = CohereModel('command-r7b-12-2024', cohere_client=mock_client)
173
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(cohere_client=mock_client))
173
174
  agent = Agent(m, result_type=list[int])
174
175
 
175
176
  result = await agent.run('Hello')
@@ -243,7 +244,7 @@ async def test_request_tool_call(allow_model_requests: None):
243
244
  ),
244
245
  ]
245
246
  mock_client = MockAsyncClientV2.create_mock(responses)
246
- m = CohereModel('command-r7b-12-2024', cohere_client=mock_client)
247
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(cohere_client=mock_client))
247
248
  agent = Agent(m, system_prompt='this is the system prompt')
248
249
 
249
250
  @agent.tool_plain
@@ -326,7 +327,7 @@ async def test_request_tool_call(allow_model_requests: None):
326
327
  async def test_multimodal(allow_model_requests: None):
327
328
  c = completion_message(AssistantMessageResponse(content=[TextAssistantMessageResponseContentItem(text='world')]))
328
329
  mock_client = MockAsyncClientV2.create_mock(c)
329
- m = CohereModel('command-r7b-12-2024', cohere_client=mock_client)
330
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(cohere_client=mock_client))
330
331
  agent = Agent(m)
331
332
 
332
333
  with pytest.raises(RuntimeError, match='Cohere does not yet support multi-modal inputs.'):
@@ -347,8 +348,16 @@ def test_model_status_error(allow_model_requests: None) -> None:
347
348
  body={'error': 'test error'},
348
349
  )
349
350
  )
350
- m = CohereModel('command-r', cohere_client=mock_client)
351
+ m = CohereModel('command-r', provider=CohereProvider(cohere_client=mock_client))
351
352
  agent = Agent(m)
352
353
  with pytest.raises(ModelHTTPError) as exc_info:
353
354
  agent.run_sync('hello')
354
355
  assert str(exc_info.value) == snapshot("status_code: 500, model_name: command-r, body: {'error': 'test error'}")
356
+
357
+
358
+ @pytest.mark.vcr()
359
+ async def test_request_simple_success_with_vcr(allow_model_requests: None, co_api_key: str):
360
+ m = CohereModel('command-r7b-12-2024', provider=CohereProvider(api_key=co_api_key))
361
+ agent = Agent(m)
362
+ result = await agent.run('hello')
363
+ assert result.data == snapshot('Hello! How can I assist you today?')
@@ -136,6 +136,7 @@ def test_first_failed_instrumented(capfire: CaptureLogfire) -> None:
136
136
  'end_time': 5000000000,
137
137
  'attributes': {
138
138
  'gen_ai.operation.name': 'chat',
139
+ 'model_request_parameters': '{"function_tools": [], "allow_text_result": true, "result_tools": []}',
139
140
  'logfire.span_type': 'span',
140
141
  'logfire.msg': 'chat fallback:function:failure_response:,function:success_response:',
141
142
  'gen_ai.usage.input_tokens': 51,
@@ -160,7 +161,7 @@ def test_first_failed_instrumented(capfire: CaptureLogfire) -> None:
160
161
  },
161
162
  ]
162
163
  ),
163
- 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}}}',
164
+ 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}, "model_request_parameters": {"type": "object"}}}',
164
165
  },
165
166
  },
166
167
  {
@@ -233,6 +234,7 @@ async def test_first_failed_instrumented_stream(capfire: CaptureLogfire) -> None
233
234
  'end_time': 5000000000,
234
235
  'attributes': {
235
236
  'gen_ai.operation.name': 'chat',
237
+ 'model_request_parameters': '{"function_tools": [], "allow_text_result": true, "result_tools": []}',
236
238
  'logfire.span_type': 'span',
237
239
  'logfire.msg': 'chat fallback:function::failure_response_stream,function::success_response_stream',
238
240
  'gen_ai.system': 'function',
@@ -241,7 +243,7 @@ async def test_first_failed_instrumented_stream(capfire: CaptureLogfire) -> None
241
243
  'gen_ai.usage.output_tokens': 2,
242
244
  'gen_ai.response.model': 'function::success_response_stream',
243
245
  'events': '[{"content": "input", "role": "user", "gen_ai.system": "function", "gen_ai.message.index": 0, "event.name": "gen_ai.user.message"}, {"index": 0, "message": {"role": "assistant", "content": "hello world"}, "gen_ai.system": "function", "event.name": "gen_ai.choice"}]',
244
- 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}}}',
246
+ 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}, "model_request_parameters": {"type": "object"}}}',
245
247
  },
246
248
  },
247
249
  {
@@ -152,6 +152,8 @@ async def test_instrumented_model(capfire: CaptureLogfire):
152
152
  'gen_ai.request.model': 'my_model',
153
153
  'server.address': 'example.com',
154
154
  'server.port': 8000,
155
+ 'model_request_parameters': '{"function_tools": [], "allow_text_result": true, "result_tools": []}',
156
+ 'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
155
157
  'gen_ai.request.temperature': 1,
156
158
  'logfire.msg': 'chat my_model',
157
159
  'logfire.span_type': 'span',
@@ -374,6 +376,8 @@ async def test_instrumented_model_stream(capfire: CaptureLogfire):
374
376
  'gen_ai.request.model': 'my_model',
375
377
  'server.address': 'example.com',
376
378
  'server.port': 8000,
379
+ 'model_request_parameters': '{"function_tools": [], "allow_text_result": true, "result_tools": []}',
380
+ 'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
377
381
  'gen_ai.request.temperature': 1,
378
382
  'logfire.msg': 'chat my_model',
379
383
  'logfire.span_type': 'span',
@@ -457,6 +461,8 @@ async def test_instrumented_model_stream_break(capfire: CaptureLogfire):
457
461
  'gen_ai.request.model': 'my_model',
458
462
  'server.address': 'example.com',
459
463
  'server.port': 8000,
464
+ 'model_request_parameters': '{"function_tools": [], "allow_text_result": true, "result_tools": []}',
465
+ 'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
460
466
  'gen_ai.request.temperature': 1,
461
467
  'logfire.msg': 'chat my_model',
462
468
  'logfire.span_type': 'span',
@@ -559,6 +565,7 @@ async def test_instrumented_model_attributes_mode(capfire: CaptureLogfire):
559
565
  'gen_ai.request.model': 'my_model',
560
566
  'server.address': 'example.com',
561
567
  'server.port': 8000,
568
+ 'model_request_parameters': '{"function_tools": [], "allow_text_result": true, "result_tools": []}',
562
569
  'gen_ai.request.temperature': 1,
563
570
  'logfire.msg': 'chat my_model',
564
571
  'logfire.span_type': 'span',
@@ -652,7 +659,7 @@ Fix the errors and try again.\
652
659
  ]
653
660
  )
654
661
  ),
655
- 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}}}',
662
+ 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}, "model_request_parameters": {"type": "object"}}}',
656
663
  },
657
664
  },
658
665
  ]
@@ -28,7 +28,7 @@ from pydantic_ai.messages import (
28
28
  from pydantic_ai.result import Usage
29
29
  from pydantic_ai.settings import ModelSettings
30
30
 
31
- from ..conftest import IsNow, TestEnv, raise_if_exception, try_import
31
+ from ..conftest import IsNow, IsStr, TestEnv, raise_if_exception, try_import
32
32
  from .mock_async_stream import MockAsyncStream
33
33
 
34
34
  with try_import() as imports_successful:
@@ -684,3 +684,13 @@ def test_model_status_error(allow_model_requests: None) -> None:
684
684
  with pytest.raises(ModelHTTPError) as exc_info:
685
685
  agent.run_sync('hello')
686
686
  assert str(exc_info.value) == snapshot("status_code: 500, model_name: gpt-4o, body: {'error': 'test error'}")
687
+
688
+
689
+ @pytest.mark.vcr()
690
+ @pytest.mark.parametrize('model_name', ['o3-mini', 'gpt-4o-mini', 'gpt-4.5-preview'])
691
+ async def test_max_completion_tokens(allow_model_requests: None, model_name: str, openai_api_key: str):
692
+ m = OpenAIModel(model_name, provider=OpenAIProvider(api_key=openai_api_key))
693
+ agent = Agent(m, model_settings=ModelSettings(max_tokens=100))
694
+
695
+ result = await agent.run('hello')
696
+ assert result.data == IsStr()
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations as _annotations
2
+
3
+ import os
4
+ from unittest.mock import patch
5
+
6
+ import httpx
7
+ import pytest
8
+
9
+ from ..conftest import try_import
10
+
11
+ with try_import() as imports_successful:
12
+ from cohere import AsyncClientV2
13
+ from cohere.core.http_client import AsyncHttpClient
14
+
15
+ from pydantic_ai.providers.cohere import CohereProvider
16
+
17
+
18
+ pytestmark = pytest.mark.skipif(not imports_successful(), reason='cohere not installed')
19
+
20
+
21
+ def test_cohere_provider() -> None:
22
+ provider = CohereProvider(api_key='api-key')
23
+ assert provider.name == 'cohere'
24
+ assert provider.base_url == 'https://api.cohere.com'
25
+ assert isinstance(provider.client, AsyncClientV2)
26
+ assert provider.client._client_wrapper._token == 'api-key' # type: ignore[reportPrivateUsage]
27
+
28
+
29
+ def test_cohere_provider_need_api_key() -> None:
30
+ with patch.dict(os.environ, {}, clear=True):
31
+ with pytest.raises(ValueError, match='CO_API_KEY'):
32
+ CohereProvider()
33
+
34
+
35
+ def test_cohere_provider_pass_http_client() -> None:
36
+ http_client = httpx.AsyncClient()
37
+ provider = CohereProvider(http_client=http_client, api_key='api-key')
38
+ # The AsyncClientV2 wraps our httpx client in an AsyncHttpClient
39
+ # So we just check that the httpx_client is an instance of AsyncHttpClient
40
+ assert isinstance(provider.client._client_wrapper.httpx_client, AsyncHttpClient) # type: ignore[reportPrivateUsage]
41
+
42
+
43
+ def test_cohere_provider_pass_cohere_client() -> None:
44
+ cohere_client = AsyncClientV2(api_key='test-api-key')
45
+ provider = CohereProvider(cohere_client=cohere_client)
46
+ assert provider.client == cohere_client
47
+
48
+
49
+ def test_cohere_provider_with_env_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
50
+ custom_base_url = 'https://custom.cohere.com/'
51
+ # Test with environment variable for base_url
52
+ monkeypatch.setenv('CO_BASE_URL', custom_base_url)
53
+ provider = CohereProvider(api_key='api-key')
54
+ assert provider.base_url == custom_base_url
@@ -12,6 +12,7 @@ from ..conftest import try_import
12
12
 
13
13
  with try_import() as imports_successful:
14
14
  from pydantic_ai.providers.anthropic import AnthropicProvider
15
+ from pydantic_ai.providers.cohere import CohereProvider
15
16
  from pydantic_ai.providers.deepseek import DeepSeekProvider
16
17
  from pydantic_ai.providers.google_gla import GoogleGLAProvider
17
18
  from pydantic_ai.providers.google_vertex import GoogleVertexProvider
@@ -21,6 +22,7 @@ with try_import() as imports_successful:
21
22
 
22
23
  test_infer_provider_params = [
23
24
  ('anthropic', AnthropicProvider, 'ANTHROPIC_API_KEY'),
25
+ ('cohere', CohereProvider, 'CO_API_KEY'),
24
26
  ('deepseek', DeepSeekProvider, 'DEEPSEEK_API_KEY'),
25
27
  ('openai', OpenAIProvider, None),
26
28
  ('google-vertex', GoogleVertexProvider, None),
@@ -86,6 +86,8 @@ def test_docs_examples( # noqa: C901
86
86
  env.set('GEMINI_API_KEY', 'testing')
87
87
  env.set('GROQ_API_KEY', 'testing')
88
88
  env.set('CO_API_KEY', 'testing')
89
+ env.set('MISTRAL_API_KEY', 'testing')
90
+ env.set('ANTHROPIC_API_KEY', 'testing')
89
91
 
90
92
  sys.path.append('tests/example_modules')
91
93
 
@@ -167,7 +167,7 @@ def test_logfire(get_logfire_summary: Callable[[], LogfireSummary], instrument:
167
167
  )
168
168
  chat_span_attributes = summary.attributes[2]
169
169
  if instrument is True or instrument.event_mode == 'attributes':
170
- attribute_mode_attributes = {k: chat_span_attributes.pop(k) for k in ['events', 'logfire.json_schema']}
170
+ attribute_mode_attributes = {k: chat_span_attributes.pop(k) for k in ['events']}
171
171
  assert attribute_mode_attributes == snapshot(
172
172
  {
173
173
  'events': IsJson(
@@ -198,7 +198,6 @@ def test_logfire(get_logfire_summary: Callable[[], LogfireSummary], instrument:
198
198
  ]
199
199
  )
200
200
  ),
201
- 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}}}',
202
201
  }
203
202
  )
204
203
 
@@ -207,6 +206,28 @@ def test_logfire(get_logfire_summary: Callable[[], LogfireSummary], instrument:
207
206
  'gen_ai.operation.name': 'chat',
208
207
  'gen_ai.system': 'test',
209
208
  'gen_ai.request.model': 'test',
209
+ 'model_request_parameters': IsJson(
210
+ snapshot(
211
+ {
212
+ 'function_tools': [
213
+ {
214
+ 'name': 'my_ret',
215
+ 'description': '',
216
+ 'parameters_json_schema': {
217
+ 'additionalProperties': False,
218
+ 'properties': {'x': {'type': 'integer'}},
219
+ 'required': ['x'],
220
+ 'type': 'object',
221
+ },
222
+ 'outer_typed_dict_key': None,
223
+ }
224
+ ],
225
+ 'allow_text_result': True,
226
+ 'result_tools': [],
227
+ }
228
+ )
229
+ ),
230
+ 'logfire.json_schema': IsJson(),
210
231
  'logfire.span_type': 'span',
211
232
  'logfire.msg': 'chat test',
212
233
  'gen_ai.response.model': 'test',
@@ -122,7 +122,6 @@ def sphinx_style_docstring(foo: int, /) -> str: # pragma: no cover
122
122
  """Sphinx style docstring.
123
123
 
124
124
  :param foo: The foo thing.
125
- :return: The result.
126
125
  """
127
126
  return str(foo)
128
127
 
@@ -187,6 +186,152 @@ def test_docstring_numpy(docstring_format: Literal['numpy', 'auto']):
187
186
  )
188
187
 
189
188
 
189
+ def test_google_style_with_returns():
190
+ agent = Agent(FunctionModel(get_json_schema))
191
+
192
+ def my_tool(x: int) -> str: # pragma: no cover
193
+ """A function that does something.
194
+
195
+ Args:
196
+ x: The input value.
197
+
198
+ Returns:
199
+ str: The result as a string.
200
+ """
201
+ return str(x)
202
+
203
+ agent.tool_plain(my_tool)
204
+ result = agent.run_sync('Hello')
205
+ json_schema = json.loads(result.data)
206
+ assert json_schema == snapshot(
207
+ {
208
+ 'name': 'my_tool',
209
+ 'description': """\
210
+ <summary>A function that does something.</summary>
211
+ <returns>
212
+ <type>str</type>
213
+ <description>The result as a string.</description>
214
+ </returns>\
215
+ """,
216
+ 'parameters_json_schema': {
217
+ 'additionalProperties': False,
218
+ 'properties': {'x': {'description': 'The input value.', 'type': 'integer'}},
219
+ 'required': ['x'],
220
+ 'type': 'object',
221
+ },
222
+ 'outer_typed_dict_key': None,
223
+ }
224
+ )
225
+
226
+
227
+ def test_sphinx_style_with_returns():
228
+ agent = Agent(FunctionModel(get_json_schema))
229
+
230
+ def my_tool(x: int) -> str: # pragma: no cover
231
+ """A sphinx function with returns.
232
+
233
+ :param x: The input value.
234
+ :rtype: str
235
+ :return: The result as a string with type.
236
+ """
237
+ return str(x)
238
+
239
+ agent.tool_plain(docstring_format='sphinx')(my_tool)
240
+ result = agent.run_sync('Hello')
241
+ json_schema = json.loads(result.data)
242
+ assert json_schema == snapshot(
243
+ {
244
+ 'name': 'my_tool',
245
+ 'description': """\
246
+ <summary>A sphinx function with returns.</summary>
247
+ <returns>
248
+ <type>str</type>
249
+ <description>The result as a string with type.</description>
250
+ </returns>\
251
+ """,
252
+ 'parameters_json_schema': {
253
+ 'additionalProperties': False,
254
+ 'properties': {'x': {'description': 'The input value.', 'type': 'integer'}},
255
+ 'required': ['x'],
256
+ 'type': 'object',
257
+ },
258
+ 'outer_typed_dict_key': None,
259
+ }
260
+ )
261
+
262
+
263
+ def test_numpy_style_with_returns():
264
+ agent = Agent(FunctionModel(get_json_schema))
265
+
266
+ def my_tool(x: int) -> str: # pragma: no cover
267
+ """A numpy function with returns.
268
+
269
+ Parameters
270
+ ----------
271
+ x : int
272
+ The input value.
273
+
274
+ Returns
275
+ -------
276
+ str
277
+ The result as a string with type.
278
+ """
279
+ return str(x)
280
+
281
+ agent.tool_plain(docstring_format='numpy')(my_tool)
282
+ result = agent.run_sync('Hello')
283
+ json_schema = json.loads(result.data)
284
+ assert json_schema == snapshot(
285
+ {
286
+ 'name': 'my_tool',
287
+ 'description': """\
288
+ <summary>A numpy function with returns.</summary>
289
+ <returns>
290
+ <type>str</type>
291
+ <description>The result as a string with type.</description>
292
+ </returns>\
293
+ """,
294
+ 'parameters_json_schema': {
295
+ 'additionalProperties': False,
296
+ 'properties': {'x': {'description': 'The input value.', 'type': 'integer'}},
297
+ 'required': ['x'],
298
+ 'type': 'object',
299
+ },
300
+ 'outer_typed_dict_key': None,
301
+ }
302
+ )
303
+
304
+
305
+ def only_returns_type() -> str: # pragma: no cover
306
+ """
307
+
308
+ Returns:
309
+ str: The result as a string.
310
+ """
311
+ return 'foo'
312
+
313
+
314
+ def test_only_returns_type():
315
+ agent = Agent(FunctionModel(get_json_schema))
316
+ agent.tool_plain(only_returns_type)
317
+
318
+ result = agent.run_sync('Hello')
319
+ json_schema = json.loads(result.data)
320
+ assert json_schema == snapshot(
321
+ {
322
+ 'name': 'only_returns_type',
323
+ 'description': """\
324
+ <returns>
325
+ <type>str</type>
326
+ <description>The result as a string.</description>
327
+ </returns>\
328
+ """,
329
+ 'parameters_json_schema': {'additionalProperties': False, 'properties': {}, 'type': 'object'},
330
+ 'outer_typed_dict_key': None,
331
+ }
332
+ )
333
+
334
+
190
335
  def unknown_docstring(**kwargs: int) -> str: # pragma: no cover
191
336
  """Unknown style docstring."""
192
337
  return str(kwargs)
@@ -572,11 +717,7 @@ agent = Agent('test', tools=[ctx_tool], deps_type=int)
572
717
 
573
718
 
574
719
  async def tool_without_return_annotation_in_docstring() -> str: # pragma: no cover
575
- """A tool that documents what it returns but doesn't have a return annotation in the docstring.
576
-
577
- Returns:
578
- A value.
579
- """
720
+ """A tool that documents what it returns but doesn't have a return annotation in the docstring."""
580
721
 
581
722
  return ''
582
723
 
@@ -591,8 +732,7 @@ def test_suppress_griffe_logging(caplog: LogCaptureFixture):
591
732
  json_schema = json.loads(result.data)
592
733
  assert json_schema == snapshot(
593
734
  {
594
- 'description': "A tool that documents what it returns but doesn't have a "
595
- 'return annotation in the docstring.',
735
+ 'description': "A tool that documents what it returns but doesn't have a return annotation in the docstring.",
596
736
  'name': 'tool_without_return_annotation_in_docstring',
597
737
  'outer_typed_dict_key': None,
598
738
  'parameters_json_schema': {'additionalProperties': False, 'properties': {}, 'type': 'object'},
@@ -1,13 +0,0 @@
1
- from threading import Thread
2
-
3
- from pydantic_graph._utils import get_event_loop
4
-
5
-
6
- def test_get_event_loop_in_thread():
7
- def get_and_close_event_loop():
8
- event_loop = get_event_loop()
9
- event_loop.close()
10
-
11
- thread = Thread(target=get_and_close_event_loop)
12
- thread.start()
13
- thread.join()
File without changes
File without changes
File without changes
File without changes