pydantic-ai-slim 0.0.29__py3-none-any.whl → 0.0.31__py3-none-any.whl

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-slim might be problematic. Click here for more details.

@@ -1,26 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import AsyncIterator, Iterator
3
+ import json
4
+ from collections.abc import AsyncIterator, Iterator, Mapping
4
5
  from contextlib import asynccontextmanager, contextmanager
5
6
  from dataclasses import dataclass, field
6
- from functools import partial
7
7
  from typing import Any, Callable, Literal
8
8
 
9
9
  import logfire_api
10
10
  from opentelemetry._events import Event, EventLogger, EventLoggerProvider, get_event_logger_provider
11
- from opentelemetry.trace import Tracer, TracerProvider, get_tracer_provider
11
+ from opentelemetry.trace import Span, Tracer, TracerProvider, get_tracer_provider
12
+ from opentelemetry.util.types import AttributeValue
13
+ from pydantic import TypeAdapter
12
14
 
13
15
  from ..messages import (
14
16
  ModelMessage,
15
17
  ModelRequest,
16
- ModelRequestPart,
17
18
  ModelResponse,
18
- RetryPromptPart,
19
- SystemPromptPart,
20
- TextPart,
21
- ToolCallPart,
22
- ToolReturnPart,
23
- UserPromptPart,
24
19
  )
25
20
  from ..settings import ModelSettings
26
21
  from ..usage import Usage
@@ -46,40 +41,44 @@ MODEL_SETTING_ATTRIBUTES: tuple[
46
41
  'frequency_penalty',
47
42
  )
48
43
 
49
- NOT_GIVEN = object()
44
+ ANY_ADAPTER = TypeAdapter[Any](Any)
50
45
 
51
46
 
52
47
  @dataclass
53
48
  class InstrumentedModel(WrapperModel):
54
- """Model which is instrumented with logfire."""
49
+ """Model which is instrumented with OpenTelemetry."""
55
50
 
56
51
  tracer: Tracer = field(repr=False)
57
52
  event_logger: EventLogger = field(repr=False)
53
+ event_mode: Literal['attributes', 'logs'] = 'attributes'
58
54
 
59
55
  def __init__(
60
56
  self,
61
57
  wrapped: Model | KnownModelName,
62
58
  tracer_provider: TracerProvider | None = None,
63
59
  event_logger_provider: EventLoggerProvider | None = None,
60
+ event_mode: Literal['attributes', 'logs'] = 'attributes',
64
61
  ):
65
62
  super().__init__(wrapped)
66
63
  tracer_provider = tracer_provider or get_tracer_provider()
67
64
  event_logger_provider = event_logger_provider or get_event_logger_provider()
68
65
  self.tracer = tracer_provider.get_tracer('pydantic-ai')
69
66
  self.event_logger = event_logger_provider.get_event_logger('pydantic-ai')
67
+ self.event_mode = event_mode
70
68
 
71
69
  @classmethod
72
70
  def from_logfire(
73
71
  cls,
74
72
  wrapped: Model | KnownModelName,
75
73
  logfire_instance: logfire_api.Logfire = logfire_api.DEFAULT_LOGFIRE_INSTANCE,
74
+ event_mode: Literal['attributes', 'logs'] = 'attributes',
76
75
  ) -> InstrumentedModel:
77
76
  if hasattr(logfire_instance.config, 'get_event_logger_provider'):
78
77
  event_provider = logfire_instance.config.get_event_logger_provider()
79
78
  else:
80
79
  event_provider = None
81
80
  tracer_provider = logfire_instance.config.get_tracer_provider()
82
- return cls(wrapped, tracer_provider, event_provider)
81
+ return cls(wrapped, tracer_provider, event_provider, event_mode)
83
82
 
