# Medición de consumo de IA por empresa (tokens + costo)

> Estado: **Fase 1 implementada** (medición global, sin bloqueo). Base lista para
> límites/cuotas y facturación por tokens sin tocar los callers.

## Objetivo

Tener una **fuente única de verdad** del consumo de tokens de IA por empresa,
**independiente del modelo/proveedor**, para:

1. Visibilidad y reporte de consumo/costo por empresa y por módulo.
2. Habilitar más adelante **límites por tokens** por empresa (gate).
3. Habilitar **facturación** (incluir el costo en el sistema), o dejar que la
   empresa use **sus propias credenciales** (opcional) — el flag
   `credencial_propia` deja lista esa distinción.

Decisión de negocio pendiente (no bloquea la medición): cobrar por tokens vs.
credenciales propias. Ver conversación 2026-08-15.

## Modelo de datos

- **`ai_consumo`** — una fila por llamada real a un LLM. Campos clave:
  `empresa_id`, `feature`, `proveedor`, `modelo`, `tokens_in`, `tokens_out`,
  `tokens_total`, `costo_usd` (derivado), `credencial_propia`, `usuario_id`,
  `referencia_id`, `latencia_ms`, `ok`, `error`, `creado_en`.
  RLS por tenant aplicado (rol `novasis_ai_readonly`).
- **`ai_tarifas`** — precio por modelo (USD por 1M tokens, `precio_in_por_1m` /
  `precio_out_por_1m`). El costo se deriva de acá al registrar cada llamada.
  Editables; valores base = list price público (ajustar según acuerdos).

Migración: `prisma/migrations/20260815_ai_consumo_medicion/migration.sql`.

## Arquitectura

- **`src/ai-usage/ai-usage.service.ts`** — `AiUsageService.registrar(entry)`.
  Calcula el costo desde `ai_tarifas` (cacheadas 5 min) y persiste en
  `ai_consumo`. **Nunca lanza** (un fallo de auditoría no rompe la IA).
- **`src/ai-usage/ai-usage.module.ts`** — módulo compartido; lo importan todos
  los módulos que llaman a un LLM.

### Puntos de captura (ambos proveedores instrumentados)

| Proveedor | Cómo registra | Callers instrumentados (feature) |
|---|---|---|
| `AiProviderService` (ai-dashboard) | 4º param `meta?: {feature, usuarioId, referenciaId}`; auto-registra. Devuelve `tokensIn`/`tokensOut` separados (antes solo la suma). | chat (`CHAT`), insights (`INSIGHTS`), tesorería/conciliación PDF (`TESORERIA`), test de conexión (`TEST`) |
| `AyudaProviderService` (ayuda-ia) | campos opcionales en `CompletarParams` (`empresa_id`, `feature`, `usuario_id`, `referencia_id`); auto-registra si viene `empresa_id`. | ayuda chat (`AYUDA`), ingesta/enriquecimiento (`AYUDA`), cartera (`CARTERA`) |

Si un caller no pasa `feature`/`empresa_id`, la llamada se registra igual con
`DESCONOCIDO` (AiProvider) o se omite (Ayuda, que requiere `empresa_id`).

## Pendiente (fases siguientes)

- **Endpoint de reporte**: consumo/costo agregado por empresa, feature y período.
- **Límite/cuota**: `verificarCuota(empresaId)` en `AiUsageService` (hoy no-op) +
  campos de límite en `ai_empresa_config` o tabla nueva; chequear **antes** de
  llamar y lanzar error amigable al exceder.
- **UI**: panel de consumo por empresa; CRUD de `ai_tarifas`.
- **Concepto de credencial de plataforma**: hoy toda key es de la empresa
  (`credencial_propia = true`). Cuando exista key de plataforma, setear el flag
  según el origen para separar el costo facturable.
