Files
syslog-harness/LITELLM-MIGRATION-PLAN.md
T
Abiba d901235c03 docs: LiteLLM migration plan — two-layer architecture with model identity gap analysis
Architecture review identifying metric accuracy issue where router silently
reroutes explicit model requests. Proposes Option A: strict passthrough for
explicit models with LiteLLM-native fallback chains. Keeps syslog-auto for
content-based routing. Awaiting Mumuni and Kagenz0 review.
2026-06-14 00:40:07 +00:00

23 KiB

LiteLLM Integration Migration Plan

Syslog Solution LLC — June 13, 2026


Executive Summary

Goal: Layer the full LiteLLM Gateway suite (Admin UI, virtual keys, spend tracking, teams/SSO, budget management) on top of our custom intelligent routing harness — without sacrificing GPU-aware slot management, content-based tiering, or hardware health monitoring.

Architecture Decision: Two-layer architecture.

                  ┌─────────────────────────────────┐
                  │     LiteLLM Gateway (Layer 1)    │
                  │     Port 4000 — Policy & UX      │
                  │  ┌─────────────────────────────┐ │
                  │  │ Admin UI (/ui)              │ │
                  │  │ Virtual Keys & Permissions  │ │
                  │  │ Teams, Users, SSO (OIDC)    │ │
                  │  │ Spend Tracking & Budgets    │ │
                  │  │ Usage Analytics Dashboard   │ │
                  │  │ Request Audit Trail         │ │
                  │  │ Global Rate Limiting        │ │
                  │  └─────────────────────────────┘ │
                  │           │                       │
                  │    Pass-through to router         │
                  └──────────┬──────────────────────┘
                             │
                  ┌──────────▼──────────────────────┐
                  │  Custom Router (Layer 2)          │
                  │  Port 9000 — Intelligence & HW   │
                  │  ┌─────────────────────────────┐ │
                  │  │ 5-Tier Content-Based Routing│ │
                  │  │ GPU Slot Management (Redis) │ │
                  │  │ Agent Spread Prevention     │ │
                  │  │ GPU Health Scoring (40/30/30)│ │
                  │  │ Sidecar VRAM/Temp/Power     │ │
                  │  │ Circuit Breaker             │ │
                  │  │ Context Window Tracking      │ │
                  │  │ Per-Request Perf Recording  │ │
                  │  │ Hardware Rate Limiting      │ │
                  │  └─────────────────────────────┘ │
                  └──────────┬──────────────────────┘
                             │
          ┌──────────────────┼──────────────────────┐
          │                  │                       │
  ┌───────▼──────┐  ┌────────▼───────┐  ┌───────────▼──────┐
  │ qwen3.6-35B  │  │ qwen3.6-27B   │  │ gemma-4-12b      │
  │ MoE/Strix    │  │ Dense/RTX3090 │  │ VLM/RTX 5070     │
  │ :8080 (llama)│  │ :8080 (llama) │  │ :8080 (llama)    │
  │ :8090 (side) │  │ :8090 (side)  │  │ :8090 (sidecar)  │
  └──────────────┘  └───────────────┘  └──────────────────┘

1. Current State Baseline

1.1 Router (router-fixed.py — port 9000, deployed on CT 116 / docker-vm)

Feature Implementation
Routing Engine 5-tier content-based: lightweight → simple_conv → medium → heavy_reasoning → default
GPU Slot Mgmt Redis atomic incr/decr, max 2 concurrent per GPU, audit loop reset
Health Checks Sidecar endpoint per GPU (VRAM, temp, util, power) + llama.cpp /health
Agent Spreading select_best_gpu() prefers GPUs with 0 other agents, then non-self GPUs
Rate Limiting Token bucket (Redis), per-tier RPM: enterprise=120, professional=60, starter=20
Auth Dual-key system (Phase 0.5): 9 new + 9 deprecated keys, admin key rotation
Performance Per-request latency/tokens/tps → Redis lists (perf:recent, perf:model:X, perf:agent:X)
Context Tracking Session-level token accumulation with compaction warnings in headers
SSE Streaming Real-time dashboard updates, per-model timeseries
Admin /admin/keys, /admin/keys/generate, /admin/keys/revoke, /admin/keys/deprecation-summary

1.2 GPU Backends

GPU Host llama.cpp Sidecar VRAM Context
qwen3.6-35B-A3B (MoE) 192.168.68.15 :8080 :8090 Strix Halo 262K
qwen3.6-27B-code (Dense) 192.168.68.8 :8080 :8090 RTX 3090 262K
gemma-4-12b (VLM) 192.168.68.110 :8080 :8090 RTX 5070 262K

