# ClaudeCode - DSPy Module for Claude Code SDK A DSPy module that wraps the Claude Code Python SDK with a signature-driven interface. Each agent instance maintains a stateful conversation session, making it perfect for multi-turn agentic workflows. Reference to the original Claude Agent Python SDK: [Claude Agent Python SDK](https://platform.claude.com/docs/en/agent-sdk/python) ## Features - **Signature-driven** - Use DSPy signatures for type safety and clarity - **Stateful sessions** - Each agent instance = one conversation session - **Smart schema handling** - Automatically handles str vs Pydantic outputs - **Rich outputs** - Get typed results + execution trace + token usage - **Multi-turn conversations** - Context preserved across calls - **Enhanced prompts** - Automatically includes signature docstrings + InputField/OutputField descriptions for better context - **Async support** - Both sync and async execution modes - **Modaic Hub Integration** - Push and pull agents from Modaic Hub ## Installation ```bash # install with uv uv add claude-agent-sdk dspy modaic nest-asyncio ``` **Prerequisites:** - Python 3.10+ - Anthropic API key set in `ANTHROPIC_API_KEY` environment variable **Note:** The Claude Code CLI is automatically bundled with the `claude-agent-sdk` package - no separate installation required! The SDK uses the bundled CLI by default. If you prefer to use a system-wide installation or a specific version: - Install separately: `curl -fsSL https://claude.ai/install.sh | bash` - Specify custom path: `ClaudeAgentOptions(cli_path="/path/to/claude")` ## Quick Start with Modaic Hub (Recommended) The fastest way to use ClaudeCode is to pull a pre-configured agent from Modaic Hub. ### 1. Set up environment ```bash # copy the example file cp .env.example .env # edit .env with your keys ANTHROPIC_API_KEY="" MODAIC_TOKEN="" # Optional, for pushing to hub ``` ### 2. Load from Modaic Hub ```python from modaic import AutoProgram from pydantic import BaseModel import dspy class FileList(BaseModel): files: list[str] class FileSignature(dspy.Signature): message: str = dspy.InputField(desc="Request to process") output: FileList = dspy.OutputField(desc="List of files") # Load pre-compiled agent from hub # Note: Only model is set via config, other params are kwargs agent = AutoProgram.from_precompiled( "farouk1/claude-code", signature=FileSignature, working_directory=".", ) # Use it! result = agent(message="List Python files here") print(result.output.files) # Typed access print(result.usage) # Token usage ``` ### 3. Override Config Options ```python from modaic import AutoProgram import dspy class MySignature(dspy.Signature): message: str = dspy.InputField(desc="Request to process") answer: str = dspy.OutputField(desc="Response") # Load with custom configuration # Model comes from config, other params are kwargs agent = AutoProgram.from_precompiled( "farouk1/claude-code", config={"model": "claude-opus-4-5-20251101"}, signature=MySignature, working_directory=".", permission_mode="acceptEdits", allowed_tools=["Read", "Write", "Bash"], ) ``` ## Local Development For local development and hacking on this project: ### Basic String Output ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig # create config config = ClaudeCodeConfig() # create agent agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory="." ) # use it result = agent(message="What files are in this directory?") print(result.answer) # String response print(result.trace) # Execution items print(result.usage) # Token counts ``` ### Structured Output with Pydantic ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig from pydantic import BaseModel, Field class BugReport(BaseModel): severity: str = Field(description="critical, high, medium, or low") description: str affected_files: list[str] # Create config with Pydantic output config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> report:BugReport", working_directory="." ) result = agent(message="Analyze the bug in error.log") print(result.report.severity) # Typed access! print(result.report.affected_files) ``` ### Push to Modaic Hub ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig # Create your agent config = ClaudeCodeConfig(model="claude-opus-4-5-20251101") agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", permission_mode="acceptEdits", ) # Test it locally result = agent(message="Test my agent") print(result.answer) # Push to Modaic Hub agent.push_to_hub("{USERNAME}/your-agent-name") ``` ## API Reference ### ClaudeCodeConfig Configuration object for ClaudeCode agents. ```python class ClaudeCodeConfig: def __init__( self, model: str = "claude-opus-4-5-20251101", # Default model ) ``` **Parameters:** - **`model`** - Claude model to use (default: `"claude-opus-4-5-20251101"`) **Important:** `ClaudeCodeConfig` only contains the model. All other parameters (`signature`, `working_directory`, `permission_mode`, `allowed_tools`, etc.) are passed as keyword arguments to `ClaudeCode.__init__()` or `AutoProgram.from_precompiled()`, not through the config object. ### ClaudeCode Main agent class. ```python class ClaudeCode(PrecompiledProgram): def __init__( self, config: ClaudeCodeConfig, **kwargs, ) ``` **Parameters:** **Core Configuration:** - **`config`** - ClaudeCodeConfig instance with model configuration - **`signature`** (required) - DSPy signature defining input/output fields (must have exactly 1 input and 1 output) - **`working_directory`** - Directory where Claude will execute commands (default: `"."`) - **`permission_mode`** - Permission mode: `"default"`, `"acceptEdits"`, `"plan"`, `"bypassPermissions"` - **`allowed_tools`** - List of allowed tool names (e.g., `["Read", "Write", "Bash", "Glob", "Grep"]`). See [Available Tools](#available-tools) section for complete list. - **`disallowed_tools`** - List of disallowed tool names - **`sandbox`** - Sandbox configuration dict. See [SDK docs](https://platform.claude.com/docs/en/agent-sdk/python#sandboxsettings) for details. - **`system_prompt`** - Custom system prompt or preset config - **`api_key`** - Anthropic API key (falls back to `ANTHROPIC_API_KEY` env var) **MCP Servers:** - **`mcp_servers`** - MCP server configurations for custom tools. See [MCP section](#using-mcp-servers) below. **Session Management:** - **`continue_conversation`** - Continue the most recent conversation (default: `False`) - **`resume`** - Session ID to resume from a previous session - **`max_turns`** - Maximum number of conversation turns - **`fork_session`** - Fork to a new session when resuming (default: `False`) **Advanced Options:** - **`permission_prompt_tool_name`** - MCP tool name for permission prompts - **`settings`** - Path to custom settings file - **`add_dirs`** - Additional directories Claude can access - **`env`** - Environment variables to pass to Claude Code - **`extra_args`** - Additional CLI arguments - **`max_buffer_size`** - Maximum bytes when buffering CLI stdout - **`cli_path`** - Custom path to Claude Code CLI executable **Callbacks and Hooks:** - **`stderr`** - Callback function for stderr output: `Callable[[str], None]` - **`can_use_tool`** - Permission callback for tool usage control - **`hooks`** - Hook configurations for intercepting events. See [SDK docs](https://platform.claude.com/docs/en/agent-sdk/python#hook-types) for details. **User and Settings:** - **`user`** - User identifier - **`include_partial_messages`** - Include partial message streaming events (default: `False`) - **`setting_sources`** - Which settings to load: `["user", "project", "local"]` **Subagents and Plugins:** - **`agents`** - Programmatically defined subagents - **`plugins`** - Custom plugins to load #### Methods ##### `__call__(**kwargs) -> Prediction` (or `forward`) Execute the agent with an input message. **Arguments:** - `**kwargs` - Must contain the input field specified in signature **Returns:** - `Prediction` object with: - **Typed output field** - Named according to signature (e.g., `result.answer`) - **`trace`** - `list[TraceItem]` - Execution trace - **`usage`** - `Usage` - Token usage statistics **Example:** ```python config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory="." ) result = agent(message="Hello") print(result.answer) # Access typed output print(result.trace) # List of execution items print(result.usage) # Token usage stats ``` ##### `push_to_hub(repo_id: str) -> None` Push the agent to Modaic Hub. **Arguments:** - `repo_id` - Repository ID in format "username/repo-name" **Example:** ```python agent.push_to_hub("your-username/your-agent") ``` ##### `aforward(**kwargs) -> Prediction` Async version of `__call__()` for use in async contexts. **Example:** ```python async def main(): config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory="." ) result = await agent.aforward(message="Hello") print(result.answer) ``` #### Properties ##### `session_id: Optional[str]` Get the session ID for this agent instance. - Returns `None` until first call - Persists across multiple calls - Useful for debugging and logging **Example:** ```python config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory="." ) print(agent.session_id) # None result = agent(message="Hello") print(agent.session_id) # 'eb1b2f39-e04c-4506-9398-b50053b1fd83' ``` ##### `config: ClaudeCodeConfig` Access to the agent's configuration (model only). ```python print(agent.config.model) # 'claude-opus-4-5-20251101' ``` **Note:** Only the `model` is stored in config. Other parameters like `working_directory`, `permission_mode`, and `allowed_tools` are instance attributes, not config properties. ## Key Implementation Details ### Config vs Kwargs The `ClaudeCodeConfig` **only** contains the `model` parameter. All other configuration options are passed as keyword arguments: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig # Config contains ONLY the model config = ClaudeCodeConfig(model="claude-opus-4-5-20251101") # All other params are kwargs agent = ClaudeCode( config, # Config with model signature="message:str -> answer:str", # Kwarg working_directory=".", # Kwarg permission_mode="acceptEdits", # Kwarg allowed_tools=["Read", "Write", "Bash"], # Kwarg ) ``` ### AutoProgram.from_precompiled When loading from Modaic Hub, the same pattern applies: ```python from modaic import AutoProgram from claude_dspy import ClaudeCodeConfig import dspy class MySignature(dspy.Signature): message: str = dspy.InputField(desc="Request") answer: str = dspy.OutputField(desc="Response") # Option 1: Use default model (no config needed) agent = AutoProgram.from_precompiled( "farouk1/claude-code", signature=MySignature, # Kwarg working_directory=".", # Kwarg permission_mode="acceptEdits", # Kwarg allowed_tools=["Read", "Write", "Bash"], # Kwarg ) # Option 2: Override model via config config = ClaudeCodeConfig(model="claude-opus-4-5-20251101") agent = AutoProgram.from_precompiled( "farouk1/claude-code", config=config, # Config with model signature=MySignature, # Kwarg working_directory=".", # Kwarg permission_mode="acceptEdits", # Kwarg allowed_tools=["Read", "Write", "Bash"], # Kwarg ) ``` ### Available Tools The `allowed_tools` parameter accepts any valid Claude Code tool name: **File Operations:** - `"Read"` - Read files and directories - `"Write"` - Write and create files - `"Edit"` - Edit existing files **Command Execution:** - `"Bash"` - Execute bash commands **Code Search:** - `"Glob"` - Search for files by pattern - `"Grep"` - Search file contents **Web Tools:** - `"WebSearch"` - Search the web - `"WebFetch"` - Fetch web content **Other Tools:** - `"NotebookEdit"` - Edit Jupyter notebooks - And other Claude Code tools... ## Advanced Features ### Using MCP Servers MCP (Model Context Protocol) servers allow you to add custom tools to Claude. The SDK supports creating in-process MCP servers with custom tools. ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig from claude_agent_sdk import tool, create_sdk_mcp_server from typing import Any import dspy # Define custom tools with @tool decorator @tool("calculate", "Perform mathematical calculations", {"expression": str}) async def calculate(args: dict[str, Any]) -> dict[str, Any]: try: result = eval(args["expression"], {"__builtins__": {}}) return { "content": [{"type": "text", "text": f"Result: {result}"}] } except Exception as e: return { "content": [{"type": "text", "text": f"Error: {str(e)}"}], "is_error": True } # Create MCP server calculator_server = create_sdk_mcp_server( name="calculator", version="1.0.0", tools=[calculate] ) # Use with ClaudeCode config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", mcp_servers={"calc": calculator_server}, allowed_tools=["mcp__calc__calculate"] # MCP tools are prefixed with "mcp____" ) result = agent(message="Calculate 123 * 456") print(result.answer) ``` ### Session Management Resume and continue conversations from previous sessions: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig config = ClaudeCodeConfig() # First conversation agent1 = ClaudeCode( config, signature="message:str -> answer:str", working_directory="." ) result1 = agent1(message="Create a file called notes.txt") session_id = agent1.session_id print(f"Session ID: {session_id}") # Resume the same conversation later agent2 = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", resume=session_id # Resume from session ID ) result2 = agent2(message="What file did we just create?") print(result2.answer) # Claude remembers the previous context! ``` ### Using Hooks Intercept and modify tool execution with hooks: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig from typing import Any async def pre_tool_logger(input_data: dict[str, Any], tool_use_id: str | None, context: Any) -> dict[str, Any]: """Log all tool usage before execution.""" tool_name = input_data.get('tool_name', 'unknown') print(f"About to use tool: {tool_name}") # Block dangerous commands if tool_name == "Bash" and "rm -rf /" in str(input_data.get('tool_input', {})): return { 'hookSpecificOutput': { 'hookEventName': 'PreToolUse', 'permissionDecision': 'deny', 'permissionDecisionReason': 'Dangerous command blocked' } } return {} config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", hooks={ 'PreToolUse': [ {'matcher': 'Bash', 'hooks': [pre_tool_logger]} ] }, allowed_tools=["Read", "Write", "Bash"] ) result = agent(message="List files in this directory") ``` ### Loading Project Settings Control which filesystem settings to load: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig config = ClaudeCodeConfig() # Load only project settings (e.g., CLAUDE.md files) agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", system_prompt={ "type": "preset", "preset": "claude_code" }, setting_sources=["project"], # Load .claude/settings.json and CLAUDE.md allowed_tools=["Read", "Write"] ) result = agent(message="Add a feature following project conventions") ``` ## Usage Patterns ### Pattern 1: Multi-turn Conversation Each agent instance maintains a stateful session: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", ) # Turn 1 result1 = agent(message="What's the main bug?") print(result1.answer) # Turn 2 - has context from Turn 1 result2 = agent(message="How do we fix it?") print(result2.answer) # Turn 3 - has context from Turn 1 + 2 result3 = agent(message="Write tests for the fix") print(result3.answer) # All use same session_id print(agent.session_id) ``` ### Pattern 2: Fresh Context Want a new conversation? Create a new agent: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig config = ClaudeCodeConfig() # Agent 1 - Task A agent1 = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", ) result1 = agent1(message="Analyze bug in module A") # Agent 2 - Task B (no context from Agent 1) agent2 = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", ) result2 = agent2(message="Analyze bug in module B") ``` ### Pattern 3: Field Descriptions for Enhanced Context Enhance prompts with signature docstrings and field descriptions - all automatically included in the prompt: ```python import dspy from claude_dspy import ClaudeCode, ClaudeCodeConfig class MySignature(dspy.Signature): """Analyze code architecture.""" # Used as task description message: str = dspy.InputField( desc="Request to process" # Provides input context ) analysis: str = dspy.OutputField( desc="A detailed markdown report with sections: " "1) Architecture overview, 2) Key components, 3) Dependencies" # Guides output format ) config = ClaudeCodeConfig() agent = ClaudeCode( config, signature=MySignature, working_directory=".", ) result = agent(message="Analyze this codebase") # The prompt sent to Claude will include: # 1. Task: "Analyze code architecture." (from docstring) # 2. Input context: "Request to process" (from InputField desc) # 3. Your message: "Analyze this codebase" # 4. Output guidance: "Please produce the following output: A detailed markdown report..." (from OutputField desc) ``` ### Pattern 4: Inspecting Execution Trace Access detailed execution information: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig, ToolUseItem, ToolResultItem config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", ) result = agent(message="Fix the bug") # Filter trace by type tool_uses = [item for item in result.trace if isinstance(item, ToolUseItem)] for tool in tool_uses: print(f"Tool: {tool.tool_name}") print(f"Input: {tool.tool_input}") tool_results = [item for item in result.trace if isinstance(item, ToolResultItem)] for result_item in tool_results: print(f"Result: {result_item.content}") print(f"Error: {result_item.is_error}") ``` ### Pattern 5: Token Usage Tracking Monitor API usage: ```python result = agent(message="...") print(f"Input tokens: {result.usage.input_tokens}") print(f"Cached tokens: {result.usage.cached_input_tokens}") print(f"Output tokens: {result.usage.output_tokens}") print(f"Total: {result.usage.total_tokens}") ``` ### Pattern 6: Safe Execution with Permissions Control what the agent can do: ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig # Read-only (safest) config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", permission_mode="default", allowed_tools=["Read"], # Only allow reading files ) # Auto-accept file edits config = ClaudeCodeConfig(model="claude-opus-4-5-20251101") agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", permission_mode="acceptEdits", allowed_tools=["Read", "Write"], # Allow reading and writing ) # Full permissions with command execution config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", permission_mode="acceptEdits", allowed_tools=["Read", "Write", "Bash"], # All tools enabled ) ``` ## Advanced Examples ### Example 1: Code Review Agent ```python from pydantic import BaseModel, Field from claude_dspy import ClaudeCode, ClaudeCodeConfig class CodeReview(BaseModel): summary: str = Field(description="High-level summary") issues: list[str] = Field(description="List of issues found") severity: str = Field(description="critical, high, medium, or low") recommendations: list[str] = Field(description="Actionable recommendations") config = ClaudeCodeConfig(model="claude-opus-4-5-20251101") agent = ClaudeCode( config, signature="message:str -> review:CodeReview", working_directory="/path/to/project", permission_mode="default", allowed_tools=["Read"], # Read-only for code review ) result = agent(message="Review the changes in src/main.py") print(f"Severity: {result.review.severity}") for issue in result.review.issues: print(f"- {issue}") ``` ### Example 2: Iterative Debugging ```python from claude_dspy import ClaudeCode, ClaudeCodeConfig config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> response:str", working_directory=".", permission_mode="acceptEdits", allowed_tools=["Read", "Write", "Bash"], ) # Turn 1: Find the bug result1 = agent(message="Find the bug in src/calculator.py") print(result1.response) # Turn 2: Propose a fix result2 = agent(message="What's the best way to fix it?") print(result2.response) # Turn 3: Implement the fix result3 = agent(message="Implement the fix") print(result3.response) # Turn 4: Write tests result4 = agent(message="Write tests for the fix") print(result4.response) ``` ### Example 3: Async Usage ```python import asyncio from claude_dspy import ClaudeCode, ClaudeCodeConfig async def main(): config = ClaudeCodeConfig() agent = ClaudeCode( config, signature="message:str -> answer:str", working_directory=".", ) # Use aforward in async context result = await agent.aforward(message="Analyze this code") print(result.answer) # Cleanup await agent.disconnect() asyncio.run(main()) ``` ## Trace Item Types When accessing `result.trace`, you'll see various item types: | Type | Fields | Description | |------|--------|-------------| | `AgentMessageItem` | `text`, `model` | Agent's text response | | `ThinkingItem` | `text`, `model` | Agent's internal reasoning | | `ToolUseItem` | `tool_name`, `tool_input`, `tool_use_id` | Tool invocation | | `ToolResultItem` | `tool_name`, `tool_use_id`, `content`, `is_error` | Tool result | | `ErrorItem` | `message`, `error_type` | Error that occurred | ## How It Works ### Signature � Claude Flow ``` 1. Define signature: 'message:str -> answer:str' 2. ClaudeCode validates (must have 1 input, 1 output) 3. __init__ creates ClaudeSDKClient with options 4. forward(message="...") extracts message 5. If output field has desc � append to message 6. If output type ` str � generate JSON schema 7. Call client.query(message) with optional output_format 8. Iterate through receive_response(), collect messages 9. Parse response (JSON if Pydantic, str otherwise) 10. Return Prediction(output=..., trace=..., usage=...) ``` ### Output Type Handling **String output:** ```python sig = dspy.Signature('message:str -> answer:str') # No schema passed to Claude Code # Response used as-is ``` **Pydantic output:** ```python sig = dspy.Signature('message:str -> report:BugReport') # JSON schema generated from BugReport # Schema passed to Claude Code via output_format # Response parsed with BugReport.model_validate_json() ``` ### Prompt Building ClaudeCode automatically builds rich prompts from your signature to provide maximum context to Claude: ```python class MySignature(dspy.Signature): """Analyze code quality.""" # 1. Task description message: str = dspy.InputField( desc="Path to file or module" # 2. Input context ) report: str = dspy.OutputField( desc="Markdown report with issues and recommendations" # 3. Output guidance ) config = ClaudeCodeConfig() agent = ClaudeCode( config, signature=MySignature, working_directory="." ) result = agent(message="Analyze src/main.py") # 4. Your actual input ``` **The final prompt sent to Claude:** ``` Task: Analyze code quality. Input context: Path to file or module Analyze src/main.py Please produce the following output: Markdown report with issues and recommendations ``` This automatic context enhancement helps Claude better understand: - **What** the overall task is (docstring) - **What** the input represents (InputField desc) - **What** format the output should have (OutputField desc) ## Troubleshooting ### Error: "ClaudeCode requires exactly 1 input field" Your signature has too many or too few fields. ClaudeCode expects exactly one input and one output: ```python # L Wrong - multiple inputs sig = dspy.Signature('context:str, question:str -> answer:str') # Correct - single input sig = dspy.Signature('message:str -> answer:str') ``` ### Error: "Failed to parse Claude response as MyModel" The model returned JSON that doesn't match your Pydantic schema. Check: 1. Schema is valid and clear 2. Field descriptions are helpful 3. Model has enough context to generate correct structure ### Error: "Claude Code CLI not found" Install Claude Code CLI: ```bash # Visit code.claude.com for installation instructions # or use npm: npm install -g @anthropic-ai/claude-code ``` ### Async event loop issues Use `aforward()` when already in an async context: ```python # L Don't do this in async context async def main(): result = agent(message="...") # Can cause issues #  Do this instead async def main(): result = await agent.aforward(message="...") ``` ## Design Philosophy ### Why 1 input, 1 output? ClaudeCode is designed for conversational agentic workflows. The input is always a message/prompt, and the output is always a response. This keeps the interface simple and predictable. For complex inputs, compose them into the message: ```python # Instead of: 'context:str, question:str -> answer:str' message = f"Context: {context}\n\nQuestion: {question}" result = agent(message=message) ``` ### Why stateful sessions? Agents often need multi-turn context (e.g., "fix the bug" � "write tests for it"). Stateful sessions make this natural without manual history management. Want fresh context? Create a new agent instance. ### Why return trace + usage? Observability is critical for agentic systems. You need to know: - What tools were used - What the agent was thinking - How many tokens were consumed - If any errors occurred The trace provides full visibility into agent execution. ## Comparison with CodexAgent | Feature | CodexAgent | ClaudeCode | |---------|-----------|-------------| | SDK | OpenAI Codex SDK | Claude Code Python SDK | | Thread management | Built-in thread ID | Session-based (ClaudeSDKClient) | | Streaming | Yes | Yes (via receive_response) | | Async support | No | Yes (aforward) | | Tool types | Codex-specific | Claude Code tools (Bash, Read, Write, etc.) | | Sandbox | Simple mode enum | Detailed config dict | | Permission control | Sandbox modes | Permission modes + allowed_tools | | Configuration | Direct parameters | Config object (ClaudeCodeConfig) | ## Examples Directory Check out the `examples/` directory for more: - `basic_string_output.py` - Simple string output - `pydantic_output.py` - Structured Pydantic output - `multi_turn_conversation.py` - Multi-turn conversation - `output_field_description.py` - Using output field descriptions - `inspect_trace.py` - Inspecting execution trace - `code_review_agent.py` - Advanced code review agent ## Contributing Issues and PRs welcome! This is an implementation of Claude Code SDK integration with DSPy. ## License See LICENSE file. ## Related Documentation - [Claude Agent SDK - Python Reference](https://platform.claude.com/docs/en/agent-sdk/python) - Complete SDK API reference - [Claude Agent SDK - Overview](https://platform.claude.com/docs/en/agent-sdk/overview) - SDK concepts and guides - [DSPy Documentation](https://dspy-docs.vercel.app/) - DSPy framework documentation - [Claude Code CLI](https://code.claude.com) - Claude Code command-line interface --- **Note:** This is a community implementation of Claude Code SDK integration with DSPy, inspired by the CodexAgent design pattern.