84
83
  async def request(
85
84
  self,
@@ -126,7 +125,7 @@ class InstrumentedModel(WrapperModel):
126
125
  # - server.port: to parse from the base_url
127
126
  # - error.type: unclear if we should do something here or just always rely on span exceptions
128
127
  # - gen_ai.request.stop_sequences/top_k: model_settings doesn't include these
129
- attributes: dict[str, Any] = {
128
+ attributes: dict[str, AttributeValue] = {
130
129
  'gen_ai.operation.name': operation,
131
130
  'gen_ai.system': system,
132
131
  'gen_ai.request.model': model_name,
@@ -134,92 +133,85 @@ class InstrumentedModel(WrapperModel):
134
133
 
135
134
  if model_settings:
136
135
  for key in MODEL_SETTING_ATTRIBUTES:
137
- if (value := model_settings.get(key, NOT_GIVEN)) is not NOT_GIVEN:
136
+ if isinstance(value := model_settings.get(key), (float, int)):
138
137
  attributes[f'gen_ai.request.{key}'] = value
139
138
 
140
- emit_event = partial(self._emit_event, system)
141
-
142
139
  with self.tracer.start_as_current_span(span_name, attributes=attributes) as span:
143
- if span.is_recording():
144
- for message in messages:
145
- if isinstance(message, ModelRequest):
146
- for part in message.parts:
147
- event_name, body = _request_part_body(part)
148
- if event_name:
149
- emit_event(event_name, body)
150
- elif isinstance(message, ModelResponse):
151
- for body in _response_bodies(message):
152
- emit_event('gen_ai.assistant.message', body)
153
140
 
154
141
  def finish(response: ModelResponse, usage: Usage):
155
142
  if not span.is_recording():
156
143
  return
157
144
 
158
- for response_body in _response_bodies(response):
159
- if response_body:
160
- emit_event(
145
+ events = self.messages_to_otel_events(messages)
146
+ for event in self.messages_to_otel_events([response]):
147
+ events.append(
148
+ Event(
161
149
  'gen_ai.choice',
162
- {
150
+ body={
163
151
  # TODO finish_reason
164
152
  'index': 0,
165
- 'message': response_body,
153
+ 'message': event.body,
166
154
  },
167
155
  )
156
+ )
168
157
  span.set_attributes(
169
158
  {
170
- k: v
171
- for k, v in {
172
- # TODO finish_reason (https://github.com/open-telemetry/semantic-conventions/issues/1277), id
173
- # https://github.com/pydantic/pydantic-ai/issues/886
174
- 'gen_ai.response.model': response.model_name or model_name,
175
- 'gen_ai.usage.input_tokens': usage.request_tokens,
176
- 'gen_ai.usage.output_tokens': usage.response_tokens,
177
- }.items()
178
- if v is not None
159
+ # TODO finish_reason (https://github.com/open-telemetry/semantic-conventions/issues/1277), id
160
+ # https://github.com/pydantic/pydantic-ai/issues/886
161
+ 'gen_ai.response.model': response.model_name or model_name,
162
+ **usage.opentelemetry_attributes(),
179
163
  }
180
164
  )
165
+ self._emit_events(system, span, events)
181
166
 
182
167
  yield finish
183
168
 
184
- def _emit_event(self, system: str, event_name: str, body: dict[str, Any]) -> None:
185
- self.event_logger.emit(Event(event_name, body=body, attributes={'gen_ai.system': system}))
186
-
187
-
188
- def _request_part_body(part: ModelRequestPart) -> tuple[str, dict[str, Any]]:
189
- if isinstance(part, SystemPromptPart):
190
- return 'gen_ai.system.message', {'content': part.content, 'role': 'system'}
191
- elif isinstance(part, UserPromptPart):
192
- return 'gen_ai.user.message', {'content': part.content, 'role': 'user'}
193
- elif isinstance(part, ToolReturnPart):
194
- return 'gen_ai.tool.message', {'content': part.content, 'role': 'tool', 'id': part.tool_call_id}
195
- elif isinstance(part, RetryPromptPart):
196
- if part.tool_name is None:
197
- return 'gen_ai.user.message', {'content': part.model_response(), 'role': 'user'}
169
+ def _emit_events(self, system: str, span: Span, events: list[Event]) -> None:
170
+ for event in events:
171
+ event.attributes = {'gen_ai.system': system, **(event.attributes or {})}
172
+ if self.event_mode == 'logs':
173
+ for event in events:
174
+ self.event_logger.emit(event)
198
175
  else:
199
- return 'gen_ai.tool.message', {'content': part.model_response(), 'role': 'tool', 'id': part.tool_call_id}
200
- else:
201
- return '', {}
202
-
203
-
204
- def _response_bodies(message: ModelResponse) -> list[dict[str, Any]]:
205
- body: dict[str, Any] = {'role': 'assistant'}
206
- result = [body]
207
- for part in message.parts:
208
- if isinstance(part, ToolCallPart):
209
- body.setdefault('tool_calls', []).append(
176
+ attr_name = 'events'
177
+ span.set_attributes(
210
178
  {
211
- 'id': part.tool_call_id,
212
- 'type': 'function', # TODO https://github.com/pydantic/pydantic-ai/issues/888
213
- 'function': {
214
- 'name': part.tool_name,
215
- 'arguments': part.args,
216
- },
179
+ attr_name: json.dumps([self.event_to_dict(event) for event in events]),
180
+ 'logfire.json_schema': json.dumps(
181
+ {
182
+ 'type': 'object',
183
+ 'properties': {attr_name: {'type': 'array'}},
184
+ }
185
+ ),
217
186
  }
218
187
  )
219
- elif isinstance(part, TextPart):
220
- if body.get('content'):
221
- body = {'role': 'assistant'}
222
- result.append(body)
223
- body['content'] = part.content
224
188
 
225
- return result
189
+ @staticmethod
190
+ def event_to_dict(event: Event) -> dict[str, Any]:
191
+ if not event.body:
192
+ body = {}
193
+ elif isinstance(event.body, Mapping):
194
+ body = event.body # type: ignore
195
+ else:
196
+ body = {'body': event.body}
197
+ return {**body, **(event.attributes or {})}
198
+
199
+ @staticmethod
200
+ def messages_to_otel_events(messages: list[ModelMessage]) -> list[Event]:
201
+ result: list[Event] = []
202
+ for message in messages:
203
+ if isinstance(message, ModelRequest):
204
+ for part in message.parts:
205
+ if hasattr(part, 'otel_event'):
206
+ result.append(part.otel_event())
207
+ elif isinstance(message, ModelResponse):
208
+ result.extend(message.otel_events())
209
+ for event in result:
210
+ try:
211
+ event.body = ANY_ADAPTER.dump_python(event.body, mode='json')
212
+ except Exception:
213
+ try:
214
+ event.body = str(event.body)
215
+ except Exception:
216
+ event.body = 'Unable to serialize event body'
217
+ return result
pydantic_ai/result.py CHANGED
@@ -7,9 +7,10 @@ from datetime import datetime
7
7
  from typing import Generic, Union, cast
8
8
 
9
9
  import logfire_api
10
- from typing_extensions import TypeVar
10
+ from typing_extensions import TypeVar, assert_type
11
11
 
12
12
  from . import _result, _utils, exceptions, messages as _messages, models
13
+ from .messages import AgentStreamEvent, FinalResultEvent
13
14
  from .tools import AgentDepsT, RunContext
14
15
  from .usage import Usage, UsageLimits
15
16
 
@@ -51,6 +52,127 @@ Usage `ResultValidatorFunc[AgentDepsT, T]`.
51
52
  _logfire = logfire_api.Logfire(otel_scope='pydantic-ai')
52
53
 
53
54
 
55
+ @dataclass
56
+ class AgentStream(Generic[AgentDepsT, ResultDataT]):
57
+ _raw_stream_response: models.StreamedResponse
58
+ _result_schema: _result.ResultSchema[ResultDataT] | None
59
+ _result_validators: list[_result.ResultValidator[AgentDepsT, ResultDataT]]
60
+ _run_ctx: RunContext[AgentDepsT]
61
+ _usage_limits: UsageLimits | None
62
+
63
+ _agent_stream_iterator: AsyncIterator[AgentStreamEvent] | None = field(default=None, init=False)
64
+ _final_result_event: FinalResultEvent | None = field(default=None, init=False)
65
+ _initial_run_ctx_usage: Usage = field(init=False)
66
+
67
+ def __post_init__(self):
68
+ self._initial_run_ctx_usage = copy(self._run_ctx.usage)
69
+
70
+ async def stream_output(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[ResultDataT]:
71
+ """Asynchronously stream the (validated) agent outputs."""
72
+ async for response in self.stream_responses(debounce_by=debounce_by):
73
+ if self._final_result_event is not None:
74
+ yield await self._validate_response(response, self._final_result_event.tool_name, allow_partial=True)
75
+ if self._final_result_event is not None:
76
+ yield await self._validate_response(
77
+ self._raw_stream_response.get(), self._final_result_event.tool_name, allow_partial=False
78
+ )
79
+
80
+ async def stream_responses(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[_messages.ModelResponse]:
81
+ """Asynchronously stream the (unvalidated) model responses for the agent."""
82
+ # if the message currently has any parts with content, yield before streaming
83
+ msg = self._raw_stream_response.get()
84
+ for part in msg.parts:
85
+ if part.has_content():
86
+ yield msg
87
+ break
88
+
89
+ async with _utils.group_by_temporal(self, debounce_by) as group_iter:
90
+ async for _items in group_iter:
91
+ yield self._raw_stream_response.get() # current state of the response
92
+
93
+ def usage(self) -> Usage:
94
+ """Return the usage of the whole run.
95
+
96
+ !!! note
97
+ This won't return the full usage until the stream is finished.
98
+ """
99
+ return self._initial_run_ctx_usage + self._raw_stream_response.usage()
100
+
101
+ async def _validate_response(
102
+ self, message: _messages.ModelResponse, result_tool_name: str | None, *, allow_partial: bool = False
103
+ ) -> ResultDataT:
104
+ """Validate a structured result message."""
105
+ if self._result_schema is not None and result_tool_name is not None:
106
+ match = self._result_schema.find_named_tool(message.parts, result_tool_name)
107
+ if match is None:
108
+ raise exceptions.UnexpectedModelBehavior(
109
+ f'Invalid response, unable to find tool: {self._result_schema.tool_names()}'
110
+ )
111
+
112
+ call, result_tool = match
113
+ result_data = result_tool.validate(call, allow_partial=allow_partial, wrap_validation_errors=False)
114
+
115
+ for validator in self._result_validators:
116
+ result_data = await validator.validate(result_data, call, self._run_ctx)
117
+ return result_data
118
+ else:
119
+ text = '\n\n'.join(x.content for x in message.parts if isinstance(x, _messages.TextPart))
120
+ for validator in self._result_validators:
121
+ text = await validator.validate(
122
+ text,
123
+ None,
124
+ self._run_ctx,
125
+ )
126
+ # Since there is no result tool, we can assume that str is compatible with ResultDataT
127
+ return cast(ResultDataT, text)
128
+
129
+ def __aiter__(self) -> AsyncIterator[AgentStreamEvent]:
130
+ """Stream [`AgentStreamEvent`][pydantic_ai.messages.AgentStreamEvent]s.
131
+
132
+ This proxies the _raw_stream_response and sends all events to the agent stream, while also checking for matches
133
+ on the result schema and emitting a [`FinalResultEvent`][pydantic_ai.messages.FinalResultEvent] if/when the
134
+ first match is found.
135
+ """
136
+ if self._agent_stream_iterator is not None:
137
+ return self._agent_stream_iterator
138
+
139
+ async def aiter():
140
+ result_schema = self._result_schema
141
+ allow_text_result = result_schema is None or result_schema.allow_text_result
142
+
143
+ def _get_final_result_event(e: _messages.ModelResponseStreamEvent) -> _messages.FinalResultEvent | None:
144
+ """Return an appropriate FinalResultEvent if `e` corresponds to a part that will produce a final result."""
145
+ if isinstance(e, _messages.PartStartEvent):
146
+ new_part = e.part
147
+ if isinstance(new_part, _messages.ToolCallPart):
148
+ if result_schema:
149
+ for call, _ in result_schema.find_tool([new_part]):
150
+ return _messages.FinalResultEvent(
151
+ tool_name=call.tool_name, tool_call_id=call.tool_call_id
152
+ )
153
+ elif allow_text_result:
154
+ assert_type(e, _messages.PartStartEvent)
155
+ return _messages.FinalResultEvent(tool_name=None, tool_call_id=None)
156
+
157
+ usage_checking_stream = _get_usage_checking_stream_response(
158
+ self._raw_stream_response, self._usage_limits, self.usage
159
+ )
160
+ async for event in usage_checking_stream:
161
+ yield event
162
+ if (final_result_event := _get_final_result_event(event)) is not None:
163
+ self._final_result_event = final_result_event
164
+ yield final_result_event
165
+ break
166
+
167
+ # If we broke out of the above loop, we need to yield the rest of the events
168
+ # If we didn't, this will just be a no-op
169
+ async for event in usage_checking_stream:
170
+ yield event
171
+
172
+ self._agent_stream_iterator = aiter()
173
+ return self._agent_stream_iterator
174
+
175
+
54
176
  @dataclass
55
177
  class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
56
178
  """Result of a streamed run that returns structured data via a tool call."""
@@ -352,6 +474,8 @@ class FinalResult(Generic[ResultDataT]):
352
474
  """The final result data."""
353
475
  tool_name: str | None
354
476
  """Name of the final result tool; `None` if the result came from unstructured text content."""
477
+ tool_call_id: str | None
478
+ """ID of the tool call that produced the final result; `None` if the result came from unstructured text content."""
355
479
 
356
480
 
357
481
  def _get_usage_checking_stream_response(
pydantic_ai/usage.py CHANGED
@@ -56,6 +56,16 @@ class Usage:
56
56
  new_usage.incr(other)
57
57
  return new_usage
58
58
 
59
+ def opentelemetry_attributes(self) -> dict[str, int]:
60
+ """Get the token limits as OpenTelemetry attributes."""
61
+ result = {
62
+ 'gen_ai.usage.input_tokens': self.request_tokens,
63
+ 'gen_ai.usage.output_tokens': self.response_tokens,
64
+ }
65
+ for key, value in (self.details or {}).items():
66
+ result[f'gen_ai.usage.details.{key}'] = value
67
+ return {k: v for k, v in result.items() if v is not None}
68
+
59
69
 
60
70
  @dataclass
61
71
  class UsageLimits:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai-slim
3
- Version: 0.0.29
3
+ Version: 0.0.31
4
4
  Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
5
5
  Author-email: Samuel Colvin <samuel@pydantic.dev>
6
6
  License-Expression: MIT
@@ -29,7 +29,8 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
29
29
  Requires-Dist: griffe>=1.3.2
30
30
  Requires-Dist: httpx>=0.27
31
31
  Requires-Dist: logfire-api>=1.2.0
32
- Requires-Dist: pydantic-graph==0.0.29
32
+ Requires-Dist: opentelemetry-api>=1.28.0
33
+ Requires-Dist: pydantic-graph==0.0.31
33
34
  Requires-Dist: pydantic>=2.10
34
35
  Provides-Extra: anthropic
35
36
  Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
@@ -44,7 +45,7 @@ Requires-Dist: logfire>=2.3; extra == 'logfire'
44
45
  Provides-Extra: mistral
45
46
  Requires-Dist: mistralai>=1.2.5; extra == 'mistral'
46
47
  Provides-Extra: openai
47
- Requires-Dist: openai>=1.61.0; extra == 'openai'
48
+ Requires-Dist: openai>=1.65.1; extra == 'openai'
48
49
  Provides-Extra: tavily
49
50
  Requires-Dist: tavily-python>=0.5.0; extra == 'tavily'
50
51
  Provides-Extra: vertexai
@@ -1,36 +1,36 @@
1
- pydantic_ai/__init__.py,sha256=Rmpjmorf8YY1PtlkXRRNN-J3ZoQDSh7chaibVGyqY0k,937
2
- pydantic_ai/_agent_graph.py,sha256=lNKTtUyVY14M0WODP5K1NUaE9zJA716-9rZutapSg8A,29042
1
+ pydantic_ai/__init__.py,sha256=xrSDxkBwpUVInbPtTVhReEecStk-mWZMttAPUAQR0Ic,927
2
+ pydantic_ai/_agent_graph.py,sha256=vvhV051rjVcPPRZ_TeL4pWwX-DptEzWgBBJnhybmIWg,30510
3
3
  pydantic_ai/_griffe.py,sha256=RYRKiLbgG97QxnazbAwlnc74XxevGHLQet-FGfq9qls,3960
4
4
  pydantic_ai/_parts_manager.py,sha256=ARfDQY1_5AIY5rNl_M2fAYHEFCe03ZxdhgjHf9qeIKw,11872
5
5
  pydantic_ai/_pydantic.py,sha256=dROz3Hmfdi0C2exq88FhefDRVo_8S3rtkXnoUHzsz0c,8753
6
- pydantic_ai/_result.py,sha256=tN1pVulf_EM4bkBvpNUWPnUXezLY-sBrJEVCFdy2nLU,10264
6
+ pydantic_ai/_result.py,sha256=mqj3YrUzr5OT00h0KfGJglwQZ6_7nV7355Pvucd08ak,10276
7
7
  pydantic_ai/_system_prompt.py,sha256=602c2jyle2R_SesOrITBDETZqsLk4BZ8Cbo8yEhmx04,1120
8
- pydantic_ai/_utils.py,sha256=w9BYSfFZiaX757fRtMRclOL1uYzyQnxV_lxqbU2WTPs,9435
9
- pydantic_ai/agent.py,sha256=wCXvGwPykn-cYf1_4bR4XqYofqwI6X2lj4L-0-MbMp4,63685
8
+ pydantic_ai/_utils.py,sha256=nx4Suswk2qjLvzphx8uQntKzFi-IzvhX_H1L7t_kJlQ,9579
9
+ pydantic_ai/agent.py,sha256=jHQ99M-kwUrUSWHPjBDmWG2AepbDS9H3YUE1NugaWGg,65625
10
10
  pydantic_ai/exceptions.py,sha256=1ujJeB3jDDQ-pH5ydBYrgStvR35-GlEW0bYGTGEr4ME,3127
11
11
  pydantic_ai/format_as_xml.py,sha256=QE7eMlg5-YUMw1_2kcI3h0uKYPZZyGkgXFDtfZTMeeI,4480
12
- pydantic_ai/messages.py,sha256=U-RgeRsMR-Ew6IoeBDrnQVONX9AwxyVd0aTnAxEA7EM,20918
12
+ pydantic_ai/messages.py,sha256=Yny2hIuExXfw9fvHDSPgbvfN91IOdcLaDEAmaCAoTBs,23751
13
13
  pydantic_ai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- pydantic_ai/result.py,sha256=Rqbog6efO1l_bFJSuAd-_ZZLoQa_rz4motOGeR_5N3I,16803
14
+ pydantic_ai/result.py,sha256=Q--JTwDfPeJw1_Mk5EhI7R9V7GusG-oAx1m9pDH50zQ,23014
15
15
  pydantic_ai/settings.py,sha256=ntuWnke9UA18aByDxk9OIhN0tAgOaPdqCEkRf-wlp8Y,3059
16
16
  pydantic_ai/tools.py,sha256=IPZuZJCSQUppz1uyLVwpfFLGoMirB8YtKWXIDQGR444,13414
17
- pydantic_ai/usage.py,sha256=60d9f6M7YEYuKMbqDGDogX4KsA73fhDtWyDXYXoIPaI,4948
17
+ pydantic_ai/usage.py,sha256=VmpU_o_RjFI65J81G1wfCwDIAYBclMjeWfLtslntFOw,5406
18
18
  pydantic_ai/common_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  pydantic_ai/common_tools/duckduckgo.py,sha256=-kSa1gGn5-NIYvtxFWrFcX2XdmfEmGxI3_wAqrb6jLI,2230
20
20
  pydantic_ai/common_tools/tavily.py,sha256=Lz35037ggkdWKa_Stj0yXBkiN_hygDefEevoRDUclF0,2560
21
- pydantic_ai/models/__init__.py,sha256=Qw_g58KzGUmuDKOBa2u3yFrNbgCXkdRSNtkhseLC1VM,13758
21
+ pydantic_ai/models/__init__.py,sha256=2A3CpdMnvllnVVX8PmlUcBs0HMGcG4RurOXsRKl0BPc,13886
22
22
  pydantic_ai/models/anthropic.py,sha256=bFtE6hku9L4l4pKJg8XER37T2ST2htArho5lPjEohAk,20637
23
23
  pydantic_ai/models/cohere.py,sha256=6F6eWPGVT7mpMXlRugbVbR-a8Q1zmb1SKS_fWOoBL80,11514
24
24
  pydantic_ai/models/fallback.py,sha256=smHwNIpxu19JsgYYjY0nmzl3yox7yQRJ0Ir08zdhnk0,4207
25
- pydantic_ai/models/function.py,sha256=EMlASu436RE-XzOTuHGkIqkS8J4WItUvwwaL08LLkX8,10948
25
+ pydantic_ai/models/function.py,sha256=THIwVJ8qI3efYLNlYXlYze_J8hc7MHB-NMb3kpknq0g,11373
26
26
  pydantic_ai/models/gemini.py,sha256=2hDTMIMf899dp-MS0tLT7m1GkXsL9KIRMBklGM0VLB4,34223
27
27
  pydantic_ai/models/groq.py,sha256=Z4sZJDu5Yxa2tZiAPp9EjSVMz4uwLhS3fW7kFSc09gI,16406
28
- pydantic_ai/models/instrumented.py,sha256=cvjHgQE_gJOH-YVQyvx9tBpGNB_Iuc8N8THn0TL0Rjk,8791
28
+ pydantic_ai/models/instrumented.py,sha256=npufEZJrR9m0_ZQB1inWFcuK3Nu5_2GdY1YtTYaIj3s,8366
29
29
  pydantic_ai/models/mistral.py,sha256=ZJ4xPcL9wJIQ5io34yP2fPyXy8GZrSvsW4itZiKPYFw,27448
30
30
  pydantic_ai/models/openai.py,sha256=koIcK_pDHmV-JFq_-VIzU-edAqGKOOzkSk5QSYWvfoc,20156
31
31
  pydantic_ai/models/test.py,sha256=Ux20cmuJFkhvI9L1N7ItHNFcd-j284TBEsrM53eWRag,16873
32
32
  pydantic_ai/models/vertexai.py,sha256=9Kp_1KMBlbP8_HRJTuFnrkkFmlJ7yFhADQYjxOgIh9Y,9523
33
33
  pydantic_ai/models/wrapper.py,sha256=Zr3fgiUBpt2N9gXds6iSwaMEtEsFKr9WwhpHjSoHa7o,1410
34
- pydantic_ai_slim-0.0.29.dist-info/METADATA,sha256=f7eLYpKWzEGmzTC8U29ZO5T4aTYMTW0Shmqa1zuEtG0,3062
35
- pydantic_ai_slim-0.0.29.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
- pydantic_ai_slim-0.0.29.dist-info/RECORD,,
34
+ pydantic_ai_slim-0.0.31.dist-info/METADATA,sha256=dgkUKEU7r9OqgIkt3enzpISWt73KVAYL8gC2APlnpWg,3103
35
+ pydantic_ai_slim-0.0.31.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
+ pydantic_ai_slim-0.0.31.dist-info/RECORD,,