Files
docs/ideas/litellm-claude-max-bridge.md
fkrebs 143343decf docs: session continuity file + claude-max-bridge /v1/responses fix
- Add RESUME.md for cross-session continuity (open items, key state, constraints)
- claude-max-bridge: implement /v1/responses (OpenAI Responses API) with
  previous_response_id chaining via server-side history injection into system prompt.
  Root cause of "session already in use": CLI leaves JSONL in un-resumable
  "dequeued" state after each --print run; fix avoids session reuse entirely.
  Also fixed: assistant content must be array-of-blocks not plain string (silent
  JS crash otherwise).
- LiteLLM: add pass_through_endpoints for /v1/responses → claude-max-bridge
- Storage, volumes, architecture docs reconciled (Vaultwarden → local zfs,
  Pocket-ID backup, WAL-G fix, apps/ decommission, Nextcloud CIFS→NFS)
- Add ideas/, proxmox-memory-audit.md, llm-benchmark.md (new docs this session)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 22:42:17 +02:00

191 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design: Claude Max Subscription → LiteLLM Gateway Bridge
**Status**: Proposal — not implemented
**Context**: LiteLLM at `https://ai.nuclide.systems` currently has no Anthropic models. Claude Max subscription (claude.ai) provides high-rate access to Sonnet 4.5/4.6 and Opus 4.7 but is decoupled from Anthropic API billing. This doc explores bridging the two.
## Problem
Claude Max and the Anthropic API are separate products with separate billing:
| | Claude Max | Anthropic API |
|---|---|---|
| Auth | OAuth / browser session | API key |
| Billing | Flat monthly subscription | Per-token |
| Rate limits | 5h rolling windows, model-specific | Tier-based RPM/TPM |
| Access | claude.ai web + Claude Code CLI | Any HTTP client |
The goal is to surface Max-subscription capacity through LiteLLM so that LobeHub, n8n, Coder workspaces, and other internal tools can call `claude-sonnet-4-6` at zero marginal cost and fall back to paid providers only when Max limits are hit.
## Approaches
### A — Session Cookie Reverse-Engineering (not recommended)
Several community projects (e.g. `claude-unofficial-api`) scrape the claude.ai WebSocket/HTTP protocol and expose an OpenAI-compatible endpoint. LiteLLM would point at this as a custom `openai/` provider.
**Pros**: Exposes the full web model lineup; streaming works.
**Cons**: Violates Anthropic ToS; breaks on any claude.ai front-end change; auth flow requires persisting browser cookies; no multimodal or tool-use parity guarantees.
**Verdict**: Avoid. Fragile and non-compliant.
---
### B — Claude Code CLI Bridge (recommended)
Claude Code CLI (`claude`) is already installed on CT 104 and authenticated with the Max subscription via `~/.claude/`. It ships a `--print` / `--output-format stream-json` mode designed for non-interactive use, and Anthropic explicitly supports programmatic use of the CLI.
A small **`claude-max-bridge`** service wraps this CLI as an OpenAI-compatible HTTP endpoint. LiteLLM registers it as a custom `openai/` base URL. No ToS issues — this is the supported surface.
```
LobeHub / n8n / Coder / Claude Code
LiteLLM Gateway (ai.nuclide.systems)
│ model: claude-sonnet-4-6 → openai/claude-sonnet-4-6
│ api_base: http://claude-max-bridge:8000
claude-max-bridge (new container, CT 104 ai-internal)
│ subprocess: claude --model ... --print --output-format stream-json
~/.claude/ (Max subscription session)
Anthropic (claude.ai)
```
**Pros**:
- Uses the officially supported programmatic interface
- Auth is already set up; no cookie management
- `claude` CLI handles retries, token limits, context window management
- Subprocess overhead is ~200400 ms cold; warm invocations faster
**Cons**:
- One subprocess per request — cannot multiplex a single session (unlike streaming HTTP)
- CLI is tied to the single authenticated user; no multi-user isolation
- Max rate limits apply per-account, same pool as interactive use
- Claude Code SDK (TypeScript) is cleaner but adds Node dependency
---
### C — Official API + Budget Cap (stop-gap)
Add Anthropic API key to LiteLLM with a hard budget cap (e.g. $20/month). Use it for Claude-specific features (tool use, long context) and let the existing free SAIA/Gemini fallback chain absorb general-purpose traffic.
**Pros**: Zero implementation work; full API feature parity.
**Cons**: Still costs money; no benefit from Max subscription.
**Verdict**: Valid fallback if B proves too complex, or as a complement for tool-heavy workloads that need the official API surface.
---
## Recommended Architecture (Option B)
### claude-max-bridge service
```
/opt/stacks/ai/claude-max-bridge/
Dockerfile
server.py # FastAPI, ~150 lines
docker-compose.yml (or entry in ai/docker-compose.yml)
```
**Dockerfile** — reuse the existing `claude` CLI install:
```dockerfile
FROM python:3.12-slim
RUN pip install fastapi uvicorn
# Mount ~/.claude from host; claude binary from host PATH or copied in
COPY server.py /app/server.py
CMD ["uvicorn", "app.server:app", "--host", "0.0.0.0", "--port", "8000"]
```
**server.py** — OpenAI `/v1/chat/completions` shim:
```python
# POST /v1/chat/completions
# Translates messages[] → claude --print --model ... --output-format stream-json
# Streams NDJSON lines back as SSE (text/event-stream)
# Maps finish_reason, usage tokens from CLI output headers
```
Key translation notes:
- `messages` → write to a temp file, pass via `--input-file` (avoids shell quoting issues)
- `stream: true` → parse `stream-json` lines, emit `data: {...}` SSE chunks
- `stream: false` → buffer all chunks, return single response object
- `max_tokens`, `temperature`, `system` → map to `--max-tokens`, `--temperature`, `--system`
- Tool use: **not supported** in first iteration; return 501 for requests with `tools`
- Model name passthrough: `claude-sonnet-4-6``--model claude-sonnet-4-6`
### LiteLLM config additions
```yaml
model_list:
- model_name: claude-sonnet-4-6
litellm_params:
model: openai/claude-sonnet-4-6
api_base: http://claude-max-bridge:8000
api_key: "dummy" # bridge ignores it; LiteLLM requires a value
stream_timeout: 120
timeout: 120
- model_name: claude-opus-4-7
litellm_params:
model: openai/claude-opus-4-7
api_base: http://claude-max-bridge:8000
api_key: "dummy"
stream_timeout: 180
timeout: 180
- model_name: claude-haiku-4-5
litellm_params:
model: openai/claude-haiku-4-5-20251001
api_base: http://claude-max-bridge:8000
api_key: "dummy"
stream_timeout: 60
timeout: 60
```
Add to `router_settings.fallbacks` — Max hits rate limit → fall to SAIA:
```yaml
fallbacks:
- claude-sonnet-4-6: [qwen3.5-397b-a17b]
- claude-opus-4-7: [qwen3.5-397b-a17b, mistral-large-latest]
- claude-haiku-4-5: [qwen3.5-122b-a10b, cerebras-llama-3.1-8b]
```
### Rate limit handling
The bridge should detect the CLI's rate-limit exit code / stderr message and return HTTP 429. LiteLLM's router will then trigger the fallback chain. No custom logic needed in LiteLLM itself.
Max limits as of 2026 (approximate, vary by plan tier):
- Sonnet 4.6: ~50 messages / 5-hour window at Pro; higher at Max
- Opus 4.7: ~1020 messages / 5-hour window
- Haiku 4.5: effectively unlimited under Max
## Volume estimate
Typical homelab workloads (LobeHub chat, n8n automations, Coder coding assist) generate maybe 200500 completions/day. Max's 5-hour windows reset 45× daily, so the practical limit is not usually hit unless Opus is used heavily.
## Risks
| Risk | Mitigation |
|---|---|
| CLI API changes between Claude Code releases | Pin the `claude` binary version; watch for breaking changes in release notes |
| Max rate limit shared with interactive use | Monitor via `claude usage`; bridge adds `X-Max-Usage` header from CLI output |
| Auth session expiry | Bridge returns 401 on auth failure; detect and alert via Gotify |
| Subprocess latency (cold start) | Keep a warm subprocess pool (12 persistent processes) using `--interactive` + message passing, or accept the 200400 ms overhead |
| No tool use support | Fallback chain routes tool-use requests to API providers (Gemini, Mistral) |
## Implementation plan
1. Write `server.py` shim (~150 lines) + Dockerfile
2. Build image on CT 104, add to `ai/docker-compose.yml`
3. Bind-mount `~/.claude` read-only into the container
4. Add model entries to `litellm-config/config.yaml`
5. Test streaming via `curl` against the bridge directly
6. Test end-to-end via LiteLLM → LobeHub
7. Watch `docker logs claude-max-bridge` for rate-limit / auth errors for 48h
## Open questions
- Does `claude --print` support all message roles (`system`, `user`, `assistant` turns)? Needs verification with multi-turn conversation format.
- Should the bridge be on CT 104 (access to `~/.claude`) or CT 109 ops (remote docker socket to CT 104)? CT 104 is simpler for v1.
- Is there an official Claude Code SDK for Python? (TypeScript SDK exists at `@anthropic-ai/claude-code`; Python wrapper may be cleaner than shelling out.)