Skip to main content

dspy-coding-agent-lms

DSPy Language Model integration for Claude Code CLI - route DSPy requests through your local Claude Code agent instead of using Anthropic APIs directly.

Overview

dspy-coding-agent-lms provides a DSPy-compatible language model that invokes the Claude Code CLI for each request, enabling:

  • Agentic capabilities: Access Claude Code's file editing, bash execution, and tool use within DSPy programs
  • Structured output: Use Claude Code's built-in JSON schema validation instead of string parsing
  • Permission control: Fine-grained control over which tools Claude can use
  • Cost tracking: Automatic transcript capture with usage and cost metrics
  • Local execution: Run Claude Code locally with your existing authentication

Installation

Prerequisites

  1. Claude Code CLI - Install and authenticate:

    npm install -g @anthropic-ai/claude-code
    claude login  # or use ANTHROPIC_API_KEY
    
  2. Python 3.11+

Install the package

# Using uv (recommended)
uv add dspy-coding-agent-lms

# Using pip
pip install dspy-coding-agent-lms

Quick Start

import dspy
from dspy_coding_agent_lms import ClaudeCodeLM

# Initialize the LM
lm = ClaudeCodeLM(model="sonnet", permission_mode="plan")

# Configure DSPy with JSONAdapter (RECOMMENDED)
# JSONAdapter uses Claude Code's native --json-schema for reliable structured output
dspy.configure(lm=lm, adapter=dspy.JSONAdapter())

# Use DSPy as normal
predict = dspy.Predict("question -> answer")
result = predict(question="What is 2+2?")
print(result.answer)

# Check cost and usage
print(f"Cost: ${lm.transcript.total_cost_usd():.4f}")

Recommended: Always use dspy.JSONAdapter() with ClaudeCodeLM. This enables native JSON schema output via Claude Code's --json-schema flag, providing more reliable structured output than string parsing.

Features

DSPy Adapters: JSONAdapter vs ChatAdapter

When using DSPy Signatures, you can choose between two adapters:

JSONAdapter (Recommended)

Uses Claude Code's native --json-schema flag for structured output. The response is pure JSON in the structured_output field.

import dspy
from dspy_coding_agent_lms import ClaudeCodeLM

lm = ClaudeCodeLM(model="sonnet")
dspy.configure(lm=lm, adapter=dspy.JSONAdapter())  # Recommended

class SentimentSignature(dspy.Signature):
    """Analyze sentiment of text."""
    text: str = dspy.InputField()
    sentiment: str = dspy.OutputField()
    confidence: float = dspy.OutputField()

result = dspy.Predict(SentimentSignature)(text="I love this!")
# Output format: {"sentiment": "positive", "confidence": 0.95}

ChatAdapter (Default)

