Skip to main content

Pydantic AI model adapter for Claude Code CLI

Project description

claude-code-model

Use Pydantic AI with your Claude Max subscription instead of paying per token.

A drop-in replacement that routes Pydantic AI through Claude Code CLI. Same code, same features, zero API costs.

The Problem

You want to use Pydantic AI for type-safe AI agents, but the API costs add up:

# This costs $3-15 per 1M tokens
from pydantic_ai.models.anthropic import AnthropicModel
model = AnthropicModel("claude-sonnet-4-20250514")

Meanwhile, your Claude Max subscription ($100-200/month) gives you unlimited Claude access via CLI, but no programmatic interface for building agents.

The Solution

claude-code-model bridges this gap. Change 2 lines and your Pydantic AI code runs on your Max subscription:

- from pydantic_ai.models.anthropic import AnthropicModel
- model = AnthropicModel("claude-sonnet-4-20250514")
+ from claude_code_model import ClaudeCodeModel
+ model = ClaudeCodeModel(model="sonnet")

That's it. Your agents, tools, schemas, and prompts stay exactly the same.

Quick Comparison

Feature Pydantic AI + API claude-code-model
Cost $3-15/M tokens $0 (Max subscription)
Structured output Yes Yes
Tool calling Yes Yes
Vision / images Yes Yes (binary only)
Type safety Yes Yes
Streaming Yes No
Concurrent requests Yes No
Migration effort 2 lines

Installation

# 1. Install Claude Code CLI (requires Node.js)
npm install -g @anthropic-ai/claude-code

# 2. Login to Claude
claude auth

# 3. Install this package
pip install claude-code-model

Usage

Basic Example

from pydantic import BaseModel
from pydantic_ai import Agent
from claude_code_model import ClaudeCodeModel

class MovieReview(BaseModel):
    title: str
    rating: int  # 1-10
    summary: str

agent = Agent(
    ClaudeCodeModel(),
    output_type=MovieReview,
    system_prompt="You are a film critic. Review movies concisely."
)

result = agent.run_sync("Review: The Matrix (1999)")
print(f"{result.output.title}: {result.output.rating}/10")
# The Matrix: 9/10

With Tools

from pydantic_ai import Agent
from claude_code_model import ClaudeCodeModel

agent = Agent(ClaudeCodeModel())

@agent.tool_plain
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    # Your implementation here
    return f"Weather in {city}: 72°F, sunny"

result = agent.run_sync("What's the weather in Tokyo?")
# Agent calls get_weather("Tokyo") and uses the result

Vision / Image Input

from pydantic_ai import Agent
from pydantic_ai.messages import BinaryContent
from claude_code_model import ClaudeCodeModel

agent = Agent(
    ClaudeCodeModel(),
    system_prompt="Describe images concisely.",
)

# Load an image as binary content
image = BinaryContent.from_path("photo.jpg")

result = agent.run_sync(["What's in this image?", image])
print(result.output)

Images are sent to the CLI via stdin JSON format using --input-format stream-json. Multiple images can be passed in a single message.

Multi-Agent Systems

from pydantic_ai import Agent
from claude_code_model import ClaudeCodeModel

# Fast agent for simple tasks
fast_agent = Agent(
    ClaudeCodeModel(model="haiku"),
    system_prompt="Quick factual responses only."
)

# Powerful agent for complex reasoning
smart_agent = Agent(
    ClaudeCodeModel(model="sonnet"),
    system_prompt="Deep analysis and reasoning."
)

# Agents can delegate to each other via tools
@smart_agent.tool_plain
async def quick_lookup(query: str) -> str:
    """Delegate simple lookups to the fast agent."""
    result = await fast_agent.run(query)
    return result.output

See examples/multi_agent_comparison.py for a complete multi-agent pipeline.

Migration from Pydantic AI

Step 1: Install

pip install claude-code-model

Step 2: Replace Import

# Before
from pydantic_ai.models.anthropic import AnthropicModel

# After
from claude_code_model import ClaudeCodeModel

Step 3: Replace Model

# Before
model = AnthropicModel("claude-sonnet-4-20250514")

# After
model = ClaudeCodeModel(model="sonnet")  # or "opus", "haiku"

That's It

All your existing code works unchanged:

# This code is IDENTICAL for both API and CLI
agent = Agent(
    model,  # <-- Only this changes
    output_type=MySchema,
    system_prompt="Your prompt here",
    retries=3,
)

@agent.tool_plain
def my_tool(arg: str) -> str:
    return do_something(arg)

