Initial implementation of claude-max-bridge
OpenAI-compatible FastAPI server wrapping the Claude Code CLI. Translates /v1/chat/completions requests into claude --print subprocess calls using stream-json input/output for multi-turn support and real streaming deltas. Features: - Full multi-turn conversation support via --input-format stream-json - Real-time streaming with --include-partial-messages delta tracking - Rate limit (429) and auth error (401) detection from CLI stderr - Configurable timeout, graceful subprocess cleanup on disconnect - /v1/models endpoint and /health check
This commit is contained in:
@@ -1,3 +1,92 @@
|
||||
# claude-max-bridge
|
||||
|
||||
OpenAI-compatible HTTP bridge over the Claude Code CLI — connects LiteLLM to the Claude Max subscription
|
||||
A small FastAPI server that exposes an **OpenAI-compatible `/v1/chat/completions`
|
||||
endpoint** and translates each request into a `claude` CLI subprocess call.
|
||||
This lets LiteLLM (or any OpenAI-compatible client) consume a Claude Max
|
||||
subscription through the locally-installed `claude` binary without needing an
|
||||
Anthropic API key.
|
||||
|
||||
## How it works
|
||||
|
||||
1. Client sends an OpenAI chat-completions request (streaming or non-streaming).
|
||||
2. The bridge extracts the system prompt and conversation turns.
|
||||
3. It spawns `claude --print --output-format stream-json --input-format stream-json ...`
|
||||
and pipes the conversation as NDJSON to stdin.
|
||||
4. It parses the NDJSON stdout, computes text deltas from partial assistant
|
||||
messages, and re-emits them as OpenAI SSE chunks (or buffers for
|
||||
non-streaming).
|
||||
5. Usage totals from the `result` event are forwarded in the final response.
|
||||
|
||||
Authentication is handled entirely by the `claude` binary; the bridge just
|
||||
bind-mounts the existing `~/.claude` credentials directory.
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---------------------|------------------------|--------------------------------------------------|
|
||||
| `CLAUDE_BIN` | `/usr/local/bin/claude`| Path to the `claude` CLI binary |
|
||||
| `CLAUDE_HOME` | `/root/.claude` | Path to the Claude config / credentials directory|
|
||||
| `LOG_LEVEL` | `INFO` | Python logging level |
|
||||
| `REQUEST_TIMEOUT_S` | `120` | Per-request subprocess timeout in seconds |
|
||||
|
||||
## Running with Docker
|
||||
|
||||
```bash
|
||||
docker build -t claude-max-bridge .
|
||||
|
||||
docker run -d \
|
||||
--name claude-max-bridge \
|
||||
-p 8000:8000 \
|
||||
-v /usr/local/bin/claude:/usr/local/bin/claude:ro \
|
||||
-v /root/.claude:/root/.claude:ro \
|
||||
claude-max-bridge
|
||||
```
|
||||
|
||||
## Supported models
|
||||
|
||||
- `claude-sonnet-4-6` (default fallback)
|
||||
- `claude-opus-4-5`
|
||||
- `claude-haiku-4-5`
|
||||
- `claude-opus-4-0`
|
||||
- `claude-sonnet-3-7`
|
||||
- `claude-haiku-3-5`
|
||||
|
||||
Unknown model names in requests fall back to `claude-sonnet-4-6`.
|
||||
|
||||
## LiteLLM configuration
|
||||
|
||||
Add these entries to your `litellm_config.yaml`:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: claude-sonnet-4-6
|
||||
litellm_params:
|
||||
model: openai/claude-sonnet-4-6
|
||||
api_base: http://claude-max-bridge:8000/v1
|
||||
api_key: "unused" # required by LiteLLM but ignored by bridge
|
||||
|
||||
- model_name: claude-opus-4-5
|
||||
litellm_params:
|
||||
model: openai/claude-opus-4-5
|
||||
api_base: http://claude-max-bridge:8000/v1
|
||||
api_key: "unused"
|
||||
```
|
||||
|
||||
Replace `claude-max-bridge` with the actual hostname or IP where the bridge
|
||||
container is running.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------------------------|------------------------------------------|
|
||||
| GET | `/health` | Liveness check, always returns `200 OK` |
|
||||
| GET | `/v1/models` | Lists supported models |
|
||||
| POST | `/v1/chat/completions` | OpenAI-compatible chat completions |
|
||||
|
||||
## Notes
|
||||
|
||||
- `temperature`, `top_p`, and other sampling parameters are silently ignored
|
||||
(logged at DEBUG level) because the CLI does not expose them.
|
||||
- Rate-limit responses from the CLI are translated to HTTP `429`.
|
||||
- Auth errors from the CLI are translated to HTTP `401`.
|
||||
- The subprocess is killed on client disconnect or timeout.
|
||||
|
||||
Reference in New Issue
Block a user