pypi-pydantic-ai

Description
Agent Framework / shim to use Pydantic with LLMs
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/pypi-pydantic-ai@0.8.0

output.md docs/

1
# Output Types and Validation
2
3
Flexible output handling supporting structured data validation using Pydantic models, text outputs, tool-based outputs, and native model outputs with comprehensive type safety.
4
5
## Capabilities
6
7
### Structured Output Types
8
9
Output configuration classes that determine how agent responses are processed and validated.
10
11
```python { .api }
12
class ToolOutput[OutputDataT]:
13
"""
14
Tool-based output configuration where tools generate the final result.
15
"""
16
def __init__(
17
self,
18
tools: list[Tool],
19
*,
20
defer: bool = False
21
):
22
"""
23
Configure tool-based output.
24
25
Parameters:
26
- tools: List of tools that can generate output
27
- defer: Whether to defer tool execution
28
"""
29
30
class NativeOutput[OutputDataT]:
31
"""
32
Native structured output configuration using model's built-in structured output.
33
"""
34
pass
35
36
class PromptedOutput[OutputDataT]:
37
"""
38
Prompted output configuration where model is prompted to return structured data.
39
"""
40
pass
41
42
class TextOutput[OutputDataT]:
43
"""
44
Text output configuration with optional conversion function.
45
"""
46
def __init__(
47
self,
48
converter: TextOutputFunc[OutputDataT] | None = None
49
):
50
"""
51
Configure text output.
52
53
Parameters:
54
- converter: Optional function to convert text to desired type
55
"""
56
```
57
58
### Deferred Tool Calls
59
60
Container for managing deferred tool execution.
61
62
```python { .api }
63
class DeferredToolCalls:
64
"""
65
Container for deferred tool calls that can be executed later.
66
"""
67
def __init__(self, tool_calls: list[ToolCallPart]): ...
68
69
def execute_all(
70
self,
71
deps: Any = None
72
) -> list[Any]:
73
"""
74
Execute all deferred tool calls.
75
76
Parameters:
77
- deps: Dependencies to pass to tools
78
79
Returns:
80
List of tool execution results
81
"""
82
```
83
84
### Structured Dictionary Factory
85
86
Factory function for creating structured dictionary types.
87
88
```python { .api }
89
def StructuredDict() -> type[dict[str, Any]]:
90
"""
91
Create structured dictionary type for flexible output handling.
92
93
Returns:
94
Dictionary type that can be used as result_type for agents
95
"""
96
```
97
98
### Output Type Aliases
99
100
Type definitions for various output configurations and functions.
101
102
```python { .api }
103
OutputMode = Literal['tools', 'json', 'str']
104
105
StructuredOutputMode = Literal['json', 'str']
106
107
OutputSpec[T_co] = (
108
type[T_co] |
109
ToolOutput[T_co] |
110
NativeOutput[T_co] |
111
PromptedOutput[T_co] |
112
TextOutput[T_co]
113
)
114
115
OutputTypeOrFunction[T_co] = (
116
OutputSpec[T_co] |
117
Callable[[str], T_co]
118
)
119
120
TextOutputFunc[T_co] = Callable[[str], T_co]
121
```
122
123
### Output Processing
124
125
Internal types and functions for output processing (advanced usage).
126
127
```python { .api }
128
class OutputTypeWrapper[T]:
129
"""Internal wrapper for output type processing."""
130
def __init__(
131
self,
132
output_type: OutputSpec[T],
133
allow_text_output: bool = True
134
): ...
135
136
def validate_output(self, data: Any) -> T: ...
137
def get_output_mode(self) -> OutputMode: ...
138
```
139
140
## Usage Examples
141
142
### Basic Structured Output with Pydantic
143
144
```python
145
from pydantic_ai import Agent
146
from pydantic import BaseModel
147
148
class WeatherInfo(BaseModel):
149
location: str
150
temperature: float
151
condition: str
152
humidity: int
153
154
# Agent with structured output
155
agent = Agent(
156
model='gpt-4',
157
system_prompt='Extract weather information from text.',
158
result_type=WeatherInfo
159
)
160
161
result = agent.run_sync(
162
'The weather in Paris is sunny, 22°C with 65% humidity'
163
)
164
165
print(result.data.location) # "Paris"
166
print(result.data.temperature) # 22.0
167
print(result.data.condition) # "sunny"
168
print(result.data.humidity) # 65
169
```
170
171
### Tool-Based Output
172
173
```python
174
from pydantic_ai import Agent, ToolOutput, tool
175
176
@tool
177
def calculate_result(numbers: list[float], operation: str) -> float:
178
"""Perform calculation on numbers."""
179
if operation == 'sum':
180
return sum(numbers)
181
elif operation == 'average':
182
return sum(numbers) / len(numbers)
183
elif operation == 'max':
184
return max(numbers)
185
else:
186
raise ValueError(f"Unknown operation: {operation}")
187
188
# Agent with tool-based output
189
agent = Agent(
190
model='gpt-4',
191
system_prompt='Use tools to calculate results.',
192
result_type=ToolOutput([calculate_result])
193
)
194
195
result = agent.run_sync('Calculate the average of 10, 20, 30, 40')
196
print(result.data) # 25.0
197
```
198
199
### Text Output with Conversion
200
201
```python
202
from pydantic_ai import Agent, TextOutput
203
import json
204
205
def parse_json_response(text: str) -> dict:
206
"""Parse JSON from model response."""
207
# Extract JSON from text if needed
208
start = text.find('{')
209
end = text.rfind('}') + 1
210
json_str = text[start:end]
211
return json.loads(json_str)
212
213
# Agent with text output conversion
214
agent = Agent(
215
model='gpt-4',
216
system_prompt='Return responses as JSON.',
217
result_type=TextOutput(parse_json_response)
218
)
219
220
result = agent.run_sync('Create a person object with name and age')
221
print(result.data) # {'name': 'John Doe', 'age': 30}
222
```
223
224
### Structured Dictionary Output
225
226
```python
227
from pydantic_ai import Agent, StructuredDict
228
229
# Agent with flexible dictionary output
230
agent = Agent(
231
model='gpt-4',
232
system_prompt='Return structured data as a dictionary.',
233
result_type=StructuredDict()
234
)
235
236
result = agent.run_sync('Create a product with name, price, and category')
237
print(result.data) # {'name': 'Laptop', 'price': 999.99, 'category': 'Electronics'}
238
```
239
240
### Native vs Prompted Output
241
242
```python
243
from pydantic_ai import Agent, NativeOutput, PromptedOutput
244
from pydantic import BaseModel
245
246
class TaskInfo(BaseModel):
247
title: str
248
priority: int
249
completed: bool
250
251
# Native structured output (uses model's built-in structured output)
252
native_agent = Agent(
253
model='gpt-4',
254
system_prompt='Create task information.',
255
result_type=NativeOutput[TaskInfo]
256
)
257
258
# Prompted structured output (prompts model to return structured data)
259
prompted_agent = Agent(
260
model='gpt-4',
261
system_prompt='Create task information.',
262
result_type=PromptedOutput[TaskInfo]
263
)
264
265
# Both work similarly but use different underlying mechanisms
266
result1 = native_agent.run_sync('Create a high priority task for code review')
267
result2 = prompted_agent.run_sync('Create a high priority task for code review')
268
269
print(result1.data.title) # "Code Review"
270
print(result1.data.priority) # 3
271
print(result1.data.completed) # False
272
```
273
274
### Deferred Tool Execution
275
276
```python
277
from pydantic_ai import Agent, ToolOutput, tool
278
279
@tool
280
def expensive_calculation(data: list[int]) -> int:
281
"""Perform expensive calculation."""
282
# Simulate expensive operation
283
return sum(x ** 2 for x in data)
284
285
# Agent with deferred tool execution
286
agent = Agent(
287
model='gpt-4',
288
system_prompt='Plan calculations but defer execution.',
289
result_type=ToolOutput([expensive_calculation], defer=True)
290
)
291
292
result = agent.run_sync('Plan calculation for numbers 1 through 100')
293
294
# result.data is now DeferredToolCalls
295
if isinstance(result.data, DeferredToolCalls):
296
# Execute when ready
297
actual_results = result.data.execute_all()
298
print(actual_results[0]) # Result of expensive calculation
299
```
300
301
### Complex Nested Output
302
303
```python
304
from pydantic_ai import Agent
305
from pydantic import BaseModel
306
from typing import List
307
308
class Address(BaseModel):
309
street: str
310
city: str
311
country: str
312
postal_code: str
313
314
class Person(BaseModel):
315
name: str
316
age: int
317
email: str
318
addresses: List[Address]
319
320
class Company(BaseModel):
321
name: str
322
employees: List[Person]
323
headquarters: Address
324
325
# Agent with complex nested output
326
agent = Agent(
327
model='gpt-4',
328
system_prompt='Extract company information.',
329
result_type=Company
330
)
331
332
result = agent.run_sync('''
333
Create a company called TechCorp with headquarters in San Francisco.
334
Include 2 employees: John (30, john@techcorp.com) and Jane (28, jane@techcorp.com).
335
''')
336
337
print(result.data.name) # "TechCorp"
338
print(len(result.data.employees)) # 2
339
print(result.data.employees[0].name) # "John"
340
print(result.data.headquarters.city) # "San Francisco"
341
```
342
343
### Custom Validation
344
345
```python
346
from pydantic_ai import Agent
347
from pydantic import BaseModel, validator
348
349
class ValidatedOutput(BaseModel):
350
score: float
351
grade: str
352
353
@validator('score')
354
def score_must_be_valid(cls, v):
355
if not 0 <= v <= 100:
356
raise ValueError('Score must be between 0 and 100')
357
return v
358
359
@validator('grade')
360
def grade_must_match_score(cls, v, values):
361
score = values.get('score', 0)
362
expected_grades = {
363
(90, 100): 'A',
364
(80, 89): 'B',
365
(70, 79): 'C',
366
(60, 69): 'D',
367
(0, 59): 'F'
368
}
369
370
for (min_score, max_score), expected_grade in expected_grades.items():
371
if min_score <= score <= max_score:
372
if v != expected_grade:
373
raise ValueError(f'Grade {v} does not match score {score}')
374
break
375
return v
376
377
# Agent with custom validation
378
agent = Agent(
379
model='gpt-4',
380
system_prompt='Grade student performance.',
381
result_type=ValidatedOutput
382
)
383
384
result = agent.run_sync('Student scored 85 points on the exam')
385
print(result.data.score) # 85.0
386
print(result.data.grade) # "B"
387
```