Uses string parsing with [[ ## field ## ]] markers. This is DSPy's default but less reliable.

dspy.configure(lm=lm, adapter=dspy.ChatAdapter())  # Default, not recommended

# Output format:
# [[ ## sentiment ## ]]
# positive
# [[ ## confidence ## ]]
# 0.95

Why JSONAdapter is recommended:

  • More reliable parsing (no regex-based extraction)
  • Native JSON schema validation by Claude Code
  • Cleaner API responses with structured_output field
  • Better type safety for complex nested structures

Direct JSON Schema

Use Claude Code's built-in JSON schema validation for reliable structured outputs:

from dspy_coding_agent_lms import ClaudeCodeLM
import json

lm = ClaudeCodeLM(model="sonnet")

schema = {
    "type": "object",
    "properties": {
        "answer": {"type": "integer"},
        "explanation": {"type": "string"}
    },
    "required": ["answer"]
}

result = lm.forward(
    prompt="What is 15% of 200?",
    json_schema=schema
)

data = json.loads(result[0]["text"])
print(f"Answer: {data['answer']}")  # Answer: 30

Tool Restrictions

Control which tools Claude can access:

# Read-only code analysis
lm = ClaudeCodeLM(
    model="sonnet",
    permission_mode="plan",
    allowed_tools=["Read", "Glob", "Grep"],
    disallowed_tools=["Bash", "Edit", "Write"],
)

# Git operations only
lm = ClaudeCodeLM(
    allowed_tools=[
        "Bash(git status)",
        "Bash(git log*)",
        "Bash(git diff*)",
    ],
)

Async Support

Execute concurrent requests for better throughput:

import asyncio
from dspy_coding_agent_lms import ClaudeCodeLM

async def main():
    lm = ClaudeCodeLM(model="sonnet")

    prompts = ["Question 1", "Question 2", "Question 3"]
    tasks = [lm.aforward(prompt=p) for p in prompts]
    results = await asyncio.gather(*tasks)

asyncio.run(main())

Transcript and Cost Tracking

Access complete interaction history with usage metrics:

lm = ClaudeCodeLM(model="sonnet")

# Make some requests...
lm.forward(prompt="Hello")
lm.forward(prompt="World")

# Access transcript
for entry in lm.transcript:
    print(f"Prompt: {entry.prompt}")
    print(f"Duration: {entry.duration_ms}ms")
    print(f"Cost: ${entry.cost_usd:.4f}")

# Aggregated stats
print(f"Total cost: ${lm.transcript.total_cost_usd():.4f}")
print(f"Total tokens: {lm.transcript.total_tokens()}")

# Export transcript
lm.transcript.save_to_file("transcript.json")

Configuration Options

Constructor Parameters

Parameter Type Default Description
model str "sonnet" Model to use (sonnet, opus, haiku)
permission_mode str "plan" Permission mode (plan, dontAsk, acceptEdits, etc.)
system_prompt str None Override default system prompt
append_system_prompt str None Append to default system prompt
dangerously_skip_permissions bool False Bypass all permission checks
allowed_tools list None Tools that can execute without prompts
disallowed_tools list None Tools that are blocked
working_directory str None Working directory for execution
timeout_seconds float 300 Command timeout
max_budget_usd float None Maximum budget limit
cache bool True Enable response caching
capture_transcript bool True Capture interaction transcript
anthropic_api_key str None Explicit API key
oauth_token str None OAuth token for Pro/Max subscribers

Permission Modes

Mode Description
plan Allow planning but require approval for execution
dontAsk Don't ask for permissions (use with caution)
acceptEdits Auto-accept file edits
default Default Claude Code behavior
delegate Delegate permission decisions
bypassPermissions Bypass all permissions (dangerous)

Authentication

The library supports two authentication methods:

  1. API Key: Set ANTHROPIC_API_KEY environment variable or pass anthropic_api_key parameter
  2. OAuth Token: Set CLAUDE_CODE_OAUTH_TOKEN or pass oauth_token parameter (for Pro/Max subscribers)
# Using environment variables (recommended)
lm = ClaudeCodeLM(model="sonnet")  # Uses ANTHROPIC_API_KEY

# Explicit credentials
lm = ClaudeCodeLM(
    model="sonnet",
    anthropic_api_key="sk-...",
)

Examples

See the examples/ directory for complete examples:

  • basic_usage.py - Simple DSPy integration
  • structured_output.py - JSON schema structured outputs
  • with_tools.py - Tool restriction configurations
  • async_usage.py - Concurrent async requests

Testing

# Run unit tests
uv run pytest tests/unit/ -v

# Run with coverage
uv run pytest tests/unit/ -v --cov=dspy_coding_agent_lms

# Run integration tests (requires Claude Code CLI)
RUN_INTEGRATION_TESTS=1 uv run pytest tests/integration/ -v

API Reference

ClaudeCodeLM

Main class for DSPy integration.

class ClaudeCodeLM:
    def forward(
        self,
        prompt: str | None = None,
        messages: list[dict] | None = None,
        json_schema: dict | None = None,
        **kwargs
    ) -> list[dict]:
        """Execute synchronous request."""

    async def aforward(
        self,
        prompt: str | None = None,
        messages: list[dict] | None = None,
        json_schema: dict | None = None,
        **kwargs
    ) -> list[dict]:
        """Execute asynchronous request."""

    @property
    def transcript(self) -> Transcript:
        """Access interaction transcript."""

    def copy(self, **kwargs) -> ClaudeCodeLM:
        """Create copy with modified parameters."""

Transcript

Interaction history with aggregation methods.

class Transcript:
    def total_cost_usd(self) -> float: ...
    def total_tokens(self) -> dict[str, int]: ...
    def total_duration_ms(self) -> int: ...
    def to_json(self) -> str: ...
    def save_to_file(self, path: str) -> None: ...

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

Acknowledgments

  • DSPy - The declarative programming framework for LMs
  • Claude Code - Anthropic's agentic coding assistant

Metadata

Release files for dspy-coding-agent-lms 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dspy-coding-agent-lms 0.1.3
File Size Uploaded
dspy_coding_agent_lms-0.1.3.tar.gz 220.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dspy-coding-agent-lms 0.1.3
File Interpreter ABI Platform
dspy_coding_agent_lms-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 247.0 kB

Release files / dspy_coding_agent_lms-0.1.3.tar.gz

Download URL dspy_coding_agent_lms-0.1.3.tar.gz
Size 220.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e2713204251092b929fc49f02b1b79e40e068b3f164ff1e99d15624b1fa8e617
BLAKE2b-256 checksum
How to use checksums
b3b76661997db88194bcfba26757ece8d83533cdb57f5e14480a1a8ad30f6970
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / dspy_coding_agent_lms-0.1.3-py3-none-any.whl

Download URL dspy_coding_agent_lms-0.1.3-py3-none-any.whl
Size 26.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6dab30522461f950113a576e77153fe0a5e6f408b3168304045f1361a991acb8
BLAKE2b-256 checksum
How to use checksums
298b659090b0475a958a114e29df8e110694427c9e299c821fb64209010e6a82
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page