tokentoll
Escanea bases de código en busca de llamadas a la API de LLM y estima los costos mensuales. Compara costos entre referencias de git para detectar regresiones de costos durante la revisión de código.
Documentación
tokentoll
Evita regresiones de costos de LLM antes de producción.
tokentoll es una puerta de CI para el costo de LLM. Analiza estáticamente Python, JavaScript y TypeScript en busca de llamadas a la API de LLM, evalúa cada pull request contra una política que tú controlas y publica un veredicto de PASS/WARN/FAIL directamente en el PR. Opcionalmente, hace fallar el flujo de trabajo cuando se viola la política, de modo que las regresiones de costos no puedan fusionarse.
Demo en vivo
Jwrede/tokentoll-demo es una pequeña aplicación LLM políglota (Python + TypeScript) conectada a la puerta de costos de tokentoll. Ya hay dos PRs abiertos contra ella:
- PR #1: Añadir helper de traducción Anthropic Haiku. Nuevo punto de llamada, dentro del presupuesto. Veredicto: PASS, flujo de trabajo en verde.
- PR #2: cambiar supportbot a gpt-4o. Un cambio de modelo que activa dos reglas de política. Veredicto: FAIL, flujo de trabajo en rojo.
Abre la pestaña de conversación de cada PR para ver el comentario de veredicto que tokentoll realmente publica.
El comentario de veredicto
Cuando un PR viola tu política, tokentoll comenta con un veredicto y una lista de hallazgos bloqueantes, luego sale con código distinto de cero para que la verificación falle. Ejemplo:
## tokentoll verdict: FAIL
**Blocking findings (2):**
- `src/agent.py:42` - per-call cost grew 15.0x (threshold 5x)
- total monthly delta +$812.00 exceeds budget $250.00
> Required action: revert the regression, raise the threshold in `.tokentoll.yml`, or add an exemption.
Cuando el PR está limpio, el veredicto es PASS y el comentario muestra solo la tabla de delta de costos. Cuando no hay política configurada, tokentoll publica un comentario informativo de delta sin veredicto.
Inicio rápido (60 segundos)
Añade .github/workflows/tokentoll.yml:
name: tokentoll
on:
pull_request:
paths:
- "**.py"
- "**.ts"
- "**.tsx"
- "**.js"
- "**.jsx"
permissions:
contents: read
pull-requests: write
jobs:
cost-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: Jwrede/tokentoll@v0.7.0
with:
fail-on-policy-violation: true
Luego añade .tokentoll.yml a la raíz de tu repositorio:
budgets:
max_monthly_delta_usd: 250
max_callsite_monthly_usd: 100
max_relative_increase: 5.0
policies:
block_unknown_models: true
fail_on_policy_violation: true
Los PRs futuros reciben un comentario de veredicto. Los PRs que superen los umbrales hacen fallar el flujo de trabajo.
Para instalaciones con pin de SHA y configuraciones de permisos mínimos, consulta docs/github-action.md. Para el esquema completo de política, consulta docs/policy.md. Para la postura de seguridad, consulta docs/security.md.
Qué detecta
Python
| SDK | Patrones |
|---|---|
| OpenAI | chat.completions.create, responses.create |
| Anthropic | messages.create, messages.stream |
| Google GenAI | models.generate_content |
| LiteLLM | completion, acompletion |
| LangChain | ChatOpenAI, ChatAnthropic, init_chat_model |
| Zhipu AI | ZhipuAiClient, ZhipuAI (modelos GLM) |
JavaScript / TypeScript (analizado mediante tree-sitter, maneja .js, .jsx, .ts, .tsx)
| SDK | Patrones |
|---|---|
| OpenAI Node SDK | client.chat.completions.create, client.responses.create, client.embeddings.create |
| Anthropic SDK | client.messages.create, client.messages.stream |
| Vercel AI SDK | generateText, streamText, generateObject, streamObject, embed, embedMany |
| LangChain.js | new ChatOpenAI, new ChatAnthropic, new ChatGoogleGenerativeAI, ... |
| Compatible con OpenAI | misma forma que OpenAI Node SDK, detectado automáticamente |
Reglas de política
El bloque de política en .tokentoll.yml controla cuándo falla un PR:
| Regla | Disparador |
|---|---|
budgets.max_monthly_delta_usd | el delta mensual total estimado supera el umbral |
budgets.max_callsite_monthly_usd | cualquier punto de llamada nuevo o modificado supera el umbral |
budgets.max_relative_increase | el costo por llamada de cualquier punto de llamada modificado crece más que este multiplicador |
policies.block_unknown_models | cualquier punto de llamada nuevo o modificado usa un modelo sin precio o sin resolver |
policies.fail_on_policy_violation | tokentoll diff sale con 1 en FAIL (comportamiento de puerta de CI) |
Cada regla es independiente. Deja un campo sin configurar para deshabilitar esa regla. Referencia completa en docs/policy.md.
CLI
pip install tokentoll
# Scan current directory for LLM API calls and their costs
tokentoll scan .
# Show cost impact of your last commit
tokentoll diff HEAD~1
# Compare two refs and fail on policy violation
tokentoll diff main..HEAD --fail-on-policy-violation
Subcomandos:
tokentoll scan [PATH...] [--format table|json|markdown] [--calls-per-month N] [--config PATH]
tokentoll diff [REF] [--base REF] [--head REF] [--format table|json|markdown|github-comment]
[--config PATH] [--fail-on-policy-violation]
tokentoll update # refresh bundled pricing data from LiteLLM
Configuración
.tokentoll.yml vive en la raíz del repositorio y se descubre automáticamente. Más allá del bloque de política:
# Per-SDK defaults for dynamic (runtime-resolved) model names
default_models:
openai: gpt-4o-mini
anthropic: claude-haiku-3-20240307
# Assumed monthly call volume per call site (used for dollar estimates)
calls_per_month: 5000
# Skip cost estimation for dynamic models entirely.
# Default false: dynamic calls are priced against the per-SDK default.
skip_dynamic_models: false
# Default excludes (tests/, examples/, docs/, cookbook/, benchmarks/, evals/,
# scripts/, notebooks/) are applied automatically. Opt out with:
use_default_excludes: false
# Additional excludes (prefix or glob)
exclude:
- "*_test.py"
- vendor/
# Per-path overrides (longest prefix match)
overrides:
- path: src/agents/
default_model: gpt-4o
calls_per_month: 10000
- path: src/azure/
skip_dynamic_models: true
Orden de resolución para valores predeterminados dinámicos de modelos: default_models (por SDK) > default_model (genérico) > valores predeterminados integrados del SDK.
Seguridad
tokentoll no requiere claves de API, no envía telemetría y se ejecuta completamente dentro de tu entorno de CI. Los datos de precios se incluyen con el paquete y se actualizan desde LiteLLM bajo demanda. Para el conjunto de permisos recomendado, el pin de SHA y el riesgo de PR de fork, consulta docs/security.md.
Servidor MCP
tokentoll incluye un servidor MCP (Model Context Protocol) para que Claude Code y otros hosts MCP puedan verificar el impacto en costos de los cambios de código LLM desde dentro de una conversación de agente:
pip install tokentoll[mcp]
claude mcp add --transport stdio tokentoll -- tokentoll-mcp
Se exponen dos herramientas: scan (estimar costos en una ruta) y diff (comparar dos refs). Ambas devuelven JSON.
Cómo funciona
Source code (.py, .ts, .tsx, .js, .jsx)
|
v
+----------------+ +------------------+
| AST scanners |-->| SDK detectors |
| ast (Python) + | | OpenAI, Anthropic|
| tree-sitter | | Google, LiteLLM, |
| (JS/TS) | | LangChain, Zhipu,|
+----------------+ | Vercel AI SDK |
+------------------+
|
v
+------------------+
| Pricing engine |
| 2200+ models |
+------------------+
|
v
+------------------+
| Diff engine |
| (old vs new) |
+------------------+
|
v
+------------------+
| Policy evaluator |
| PASS/WARN/FAIL |
+------------------+
|
v
+------------------+
| PR comment / CLI |
| output |
+------------------+
Un motor de propagación constante de múltiples pasadas resuelve nombres de modelos a través de asignaciones de variables, respaldos de os.getenv() / process.env.X, valores predeterminados de funciones, atributos de clases, argumentos de constructores, literales de diccionarios y objetos, desempaquetado de **kwargs y envoltorios de proveedores de Vercel AI SDK (openai("gpt-4o")), de modo que el código del mundo real con indirección aún produzca estimaciones útiles.
Datos de precios
Los precios están incluidos y funcionan sin conexión. Para actualizar desde LiteLLM:
tokentoll update
Cobertura: más de 300 modelos en OpenAI, Anthropic, Google, AWS Bedrock, Azure y más, además de más de 2200 entradas del catálogo combinado de LiteLLM.
Limitaciones
- Solo análisis estático. Los modelos cargados desde bases de datos o configuración remota no se pueden resolver; tokentoll recurre al valor predeterminado configurado por SDK y marca el punto de llamada como
(default). - Las estimaciones de tokens usan una heurística de caracteres/4 a menos que tiktoken esté instalado (
pip install tokentoll[tiktoken]). - Las estimaciones mensuales asumen un volumen de llamadas uniforme por punto de llamada. Anula por proyecto con
calls_per_montho por ruta conoverrides. - La resolución JS/TS es solo dentro del mismo archivo. Importar un nombre de modelo desde otro módulo produce un punto de llamada dinámico en lugar de un valor resuelto.
Hoja de ruta
- v0.9: Repositorio de demostración público con un PR que falla conocido, estudio de caso de gpt-researcher, sección de adopción ampliada
- Futuro: Inferencia de frecuencia de llamadas consciente del contexto (rutas FastAPI frente a scripts frente a bucles); resolución de importaciones entre archivos para JS/TS
Licencia
MIT