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.
- pydantic_ai/__init__.py +2 -2
- pydantic_ai/_agent_graph.py +97 -44
- pydantic_ai/_result.py +3 -3
- pydantic_ai/_utils.py +2 -0
- pydantic_ai/agent.py +95 -67
- pydantic_ai/messages.py +71 -1
- pydantic_ai/models/__init__.py +4 -0
- pydantic_ai/models/function.py +15 -4
- pydantic_ai/models/instrumented.py +70 -78
- pydantic_ai/result.py +125 -1
- pydantic_ai/usage.py +10 -0
- {pydantic_ai_slim-0.0.29.dist-info → pydantic_ai_slim-0.0.31.dist-info}/METADATA +4 -3
- {pydantic_ai_slim-0.0.29.dist-info → pydantic_ai_slim-0.0.31.dist-info}/RECORD +14 -14
- {pydantic_ai_slim-0.0.29.dist-info → pydantic_ai_slim-0.0.31.dist-info}/WHEEL +0 -0
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
44
|
+
ANY_ADAPTER = TypeAdapter[Any](Any)
|
|
50
45
|
|
|
51
46
|
|
|
52
47
|
@dataclass
|
|
53
48
|
class InstrumentedModel(WrapperModel):
|
|
54
|
-
"""Model which is instrumented with
|
|
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,
|
|
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,
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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':
|
|
153
|
+
'message': event.body,
|
|
166
154
|
},
|
|
167
155
|
)
|
|
156
|
+
)
|
|
168
157
|
span.set_attributes(
|
|
169
158
|
{
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
200
|
-
|
|
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
|
-
|
|
212
|
-
'
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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.
|
|
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=
|
|
2
|
-
pydantic_ai/_agent_graph.py,sha256=
|
|
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=
|
|
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=
|
|
9
|
-
pydantic_ai/agent.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
35
|
-
pydantic_ai_slim-0.0.
|
|
36
|
-
pydantic_ai_slim-0.0.
|
|
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,,
|
|
File without changes
|