1.3 Existing LiteLLM Attempt

  • /root/litellm-fix.sh — previous setup script for docker-vm
  • Configured with Postgres, host networking, master key
  • Never productionized — still in exploratory phase

2. What LiteLLM Brings (That We Don't Have)

Feature Our Router LiteLLM Value Add
Admin UI Full dashboard at /ui Non-technical users can manage keys, view spend
Virtual Key Permissions (binary key→tier) Granular: per-model, per-team, budget caps Fine-grained access control
Spend Tracking Per-request $ cost with model-specific pricing Billing, cost allocation, client invoicing
Teams & Orgs Multi-tenant: org→team→user hierarchy Segregate clients/projects
SSO/OIDC Google, GitHub, Microsoft, Okta, Keycloak Enterprise auth integration
Budget Alerts Per-key, per-user, per-team budget with webhooks Prevent overspend
Usage Analytics ⚠️ (custom /metrics) Built-in: daily trends, model breakdown, per-customer Better visualization
100+ Provider Support (3 local GPUs) OpenAI, Anthropic, Bedrock, Vertex, etc. Future cloud model access
Fallback Chains Multi-provider: OpenAI→Azure→Together External model resilience
RPM/TPM Weighted LB Weighted load balancing across deployments Fine-grained traffic shaping

3. What We Keep (That LiteLLM Doesn't Have)

Feature Why We Must Keep It
Content-based 5-tier routing LiteLLM routes by model name only; we analyze prompt complexity, tokens, turns, and routing_hints
GPU hardware health scoring LiteLLM doesn't monitor VRAM, temp, power — our 40/30/30 scoring prevents routing to overheating GPUs
GPU slot management LiteLLM doesn't know about llama.cpp --parallel limits; our Redis counters prevent overloading
Agent spread prevention Our select_best_gpu() spreads agents across GPUs to prevent hotspots; LiteLLM only does simple-shuffle
Cross-turn context tracking Session-level token accumulation with compaction warnings via X-Context-Warning headers
GPU sidecar metrics VRAM %, GPU utilization %, power draw, temperature — exposed via /metrics and SSE dashboard
Circuit breaker 39 failures caught June 12; LiteLLM's allowed_fails/cooldown is less granular

4. Migration Architecture

4.1 Principle: "LiteLLM is the lobby, our router is the engine room"

  • LiteLLM handles everything a user/admin touches: keys, teams, budgets, spend logs, SSO, the UI
  • Custom Router handles everything the GPUs need: health checks, slot booking, content-based routing, hardware monitoring, circuit breaking

4.2 Flow

Agent Request
     │
     ▼
┌─────────────────────────────────────────────┐
│ LiteLLM Gateway (:4000)                      │
│                                              │
│ 1. Authenticate virtual key (sk-litellm-...) │
│ 2. Check key permissions (model access)      │
│ 3. Check budget (per-key, per-user, per-team)│
│ 4. Check team rate limits                    │
│ 5. Log request metadata                      │
│ 6. Forward to custom router as OpenAI-compat  │
│    POST http://router:9000/v1/chat/completions│
│    Headers: Authorization: Bearer <agent-key> │
│             X-LiteLLM-User: <user-id>         │
│             X-LiteLLM-Team: <team-id>         │
│             X-Session-Id: <session>           │
│                                              │
│ 7. On response: log spend, update budgets     │
│ 8. Return response to agent                   │
└──────────────┬──────────────────────────────┘
               │
               ▼
┌──────────────────────────────────────────────┐
│ Custom Router (:9000)                         │
│                                               │
│ 1. Authenticate agent key (sk-syslog-...)     │
│ 2. Hardware rate limit (per-tier RPM)         │
│ 3. Content-based tier routing                 │
│    - Estimate tokens, detect system msg       │
│    - Count turns, check routing_hints         │
│ 4. GPU slot availability (Redis counter)      │
│ 5. GPU health check (sidecar)                 │
│ 6. Agent spread logic (select_best_gpu)       │
│ 7. Queue if saturated (with timeout)          │
│ 8. Forward to selected llama.cpp GPU          │
│ 9. Track context window, set compaction header│
│ 10. Record performance metrics                │
│ 11. Return response (with routing metadata)   │
└──────────────┬───────────────────────────────┘
               │
               ▼
┌──────────────────────────────────────────────┐
│ llama.cpp GPU (:8080)                         │
└──────────────────────────────────────────────┘

4.3 LiteLLM Config (config.yaml)

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  database_url: postgresql://litellm:${POSTGRES_PASSWORD}@postgres:5432/litellm
  store_model_in_db: true

model_list:
  # All three GPUs exposed as a single virtual "syslog-router" model
  # LiteLLM passes through to our router, which handles actual GPU selection
  - model_name: syslog-auto           # Default auto-routing
    litellm_params:
      model: openai/syslog-auto       # Using OpenAI-compatible format
      api_base: http://router:9000/v1
      api_key: os.environ/ROUTER_API_KEY
      rpm: 600                         # Cap total RPM across all GPUs
  
  # Individual GPU pass-through (for explicit model requests)
  - model_name: qwen3.6-35B-A3B
    litellm_params:
      model: openai/qwen3.6-35B-A3B
      api_base: http://router:9000/v1
      api_key: os.environ/ROUTER_API_KEY
  
  - model_name: qwen3.6-27B-code
    litellm_params:
      model: openai/qwen3.6-27B-code
      api_base: http://router:9000/v1
      api_key: os.environ/ROUTER_API_KEY

  - model_name: gemma-4-12b
    litellm_params:
      model: openai/gemma-4-12b
      api_base: http://router:9000/v1
      api_key: os.environ/ROUTER_API_KEY

litellm_settings:
  num_retries: 0                     # Disabled — our router handles retry
  request_timeout: 600               # Match our 10-min llama-server timeout
  set_verbose: true
  failure_callback: ["prometheus"]   # Optional: export to Prometheus

router_settings:
  routing_strategy: "usage-based-routing"  # For external models only
  # Note: All local GPU routing is handled by custom router
  enable_loadbalancing_on_proxy: false     # Disable LiteLLM's internal LB
  allowed_fails: 100                       # Don't cooldown — our circuit breaker handles

# Cost tracking: map model names to per-token pricing
# These are passed through from our router's X-Usage-Tokens header

4.4 Router Modifications (Light Touch)

Minimal changes to router-fixed.py — the router remains largely unchanged:

  1. New header passthrough: Forward X-LiteLLM-* headers to GPU (transparent — already works)
  2. New endpoint for health passthrough: GET /v1/models already works
  3. Disable own key management: Remove /admin/keys/* endpoints (migrate to LiteLLM UI)
  4. Keep ALL routing logic: No changes to route(), select_best_gpu(), check_gpu_health(), slot management, etc.
  5. Add LiteLLM-compatible response: Return X-Usage-Tokens header so LiteLLM can track token costs
# ADD to router-fixed.py chat() response:
resp.headers["X-Usage-Tokens"] = json.dumps({
    "prompt_tokens": prompt_tokens,
    "completion_tokens": completion_tokens,
    "model": model
})

5. Deployment Plan (3 Phases)

Phase 1: Shadow Mode (Week 1) — Zero Risk

Goal: Deploy LiteLLM alongside existing router, test in shadow mode.

Agent → LiteLLM (:4000) → Router (:9000) → GPU
         (new, testing)    (existing, unchanged)
         
Agent can also directly hit :9000 as fallback

Tasks:

  1. Deploy Postgres + LiteLLM on docker-vm

    cd /opt/litellm
    # Apply litellm-fix.sh (already prepared)
    docker compose up -d
    
  2. Create config.yaml with router as upstream (see §4.3)

  3. Create virtual keys for test agents via LiteLLM UI

    • Mirror existing API_KEYS in LiteLLM's key store
    • Set per-key budgets (test with $100 cap)
  4. Verify pass-through works

    curl -X POST http://docker-vm:4000/v1/chat/completions \
      -H "Authorization: Bearer sk-litellm-test-key" \
      -H "Content-Type: application/json" \
      -d '{"model":"syslog-auto","messages":[{"role":"user","content":"test"}]}'
    
  5. Run 24-hour shadow: Both :4000 and :9000 active, agents use :9000

    • Monitor LiteLLM spend logs vs router metrics — confirm parity
    • Verify GPU health metrics unaffected

Phase 2: Cutover (Week 2) — Gradual Migration

Goal: Move agents one-by-one to LiteLLM endpoint.

Tasks:

  1. Migrate API keys to LiteLLM virtual keys:

    • Create virtual key per agent in LiteLLM UI
    • Set model access: syslog-auto (default), plus individual GPU models
    • Set per-agent budget limits
    • Create teams: "Core Agents" (Abiba, Mumuni, Tanko), "Dev Agents" (Kagenz0, Koby, Koonimo)
  2. Update agent configs:

    • Change OPENAI_API_BASE from http://docker-vm:9000/v1http://docker-vm:4000/v1
    • Replace agent API keys with LiteLLM virtual keys
    • Test each agent one at a time
  3. Migrate admin functions:

    • Key creation/revocation → LiteLLM UI
    • Rate limit management → LiteLLM per-key RPM + router hardware RPM (dual enforcement)
    • Deprecated key tracking → LiteLLM UI key list
  4. Enable SSO (optional, Phase 2+):

    general_settings:
      litellm_dashboard_sso: true
      sso_provider: "google"  # or github, microsoft, keycloak
      sso_client_id: os.environ/SSO_CLIENT_ID
      sso_client_secret: os.environ/SSO_CLIENT_SECRET
    
  5. Keep router :9000 accessible as emergency fallback for 48 hours

Phase 3: Production Hardening (Week 3+) — Optimize

Goal: Lock down, optimize, monitor.

Tasks:

  1. Remove deprecated router endpoints:

    • Drop /admin/keys/* — fully migrated to LiteLLM UI
    • Drop Phase 0 dual-key logic (LiteLLM handles key rotation)
    • Simplify API_KEYS to single ROUTER_API_KEY
  2. Add LiteLLM observability:

    • Prometheus metrics export
    • Slack/email budget alerts
    • Daily spend report webhook
  3. Enable LiteLLM caching (Redis, shared with router):

    router_settings:
      redis_host: os.environ/REDIS_HOST
      redis_port: 6379
      cache: true
      cache_ttl: 3600
    
  4. Optional: External model fallbacks

    • Add Anthropic Claude as fallback for code-heavy requests
    • Add OpenAI GPT-4o as fallback for reasoning overflow
    • LiteLLM's native fallback chains handle this cleanly
  5. Router slim-down: Extract GPU health metrics to dedicated /health only

    • Keep: routing, slots, health checks, performance recording
    • Remove: key management, dual-key logic, admin endpoints

6. Nginx Configuration

The existing nginx config routes /admin/ → router :9000. This MUST change:

# OLD (remove)
# location /admin/ {
#     proxy_pass http://127.0.0.1:9000/admin/;
# }

# NEW
location /ui/ {
    proxy_pass http://127.0.0.1:4000/ui/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

location /v1/ {
    # Primary: LiteLLM gateway
    proxy_pass http://127.0.0.1:4000/v1/;
    proxy_set_header Host $host;
    proxy_read_timeout 600s;
    # Fallback: direct router (if LiteLLM down)
    # error_page 502 = @router_fallback;
}

location @router_fallback {
    proxy_pass http://127.0.0.1:9000/v1/;
}

# Keep router metrics accessible (not behind LiteLLM)
location /router/ {
    proxy_pass http://127.0.0.1:9000/;
    # Rewrite /router/stream → :9000/stream
    # Rewrite /router/metrics → :9000/metrics
}

# Health check — combines both layers
location /health {
    # Check LiteLLM first, then router
    proxy_pass http://127.0.0.1:4000/health;
}

7. Docker Compose (docker-compose.yml on docker-vm)

services:
  # Layer 1: LiteLLM Gateway (Policy & Admin)
  litellm:
    image: ghcr.io/berriai/litellm:main-stable
    network_mode: "host"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    environment:
      - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
      - UI_USERNAME=admin
      - UI_PASSWORD=${UI_PASSWORD}
      - DATABASE_URL=postgresql://litellm:${POSTGRES_PASSWORD}@localhost:5432/litellm
      - STORE_MODEL_IN_DB=True
      - ROUTER_API_KEY=${ROUTER_API_KEY}
    command:
      - --config
      - /app/config.yaml
      - --port
      - "4000"
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

  # Database for LiteLLM
  postgres:
    image: postgres:16-alpine
    network_mode: "host"
    environment:
      - POSTGRES_DB=litellm
      - POSTGRES_USER=litellm
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  # Layer 2: Custom Router (Intelligence & Hardware)
  # Already deployed separately — not in this compose file
  # The router is managed by the existing harness deployment on CT 116

volumes:
  pgdata:

8. Risk Mitigation

Risk Mitigation
LiteLLM adds latency overhead Shadow mode measures: <50ms extra is acceptable for admin features. LiteLLM is a thin proxy.
LiteLLM down = all agents down Nginx fallback to router :9000 direct (see §6). Agents can also be configured with dual endpoints.
Key sync drift (LiteLLM keys ≠ router keys) Single-source: LiteLLM is key authority. Router uses one ROUTER_API_KEY from LiteLLM's perspective. Agent keys live in LiteLLM only.
Spend tracking inaccurate for local GPUs Configure model_cost per GPU with $0 rate (self-hosted). Optionally track "internal cost" via custom pricing.
Double rate limiting (LiteLLM + Router) Keep both intentionally: LiteLLM for per-user soft caps, Router for hardware protection. Non-overlapping concerns.
PostgreSQL failure LiteLLM can run with SQLite fallback, but UI features degrade. Postgres is the recommended path.
Router custom logic becomes a black box to LiteLLM Acceptable trade-off. LiteLLM sees router as opaque OpenAI endpoint. GPU-level routing decisions are router's domain.

9. Success Metrics

Metric Before After
Key management Manual CLI + env vars + redeploy UI-based, instant, no redeploy
Spend visibility None Per-agent, per-team, per-model $ tracking
Access control Tier-based (3 levels) Per-key, per-model, budget-capped
New agent onboarding Generate key, update env var, redeploy router Create in UI, share key
Admin UX curl + JSON responses Visual dashboard, graphs, search
Audit trail Router logs (stdout only) Database-backed with UI search
SSO None Google/GitHub/Microsoft OIDC
Budget enforcement None Automatic: key suspended at $limit
GPU routing intelligence Full (unchanged) Full (unchanged)
GPU health monitoring Full (unchanged) Full (unchanged)

10. Migration Commands (Quick Reference)

# On docker-vm (CT 116):

# 1. Deploy LiteLLM stack
cd /opt/litellm
docker compose down -v   # Clean slate
docker compose up -d     # Postgres + LiteLLM

# 2. Verify
curl http://localhost:4000/health
curl http://localhost:4000/ui   # Admin dashboard

# 3. Create first virtual key via UI or CLI
docker compose exec litellm litellm-proxy keys create \
  --key-alias "abiba-test" \
  --models "syslog-auto" \
  --max-budget 10.0 \
  --team-id "core-agents"

# 4. Test end-to-end
curl -X POST http://localhost:4000/v1/chat/completions \
  -H "Authorization: Bearer <virtual-key>" \
  -d '{"model":"syslog-auto","messages":[{"role":"user","content":"Hello"}]}'

# 5. Update nginx (see §6)
nginx -t && nginx -s reload

# 6. Monitor both layers
curl http://localhost:4000/global/spend/logs   # LiteLLM spend
curl http://localhost:9000/metrics             # Router GPU metrics
curl http://localhost:9000/stream              # Router SSE dashboard

Appendix A: Router Slim-Down (Phase 3)

After full migration, router-fixed.py can be simplified by removing:

# REMOVE (migrated to LiteLLM):
- API_KEYS validation logic (keep single ROUTER_API_KEY)
- Dual-key deprecation tracking
- /admin/keys, /admin/keys/generate, /admin/keys/revoke
- /admin/keys/deprecation-summary
- Phase 0 deprecated key logging
- check_rate_limit() (optional  keep as hardware safety net)

# KEEP:
- route()  all 5 tiers
- select_best_gpu()
- check_gpu_health()
- is_gpu_busy(), gpu_active_count(), gpu_incr/decr()
- estimate_tokens()
- store_perf_record()
- GPU_SIDECARS, GPU_URLS, GPU_MAX_CONCURRENT, GPU_CONTEXT
- counter_audit_loop()
- /v1/chat/completions  core routing endpoint
- /v1/models
- /health
- /metrics, /metrics/performance, /metrics/scatter, /metrics/timeseries
- /stream  SSE dashboard

Appendix B: LiteLLM Cost Config for Local GPUs

# In config.yaml — map models to per-token pricing for spend tracking
litellm_settings:
  model_cost:
    qwen3.6-35B-A3B:
      input_cost_per_token: 0.0        # Self-hosted, no external cost
      output_cost_per_token: 0.0
    qwen3.6-27B-code:
      input_cost_per_token: 0.0
      output_cost_per_token: 0.0
    gemma-4-12b:
      input_cost_per_token: 0.0
      output_cost_per_token: 0.0
  # For internal cost allocation, set symbolic rates:
  # e.g., MoE = $2/M tokens, Dense = $1/M tokens, VLM = $0.50/M tokens

Plan drafted: 2026-06-13 by Abiba 🦊 Status: Ready for Kwame review