SDK (PyPI)
Mide localmente un deployment vLLM existente con la CLI de Lutflow
Lutflow SDK
Tier 0 mide un servidor vLLM que ya ejecutas. Lee sus métricas Prometheus durante una ventana y muestra throughput, capacidad ociosa y eficiencia activa. Con una tarifa horaria declarada, también modela el costo asignado por token procesado. No necesita cuenta de Lutflow, backend alojado, agente Kubernetes ni sensor GPU. Esta página describe lutflow 0.3.2; las versiones 0.2.0 y 0.2.1 fueron retiradas (yanked) de PyPI.
Requisitos
- Python 3.9+ para el CLI; los extras
anthropicyallrequieren Python 3.10+. - Un endpoint vLLM activo con
/metricsaccesible desde esta máquina. Si no tienes uno, consulta vLLM en GKE con una L4. - Para cifras en dólares: tu precio horario real de GPU, si cubre una instancia completa o una sola GPU, cuántas GPUs cubre ese precio y cuántas usa el deployment.
Comprueba los contadores antes de instalar (cambia la URL según tu servidor):
curl -fsS http://localhost:8000/metrics | grep -E 'vllm:(prompt|generation)_tokens_total'Si no aparecen contadores, revisa la dirección y la configuración de métricas de vLLM. Esta ruta de medición no admite todavía las métricas de TGI u Ollama.
Instalar
La forma más rápida de usar el comando lutflow es uv o pipx. Cada uno mantiene lutflow en su propio entorno aislado, así que no cambia nada de lo que ya tienes instalado:
# uv: ejecutarlo sin instalar nada de forma permanente
uvx --from 'lutflow>=0.3.2,<0.4' lutflow --version
# uv: instalar el comando de forma permanente
uv tool install 'lutflow>=0.3.2,<0.4'
# pipx: instalar el comando de forma permanente
pipx install 'lutflow>=0.3.2,<0.4'El rango de versiones hace que la instalación falle en lugar de elegir en silencio una versión anterior. Con uvx, escribe cada comando como uvx --from 'lutflow>=0.3.2,<0.4' lutflow ...; después de uv tool install o pipx install, el comando es simplemente lutflow.
Para usar lutflow desde tu propio código Python, instálalo en un entorno virtual:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install 'lutflow>=0.3.2,<0.4'
lutflow --versionEn un proyecto de uv, uv add 'lutflow>=0.3.2,<0.4' hace lo mismo.
Comprobar el endpoint antes de medir
Measurable significa una identidad de proceso del motor y contadores atribuibles, con las familias tipadas y los marcadores de creación que exige el perfil.
lutflow check está disponible desde la 0.3.1. Lee una exposición /metrics de vLLM sin generar tráfico de inferencia; no es una medición. Apunta al motor, no al router. Si agregas --model, usa el nombre con el que se sirve el modelo:
lutflow check --endpoint http://localhost:8000 \
--model Qwen/Qwen2.5-3B-Instruct --json check.json
# O inspeccionar una exposición guardada sin contactar a un servidor:
lutflow check --metrics-file saved.prom --json check.jsonEl perfil de vLLM v0.30.0 exige exactamente un process_start_time_seconds, cinco familias de métricas presentes y tipadas, y muestras _created para los contadores del motor en cada conjunto de etiquetas. Con --model, las muestras del descriptor deben coincidir con ese nombre y /v1/models debe listarlo al consultar un servidor. Un vLLM anterior puede fallar este perfil aunque measure funcione; un FAIL no demuestra por sí solo que sea imposible detectar un reinicio.
| Salida | Resultado | Significado |
|---|---|---|
| 0 | PASS | La exposición cumple las reglas del perfil. |
| 1 | FAIL | Una regla falla de forma demostrable. Ese fallo prevalece aunque no se pueda leer la lista de modelos. |
| 3 | UNREADABLE | Sin veredicto: falló una lectura necesaria, se rechazó una redirección o el cuerpo no es una exposición. |
| 2 | Error de uso | Opciones inválidas, incluidas ambas fuentes de entrada o ninguna. |
Si no se puede escribir --json, la salida es 1 incluso después de un PASS. Un endpoint ilegible da salida 3 en check, pero 2 en measure/probe.
check.json usa tier0.check.v1: veredicto, razones/hallazgos, familias requeridas y encontradas, información del modelo, hora, fuente y una huella de la exposición. Las familias y etiquetas no parseadas quedan en null, no en una lista vacía; models_served queda en null si no se consultó. El endpoint conserva esquema, host, puerto y ruta, pero excluye credenciales, query y fragmento. A diferencia del informe de medición, puede revelar direcciones de infraestructura: revísalo antes de compartirlo.
Un PASS acredita las señales de identidad disponibles en ese instante. No inspecciona sensores GPU, nodos, cuotas ni la especificación del pod; tampoco certifica una ventana posterior, latencia, costo o calidad de respuestas. Después usa measure o probe, con confirmación, durante una ventana.
Acceso en Kubernetes y entorno de las solicitudes
El permiso de solo lectura pods/proxy GET en el namespace alcanza para check mediante un kubectl proxy local; el endpoint puede incluir esa ruta. Esto no autoriza un despliegue ni requiere port-forward:
lutflow check --endpoint \
'http://127.0.0.1:8001/api/v1/namespaces/<NAMESPACE>/pods/<ENGINE_POD>:8000/proxy'probe necesita un cliente dentro del cluster o un port-forward con permiso para generar carga; no funciona con la base que incluye la ruta del proxy usada por check. Para measure mediante ese proxy, usa la URL completa que termina en /proxy/metrics.
Las solicitudes rechazan redirecciones y usan un timeout de 5 s. HTTPX usa trust_env: las variables de proxy (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY) y de CA (SSL_CERT_FILE, SSL_CERT_DIR) afectan la ruta y TLS. Las versiones admitidas de HTTPX no cargan credenciales de .netrc mediante trust_env. Consulta el contrato versionado de check.
Observar tráfico existente
Reemplaza 0.85 por el precio horario de tu factura o proveedor:
lutflow measure --endpoint http://localhost:8000 \
--duration 300 --interval 10 \
--gpu-hourly-rate 0.85 --rate-basis instance \
--gpus-in-basis 1 --gpu-count 1 \
--json measurement.jsoninstance indica que el precio cubre toda la máquina. Usa per_gpu si cubre una sola GPU. --gpus-in-basis indica cuántas GPUs cubre el precio de la instancia y --gpu-count cuántas paga este despliegue. Lutflow no adivina estos datos ni sustituye precios silenciosamente. Sin un precio verificado, omite las cuatro opciones: el informe sale igual, con los campos en dólares como unavailable. measure solo lee métricas; no envía solicitudes de inferencia.
El informe separa economía asignada de toda la ventana, incluido el tiempo ocioso, y eficiencia activa de los intervalos productivos. En este informe, "tokens útiles" son entrada más generación, no una medida de calidad de las respuestas. Las cifras en dólares se derivan de la tarifa declarada y del trabajo observado; no son cargos conciliados de facturación. Sin trabajo útil, el costo por token útil queda indefinido. Potencia, energía, VRAM y utilización GPU requieren otro sensor y se muestran como unavailable, nunca como cero, en este Tier 0. El JSON queda en tu máquina.
Si el endpoint sirve varios modelos, pasa --model MODEL_ID para elegir uno; Lutflow no suma sus tokens en una cifra engañosa.
Una ventana tiene que venir de un solo proceso del motor, con contadores continuos. Desde 0.3.0, si el motor se reinicia, un contador de tokens falta en algunas lecturas o cambia la identidad del proceso dentro de la ventana, measure y probe terminan con código de salida 1 y sin informe. La 0.2.1 informaba algunas de esas ventanas con salida 0. Detectar un reinicio cuyos contadores siguen subiendo requiere que el motor publique la identidad de su proceso; ver limitaciones conocidas.
Generar tráfico de prueba acotado
Usa probe solo cuando tengas permiso para generar solicitudes en ese servidor. Reemplaza el modelo y el precio:
lutflow probe --endpoint http://localhost:8000 \
--model Qwen/Qwen2.5-3B-Instruct \
--concurrency 8 --duration 120 --interval 10 \
--gpu-hourly-rate 0.85 --rate-basis instance \
--gpus-in-basis 1 --gpu-count 1 \
--json measurement.jsonprobe muestra un plan de carga y exige escribir yes. Usa prompts sintéticos incluidos en el paquete y marca la salida como carga inducida. Sus límites vienen en el paquete y no se pueden subir: 16 flujos concurrentes, 600 segundos y 5000 solicitudes. Nunca se ejecuta implícitamente dentro de measure.
Novedades de 0.3.2
- TPOT en vLLM v0.30.0: si falta
vllm:time_per_output_token_seconds, usavllm:inter_token_latency_seconds. Es la media de la ventana por intervalo entre tokens de salida,Δsum / Δcount, etiquetadaderived; no es una media por solicitud. La familia anterior tiene prioridad cuando está presente; no se usavllm:request_time_per_output_token_seconds. - Continuidad de TPOT: compara sumas y conteos dentro de la misma familia, incluso entre lecturas separadas por huecos. Un cambio de familia conserva la ventana con TPOT no disponible, sin un falso reinicio. Los reinicios dentro de una familia, los huecos en contadores de tokens y los cambios de identidad del proceso siguen rechazando la ventana.
- p95: al interpolarla dentro de un bucket, agrega
latency_p95_bucket_bounded_<lower>_<upper>_msawarnings[]. No cambia el valor, su fuente ni el esquematier0.measure.v1.
Limitaciones conocidas
Estas son las limitaciones conocidas que siguen presentes en 0.3.2, según su changelog versionado. Los planes de una versión futura no cambian el comportamiento del paquete instalado aquí.
- Detectar un reinicio depende de que el motor publique su identidad. Si vLLM no publica
process_start_time_secondsni las series_createdde sus contadores de tokens, un reinicio cuyos contadores siguen subiendo puede pasar inadvertido. La ventana entonces no se rechaza:warnings[]llevaengine_process_identity_unpublished_restart_with_rising_counters_not_detectable. Antes de medir, comprueba que/metricscontiene esas series. - La p95 solo se resuelve hasta un bucket del histograma. Se interpola dentro del bucket del motor que la contiene. Con los buckets de latencia de punta a punta por defecto de vLLM, una ventana en la que todas las solicitudes tardan entre 2,5 y 5 segundos informa 4875 ms, sea cual sea la cola real. Léela como "dentro de ese bucket".
- Los precios de los wrappers requieren revisión. La tabla integrada sigue desactualizada, con tarifas de entrada y salida separadas. Un modelo desconocido usa silenciosamente el precio de
gpt-4o-mini. El overrideprice_per_1k_usdaplica una sola tarifa por cada mil tokens tanto a entrada como a salida.lutflow measureusa las tarifas que declaras, no esa tabla.
Otras capacidades del SDK
lutflow measure --from-telemetry export.jsonl sigue disponible para un export de telemetría GPU existente. Es un análisis offline con requisitos de datos distintos de --endpoint.
El SDK de Python también ofrece wrappers para OpenAI, Anthropic y Gemini con acciones de presupuesto dentro del proceso, como raise, warn y callback. Esas acciones afectan tu proceso Python, no un deployment Kubernetes ni el cluster. Consulta el README del paquete para ejemplos de API y la licencia.