result = agent.run_sync("Your query")
print(result.output)  # Typed result

Configuration

from claude_code_model import ClaudeCodeModel

model = ClaudeCodeModel(
    model="sonnet",    # "sonnet" (default), "opus", or "haiku"
    timeout=30,        # Seconds per CLI request (default: 30)
    verbose=False,     # Enable debug logging
)

Model Selection

Model Speed Capability Use Case
haiku Fast Good Simple tasks, quick lookups
sonnet Medium Great Most tasks (default)
opus Slow Best Complex reasoning, analysis

Debug Mode

model = ClaudeCodeModel(verbose=True)

Shows request/response timing, prompts sent, tool calls detected, and JSON extraction status.

How It Works

Your Code                      claude-code-model              Claude CLI
─────────────────────────────────────────────────────────────────────────
agent.run_sync("prompt")  →   Build prompt string      →   claude -p "..."
                              Add tool definitions          --model sonnet
                              Add JSON schema

result.output             ←   Parse response           ←   {"rating": 9, ...}
                              Extract JSON
                              Validate with Pydantic

The adapter:

  1. Converts Pydantic AI messages → single prompt string
  2. Adds tool definitions with format instructions
  3. Adds JSON schema requirements for structured output
  4. Calls claude -p "prompt" --model sonnet via subprocess
  5. Parses response for tool calls or JSON output
  6. Returns typed ModelResponse to Pydantic AI

Examples

Run the examples to see it in action:

# Basic structured output
uv run python examples/simple.py

# Interactive tool usage
uv run python examples/with_tools.py

# Multi-agent pipeline with comparison
uv run python examples/multi_agent_comparison.py

Limitations

No streaming — CLI doesn't support streaming well. Each request returns complete.

No concurrent requests — Run one request at a time. CLI sessions don't parallelize.

No token counting — CLI doesn't report token usage. result.usage returns zeros.

Image URLs not supported — Only binary image data (BinaryContent) is supported. ImageUrl references are not sent to the CLI.

Slower than API — Each CLI invocation takes 2-10 seconds overhead. Multi-step tasks with tool calls take longer.

Tool calling is prompt-based — Unlike native API tools, this adapter instructs Claude to output TOOL_CALL: name({args}) format. Works well but occasionally needs retries. Use retries=3-5 on your agents.

Exceptions

from claude_code_model import (
    ClaudeCodeError,           # Base exception
    ClaudeCodeNotFoundError,   # CLI not installed
    ClaudeCodeTimeoutError,    # Request timed out
    ClaudeCodeExecutionError,  # CLI returned error
)

Development

git clone https://github.com/yourusername/claude-code-model.git
cd claude-code-model
uv sync

# Run tests
uv run pytest

# Type check
uv run mypy src/

# Lint
uv run ruff check src/ tests/

Requirements

  • Python 3.11+
  • Claude Code CLI installed and authenticated
  • Claude Max subscription (for unlimited CLI access)
  • pydantic-ai >= 0.1.0
  • pydantic >= 2.0.0

License

MIT — see LICENSE.

Links


Disclaimer: Unofficial adapter, not affiliated with Anthropic. Use in accordance with Claude's Terms of Service.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_code_model-0.4.0.tar.gz (228.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_code_model-0.4.0-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file claude_code_model-0.4.0.tar.gz.

File metadata

  • Download URL: claude_code_model-0.4.0.tar.gz
  • Upload date:
  • Size: 228.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for claude_code_model-0.4.0.tar.gz
Algorithm Hash digest
SHA256 fc93b0aecba7963c834be3d9db4f93ac961ab542c6d4c4c06404ed0fd1e68ec3
MD5 3ac14f41cc89fa80f7880fe651e1b701
BLAKE2b-256 a3ab7ecbd2a0ace941c5eb5ec5dd837462724a47e3053e1b113f7c538155af3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_code_model-0.4.0.tar.gz:

Publisher: publish.yml on aleksandr-bogdanov/claude-code-model

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_code_model-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_code_model-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c69f2cbd818a6a318e9588322bbcb42886b1193bd29d6ff4cf3707295a3de4a5
MD5 b114a4603a269252aee272633c7594b4
BLAKE2b-256 76bea989f49702a4905c9e8c23e58a56b017005c8f8d240f4a86119334a37ec4

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_code_model-0.4.0-py3-none-any.whl:

Publisher: publish.yml on aleksandr-bogdanov/claude-code-model

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page