Skip to content

About

Ready to go local AI gateway: LiteLLM proxy + Langfuse observability + Caddy reverse proxy. Per-tool tracing, session evaluation, and zero-config trace enrichment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

litellm-langfuse-caddy

Local AI gateway with full observability

Route all your LLM traffic through a single proxy with automatic tracing,
per-tool attribution, and session evaluation.

Docker Compose LiteLLM Langfuse MIT License

Quick Start · Features · Architecture · Providers · Maintenance


Quick Start

git clone https://github.com/aaronmorris-dev/litellm-langfuse-caddy.git
cd litellm-langfuse-caddy

cp .env.example .env
cp litellm/config.example.yaml litellm/config.yaml

# Generate secrets and edit .env:
openssl rand -hex 32   # → SALT, ENCRYPTION_KEY, NEXTAUTH_SECRET
openssl rand -hex 16   # → LITELLM_MASTER_KEY

# Uncomment your providers in litellm/config.yaml, then:
chmod +x scripts/*.sh
./scripts/start.sh
LiteLLM Proxy localhost:4000
LiteLLM Admin localhost:4000/ui
Langfuse localhost:5002
MinIO Console localhost:9091

First-Time Setup

After ./scripts/start.sh completes:

1 Go to localhost:5002 — sign up, create an org and project
2 Settings → API Keys → Create. Copy pk-lf-... and sk-lf-... into .env
3 Run docker compose up -d to pick up the new keys
4 Go to localhost:4000/ui → Virtual Keys → create per-tool keys with descriptive aliases

Architecture

flowchart TD
    CLI["🖥️ CLI Tools
Claude Code · Gemini CLI · OpenAI SDK"]

    CLI -->|":4000 · :5002"| Caddy

    subgraph compose[" Docker Compose Stack "]
        Caddy["Caddy
Reverse Proxy · SSE"]

        Caddy --> LiteLLM["LiteLLM
Unified LLM Proxy"]
        Caddy --> Langfuse["Langfuse
Observability"]

        LiteLLM -.->|"traces"| Langfuse
        Worker["⚙️ Langfuse Worker"]

        LiteLLM --> PG & Redis
        Langfuse & Worker --> PG & CH & Redis & MinIO

        PG[("PostgreSQL")]
        CH[("ClickHouse")]
        Redis[("Redis")]
        MinIO[("MinIO")]
        PG ~~~ CH ~~~ Redis ~~~ MinIO
    end

    LiteLLM --> Providers["Bedrock · Vertex AI · Gemini · OpenAI · Anthropic"]
Loading

Features

  • Multi-provider routing — Bedrock, Vertex AI, Gemini, OpenAI, and Anthropic behind one endpoint. Switch models without changing your code.
  • Per-tool tracking — Give each tool its own virtual key. Every request is automatically tagged to its source in Langfuse.
  • Automatic trace enrichment — langfuse_enrich.py maps virtual keys to trace names, daily sessions, and tags. No client changes needed.
  • Session evaluation — An LLM-as-judge script scores sessions on task completion, approach, and communication.
  • Zero-buffered streaming — Caddy delivers tokens instantly with 10-minute response timeouts.
  • Hardened by default — Memory limits, health checks, log rotation, and privilege lockdown on every service.

Services

Service Image What it does Port
Caddy caddy:2-alpine Routes traffic to LiteLLM and Langfuse with zero-buffered streaming :4000 :5002
LiteLLM ghcr.io/berriai/litellm:main-v1.81.14-stable Sends requests to any provider through one API — routing, keys, and caching —
Langfuse langfuse/langfuse:3 Dashboard for traces, sessions, scores, and usage analytics —
Langfuse Worker langfuse/langfuse-worker:3 Processes incoming traces in the background —
PostgreSQL postgres:17-alpine Stores keys, spend logs, projects, and user data —
ClickHouse clickhouse/clickhouse-server:26.2 Fast analytics storage for traces and observations —
Redis redis:7.4-alpine Response cache and background job queue —
MinIO cgr.dev/chainguard/minio Object storage for event logs and media uploads :9090 :9091

Provider Setup

AWS Bedrock — Claude via AWS SSO
# 1. Configure SSO profile
aws configure sso --profile your-profile-name

# 2. Login
aws sso login --profile your-profile-name

Add to .env:

AWS_PROFILE=your-profile-name

Uncomment in docker-compose.yaml under litellm volumes:

- ~/.aws:/root/.aws # writable — SSO needs token cache

Uncomment models in litellm/config.yaml:

- model_name: claude-sonnet-4-6
  litellm_params:
    model: bedrock/us.anthropic.claude-sonnet-4-6

Note: The ~/.aws mount must NOT be :ro — AWS SSO writes token cache files during credential refresh.

Google Vertex AI — Gemini via Application Default Credentials
gcloud auth application-default login

Add to .env:

GOOGLE_APPLICATION_CREDENTIALS=/root/.config/gcloud/application_default_credentials.json

Uncomment in docker-compose.yaml under litellm volumes:

- ~/.config/gcloud:/root/.config/gcloud:ro

Add models to litellm/config.yaml:

- model_name: gemini-2.5-flash
  litellm_params:
    model: vertex_ai/gemini-2.5-flash
    vertex_project: your-gcp-project
    vertex_location: us-central1
Gemini API — Key-based (no GCP project needed)

Get a key from Google AI Studio, then add to .env:

GEMINI_API_KEY=your-key

Add to litellm/config.yaml:

- model_name: gemini-2.5-flash
  litellm_params:
    model: gemini/gemini-2.5-flash
OpenAI / Anthropic — Direct API keys

Add the env var to the litellm service in docker-compose.yaml:

- OPENAI_API_KEY=${OPENAI_API_KEY}
# or
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}

Add to litellm/config.yaml:

- model_name: gpt-4o
  litellm_params:
    model: openai/gpt-4o

- model_name: claude-sonnet-4-6
  litellm_params:
    model: anthropic/claude-sonnet-4-6

Using the Gateway

Claude Code skill (built-in)

This repo includes a Claude Code skill at .claude/commands/llm-ops/gateway/. Open the project in Claude Code and run /llm-ops:gateway to diagnose, install, or configure the stack conversationally. The bundled diagnostic checks Docker, containers, API health, credentials, and environment variables in one pass.

Route CLI tools through the proxy

source scripts/gateway-env.sh

# Tools that use the OpenAI or Anthropic SDK now route through LiteLLM:
# ANTHROPIC_BASE_URL=http://localhost:4000
# OPENAI_BASE_URL=http://localhost:4000/v1

See examples/transparent-routing.md for permanent shell setup, per-tool virtual keys, and SDK examples. Tool-specific config examples are in examples/.claude/ and examples/.codex/.

How trace enrichment works

flowchart LR
    Key["Virtual Key alias · tags · user_id"] --> Enrich["langfuse_enrich.py
Runs on every request"]
    Enrich --> Name["Trace Name
claude"]
    Enrich --> Session["Daily Session
claude-2026-03-14"]
    Enrich --> Tags["Tags
[claude]"]
Loading

Create virtual keys in LiteLLM Admin:

What you set Example What shows in Langfuse
Key Alias claude Trace name: claude
User ID alice User: alice
Metadata tags {"tags": ["claude"]} Tags: ["claude"]

The enrichment hook automatically groups traces into daily sessions (e.g., claude-2026-03-14) — one per tool per day.


Maintenance

Prune old data
# Deletes: spend logs (30d), error logs (14d), traces (30d), scores (60d)
./scripts/prune-postgres.sh

This cleans both PostgreSQL (LiteLLM spend/error logs) and ClickHouse (Langfuse traces/observations).

Evaluate a session

Uses LLM-as-judge to score sessions on 4 dimensions (0.0–1.0), then posts scores back to Langfuse.

# Requires: uv (https://docs.astral.sh/uv/)
uv run --script scripts/eval-session.py --today claude
uv run --script scripts/eval-session.py claude-2026-03-14
uv run --script scripts/eval-session.py claude-2026-03-14 --dry-run --verbose
Dimension Weight What it measures
task_completion 50% Did the assistant complete the user's goals?
approach_quality 30% Engineering soundness and efficiency
communication 20% Clarity, concision, appropriateness
overall — Weighted composite of the above
Check provider credentials
./scripts/refresh-credentials.sh

Checks AWS SSO session and GCloud ADC token, refreshing expired credentials interactively.

Stop / restart / logs
./scripts/start.sh         # Start all services (with validation)
./scripts/stop.sh          # Stop all services
docker compose logs -f     # Follow all logs
docker compose ps          # Check service status

Hardening Details

Every service is locked down by default:

What Where Why
Memory limits All 8 services Each service capped (128 MB – 1.5 GB) to prevent runaway use
Privilege lockdown All 8 services Containers cannot escalate permissions
Read-only filesystem Caddy Root filesystem is immutable; temp storage for runtime data
Process management LiteLLM, Langfuse, Worker, MinIO Clean signal handling via tini
UTC everywhere PostgreSQL, ClickHouse All timestamps in UTC — no timezone surprises
Log rotation All 8 services 5–20 MB per log file, 3–5 files kept
Health checks 6 of 8 services Automatic probes with retries and startup grace periods
Graceful shutdown LiteLLM (30s), Langfuse (15s), PG (30s), CH (15s) Services finish in-flight work before stopping
Localhost only Caddy, MinIO Exposed ports bound to 127.0.0.1 — not reachable externally

Prerequisites

  • OrbStack (recommended) or any Docker-compatible runtime — OrbStack uses less memory and starts faster, which helps with an 8-service stack
  • ~4 GB RAM available for containers
  • Credentials for at least one LLM provider

File Structure

.
├── .claude/commands/llm-ops/gateway/  # Claude Code skill (diagnose, install, configure)
│   ├── SKILL.md                       # Skill definition and instructions
│   ├── scripts/diagnose.sh            # Automated diagnostic cascade
│   └── references/topology.md         # Service topology and known issues
├── docker-compose.yaml                # 8-service stack definition
├── Caddyfile                          # Reverse proxy config (SSE, timeouts, logging)
├── .env.example                       # All required environment variables
├── litellm/
│   ├── config.example.yaml            # LiteLLM model config template
│   └── langfuse_enrich.py             # Trace enrichment hook (auto-loaded by LiteLLM)
├── examples/
│   ├── .claude/settings.json          # Claude Code proxy config
│   ├── .codex/config.yaml             # Codex CLI proxy config
│   └── transparent-routing.md         # Guide: route any tool through the gateway
└── scripts/
    ├── start.sh                       # Startup with validation
    ├── stop.sh                        # Graceful shutdown
    ├── gateway-env.sh                 # Source to route tools through the proxy
    ├── refresh-credentials.sh         # Check/refresh provider credentials
    ├── prune-postgres.sh              # Data retention maintenance
    ├── prune-postgres.sql             # PostgreSQL pruning policy
    └── eval-session.py                # LLM-as-judge session evaluation

MIT License · Built for developers who want observability without the overhead.

About

Ready to go local AI gateway: LiteLLM proxy + Langfuse observability + Caddy reverse proxy. Per-tool tracing, session evaluation, and zero-config trace enrichment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages