Saltar al contenido
AI•5 min de lectura

LiteLLM: llaves, presupuestos y la factura de tu vLLM

Tu vLLM está en internet con una sola API key compartida. Eso está bien para ti solo. Para un equipo es un desastre: nadie sabe quién gasta qué, y quitarle el acceso a alguien significa cambiar la key de todos.

La solución es un gateway. Entre los clientes y tu GPU metemos LiteLLM: llaves virtuales por persona, presupuestos, y cada request registrado con su dueño. En mi PoC, esta fue la pieza que más valor dio, incluso antes de calcular ahorros.

   Tu equipo (opencode)  ──https──►  pod
                                     ├─ LiteLLM  :4000  (keys, logs, presupuestos)
                                     │     └──localhost──►
                                     └─ vLLM     :8000   (el modelo)
                                          + Postgres :5432 (datos del gateway)

Nadie habla directo con la GPU. Si mañana cambias vLLM por otro backend, cambias una línea del config y tu equipo no se entera.

1. Postgres (donde viven las keys)

# terminal del pod
apt-get update -qq && apt-get install -y -qq postgresql
service postgresql start
su - postgres -c "psql -c \"ALTER USER postgres PASSWORD 'litellm';\""
su - postgres -c "createdb litellm"

Sin base de datos, LiteLLM arranca en modo memoria: las keys se pierden al reiniciar y la UI no funciona.

2. LiteLLM en su propio venv

# terminal del pod
uv venv /root/venv-litellm --python 3.12
source /root/venv-litellm/bin/activate
uv pip install 'litellm[proxy]'

Environments separados a propósito. LiteLLM y vLLM piden versiones distintas de las mismas librerías; si los mezclas en un venv, rompes vLLM.

3. La configuración

# terminal del pod
mkdir -p /workspace/poc && cat > /workspace/poc/litellm-config.yaml <<'EOF'
model_list:
  - model_name: qwen3-coder-30b
    litellm_params:
      model: openai/qwen3-coder-30b
      api_base: http://localhost:8000/v1
      api_key: os.environ/VLLM_API_KEY
      input_cost_per_token: 0
      output_cost_per_token: 0

litellm_settings:
  drop_params: true

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
EOF

Tres decisiones del config: model_name es el alias que usarán tus clientes (debe coincidir exacto con lo que pongas en opencode en el paso 5). api_base apunta a tu vLLM local, que sigue privado. Y los costos por token van en 0 porque tu GPU cobra por hora, no por token; el modelo de costos real viene al final.

Crea también la master key si no la tienes del capítulo 4:

# terminal del pod
echo 'export LITELLM_MASTER_KEY=sk-master-cambia-esto-456' >> ~/.bashrc
source ~/.bashrc

4. Levantar el gateway

# terminal del pod
cd /workspace/poc
export DATABASE_URL="postgresql://postgres:litellm@localhost:5432/litellm"
export UI_USERNAME=admin
export UI_PASSWORD=cambia-esto
export STORE_MODEL_IN_DB=true

nohup litellm --config litellm-config.yaml --host 0.0.0.0 --port 4000 \
  > /workspace/litellm.log 2>&1 &

tail -f /workspace/litellm.log

El primer arranque tarda 1-2 minutos preparando la base de datos. Sal del tail con Ctrl-C cuando veas Uvicorn running on http://0.0.0.0:4000.

5. Crear tu primera llave virtual

# terminal del pod
curl -s -X POST localhost:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key_alias":"tu-nombre","models":["qwen3-coder-30b"],"max_budget":5}'

Devuelve {"key":"sk-..."}. Esa es la key de una persona, con presupuesto de $5. Para otra persona, el mismo comando con otro alias: cada una aparece por separado con sus tokens y su gasto, y se revoca en segundos.

Abre la UI en https://TU_POD_ID-4000.proxy.runpod.net/ui (login con UI_USERNAME/UI_PASSWORD): ahí viven Virtual Keys, Logs (cada request con su dueño, tokens y latencia) y Usage.

6. La prueba que lo demuestra todo

# SIN key → debe responder 401
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  https://TU_POD_ID-4000.proxy.runpod.net/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-coder-30b","messages":[{"role":"user","content":"hola"}]}'

# CON key → debe responder 200
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  https://TU_POD_ID-4000.proxy.runpod.net/v1/chat/completions \
  -H "Authorization: Bearer sk-TU-KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-coder-30b","messages":[{"role":"user","content":"hola"}]}'

401 sin key, 200 con key. Tu GPU ya no es de acceso público.

7. Conecta tu agente

En tu máquina, apunta opencode (o cualquier cliente OpenAI-compatible) al gateway:

// ~/.config/opencode/opencode.json
{
  "provider": {
    "poc": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "PoC RunPod",
      "options": {
        "baseURL": "https://TU_POD_ID-4000.proxy.runpod.net/v1",
        "apiKey": "sk-TU-KEY-VIRTUAL"
      },
      "models": {
        "qwen3-coder-30b": {
          "name": "Qwen3-Coder 30B FP8 (L40S)",
          "limit": { "context": 65536, "output": 8192 }
        }
      }
    }
  },
  "model": "poc/qwen3-coder-30b"
}

El limit.context: 65536 no es decorativo: le dice al cliente que compacte el contexto antes de reventar el límite del servidor. Sin eso, la sesión muere cuando crece.

La conversación de costos

Ahora sí, la pregunta que sostiene esta guía: ¿cuándo conviene esto?

  • Tu GPU cuesta por hora (~$1,09/h ≈ $796/mes siempre encendida), no por token.
  • Las suscripciones cuestan por persona (~$200/mes cada una).
  • El costo propio es por capacidad: un servidor atiende a muchos, así que el costo por persona baja cuando el equipo crece.

Con mis números: a 10 ingenieros el ahorro es marginal; a 30 llega al 24-37% (con contrato reservado); a 100, al 50-62%. Y hay un argumento que gana incluso sin ahorro: saber quién usa qué, con cuántos tokens y qué resultado. Mi recomendación formal a mi empresa fue no migrar todavía, y por qué, en El informe que dijo "no migren todavía".

La pieza que falta para el caso completo es el routing: lo barato y repetitivo a tu vLLM, lo difícil al modelo premium. Lo cubrí en Model routing y costos.

Checkpoint y el error típico

Checkpoint: un gateway con llaves por persona, presupuestos y logs; tu agente conectado a tu propia GPU. Y la tabla de costos para decidir si vale la pena en tu equipo.

El error típico: un model_name distinto entre el config de LiteLLM y tu cliente. El error dice "model not found" y parece del servidor, cuando es un alias que no coincide. Segundo error clásico: instalar LiteLLM en el mismo venv de vLLM.

Siguiente capítulo (el último)

Todo lo que construimos va a fallar en algún momento. El capítulo final es el runbook de rescate: los 7 errores con los que me topé, sus síntomas exactos y sus fixes.

¿Le pondrías gateway a un servidor de una sola persona? A mí me cambió el argumento de venta completo. Cuéntame en los comentarios.


> Más posts