aboutsummaryrefslogtreecommitdiffstats
path: root/packages/multillm-claude/README.md
blob: e5a47ad157966658d55ebce8abf6be69f32cb6c3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# multillm-claude

Claude Agent SDK provider for [multillm](../multillm).

This provider enables autonomous agents that can use tools and perform multi-step tasks.

## Installation

```bash
pip install multillm-claude
```

## Configuration

Choose one of the following methods (priority: direct config > env var > config file):

**Option 1: Claude CLI OAuth (recommended)**
```bash
claude login
```

**Option 2: Config file**

Create `~/.config/multillm/providers/claude.json`:

```json
{
  "api_key": "sk-ant-..."
}
```

Or for OAuth token:

```json
{
  "oauth_token": "sk-ant-oat01-..."
}
```

Set permissions:
```bash
chmod 600 ~/.config/multillm/providers/claude.json
```

**Option 3: Environment variables**

```bash
# API Key
export ANTHROPIC_API_KEY=sk-ant-...

# Or OAuth Token
export CLAUDE_CODE_OAUTH_TOKEN=your-token
```

**Option 4: Direct config**

```python
client = multillm.Client({
    "claude": {"api_key": "sk-ant-..."}
})
```

### Config File Fields

| Field | Required | Description |
|-------|----------|-------------|
| `api_key` | No* | Your Anthropic API key |
| `oauth_token` | No* | Your Claude OAuth token |

*One of `api_key` or `oauth_token` must be provided, unless using `claude login`.

## Usage

```python
import asyncio
import multillm

async def main():
    client = multillm.Client()

    # Simple query with tools
    answer = await client.single(
        "claude/default",
        "What Python version is installed?",
        allowed_tools=["Bash"],
        permission_mode="acceptEdits",
    )
    print(answer)

    # Streaming agent events
    options = multillm.AgentOptions(
        system_prompt="You are a helpful assistant.",
        max_turns=10,
        allowed_tools=["Read", "Write", "Bash"],
        permission_mode="acceptEdits",
    )

    async for msg in client.run("claude/default", "Create hello.py", options):
        if msg.type == "text":
            print(msg.content, end="")
        elif msg.type == "tool_use":
            print(f"\n[{msg.tool_name}]", end="")

asyncio.run(main())
```

## Model Selection

Specify the model after the provider prefix:

- `claude/default` - Uses the default model
- `claude/claude-sonnet-4-20250514` - Claude Sonnet
- `claude/claude-opus-4-20250514` - Claude Opus

## Tool Support

### Supported Tools

These tools work correctly with the Claude Agent SDK provider:

| Tool | Description | Permission Required |
|------|-------------|---------------------|
| `Bash` | Execute bash commands | Yes |
| `Read` | Read files from filesystem | No |
| `Write` | Create or overwrite files | Yes |
| `Edit` | Edit existing files | Yes |
| `Glob` | Find files by pattern | No |
| `Grep` | Search file contents | No |
| `Task` | Launch sub-agents | Varies |
| `WebFetch` | Fetch web content | No |
| `WebSearch` | Search the web | No |
| `NotebookEdit` | Edit Jupyter notebooks | Yes |
| `KillShell` | Kill background shells | Yes |
| `EnterPlanMode` | Enter planning mode | No |
| `ExitPlanMode` | Exit planning mode | No |

### Interactive Tools

| Tool | CLI Support | Notes |
|------|-------------|-------|
| `AskUserQuestion` | ✅ **Auto-converts** | CLI automatically uses custom `ask_user` tool |
| Custom tools | ✅ Full support | Provide Tool objects with interactive handlers |

**About Interactive Tools with Claude:**

The Claude Agent SDK's built-in `AskUserQuestion` runs in a subprocess and can't access our terminal's stdin/stdout for interactive prompting. To solve this, **multillm-cli automatically provides a custom interactive tool** when you request `AskUserQuestion`.

**What happens:**

