pydantic-ai-slim 0.4.0__py3-none-any.whl → 0.4.1__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/_a2a.py +152 -38
- pydantic_ai/agent.py +2 -2
- {pydantic_ai_slim-0.4.0.dist-info → pydantic_ai_slim-0.4.1.dist-info}/METADATA +4 -4
- {pydantic_ai_slim-0.4.0.dist-info → pydantic_ai_slim-0.4.1.dist-info}/RECORD +7 -7
- {pydantic_ai_slim-0.4.0.dist-info → pydantic_ai_slim-0.4.1.dist-info}/WHEEL +0 -0
- {pydantic_ai_slim-0.4.0.dist-info → pydantic_ai_slim-0.4.1.dist-info}/entry_points.txt +0 -0
- {pydantic_ai_slim-0.4.0.dist-info → pydantic_ai_slim-0.4.1.dist-info}/licenses/LICENSE +0 -0
pydantic_ai/_a2a.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations, annotations as _annotations
|
|
2
2
|
|
|
3
|
+
import uuid
|
|
3
4
|
from collections.abc import AsyncIterator, Sequence
|
|
4
5
|
from contextlib import asynccontextmanager
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
from functools import partial
|
|
7
|
-
from typing import Any, Generic
|
|
8
|
+
from typing import Any, Generic, TypeVar
|
|
8
9
|
|
|
10
|
+
from pydantic import TypeAdapter
|
|
9
11
|
from typing_extensions import assert_never
|
|
10
12
|
|
|
11
13
|
from pydantic_ai.messages import (
|
|
@@ -19,12 +21,17 @@ from pydantic_ai.messages import (
|
|
|
19
21
|
ModelResponse,
|
|
20
22
|
ModelResponsePart,
|
|
21
23
|
TextPart,
|
|
24
|
+
ThinkingPart,
|
|
25
|
+
ToolCallPart,
|
|
22
26
|
UserPromptPart,
|
|
23
27
|
VideoUrl,
|
|
24
28
|
)
|
|
25
29
|
|
|
26
30
|
from .agent import Agent, AgentDepsT, OutputDataT
|
|
27
31
|
|
|
32
|
+
# AgentWorker output type needs to be invariant for use in both parameter and return positions
|
|
33
|
+
WorkerOutputT = TypeVar('WorkerOutputT')
|
|
34
|
+
|
|
28
35
|
try:
|
|
29
36
|
from starlette.middleware import Middleware
|
|
30
37
|
from starlette.routing import Route
|
|
@@ -33,10 +40,11 @@ try:
|
|
|
33
40
|
from fasta2a.applications import FastA2A
|
|
34
41
|
from fasta2a.broker import Broker, InMemoryBroker
|
|
35
42
|
from fasta2a.schema import (
|
|
43
|
+
AgentProvider,
|
|
36
44
|
Artifact,
|
|
45
|
+
DataPart,
|
|
37
46
|
Message,
|
|
38
47
|
Part,
|
|
39
|
-
Provider,
|
|
40
48
|
Skill,
|
|
41
49
|
TaskIdParams,
|
|
42
50
|
TaskSendParams,
|
|
@@ -72,7 +80,7 @@ def agent_to_a2a(
|
|
|
72
80
|
url: str = 'http://localhost:8000',
|
|
73
81
|
version: str = '1.0.0',
|
|
74
82
|
description: str | None = None,
|
|
75
|
-
provider:
|
|
83
|
+
provider: AgentProvider | None = None,
|
|
76
84
|
skills: list[Skill] | None = None,
|
|
77
85
|
# Starlette
|
|
78
86
|
debug: bool = False,
|
|
@@ -106,59 +114,121 @@ def agent_to_a2a(
|
|
|
106
114
|
|
|
107
115
|
|
|
108
116
|
@dataclass
|
|
109
|
-
class AgentWorker(Worker, Generic[
|
|
117
|
+
class AgentWorker(Worker[list[ModelMessage]], Generic[WorkerOutputT, AgentDepsT]):
|
|
110
118
|
"""A worker that uses an agent to execute tasks."""
|
|
111
119
|
|
|
112
|
-
agent: Agent[AgentDepsT,
|
|
120
|
+
agent: Agent[AgentDepsT, WorkerOutputT]
|
|
113
121
|
|
|
114
122
|
async def run_task(self, params: TaskSendParams) -> None:
|
|
115
|
-
task = await self.storage.load_task(params['id']
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
task = await self.storage.load_task(params['id'])
|
|
124
|
+
if task is None:
|
|
125
|
+
raise ValueError(f'Task {params["id"]} not found') # pragma: no cover
|
|
126
|
+
|
|
127
|
+
# TODO(Marcelo): Should we lock `run_task` on the `context_id`?
|
|
128
|
+
# Ensure this task hasn't been run before
|
|
129
|
+
if task['status']['state'] != 'submitted':
|
|
130
|
+
raise ValueError( # pragma: no cover
|
|
131
|
+
f'Task {params["id"]} has already been processed (state: {task["status"]["state"]})'
|
|
132
|
+
)
|
|
118
133
|
|
|
119
134
|
await self.storage.update_task(task['id'], state='working')
|
|
120
135
|
|
|
121
|
-
#
|
|
122
|
-
|
|
136
|
+
# Load context - contains pydantic-ai message history from previous tasks in this conversation
|
|
137
|
+
message_history = await self.storage.load_context(task['context_id']) or []
|
|
138
|
+
message_history.extend(self.build_message_history(task.get('history', [])))
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
result = await self.agent.run(message_history=message_history) # type: ignore
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
message_history = self.build_message_history(task_history=task_history)
|
|
143
|
+
await self.storage.update_context(task['context_id'], result.all_messages())
|
|
126
144
|
|
|
127
|
-
|
|
128
|
-
|
|
145
|
+
# Convert new messages to A2A format for task history
|
|
146
|
+
a2a_messages: list[Message] = []
|
|
147
|
+
|
|
148
|
+
for message in result.new_messages():
|
|
149
|
+
if isinstance(message, ModelRequest):
|
|
150
|
+
# Skip user prompts - they're already in task history
|
|
151
|
+
continue
|
|
152
|
+
else:
|
|
153
|
+
# Convert response parts to A2A format
|
|
154
|
+
a2a_parts = self._response_parts_to_a2a(message.parts)
|
|
155
|
+
if a2a_parts: # Add if there are visible parts (text/thinking)
|
|
156
|
+
a2a_messages.append(
|
|
157
|
+
Message(role='agent', parts=a2a_parts, kind='message', message_id=str(uuid.uuid4()))
|
|
158
|
+
)
|
|
129
159
|
|
|
130
|
-
|
|
131
|
-
|
|
160
|
+
artifacts = self.build_artifacts(result.output)
|
|
161
|
+
except Exception:
|
|
162
|
+
await self.storage.update_task(task['id'], state='failed')
|
|
163
|
+
raise
|
|
164
|
+
else:
|
|
165
|
+
await self.storage.update_task(
|
|
166
|
+
task['id'], state='completed', new_artifacts=artifacts, new_messages=a2a_messages
|
|
167
|
+
)
|
|
132
168
|
|
|
133
169
|
async def cancel_task(self, params: TaskIdParams) -> None:
|
|
134
170
|
pass
|
|
135
171
|
|
|
136
|
-
def build_artifacts(self, result:
|
|
137
|
-
|
|
138
|
-
return [Artifact(name='result', index=0, parts=[A2ATextPart(type='text', text=str(result))])]
|
|
172
|
+
def build_artifacts(self, result: WorkerOutputT) -> list[Artifact]:
|
|
173
|
+
"""Build artifacts from agent result.
|
|
139
174
|
|
|
140
|
-
|
|
175
|
+
All agent outputs become artifacts to mark them as durable task outputs.
|
|
176
|
+
For string results, we use TextPart. For structured data, we use DataPart.
|
|
177
|
+
Metadata is included to preserve type information.
|
|
178
|
+
"""
|
|
179
|
+
artifact_id = str(uuid.uuid4())
|
|
180
|
+
part = self._convert_result_to_part(result)
|
|
181
|
+
return [Artifact(artifact_id=artifact_id, name='result', parts=[part])]
|
|
182
|
+
|
|
183
|
+
def _convert_result_to_part(self, result: WorkerOutputT) -> Part:
|
|
184
|
+
"""Convert agent result to a Part (TextPart or DataPart).
|
|
185
|
+
|
|
186
|
+
For string results, returns a TextPart.
|
|
187
|
+
For structured data, returns a DataPart with properly serialized data.
|
|
188
|
+
"""
|
|
189
|
+
if isinstance(result, str):
|
|
190
|
+
return A2ATextPart(kind='text', text=result)
|
|
191
|
+
else:
|
|
192
|
+
output_type = type(result)
|
|
193
|
+
type_adapter = TypeAdapter(output_type)
|
|
194
|
+
data = type_adapter.dump_python(result, mode='json')
|
|
195
|
+
json_schema = type_adapter.json_schema(mode='serialization')
|
|
196
|
+
return DataPart(kind='data', data={'result': data}, metadata={'json_schema': json_schema})
|
|
197
|
+
|
|
198
|
+
def build_message_history(self, history: list[Message]) -> list[ModelMessage]:
|
|
141
199
|
model_messages: list[ModelMessage] = []
|
|
142
|
-
for message in
|
|
200
|
+
for message in history:
|
|
143
201
|
if message['role'] == 'user':
|
|
144
|
-
model_messages.append(ModelRequest(parts=self.
|
|
202
|
+
model_messages.append(ModelRequest(parts=self._request_parts_from_a2a(message['parts'])))
|
|
145
203
|
else:
|
|
146
|
-
model_messages.append(ModelResponse(parts=self.
|
|
204
|
+
model_messages.append(ModelResponse(parts=self._response_parts_from_a2a(message['parts'])))
|
|
147
205
|
return model_messages
|
|
148
206
|
|
|
149
|
-
def
|
|
207
|
+
def _request_parts_from_a2a(self, parts: list[Part]) -> list[ModelRequestPart]:
|
|
208
|
+
"""Convert A2A Part objects to pydantic-ai ModelRequestPart objects.
|
|
209
|
+
|
|
210
|
+
This handles the conversion from A2A protocol parts (text, file, data) to
|
|
211
|
+
pydantic-ai's internal request parts (UserPromptPart with various content types).
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
parts: List of A2A Part objects from incoming messages
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
List of ModelRequestPart objects for the pydantic-ai agent
|
|
218
|
+
"""
|
|
150
219
|
model_parts: list[ModelRequestPart] = []
|
|
151
220
|
for part in parts:
|
|
152
|
-
if part['
|
|
221
|
+
if part['kind'] == 'text':
|
|
153
222
|
model_parts.append(UserPromptPart(content=part['text']))
|
|
154
|
-
elif part['
|
|
155
|
-
|
|
156
|
-
if '
|
|
157
|
-
data =
|
|
158
|
-
|
|
223
|
+
elif part['kind'] == 'file':
|
|
224
|
+
file_content = part['file']
|
|
225
|
+
if 'bytes' in file_content:
|
|
226
|
+
data = file_content['bytes'].encode('utf-8')
|
|
227
|
+
mime_type = file_content.get('mime_type', 'application/octet-stream')
|
|
228
|
+
content = BinaryContent(data=data, media_type=mime_type)
|
|
159
229
|
model_parts.append(UserPromptPart(content=[content]))
|
|
160
230
|
else:
|
|
161
|
-
url =
|
|
231
|
+
url = file_content['uri']
|
|
162
232
|
for url_cls in (DocumentUrl, AudioUrl, ImageUrl, VideoUrl):
|
|
163
233
|
content = url_cls(url=url)
|
|
164
234
|
try:
|
|
@@ -168,24 +238,68 @@ class AgentWorker(Worker, Generic[AgentDepsT, OutputDataT]):
|
|
|
168
238
|
else:
|
|
169
239
|
break
|
|
170
240
|
else:
|
|
171
|
-
raise ValueError(f'
|
|
241
|
+
raise ValueError(f'Unsupported file type: {url}') # pragma: no cover
|
|
172
242
|
model_parts.append(UserPromptPart(content=[content]))
|
|
173
|
-
elif part['
|
|
174
|
-
# TODO(Marcelo): Maybe we should use this for `ToolReturnPart`, and `RetryPromptPart`.
|
|
243
|
+
elif part['kind'] == 'data':
|
|
175
244
|
raise NotImplementedError('Data parts are not supported yet.')
|
|
176
245
|
else:
|
|
177
246
|
assert_never(part)
|
|
178
247
|
return model_parts
|
|
179
248
|
|
|
180
|
-
def
|
|
249
|
+
def _response_parts_from_a2a(self, parts: list[Part]) -> list[ModelResponsePart]:
|
|
250
|
+
"""Convert A2A Part objects to pydantic-ai ModelResponsePart objects.
|
|
251
|
+
|
|
252
|
+
This handles the conversion from A2A protocol parts (text, file, data) to
|
|
253
|
+
pydantic-ai's internal response parts. Currently only supports text parts
|
|
254
|
+
as agent responses in A2A are expected to be text-based.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
parts: List of A2A Part objects from stored agent messages
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
List of ModelResponsePart objects for message history
|
|
261
|
+
"""
|
|
181
262
|
model_parts: list[ModelResponsePart] = []
|
|
182
263
|
for part in parts:
|
|
183
|
-
if part['
|
|
264
|
+
if part['kind'] == 'text':
|
|
184
265
|
model_parts.append(TextPart(content=part['text']))
|
|
185
|
-
elif part['
|
|
266
|
+
elif part['kind'] == 'file': # pragma: no cover
|
|
186
267
|
raise NotImplementedError('File parts are not supported yet.')
|
|
187
|
-
elif part['
|
|
268
|
+
elif part['kind'] == 'data': # pragma: no cover
|
|
188
269
|
raise NotImplementedError('Data parts are not supported yet.')
|
|
189
270
|
else: # pragma: no cover
|
|
190
271
|
assert_never(part)
|
|
191
272
|
return model_parts
|
|
273
|
+
|
|
274
|
+
def _response_parts_to_a2a(self, parts: list[ModelResponsePart]) -> list[Part]:
|
|
275
|
+
"""Convert pydantic-ai ModelResponsePart objects to A2A Part objects.
|
|
276
|
+
|
|
277
|
+
This handles the conversion from pydantic-ai's internal response parts to
|
|
278
|
+
A2A protocol parts. Different part types are handled as follows:
|
|
279
|
+
- TextPart: Converted directly to A2A TextPart
|
|
280
|
+
- ThinkingPart: Converted to TextPart with metadata indicating it's thinking
|
|
281
|
+
- ToolCallPart: Skipped (internal to agent execution)
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
parts: List of ModelResponsePart objects from agent response
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
List of A2A Part objects suitable for sending via A2A protocol
|
|
288
|
+
"""
|
|
289
|
+
a2a_parts: list[Part] = []
|
|
290
|
+
for part in parts:
|
|
291
|
+
if isinstance(part, TextPart):
|
|
292
|
+
a2a_parts.append(A2ATextPart(kind='text', text=part.content))
|
|
293
|
+
elif isinstance(part, ThinkingPart):
|
|
294
|
+
# Convert thinking to text with metadata
|
|
295
|
+
a2a_parts.append(
|
|
296
|
+
A2ATextPart(
|
|
297
|
+
kind='text',
|
|
298
|
+
text=part.content,
|
|
299
|
+
metadata={'type': 'thinking', 'thinking_id': part.id, 'signature': part.signature},
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
elif isinstance(part, ToolCallPart):
|
|
303
|
+
# Skip tool calls - they're internal to agent execution
|
|
304
|
+
pass
|
|
305
|
+
return a2a_parts
|
pydantic_ai/agent.py
CHANGED
|
@@ -63,7 +63,7 @@ if TYPE_CHECKING:
|
|
|
63
63
|
|
|
64
64
|
from fasta2a.applications import FastA2A
|
|
65
65
|
from fasta2a.broker import Broker
|
|
66
|
-
from fasta2a.schema import
|
|
66
|
+
from fasta2a.schema import AgentProvider, Skill
|
|
67
67
|
from fasta2a.storage import Storage
|
|
68
68
|
from pydantic_ai.mcp import MCPServer
|
|
69
69
|
|
|
@@ -1764,7 +1764,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1764
1764
|
url: str = 'http://localhost:8000',
|
|
1765
1765
|
version: str = '1.0.0',
|
|
1766
1766
|
description: str | None = None,
|
|
1767
|
-
provider:
|
|
1767
|
+
provider: AgentProvider | None = None,
|
|
1768
1768
|
skills: list[Skill] | None = None,
|
|
1769
1769
|
# Starlette
|
|
1770
1770
|
debug: bool = False,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai-slim
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
|
|
5
5
|
Author-email: Samuel Colvin <samuel@pydantic.dev>, Marcelo Trylesinski <marcelotryle@gmail.com>, David Montague <david@pydantic.dev>, Alex Hall <alex@pydantic.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,11 +30,11 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
|
|
|
30
30
|
Requires-Dist: griffe>=1.3.2
|
|
31
31
|
Requires-Dist: httpx>=0.27
|
|
32
32
|
Requires-Dist: opentelemetry-api>=1.28.0
|
|
33
|
-
Requires-Dist: pydantic-graph==0.4.
|
|
33
|
+
Requires-Dist: pydantic-graph==0.4.1
|
|
34
34
|
Requires-Dist: pydantic>=2.10
|
|
35
35
|
Requires-Dist: typing-inspection>=0.4.0
|
|
36
36
|
Provides-Extra: a2a
|
|
37
|
-
Requires-Dist: fasta2a==0.4.
|
|
37
|
+
Requires-Dist: fasta2a==0.4.1; extra == 'a2a'
|
|
38
38
|
Provides-Extra: anthropic
|
|
39
39
|
Requires-Dist: anthropic>=0.52.0; extra == 'anthropic'
|
|
40
40
|
Provides-Extra: bedrock
|
|
@@ -48,7 +48,7 @@ Requires-Dist: cohere>=5.13.11; (platform_system != 'Emscripten') and extra == '
|
|
|
48
48
|
Provides-Extra: duckduckgo
|
|
49
49
|
Requires-Dist: duckduckgo-search>=7.0.0; extra == 'duckduckgo'
|
|
50
50
|
Provides-Extra: evals
|
|
51
|
-
Requires-Dist: pydantic-evals==0.4.
|
|
51
|
+
Requires-Dist: pydantic-evals==0.4.1; extra == 'evals'
|
|
52
52
|
Provides-Extra: google
|
|
53
53
|
Requires-Dist: google-genai>=1.24.0; extra == 'google'
|
|
54
54
|
Provides-Extra: groq
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pydantic_ai/__init__.py,sha256=Ns04g4Efqkzwccs8w2nGphfWbptMlIJYG8vIJbGGyG0,1262
|
|
2
2
|
pydantic_ai/__main__.py,sha256=Q_zJU15DUA01YtlJ2mnaLCoId2YmgmreVEERGuQT-Y0,132
|
|
3
|
-
pydantic_ai/_a2a.py,sha256=
|
|
3
|
+
pydantic_ai/_a2a.py,sha256=G6W8zLRE5FNug19GieVkYuGPw5CA44YeZnS7GTN7M30,12068
|
|
4
4
|
pydantic_ai/_agent_graph.py,sha256=rtzyBXN4bzEDBeRkRwF031ORktSMbuGz9toZmSqUxNI,42153
|
|
5
5
|
pydantic_ai/_cli.py,sha256=R-sE-9gYqPxV5-5utso4g-bzAKMiTCdo33XOVqE0ZEg,13206
|
|
6
6
|
pydantic_ai/_function_schema.py,sha256=BZus5y51eqiGQKxQIcCiDoSPml3AtAb12-st_aujU2k,10813
|
|
@@ -12,7 +12,7 @@ pydantic_ai/_run_context.py,sha256=zNkSyiQSH-YweO39ii3iB2taouUOodo3sTjz2Lrj4Pc,1
|
|
|
12
12
|
pydantic_ai/_system_prompt.py,sha256=lUSq-gDZjlYTGtd6BUm54yEvTIvgdwBmJ8mLsNZZtYU,1142
|
|
13
13
|
pydantic_ai/_thinking_part.py,sha256=mzx2RZSfiQxAKpljEflrcXRXmFKxtp6bKVyorY3UYZk,1554
|
|
14
14
|
pydantic_ai/_utils.py,sha256=SGXEiGCnMae1Iz_eZKUs6ni_tGMPkDaJ4W3W3YMoP5w,15545
|
|
15
|
-
pydantic_ai/agent.py,sha256=
|
|
15
|
+
pydantic_ai/agent.py,sha256=zvQgEG9eFG7entCTum3QSApHbNU8RvAE_ydscPaMAC4,96196
|
|
16
16
|
pydantic_ai/direct.py,sha256=WRfgke3zm-eeR39LTuh9XI2TrdHXAqO81eDvFwih4Ko,14803
|
|
17
17
|
pydantic_ai/exceptions.py,sha256=IdFw594Ou7Vn4YFa7xdZ040_j_6nmyA3MPANbC7sys4,3175
|
|
18
18
|
pydantic_ai/format_as_xml.py,sha256=IINfh1evWDphGahqHNLBArB5dQ4NIqS3S-kru35ztGg,372
|
|
@@ -76,8 +76,8 @@ pydantic_ai/providers/mistral.py,sha256=EIUSENjFuGzBhvbdrarUTM4VPkesIMnZrzfnEKHO
|
|
|
76
76
|
pydantic_ai/providers/openai.py,sha256=7iGij0EaFylab7dTZAZDgXr78tr-HsZrn9EI9AkWBNQ,3091
|
|
77
77
|
pydantic_ai/providers/openrouter.py,sha256=NXjNdnlXIBrBMMqbzcWQnowXOuZh4NHikXenBn5h3mc,4061
|
|
78
78
|
pydantic_ai/providers/together.py,sha256=zFVSMSm5jXbpkNouvBOTjWrPmlPpCp6sQS5LMSyVjrQ,3482
|
|
79
|
-
pydantic_ai_slim-0.4.
|
|
80
|
-
pydantic_ai_slim-0.4.
|
|
81
|
-
pydantic_ai_slim-0.4.
|
|
82
|
-
pydantic_ai_slim-0.4.
|
|
83
|
-
pydantic_ai_slim-0.4.
|
|
79
|
+
pydantic_ai_slim-0.4.1.dist-info/METADATA,sha256=PqGrAd6qbv0rxMgiCa6N1Lo1mBvMb3XUC_FGaxuzeAY,3846
|
|
80
|
+
pydantic_ai_slim-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
81
|
+
pydantic_ai_slim-0.4.1.dist-info/entry_points.txt,sha256=kbKxe2VtDCYS06hsI7P3uZGxcVC08-FPt1rxeiMpIps,50
|
|
82
|
+
pydantic_ai_slim-0.4.1.dist-info/licenses/LICENSE,sha256=vA6Jc482lEyBBuGUfD1pYx-cM7jxvLYOxPidZ30t_PQ,1100
|
|
83
|
+
pydantic_ai_slim-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|