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

8.1 KiB
Raw Permalink Blame History

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

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.


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.


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:

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:

# 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

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:

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.)