When you use `--allowed-tools AskUserQuestion`, the CLI:
1. Removes the built-in AskUserQuestion (which doesn't work interactively)
2. Adds a custom `ask_user` tool with an interactive handler
3. The agent uses this tool instead, with full interactive support

**Usage:**

```bash
# Request AskUserQuestion - CLI auto-provides working alternative
multillm -m claude/default \
  -p "Ask me about my preferences and create a summary" \
  --allowed-tools AskUserQuestion \
  --permission-mode acceptEdits
```

**What you'll see:**
```
ℹ️  Using custom 'ask_user' tool instead of AskUserQuestion for interactive prompting

======================================================================
❓ QUESTION FROM ASSISTANT
======================================================================

What is your favorite programming language?

Suggested options:
  1. Python
  2. JavaScript
  3. Rust

Your answer: 1
======================================================================
```

**Programmatic usage:**

For programmatic use, provide your own `ask_user` tool with an interactive handler:

```python
import multillm

# Define interactive ask_user tool
ask_user_tool = multillm.Tool(
    name="ask_user",
    description="Ask the user a question",
    parameters={
        "type": "object",
        "properties": {
            "question": {"type": "string"},
            "options": {"type": "array", "items": {"type": "string"}}
        },
        "required": ["question"]
    },
    handler=lambda args: {
        "answer": input(f"\n{args['question']}\nYour answer: ")
    }
)

# Use with Claude
async for msg in client.run(
    "claude/default",
    "Ask me questions",
    options=multillm.AgentOptions(max_turns=10),
    tools=[ask_user_tool]  # Provide custom tool
):
    if msg.type == "text":
        print(msg.content)
```

**Why not use the built-in AskUserQuestion?**

The SDK's built-in `AskUserQuestion` is designed for Claude Code CLI's interactive mode where the subprocess has special stdin/stdout handling. In multillm, this doesn't work because:
- The SDK subprocess can't access our terminal
- We can't intercept and respond to built-in tool calls
- The tool returns an error instead of prompting

**Solution:** Use custom tools (which the CLI provides automatically!)

**Comparison:**

| Approach | Works? | How |
|----------|--------|-----|
| `--allowed-tools AskUserQuestion` | ✅ Yes | CLI auto-converts to custom tool |
| Custom `ask_user` Tool | ✅ Yes | Provide Tool object with handler |
| SDK built-in (direct) | ❌ No | Subprocess can't access stdin/stdout |
| Chat provider `ask_user` | ✅ Yes | Via agentwrap |

## Agent Options

```python
options = multillm.AgentOptions(
    system_prompt="...",           # System prompt
    max_turns=10,                  # Maximum agent turns
    allowed_tools=["Read", "Bash"], # Tools the agent can use
    permission_mode="acceptEdits", # Permission handling
    working_directory="/path",     # Working directory
)
```

## Message Types

When streaming with `client.run()`:

| Type | Description |
|------|-------------|
| `text` | Text content from the agent |
| `tool_use` | Agent is using a tool |
| `tool_result` | Result from a tool |
| `result` | Final result |
| `system` | System messages |

## Debugging

### Enable Debug Mode

Set the `MULTILLM_DEBUG` environment variable to see detailed error information and SDK options:

```bash
export MULTILLM_DEBUG=1
python your_script.py
```

This will show:
- Detailed error messages with full stderr/stdout
- SDK configuration options
- Full Python tracebacks

### Common Issues

**Error: "Command failed with exit code 1"**

The provider now captures and displays all available error information to stderr. Look for:

```
======================================================================
CLAUDE AGENT SDK ERROR
======================================================================
Error: <detailed error message>

Error Details:
  stderr: <actual error from subprocess>
  stdout: <subprocess output>
  exit_code: 1
======================================================================
```

**Authentication Errors:**
```bash
# Check authentication status
claude login --check

# Re-authenticate if needed
claude login
```

**Permission Errors:**

Always specify `permission_mode` when using tools:

```python
result = await client.single(
    "claude/default",
    "List files",
    allowed_tools=["Bash"],
    permission_mode="acceptEdits"  # Required!
)
```

### Full Debugging Guide

See [DEBUGGING.md](./DEBUGGING.md) for comprehensive debugging information, including:
- How to read error messages
- Common issues and solutions
- Debug mode usage
- Testing error handling
- Reporting bugs