{"id":52087107,"url":"https://github.com/zetesis-labs/litellm-langfuse-lab","last_synced_at":"2026-08-04T02:01:07.343Z","repository":{"id":373859129,"uuid":"1317142838","full_name":"Zetesis-Labs/litellm-langfuse-lab","owner":"Zetesis-Labs","description":"Devcontainer y despliegue en Kubernetes de LiteLLM + Langfuse v4, con una FastAPI que consume modelos y traza el gasto real","archived":false,"fork":false,"pushed_at":"2026-07-30T10:35:07.000Z","size":79,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-30T12:07:07.322Z","etag":null,"topics":["cost-tracking","devcontainer","fastapi","helm","kubernetes","langfuse","litellm","llm-observability","llmops"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Zetesis-Labs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null,"disclosure":null}},"created_at":"2026-07-30T10:28:55.000Z","updated_at":"2026-07-30T10:35:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Zetesis-Labs/litellm-langfuse-lab","commit_stats":null,"previous_names":["zetesis-labs/litellm-langfuse-lab"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Zetesis-Labs/litellm-langfuse-lab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zetesis-Labs%2Flitellm-langfuse-lab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zetesis-Labs%2Flitellm-langfuse-lab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zetesis-Labs%2Flitellm-langfuse-lab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zetesis-Labs%2Flitellm-langfuse-lab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Zetesis-Labs","download_url":"https://codeload.github.com/Zetesis-Labs/litellm-langfuse-lab/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zetesis-Labs%2Flitellm-langfuse-lab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36257516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-08-04T02:00:06.901Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cost-tracking","devcontainer","fastapi","helm","kubernetes","langfuse","litellm","llm-observability","llmops"],"created_at":"2026-08-04T02:01:06.204Z","updated_at":"2026-08-04T02:01:07.336Z","avatar_url":"https://github.com/Zetesis-Labs.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LLM Observability Lab\n\n[![CI](https://github.com/Zetesis-Labs/litellm-langfuse-lab/actions/workflows/ci.yml/badge.svg)](https://github.com/Zetesis-Labs/litellm-langfuse-lab/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nDevcontainer con **LiteLLM** (gateway de modelos) y **Langfuse v4** (observabilidad) conectados\nentre sí, y una **FastAPI** que los consume para hacer consultas a modelos con trazabilidad del\ngasto real.\n\n```\nFastAPI  ──HTTP──▶  LiteLLM proxy  ──▶  Anthropic / OpenAI / DeepInfra\n   │                     │\n   │                     └── calcula el coste y lo devuelve en x-litellm-response-cost\n   │                          y lo persiste en Postgres (/spend/logs, budgets por key)\n   │\n   └──SDK Langfuse v4──▶  Langfuse  (traza con coste, tokens, sesión, usuario y etiquetas)\n```\n\n## Arranque\n\n```bash\ncp .env.example .env      # opcional: sin .env arranca igual, pero solo responde el modelo `mock`\nmake up\nmake smoke                # valida el camino completo de punta a punta\n```\n\n| Servicio | URL | Credenciales |\n|---|---|---|\n| FastAPI (Swagger) | http://localhost:8000/docs | — |\n| Langfuse | http://localhost:3100 | `lab@example.com` / `labpassword` |\n| LiteLLM (Swagger) | http://localhost:4000 | master key de `.env` |\n| MinIO (consola) | http://localhost:9191 | `minio` / `miniosecret` |\n\n`make smoke` hace una consulta, muestra el coste calculado, espera a que Langfuse la ingiera y\nte devuelve el gasto ya registrado allí. Si eso pasa, el stack está bien conectado.\n\n## Usar modelos reales\n\nPon la clave del proveedor en `.env` (`ANTHROPIC_API_KEY=...`), `make up` para recargar, y:\n\n```bash\nmake smoke M=claude-sonnet-5\n```\n\nModelos publicados en `litellm/config.yaml`: `mock`, `claude-opus-5`, `claude-sonnet-5`,\n`claude-haiku-4-5`, `gpt-4o`. Añadir uno es una entrada más en `model_list`.\n\nEl modelo `mock` no llama a ningún proveedor y **no necesita clave**, pero lleva precios\ndeclarados: recorre exactamente el mismo camino de coste que un modelo real, así que sirve para\nvalidar el pipeline sin gastar dinero.\n\n## Endpoints de la API\n\n| Endpoint | Para qué |\n|---|---|\n| `POST /ask` | Consulta un modelo. Devuelve respuesta, tokens, **coste en USD**, `trace_id` y el enlace a la traza |\n| `GET /traces/{trace_id}` | Lee de vuelta desde Langfuse el gasto y los metadatos de esa traza |\n| `GET /models` | Modelos que publica el proxy |\n| `GET /spend/summary` | Gasto agregado (`/spend/logs` de LiteLLM) |\n| `GET /spend/detail` | Una fila por llamada |\n| `GET /spend/by-key` | Gasto por virtual key |\n| `GET /healthz` | Estado de la API, del proxy y de Langfuse |\n\n```bash\ncurl -X POST http://localhost:8000/ask -H 'Content-Type: application/json' -d '{\n  \"prompt\": \"Resume la teoría de juegos en una frase\",\n  \"model\": \"claude-sonnet-5\",\n  \"session_id\": \"conversacion-42\",\n  \"user_id\": \"ruben\",\n  \"tags\": [\"demo\"]\n}'\n```\n\n`session_id`, `user_id` y `tags` se propagan a Langfuse, que agrega el gasto por sesión y por\nusuario en su UI.\n\n## Atribuir gasto por consumidor\n\nPor defecto la API usa el master key del proxy, que no tiene presupuesto propio. Para atribuir\ngasto y poner topes por consumidor, crea virtual keys:\n\n```bash\nmake key A=equipo-datos B=10     # alias equipo-datos, tope de 10 USD / 30 días\n```\n\nApunta `LITELLM_API_KEY` a la clave devuelta y su gasto aparecerá en `/spend/by-key`. Al superar\n`max_budget` LiteLLM rechaza las llamadas.\n\n## Comandos\n\n```bash\nmake up        # arranca (o recarga) el stack\nmake smoke     # prueba de punta a punta      (make smoke M=claude-sonnet-5)\nmake logs      # sigue los logs               (make logs S=litellm)\nmake key       # crea una virtual key         (make key A=alias B=presupuesto)\nmake secrets   # genera secretos para .env\nmake down      # para el stack, conserva los datos\nmake reset     # para el stack y BORRA los datos\n```\n\n## Devcontainer\n\n`.devcontainer/` reutiliza el mismo `docker-compose.yml`; el servicio `api` se abre como\ncontenedor de desarrollo con el código montado en `/workspace`. Al abrirlo en VS Code, el\nservicio arranca con `sleep infinity` en lugar de uvicorn, así que el servidor lo lanzas tú:\n\n```bash\nuv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload\n```\n\nFuera del devcontainer, `make up` arranca uvicorn automáticamente con recarga en caliente.\n\n## Decisiones que conviene conocer\n\n**Quién emite las trazas.** Las emite la FastAPI con el SDK de Langfuse v4, no LiteLLM. Los dos\ncallbacks de LiteLLM fallan contra Langfuse v4:\n\n- `success_callback: [\"langfuse\"]` usa la API de ingesta del SDK v2. Langfuse v4 arranca en modo\n  `events_only` y solo acepta ahí eventos de tipo `score` y `log`: todo lo demás vuelve como\n  error 400 por evento.\n- `callbacks: [\"langfuse_otel\"]` sí ingiere por OTLP, pero llega sin coste ni `session_id`/\n  `user_id`, y arrastra los spans internos del proxy (Postgres, auth) como ruido.\n\nEmitiéndolas desde la aplicación se obtiene el coste real (el que calcula LiteLLM, propagado\ncomo `cost_details`), los metadatos de negocio y una única observación limpia por llamada.\nLiteLLM sigue siendo la fuente de verdad del gasto: `/spend/logs` y los presupuestos por key.\n\nEl `trace_id` viaja al proxy en la cabecera `x-litellm-trace-id`, de modo que una traza de\nLangfuse y su fila en `/spend/logs` se pueden cruzar.\n\n**Si se activara el callback de LiteLLM además del SDK**, habría dos generaciones por llamada y\nel gasto se contaría dos veces: `litellm` es un scope reconocido por el SDK de Langfuse.\n\n**Versiones pineadas.** `langfuse/langfuse:4` y `langfuse-worker:4` (la etiqueta `latest` sigue\napuntando a la serie 3), ClickHouse 25.12 (v4 exige 25.12 como mínimo), Postgres 17, Redis 7 con\n`noeviction` y `ghcr.io/berriai/litellm-database:v1.94.0` (la variante `-database` es la que trae\nPrisma, necesario para el gasto persistido; la etiqueta `main-stable` está deprecada).\n\n**Un solo Postgres** con dos bases, `langfuse` y `litellm`, creadas al inicializar el contenedor.\n\n**Costes a 0.** Si `/spend/summary` muestra `spend: 0.0` para un modelo, LiteLLM no conoce su\nprecio: declara `input_cost_per_token` / `output_cost_per_token` en su entrada de `model_list`\n(hay una plantilla comentada con los precios de Anthropic al final del fichero).\n\n**Secretos.** Los valores por defecto son de desarrollo y están en claro en `docker-compose.yml`.\nPara cualquier cosa expuesta, `make secrets` y pégalos en `.env`. Ojo con los formatos que exige\nLangfuse: `NEXTAUTH_SECRET` y `SALT` en base64, `ENCRYPTION_KEY` en hex de exactamente 64\ncaracteres.\n\n## Kubernetes\n\nEl despliegue en EKS con los charts oficiales está en [`deploy/README.md`](deploy/README.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzetesis-labs%2Flitellm-langfuse-lab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzetesis-labs%2Flitellm-langfuse-lab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzetesis-labs%2Flitellm-langfuse-lab/lists"}