Add more functionality to signature description parsing
This commit is contained in:
195
README.md
195
README.md
@@ -9,7 +9,7 @@ A DSPy module that wraps the Claude Code Python SDK with a signature-driven inte
|
||||
- **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
|
||||
- **Output field descriptions** - Automatically enhance prompts
|
||||
- **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
|
||||
|
||||
@@ -75,7 +75,7 @@ agent = AutoProgram.from_precompiled(
|
||||
"farouk1/claude-code",
|
||||
config={
|
||||
"signature": "message:str -> answer:str",
|
||||
"model": "sonnet",
|
||||
"model": "claude-opus-4-5-20251101",
|
||||
"permission_mode": "acceptEdits",
|
||||
"allowed_tools": ["Read", "Write", "Bash"],
|
||||
}
|
||||
@@ -92,14 +92,15 @@ For local development and creating your own agents:
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
# Create config
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
|
||||
# Create agent
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory="."
|
||||
)
|
||||
|
||||
# Create agent
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Use it
|
||||
result = agent(message="What files are in this directory?")
|
||||
print(result.answer) # String response
|
||||
@@ -119,13 +120,14 @@ class BugReport(BaseModel):
|
||||
affected_files: list[str]
|
||||
|
||||
# Create config with Pydantic output
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> report:BugReport",
|
||||
working_directory="."
|
||||
)
|
||||
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
result = agent(message="Analyze the bug in error.log")
|
||||
print(result.report.severity) # Typed access!
|
||||
print(result.report.affected_files)
|
||||
@@ -137,15 +139,15 @@ print(result.report.affected_files)
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
# Create your agent
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig(model="claude-opus-4-5-20251101")
|
||||
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
model="claude-opus-4-5-20251101",
|
||||
permission_mode="acceptEdits",
|
||||
)
|
||||
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Test it locally
|
||||
result = agent(message="Test my agent")
|
||||
print(result.answer)
|
||||
@@ -164,9 +166,25 @@ Configuration object for ClaudeCode agents.
|
||||
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"`)
|
||||
|
||||
### ClaudeCode
|
||||
|
||||
Main agent class.
|
||||
|
||||
```python
|
||||
class ClaudeCode(PrecompiledProgram):
|
||||
def __init__(
|
||||
self,
|
||||
config: ClaudeCodeConfig,
|
||||
signature: str | type[Signature], # Required
|
||||
working_directory: str = ".", # Default: "."
|
||||
model: str = "claude-opus-4-5-20251101", # Default model
|
||||
permission_mode: str | None = None, # Optional
|
||||
allowed_tools: list[str] | None = None, # Optional
|
||||
disallowed_tools: list[str] | None = None, # Optional
|
||||
@@ -178,9 +196,9 @@ class ClaudeCodeConfig:
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **`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: `"."`)
|
||||
- **`model`** - Claude model to use (default: `"claude-opus-4-5-20251101"`)
|
||||
- **`permission_mode`** - Permission mode: `"default"`, `"acceptEdits"`, `"plan"`, `"bypassPermissions"`
|
||||
- **`allowed_tools`** - List of allowed tool names (e.g., `["Read", "Write", "Bash"]`)
|
||||
- **`disallowed_tools`** - List of disallowed tool names
|
||||
@@ -188,19 +206,6 @@ class ClaudeCodeConfig:
|
||||
- **`system_prompt`** - Custom system prompt or preset config
|
||||
- **`api_key`** - Anthropic API key (falls back to `ANTHROPIC_API_KEY` env var)
|
||||
|
||||
### ClaudeCode
|
||||
|
||||
Main agent class.
|
||||
|
||||
```python
|
||||
class ClaudeCode(PrecompiledProgram):
|
||||
def __init__(self, config: ClaudeCodeConfig)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **`config`** - ClaudeCodeConfig instance with agent configuration
|
||||
|
||||
#### Methods
|
||||
|
||||
##### `__call__(**kwargs) -> Prediction` (or `forward`)
|
||||
@@ -218,11 +223,12 @@ Execute the agent with an input message.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory="."
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
result = agent(message="Hello")
|
||||
print(result.answer) # Access typed output
|
||||
@@ -249,11 +255,12 @@ Async version of `__call__()` for use in async contexts.
|
||||
**Example:**
|
||||
```python
|
||||
async def main():
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory="."
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
result = await agent.aforward(message="Hello")
|
||||
print(result.answer)
|
||||
```
|
||||
@@ -270,11 +277,12 @@ Get the session ID for this agent instance.
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory="."
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
print(agent.session_id) # None
|
||||
|
||||
@@ -300,11 +308,12 @@ Each agent instance maintains a stateful session:
|
||||
```python
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Turn 1
|
||||
result1 = agent(message="What's the main bug?")
|
||||
@@ -329,45 +338,57 @@ Want a new conversation? Create a new agent:
|
||||
```python
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
|
||||
# Agent 1 - Task A
|
||||
agent1 = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
)
|
||||
|
||||
# Agent 1 - Task A
|
||||
agent1 = ClaudeCode(config)
|
||||
result1 = agent1(message="Analyze bug in module A")
|
||||
|
||||
# Agent 2 - Task B (no context from Agent 1)
|
||||
agent2 = ClaudeCode(config)
|
||||
agent2 = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
)
|
||||
result2 = agent2(message="Analyze bug in module B")
|
||||
```
|
||||
|
||||
### Pattern 3: Output Field Descriptions
|
||||
### Pattern 3: Field Descriptions for Enhanced Context
|
||||
|
||||
Enhance prompts with field descriptions:
|
||||
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."""
|
||||
"""Analyze code architecture.""" # Used as task description
|
||||
|
||||
message: str = dspy.InputField()
|
||||
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"
|
||||
"1) Architecture overview, 2) Key components, 3) Dependencies" # Guides output format
|
||||
)
|
||||
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature=MySignature,
|
||||
working_directory=".",
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
result = agent(message="Analyze this codebase")
|
||||
|
||||
# The description is automatically appended to the prompt
|
||||
# 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
|
||||
@@ -377,11 +398,12 @@ Access detailed execution information:
|
||||
```python
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig, ToolUseItem, ToolResultItem
|
||||
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
result = agent(message="Fix the bug")
|
||||
|
||||
@@ -418,30 +440,33 @@ Control what the agent can do:
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
# Read-only (safest)
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
permission_mode="default",
|
||||
allowed_tools=["Read", "Glob", "Grep"],
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Auto-accept file edits
|
||||
config = ClaudeCodeConfig(
|
||||
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", "Edit"],
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Sandbox mode for command execution
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
sandbox={"enabled": True},
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
```
|
||||
|
||||
## Advanced Examples
|
||||
@@ -458,14 +483,14 @@ class CodeReview(BaseModel):
|
||||
severity: str = Field(description="critical, high, medium, or low")
|
||||
recommendations: list[str] = Field(description="Actionable recommendations")
|
||||
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig(model="claude-opus-4-5-20251101")
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> review:CodeReview",
|
||||
working_directory="/path/to/project",
|
||||
model="sonnet",
|
||||
permission_mode="default",
|
||||
allowed_tools=["Read", "Glob", "Grep"],
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
result = agent(message="Review the changes in src/main.py")
|
||||
|
||||
@@ -479,13 +504,14 @@ for issue in result.review.issues:
|
||||
```python
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> response:str",
|
||||
working_directory=".",
|
||||
permission_mode="acceptEdits",
|
||||
allowed_tools=["Read", "Write", "Bash"],
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Turn 1: Find the bug
|
||||
result1 = agent(message="Find the bug in src/calculator.py")
|
||||
@@ -511,11 +537,12 @@ import asyncio
|
||||
from claude_dspy import ClaudeCode, ClaudeCodeConfig
|
||||
|
||||
async def main():
|
||||
config = ClaudeCodeConfig(
|
||||
config = ClaudeCodeConfig()
|
||||
agent = ClaudeCode(
|
||||
config,
|
||||
signature="message:str -> answer:str",
|
||||
working_directory=".",
|
||||
)
|
||||
agent = ClaudeCode(config)
|
||||
|
||||
# Use aforward in async context
|
||||
result = await agent.aforward(message="Analyze this code")
|
||||
@@ -582,6 +609,46 @@ sig = dspy.Signature('message:str -> report:BugReport')
|
||||
# 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"
|
||||
|
||||
Reference in New Issue
Block a user