# Pydantic AI Claude Code
Use your local Claude Code CLI as a Pydantic AI model provider.
This package provides a Pydantic AI-compatible model implementation that wraps the local Claude CLI, enabling you to use Claude locally with all Pydantic AI features including structured responses, tool calling, streaming, and multi-turn conversations.
## Features
- **Full Pydantic AI Compatibility**: Drop-in replacement for any Pydantic AI model
- **Structured Responses**: Get validated, typed responses using Pydantic models
- **Custom Tool Calling**: Use your own Python functions as tools
- **True Streaming**: Real-time response streaming via Claude CLI's stream-json mode
- **Local Execution**: All processing happens locally on your machine
- **Session Persistence**: Maintain conversation context across multiple requests
- **Additional Files**: Provide local files for Claude to read and analyze
- **Automatic Response Saving**: Raw prompts and responses saved for debugging
- **Configurable**: Fine-tune permissions, working directories, and tool access
## Installation
```bash
# Using uv (recommended)
uv add pydantic-ai-claude-code
# Using pip
pip install pydantic-ai-claude-code
```
**Prerequisites**: You must have [Claude Code CLI](https://claude.com/claude-code) installed and authenticated on your system.
## Quick Start
### Basic Usage (String Format - Simplest!)
```python
import pydantic_ai_claude_code # Register the provider
from pydantic_ai import Agent
# Just use the string format - easiest way!
agent = Agent('claude-code:sonnet')
# Run a simple query
result = agent.run_sync("What is 2+2?")
print(result.output) # Output: 4
```
### Structured Responses
```python
import pydantic_ai_claude_code
from pydantic import BaseModel
from pydantic_ai import Agent
class Analysis(BaseModel):
complexity: int # 1-10
maintainability: str
suggestions: list[str]
# String format works with all Pydantic AI features
agent = Agent('claude-code:sonnet', output_type=Analysis)
result = agent.run_sync("Analyze this code: def foo(): pass")
print(f"Complexity: {result.output.complexity}")
```
### Custom Tools
```python
import pydantic_ai_claude_code
from pydantic_ai import Agent
def get_weather(city: str) -> str:
"""Get weather for a city."""
return f"Weather in {city}: Sunny, 22°C"
agent = Agent(
'claude-code:sonnet',
tools=[get_weather],
)
result = agent.run_sync("What's the weather in Paris?")
print(result.output)
```
### Streaming
```python
import pydantic_ai_claude_code
from pydantic_ai import Agent
agent = Agent('claude-code:sonnet')
async with agent.run_stream('Write a haiku about code') as result:
async for text in result.stream_text():
print(text, end='', flush=True)
```
## Advanced Configuration
When you need fine-grained control, use the explicit model and provider:
```python
from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeCodeModel, ClaudeCodeProvider
# Custom provider with specific settings
provider = ClaudeCodeProvider(
working_directory="/path/to/project",
allowed_tools=["Read", "Edit", "Grep"], # Restrict tool access
permission_mode="acceptEdits",
use_temp_workspace=False, # Use specific directory instead of /tmp
)
model = ClaudeCodeModel("sonnet", provider=provider)
agent = Agent(model)
result = agent.run_sync("Analyze the codebase structure")
```
### Temporary Workspace
```python
from pydantic_ai_claude_code import ClaudeCodeModel, ClaudeCodeProvider
# Create isolated workspace that auto-cleans
with ClaudeCodeProvider(use_temp_workspace=True) as provider:
model = ClaudeCodeModel("sonnet", provider=provider)
agent = Agent(model)
result = agent.run_sync("Create a test file")
# Workspace automatically cleaned up
```
### Providing Additional Files
Provide local files for Claude to read and analyze:
```python
from pathlib import Path
from pydantic_ai import Agent
agent = Agent('claude-code:sonnet')
result = agent.run_sync(
"Read utils.py and config.json. Summarize what they configure.",
model_settings={
"additional_files": {
"utils.py": Path("src/utils.py"), # Copy single file
"config.json": Path("config/prod.json"), # From different location
"docs/spec.md": Path("specs/feature.md"), # Into subdirectory
}
}
)
```
Files are copied into the working directory before execution, and Claude can reference them directly:
- `"Read utils.py"`
- `"Read config.json"`
- `"Read docs/spec.md"`
Each execution gets its own numbered subdirectory with isolated file copies.
### Binary Content (Standard Interface)
Use Pydantic AI's standard `BinaryContent` interface to attach images, PDFs, and other binary files. This works identically across all model providers:
```python
from pathlib import Path
from pydantic_ai import Agent, BinaryContent
agent = Agent('claude-code:sonnet')
# Send an image from file
image_data = Path('photo.jpg').read_bytes()
result = agent.run_sync(
[
'What is in this image?',
BinaryContent(data=image_data, media_type='image/jpeg'),
]
)
# Send a PDF document
pdf_data = Path('document.pdf').read_bytes()
result = agent.run_sync(
[
'Summarize this document:',
BinaryContent(data=pdf_data, media_type='application/pdf'),
]
)
# Compare multiple images
result = agent.run_sync(
[
'Compare these images:',
BinaryContent(data=image1_data, media_type='image/png'),
'and',
BinaryContent(data=image2_data, media_type='image/jpeg'),
]
)
```
**How it works:**
- Binary content is automatically written to files in the working directory
- Files are referenced in the prompt (e.g., `[Image: filename.png]`)
- Claude Code CLI can then read the files directly
- No Claude Code-specific code needed - this is standard Pydantic AI!
**Supported formats:**
- Images: PNG, JPEG, GIF, WebP, etc.
- Documents: PDF, TXT, JSON, etc.
- Audio: MP3, WAV, etc.
- Video: MP4, WebM, etc.
**Benefits:**
- **Portable code** - works with OpenAI, Anthropic, Google, and Claude Code
- **Standard interface** - same `BinaryContent` class for all providers
- **No provider lock-in** - switch between cloud and local with one line change
### Error Handling
#### OAuth Token Expiration
For long-running processes (>7 hours), OAuth tokens may expire. Handle gracefully with `ClaudeOAuthError`:
```python
from pydantic_ai import Agent
from pydantic_ai_claude_code import ClaudeOAuthError
agent = Agent('claude-code:sonnet')
try:
result = agent.run_sync("Process large dataset")
except ClaudeOAuthError as e:
print(f"Authentication expired: {e}")
print(f"Please run: {e.reauth_instruction}") # "Please run /login"
# Prompt user to re-authenticate, then retry
except RuntimeError as e:
# Handle other CLI errors
print(f"CLI error: {e}")
```
**For batch processing with automatic retry:**
```python
from pydantic_ai_claude_code import ClaudeOAuthError
import time
def process_batch_with_retry(items, max_auth_retries=3):
"""Process items with OAuth re-authentication support."""
results = []
for item in items:
auth_retries = 0
while auth_retries < max_auth_retries:
try:
result = agent.run_sync(f"Process: {item}")
results.append(result.output)
break # Success
except ClaudeOAuthError as e:
auth_retries += 1
print(f"\n{'='*60}")
print(f"OAuth token expired: {e.reauth_instruction}")
print(f"{'='*60}\n")
if auth_retries >= max_auth_retries:
raise # Give up after max retries
input("Press Enter after running /login to continue...")
time.sleep(2) # Brief pause before retry
return results
```
### Logging
The package uses Python's standard logging module. To enable debug logging in your application:
```python
import logging
# Enable debug logging for pydantic-ai-claude-code
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('pydantic_ai_claude_code').setLevel(logging.DEBUG)
# Or just for specific components
logging.getLogger('pydantic_ai_claude_code.model').setLevel(logging.DEBUG)
logging.getLogger('pydantic_ai_claude_code.utils').setLevel(logging.INFO)
```
This will log:
- Model initialization and configuration
- CLI command execution and responses
- Message formatting and conversion
- Tool call parsing and execution
- Structured output handling
- Streaming events and completion
## Available Models
- `claude-code:sonnet` - Claude 3.5 Sonnet (default, recommended)
- `claude-code:opus` - Claude 3 Opus (most capable)
- `claude-code:haiku` - Claude 3.5 Haiku (fastest)
Or use full model names like `claude-code:claude-sonnet-4-5-20250929`
## Integration with Existing Projects
Replace your current LLM calls with Claude Code:
**Before:**
```python
agent = Agent('openai:gpt-4o')
# or
agent = Agent('anthropic:claude-3-5-sonnet-latest')
```
**After:**
```python
import pydantic_ai_claude_code # Add this import
agent = Agent('claude-code:sonnet') # Change this line
```
Everything else stays the same! All your tools, structured outputs, dependencies, and streaming code works identically.
## Key Differences from Cloud Providers
| Aspect | Cloud Providers | Claude Code Local |
|--------|----------------|-------------------|
| **Execution** | Remote API calls | Local on your machine |
| **Cost** | Per-token pricing | Uses Claude desktop subscription |
| **Data Privacy** | Data sent to cloud | Data stays local |
| **Speed** | Network latency | Local execution |
| **API Key** | Required | Not needed (uses local auth) |
## Examples
See the `examples/` directory for more demonstrations:
- `basic_example.py` - Simple queries and usage tracking
- `structured_example.py` - Structured output with Pydantic models
- `async_example.py` - Async/await usage patterns
- `advanced_example.py` - Custom provider configurations
- `tools_and_streaming.py` - Custom tools and streaming responses
- `binary_content_example.py` - Standard interface for images, PDFs, and binary files
- `additional_files_example.py` - Providing local files for analysis (Claude Code-specific)
## License
MIT License
Raw data
{
"_id": null,
"home_page": null,
"name": "pydantic-ai-claude-code",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "pydantic, pydantic-ai, claude, claude-code, ai, llm, agent",
"author": "mark",
"author_email": "mark <wernsdorfer@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/02/2b/624a250d42117e34da4d3d104ebf9e230a0f17be9449b4b9c5677488663d/pydantic_ai_claude_code-0.7.4.tar.gz",
"platform": null,
"description": "# Pydantic AI Claude Code\n\nUse your local Claude Code CLI as a Pydantic AI model provider.\n\nThis package provides a Pydantic AI-compatible model implementation that wraps the local Claude CLI, enabling you to use Claude locally with all Pydantic AI features including structured responses, tool calling, streaming, and multi-turn conversations.\n\n## Features\n\n- **Full Pydantic AI Compatibility**: Drop-in replacement for any Pydantic AI model\n- **Structured Responses**: Get validated, typed responses using Pydantic models\n- **Custom Tool Calling**: Use your own Python functions as tools\n- **True Streaming**: Real-time response streaming via Claude CLI's stream-json mode\n- **Local Execution**: All processing happens locally on your machine\n- **Session Persistence**: Maintain conversation context across multiple requests\n- **Additional Files**: Provide local files for Claude to read and analyze\n- **Automatic Response Saving**: Raw prompts and responses saved for debugging\n- **Configurable**: Fine-tune permissions, working directories, and tool access\n\n## Installation\n\n```bash\n# Using uv (recommended)\nuv add pydantic-ai-claude-code\n\n# Using pip\npip install pydantic-ai-claude-code\n```\n\n**Prerequisites**: You must have [Claude Code CLI](https://claude.com/claude-code) installed and authenticated on your system.\n\n## Quick Start\n\n### Basic Usage (String Format - Simplest!)\n\n```python\nimport pydantic_ai_claude_code # Register the provider\n\nfrom pydantic_ai import Agent\n\n# Just use the string format - easiest way!\nagent = Agent('claude-code:sonnet')\n\n# Run a simple query\nresult = agent.run_sync(\"What is 2+2?\")\nprint(result.output) # Output: 4\n```\n\n### Structured Responses\n\n```python\nimport pydantic_ai_claude_code\n\nfrom pydantic import BaseModel\nfrom pydantic_ai import Agent\n\nclass Analysis(BaseModel):\n complexity: int # 1-10\n maintainability: str\n suggestions: list[str]\n\n# String format works with all Pydantic AI features\nagent = Agent('claude-code:sonnet', output_type=Analysis)\n\nresult = agent.run_sync(\"Analyze this code: def foo(): pass\")\nprint(f\"Complexity: {result.output.complexity}\")\n```\n\n### Custom Tools\n\n```python\nimport pydantic_ai_claude_code\n\nfrom pydantic_ai import Agent\n\ndef get_weather(city: str) -> str:\n \"\"\"Get weather for a city.\"\"\"\n return f\"Weather in {city}: Sunny, 22\u00b0C\"\n\nagent = Agent(\n 'claude-code:sonnet',\n tools=[get_weather],\n)\n\nresult = agent.run_sync(\"What's the weather in Paris?\")\nprint(result.output)\n```\n\n### Streaming\n\n```python\nimport pydantic_ai_claude_code\n\nfrom pydantic_ai import Agent\n\nagent = Agent('claude-code:sonnet')\n\nasync with agent.run_stream('Write a haiku about code') as result:\n async for text in result.stream_text():\n print(text, end='', flush=True)\n```\n\n## Advanced Configuration\n\nWhen you need fine-grained control, use the explicit model and provider:\n\n```python\nfrom pydantic_ai import Agent\nfrom pydantic_ai_claude_code import ClaudeCodeModel, ClaudeCodeProvider\n\n# Custom provider with specific settings\nprovider = ClaudeCodeProvider(\n working_directory=\"/path/to/project\",\n allowed_tools=[\"Read\", \"Edit\", \"Grep\"], # Restrict tool access\n permission_mode=\"acceptEdits\",\n use_temp_workspace=False, # Use specific directory instead of /tmp\n)\n\nmodel = ClaudeCodeModel(\"sonnet\", provider=provider)\nagent = Agent(model)\n\nresult = agent.run_sync(\"Analyze the codebase structure\")\n```\n\n### Temporary Workspace\n\n```python\nfrom pydantic_ai_claude_code import ClaudeCodeModel, ClaudeCodeProvider\n\n# Create isolated workspace that auto-cleans\nwith ClaudeCodeProvider(use_temp_workspace=True) as provider:\n model = ClaudeCodeModel(\"sonnet\", provider=provider)\n agent = Agent(model)\n result = agent.run_sync(\"Create a test file\")\n# Workspace automatically cleaned up\n```\n\n### Providing Additional Files\n\nProvide local files for Claude to read and analyze:\n\n```python\nfrom pathlib import Path\nfrom pydantic_ai import Agent\n\nagent = Agent('claude-code:sonnet')\n\nresult = agent.run_sync(\n \"Read utils.py and config.json. Summarize what they configure.\",\n model_settings={\n \"additional_files\": {\n \"utils.py\": Path(\"src/utils.py\"), # Copy single file\n \"config.json\": Path(\"config/prod.json\"), # From different location\n \"docs/spec.md\": Path(\"specs/feature.md\"), # Into subdirectory\n }\n }\n)\n```\n\nFiles are copied into the working directory before execution, and Claude can reference them directly:\n\n- `\"Read utils.py\"`\n- `\"Read config.json\"`\n- `\"Read docs/spec.md\"`\n\nEach execution gets its own numbered subdirectory with isolated file copies.\n\n### Binary Content (Standard Interface)\n\nUse Pydantic AI's standard `BinaryContent` interface to attach images, PDFs, and other binary files. This works identically across all model providers:\n\n```python\nfrom pathlib import Path\nfrom pydantic_ai import Agent, BinaryContent\n\nagent = Agent('claude-code:sonnet')\n\n# Send an image from file\nimage_data = Path('photo.jpg').read_bytes()\nresult = agent.run_sync(\n [\n 'What is in this image?',\n BinaryContent(data=image_data, media_type='image/jpeg'),\n ]\n)\n\n# Send a PDF document\npdf_data = Path('document.pdf').read_bytes()\nresult = agent.run_sync(\n [\n 'Summarize this document:',\n BinaryContent(data=pdf_data, media_type='application/pdf'),\n ]\n)\n\n# Compare multiple images\nresult = agent.run_sync(\n [\n 'Compare these images:',\n BinaryContent(data=image1_data, media_type='image/png'),\n 'and',\n BinaryContent(data=image2_data, media_type='image/jpeg'),\n ]\n)\n```\n\n**How it works:**\n\n- Binary content is automatically written to files in the working directory\n- Files are referenced in the prompt (e.g., `[Image: filename.png]`)\n- Claude Code CLI can then read the files directly\n- No Claude Code-specific code needed - this is standard Pydantic AI!\n\n**Supported formats:**\n- Images: PNG, JPEG, GIF, WebP, etc.\n- Documents: PDF, TXT, JSON, etc.\n- Audio: MP3, WAV, etc.\n- Video: MP4, WebM, etc.\n\n**Benefits:**\n- **Portable code** - works with OpenAI, Anthropic, Google, and Claude Code\n- **Standard interface** - same `BinaryContent` class for all providers\n- **No provider lock-in** - switch between cloud and local with one line change\n\n### Error Handling\n\n#### OAuth Token Expiration\n\nFor long-running processes (>7 hours), OAuth tokens may expire. Handle gracefully with `ClaudeOAuthError`:\n\n```python\nfrom pydantic_ai import Agent\nfrom pydantic_ai_claude_code import ClaudeOAuthError\n\nagent = Agent('claude-code:sonnet')\n\ntry:\n result = agent.run_sync(\"Process large dataset\")\nexcept ClaudeOAuthError as e:\n print(f\"Authentication expired: {e}\")\n print(f\"Please run: {e.reauth_instruction}\") # \"Please run /login\"\n # Prompt user to re-authenticate, then retry\nexcept RuntimeError as e:\n # Handle other CLI errors\n print(f\"CLI error: {e}\")\n```\n\n**For batch processing with automatic retry:**\n\n```python\nfrom pydantic_ai_claude_code import ClaudeOAuthError\nimport time\n\ndef process_batch_with_retry(items, max_auth_retries=3):\n \"\"\"Process items with OAuth re-authentication support.\"\"\"\n results = []\n\n for item in items:\n auth_retries = 0\n while auth_retries < max_auth_retries:\n try:\n result = agent.run_sync(f\"Process: {item}\")\n results.append(result.output)\n break # Success\n\n except ClaudeOAuthError as e:\n auth_retries += 1\n print(f\"\\n{'='*60}\")\n print(f\"OAuth token expired: {e.reauth_instruction}\")\n print(f\"{'='*60}\\n\")\n\n if auth_retries >= max_auth_retries:\n raise # Give up after max retries\n\n input(\"Press Enter after running /login to continue...\")\n time.sleep(2) # Brief pause before retry\n\n return results\n```\n\n### Logging\n\nThe package uses Python's standard logging module. To enable debug logging in your application:\n\n```python\nimport logging\n\n# Enable debug logging for pydantic-ai-claude-code\nlogging.basicConfig(level=logging.DEBUG)\nlogging.getLogger('pydantic_ai_claude_code').setLevel(logging.DEBUG)\n\n# Or just for specific components\nlogging.getLogger('pydantic_ai_claude_code.model').setLevel(logging.DEBUG)\nlogging.getLogger('pydantic_ai_claude_code.utils').setLevel(logging.INFO)\n```\n\nThis will log:\n\n- Model initialization and configuration\n- CLI command execution and responses\n- Message formatting and conversion\n- Tool call parsing and execution\n- Structured output handling\n- Streaming events and completion\n\n## Available Models\n\n- `claude-code:sonnet` - Claude 3.5 Sonnet (default, recommended)\n- `claude-code:opus` - Claude 3 Opus (most capable)\n- `claude-code:haiku` - Claude 3.5 Haiku (fastest)\n\nOr use full model names like `claude-code:claude-sonnet-4-5-20250929`\n\n## Integration with Existing Projects\n\nReplace your current LLM calls with Claude Code:\n\n**Before:**\n\n```python\nagent = Agent('openai:gpt-4o')\n# or\nagent = Agent('anthropic:claude-3-5-sonnet-latest')\n```\n\n**After:**\n\n```python\nimport pydantic_ai_claude_code # Add this import\n\nagent = Agent('claude-code:sonnet') # Change this line\n```\n\nEverything else stays the same! All your tools, structured outputs, dependencies, and streaming code works identically.\n\n## Key Differences from Cloud Providers\n\n| Aspect | Cloud Providers | Claude Code Local |\n|--------|----------------|-------------------|\n| **Execution** | Remote API calls | Local on your machine |\n| **Cost** | Per-token pricing | Uses Claude desktop subscription |\n| **Data Privacy** | Data sent to cloud | Data stays local |\n| **Speed** | Network latency | Local execution |\n| **API Key** | Required | Not needed (uses local auth) |\n\n## Examples\n\nSee the `examples/` directory for more demonstrations:\n\n- `basic_example.py` - Simple queries and usage tracking\n- `structured_example.py` - Structured output with Pydantic models\n- `async_example.py` - Async/await usage patterns\n- `advanced_example.py` - Custom provider configurations\n- `tools_and_streaming.py` - Custom tools and streaming responses\n- `binary_content_example.py` - Standard interface for images, PDFs, and binary files\n- `additional_files_example.py` - Providing local files for analysis (Claude Code-specific)\n\n## License\n\nMIT License\n",
"bugtrack_url": null,
"license": null,
"summary": "Use local Claude Code CLI as a Pydantic AI model provider with full support for structured responses, tools, and streaming",
"version": "0.7.4",
"project_urls": {
"Documentation": "https://github.com/wehnsdaefflae/pydantic_ai_claude_code#readme",
"Homepage": "https://github.com/wehnsdaefflae/pydantic_ai_claude_code",
"Issues": "https://github.com/wehnsdaefflae/pydantic_ai_claude_code/issues",
"Repository": "https://github.com/wehnsdaefflae/pydantic_ai_claude_code"
},
"split_keywords": [
"pydantic",
" pydantic-ai",
" claude",
" claude-code",
" ai",
" llm",
" agent"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "4cd2d1f72f1fc6dee76ad44be8d9e072de242b9d8179649e505cfee527254d4b",
"md5": "1dd5843a06e2a19594acb647d2e4950f",
"sha256": "6cce76161ec309280cc5b2380f9720b74c002a3cea56f74c713a16e05db57a5a"
},
"downloads": -1,
"filename": "pydantic_ai_claude_code-0.7.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1dd5843a06e2a19594acb647d2e4950f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 53763,
"upload_time": "2025-10-22T09:21:33",
"upload_time_iso_8601": "2025-10-22T09:21:33.929445Z",
"url": "https://files.pythonhosted.org/packages/4c/d2/d1f72f1fc6dee76ad44be8d9e072de242b9d8179649e505cfee527254d4b/pydantic_ai_claude_code-0.7.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "022b624a250d42117e34da4d3d104ebf9e230a0f17be9449b4b9c5677488663d",
"md5": "24c8312ce6f7c203ed21c3abe3f182b0",
"sha256": "303d9ebf2eb461fd9ec8b3038b8efc05a6942c23055e84445d4d7e2fd14b5d42"
},
"downloads": -1,
"filename": "pydantic_ai_claude_code-0.7.4.tar.gz",
"has_sig": false,
"md5_digest": "24c8312ce6f7c203ed21c3abe3f182b0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 46755,
"upload_time": "2025-10-22T09:21:35",
"upload_time_iso_8601": "2025-10-22T09:21:35.370286Z",
"url": "https://files.pythonhosted.org/packages/02/2b/624a250d42117e34da4d3d104ebf9e230a0f17be9449b4b9c5677488663d/pydantic_ai_claude_code-0.7.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-22 09:21:35",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "wehnsdaefflae",
"github_project": "pydantic_ai_claude_code#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pydantic-ai-claude-code"
}