|
1 | 1 | # PydanticAI Examples |
2 | 2 |
|
| 3 | +A comprehensive collection of examples demonstrating PydanticAI framework capabilities, from basic model requests to advanced document processing with schema validation. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +### System Requirements |
| 8 | + |
| 9 | +- Python 3.10+ |
| 10 | +- `uv` package manager |
| 11 | + |
| 12 | +### Environment Setup |
| 13 | + |
| 14 | +```bash |
| 15 | +# Install dependencies |
| 16 | +uv sync |
| 17 | + |
| 18 | +# Create .env file with your API key |
| 19 | +echo "OPENAI_API_KEY=your-key-here" > .env |
| 20 | +``` |
| 21 | + |
| 22 | +**Note**: Most examples use OpenAI's GPT-5.1. Ensure your API key has appropriate permissions and sufficient quota. |
| 23 | + |
| 24 | +## Learning Path |
| 25 | + |
| 26 | +**Recommended order for learning PydanticAI**: |
| 27 | + |
| 28 | +1. **[Direct Model Requests](direct_model_request/)** - Understand basic LLM API calls |
| 29 | +2. **[Temperature](temperature/)** - Understand model parameters |
| 30 | +3. **[Reasoning Effort](reasoning_effort/)** - Uncover how the reasoning effort may change the model's output |
| 31 | +4. **[Basic Sentiment](basic_sentiment/)** - Learn structured outputs with Pydantic |
| 32 | +5. **[Dynamic Classification](dynamic_classification/)** - Runtime schema generation |
| 33 | +6. **[Bielik](bielik_example/)** - Local models and tools |
| 34 | +7. **[History Processor](history_processor/)** - Multi-turn conversations |
| 35 | +8. **[OCR Parsing](ocr_parsing_demo/)** - Complex real-world document processing |
| 36 | + |
| 37 | +## Examples Overview |
| 38 | + |
3 | 39 | ### 1. Direct Model Requests |
4 | 40 |
|
5 | 41 | **Location**: `direct_model_request/` |
@@ -32,6 +68,7 @@ Demonstrates reasoning_effort parameter for gpt-5.2. |
32 | 68 |
|
33 | 69 | - Control depth of internal reasoning |
34 | 70 | - Complex problem-solving examples |
| 71 | +- Trade-off between accuracy and latency |
35 | 72 |
|
36 | 73 | [View Example →](reasoning_effort/) |
37 | 74 |
|
@@ -71,6 +108,36 @@ Learn to use **Bielik**, a Polish language LLM, with PydanticAI running locally |
71 | 108 |
|
72 | 109 | [View Example →](bielik_example/) |
73 | 110 |
|
| 111 | +### 7. Conversation History Management |
| 112 | + |
| 113 | +**Location**: `history_processor/` |
| 114 | + |
| 115 | +Learn how to manage conversation history in AI agents. |
| 116 | + |
| 117 | +- Basic history handling and inspection |
| 118 | +- Multi-turn conversations with context awareness |
| 119 | +- History persistence (JSON and Database) |
| 120 | +- Advanced filtering and transformation |
| 121 | +- Context window management strategies (fixed, dynamic, and tool-aware) |
| 122 | +- Production-ready database archival |
| 123 | + |
| 124 | +[View Example →](history_processor/) |
| 125 | + |
| 126 | +### 8. OCR Parsing with Data Validation |
| 127 | + |
| 128 | +**Location**: `ocr_parsing/` |
| 129 | + |
| 130 | +Learn how to work with documents using PydanticAI for OCR (Optical Character Recognition). |
| 131 | + |
| 132 | +- **Basic OCR**: Unstructured text extraction from PDFs |
| 133 | +- **Structured Output**: Type-safe document analysis with schema validation |
| 134 | +- **Validation Errors**: Error handling when LLM output doesn't match schema |
| 135 | +- PDF to image conversion pipeline |
| 136 | +- Parallel async processing with concurrency control |
| 137 | +- Production-ready document processing patterns |
| 138 | + |
| 139 | +[View Example →](ocr_parsing/) |
| 140 | + |
74 | 141 | ## Quick Start |
75 | 142 |
|
76 | 143 | ### Setup |
@@ -107,82 +174,100 @@ cd dynamic_classification |
107 | 174 | uv run dynamic_classifier.py |
108 | 175 |
|
109 | 176 | # Bielik local model examples |
| 177 | +# Note: Requires Ollama setup (see bielik_example/README.md) |
110 | 178 | cd bielik_example |
111 | | -uv run python bielik_basic_inference.py |
112 | | -uv run python bielik_basic_tools.py |
| 179 | +uv run bielik_basic_inference.py |
| 180 | +uv run bielik_basic_tools.py |
| 181 | + |
| 182 | +# History processor - Run individual examples |
| 183 | +cd history_processor |
| 184 | +uv run 1_basic_history_handling.py |
| 185 | +uv run 2_continuous_history.py |
| 186 | +uv run 3_history_usage.py |
| 187 | +uv run 4_history_filtering.py |
| 188 | +uv run 5a_history_length_fixed.py |
| 189 | +uv run 5b_history_length_dynamic.py |
| 190 | +uv run 5c_history_with_tools.py |
| 191 | +uv run 6_persistent_history.py |
| 192 | + |
| 193 | +# OCR Parsing - Run examples in order |
| 194 | +cd ocr_parsing |
| 195 | +uv run 1_basic_ocr_demo.py |
| 196 | +uv run 2_ocr_with_structured_output.py |
| 197 | +uv run 3_ocr_validation.py # Uncomment validation line in code first |
113 | 198 | ``` |
114 | 199 |
|
115 | | -### 6. History Processor |
| 200 | +## Key Concepts Demonstrated |
116 | 201 |
|
117 | | -**Location**: `history_processor/` |
| 202 | +### Agents |
118 | 203 |
|
119 | | -Learn how to manage conversation history in AI agents. |
| 204 | +Most examples use PydanticAI's `Agent` class, which wraps an LLM with: |
120 | 205 |
|
121 | | -- Basic history handling and inspection |
122 | | -- Multi-turn conversations with context |
123 | | -- History persistence (JSON and Database) |
124 | | -- Advanced filtering and transformation |
125 | | -- Context window management strategies (fixed, dynamic, and tool-aware) |
126 | | -- Production-ready database archival |
| 206 | +- System prompts to guide behavior |
| 207 | +- Output type schemas for structured responses |
| 208 | +- Async/await support for concurrent requests |
127 | 209 |
|
128 | | -[View Example →](history_processor/) |
| 210 | +### Tools |
129 | 211 |
|
130 | | -## Quick Start |
| 212 | +It's worth noticing that since those are examples, most of them are pretty basic. However, it's easy to add an a tool for given agent. Let's look at **[OCR Parsing](ocr_parsing/) code. |
131 | 213 |
|
132 | | -### Setup - General |
| 214 | +Currently the Agent does all the work itself - classifies document, parses the output, does the OCR and so on for every document in the same way. But what if we'd like to have a different behavior based on the document type? |
133 | 215 |
|
134 | | -```bash |
135 | | -# Install dependencies |
136 | | -uv sync |
| 216 | +```python |
| 217 | +from pydantic_ai import Agent, RunContext |
| 218 | +from my_schemas import OCRInvoiceOutput, ReportOcrOutput |
137 | 219 |
|
138 | | -# Set API key |
139 | | -echo "OPENAI_API_KEY=your-key" > .env |
| 220 | +# The Agent acts as a router, deciding which tool to call |
| 221 | +# based on the document's visual or textual cues. |
| 222 | +agent = Agent( |
| 223 | + 'openai:gpt-5.1', |
| 224 | + system_prompt="Analyze the document and use the appropriate tool for parsing." |
| 225 | +) |
| 226 | + |
| 227 | +@agent.tool |
| 228 | +async def parse_invoice(ctx: RunContext[MyDeps], data: bytes) -> OCRInvoiceOutput: |
| 229 | + """Use this tool when the document is identified as an Invoice.""" |
| 230 | + # Your specialized OCR & validation logic here |
| 231 | + return await ctx.deps.ocr_service.process(data, schema=OCRInvoiceOutput) |
| 232 | + |
| 233 | +@agent.tool |
| 234 | +async def parse_report(ctx: RunContext[MyDeps], data: bytes) -> ReportOcrOutput: |
| 235 | + """Use this tool when the document is a multi-page Annual Report.""" |
| 236 | + # Custom logic for complex reports |
| 237 | + return await ctx.deps.ocr_service.process(data, schema=ReportOcrOutput) |
140 | 238 | ``` |
141 | 239 |
|
142 | | -### Run Examples 1-5 |
| 240 | +### Structured Outputs |
143 | 241 |
|
144 | | -```bash |
145 | | -# Direct model requests |
146 | | -cd direct_model_request |
147 | | -uv run direct_request_demo.py |
| 242 | +Examples show how to enforce type safety using Pydantic `BaseModel`: |
148 | 243 |
|
149 | | -# Model parameters |
150 | | -cd temperature |
151 | | -uv run temperature_demo.py |
| 244 | +- Basic classification: `Literal` types |
| 245 | +- Dynamic classification: `create_model()` for runtime schemas |
| 246 | +- OCR parsing: Complex nested schemas with validation |
152 | 247 |
|
153 | | -# Reasoning effort |
154 | | -cd reasoning_effort |
155 | | -uv run reasoning_demo.py |
| 248 | +### Async Concurrency |
156 | 249 |
|
157 | | -# Basic sentiment classifier |
158 | | -cd basic_sentiment |
159 | | -uv run sentiment_classifier.py |
| 250 | +Several examples demonstrate async patterns: |
160 | 251 |
|
161 | | -# Dynamic classifier |
162 | | -cd dynamic_classification |
163 | | -uv run dynamic_classifier.py |
164 | | -``` |
| 252 | +- Parallel processing with `asyncio.gather()` |
| 253 | +- Semaphore-based rate limiting |
| 254 | +- Efficient handling of multiple documents |
165 | 255 |
|
166 | | -### Run Example 6 - History Processor |
| 256 | +### Context & History |
167 | 257 |
|
168 | | -```bash |
169 | | -cd history_processor |
| 258 | +Learn how to manage conversational context: |
170 | 259 |
|
171 | | -# Configure environment |
172 | | -cp .env.example .env |
173 | | -# Edit .env and add your OpenAI API key (i.e., with `nano`) |
174 | | -nano .env |
175 | | - |
176 | | -# Run individual examples |
177 | | -uv run python 1_basic_history_handling.py |
178 | | -uv run python 2_continuous_history.py |
179 | | -uv run python 3_history_usage.py |
180 | | -uv run python 4_history_filtering.py |
181 | | -uv run python 5a_history_length_fixed.py |
182 | | -uv run python 5b_history_length_dynamic.py |
183 | | -uv run python 5c_history_with_tools.py |
184 | | -uv run python 6_persistent_history.py |
185 | | -``` |
| 260 | +- Persistent history storage |
| 261 | +- Token-aware context windowing |
| 262 | +- History filtering and transformation |
| 263 | + |
| 264 | +### Local Models |
| 265 | + |
| 266 | +Bielik example shows alternative to cloud APIs: |
| 267 | + |
| 268 | +- Local model serving with Ollama |
| 269 | +- Custom tool integration |
| 270 | +- Same agent patterns as OpenAI models |
186 | 271 |
|
187 | 272 | ## Project Structure |
188 | 273 |
|
@@ -218,13 +303,54 @@ uv run python 6_persistent_history.py |
218 | 303 | │ ├── 5c_history_with_tools.py |
219 | 304 | │ ├── 6_persistent_history.py |
220 | 305 | │ ├── README.md |
221 | | -│ ├── .env.example |
222 | | -│ └── pyproject.toml |
| 306 | +│ ├── output_3.json |
| 307 | +├── ocr_parsing/ |
| 308 | +│ ├── 1_basic_ocr_demo.py |
| 309 | +│ ├── 2_ocr_with_structured_output.py |
| 310 | +│ ├── 3_ocr_validation.py |
| 311 | +│ ├── README.md |
| 312 | +│ ├── files/ |
| 313 | +│ │ ├── samples/ # Sample PDF documents |
| 314 | +│ │ ├── temp_files/ # Temporary image files during processing |
| 315 | +│ │ ├── results/ # Output JSON files |
223 | 316 | ├── pyproject.toml |
224 | 317 | └── README.md |
225 | 318 | ``` |
226 | 319 |
|
| 320 | +## Common Issues & Troubleshooting |
| 321 | + |
| 322 | +### API Key Issues |
| 323 | + |
| 324 | +- Ensure `OPENAI_API_KEY` is set in `.env` |
| 325 | +- Verify key has appropriate permissions |
| 326 | +- Check for rate limiting (503 errors) |
| 327 | + |
| 328 | +### Import Errors |
| 329 | + |
| 330 | +- Run `uv sync` to install all dependencies |
| 331 | +- Verify you're using Python 3.10+ |
| 332 | + |
| 333 | +### Async Issues |
| 334 | + |
| 335 | +- Some examples require async-compatible event loops |
| 336 | +- On Windows, you may need to set event loop policy |
| 337 | + |
| 338 | +### OCR-Specific Issues |
| 339 | + |
| 340 | +- **poppler not found**: Install via your package manager (brew/apt/choco) |
| 341 | +- **PDF conversion fails**: Ensure PDF is valid and readable |
| 342 | +- **Rate limiting**: Reduce semaphore value in `ocr_parsing/shared_fns.py` |
| 343 | + |
| 344 | +See individual example READMEs for specific setup requirements. |
| 345 | + |
227 | 346 | ## Resources |
228 | 347 |
|
229 | | -- [Pydantic AI Documentation](https://ai.pydantic.dev/) |
230 | | -- [Python Documentation](https://docs.python.org/) |
| 348 | +- [Python Documentation](https://docs.python.org/3/) |
| 349 | +- [PydanticAI Documentation](https://ai.pydantic.dev/) |
| 350 | +- [Pydantic Documentation](https://docs.pydantic.dev/) |
| 351 | +- [OpenAI API Reference](https://platform.openai.com/docs/api-reference) |
| 352 | +- [Python asyncio Guide](https://docs.python.org/3/library/asyncio.html) |
| 353 | + |
| 354 | +## Contributing |
| 355 | + |
| 356 | +Found an issue or have an improvement? Feel free to contribute to this example repository. |
0 commit comments