Skip to content

Metadatos de modelo y cálculo de coste

源码版本v0.73.1

models.ts es la entrada al catálogo de modelos de pi-ai: getModel consulta el registro y devuelve el Model, calculateCost calcula el coste por usage, getSupportedThinkingLevels lista los niveles de thinking soportados por un modelo y clampThinkingLevel ajusta el nivel pedido al valor legal más cercano. Los datos del modelo no están en este archivo, sino en models.generated.ts (autogenerado; esta página no lo desglosa); models.ts sólo consulta y calcula.

Responsabilidades

  1. Inicialización del catálogo: al cargar el módulo, MODELS (de models.generated.ts) se aplana a un Map<provider, Map<id, Model>>. Ver packages/ai/src/models.ts:4-13.
  2. Consultar modelo: getModel(provider, modelId) devuelve el Model por provider + id, con genéricos preservando el tipo api. Ver packages/ai/src/models.ts:20-26.
  3. Listar providers / modelos: getProviders() y getModels(provider) para los listados de UI. Ver packages/ai/src/models.ts:28-37.
  4. Cálculo de coste: calculateCost(model, usage) multiplica precio por millón de tokens × uso, rellena los campos de usage.cost y devuelve. Ver packages/ai/src/models.ts:39-46.
  5. Niveles de thinking: getSupportedThinkingLevels filtra los niveles null en model.thinkingLevelMap; clampThinkingLevel ajusta el nivel pedido a un valor legal. Ver packages/ai/src/models.ts:48-80.
  6. Igualdad de modelos: modelsAreEqual compara id + provider, para los cambios de UI. Ver packages/ai/src/models.ts:86-92.

Motivación de diseño

¿Por qué separar models.generated.ts y models.ts? Porque el catálogo de modelos (precios, longitud de contexto, soporte de thinking) es un dato que cambia con frecuencia: el proveedor cambia precios, lanza modelos nuevos, ajusta campos de la API. Aislarlo de la lógica de consulta/cálculo de models.ts permite que el generador corra una vez y produzca datos estáticos, mientras la lógica de consulta se mantiene estable. Esta página no cubre el generador, sólo el lado de consulta.

¿Por qué ModelThinkingLevel usa seis niveles off | minimal | low | medium | high | xhigh? Porque la granularidad del control de thinking varía mucho entre providers: Anthropic da budget tokens, OpenAI Responses da effort, Google da thinking budget. Se abstrae a 6 niveles; thinkingLevelMap mapea cada nivel a parámetros específicos del provider, y null indica "este nivel no está soportado para este modelo". clampThinkingLevel resuelve casos como "se pide high pero el modelo sólo llega a medium": busca hacia abajo el valor legal más cercano, garantizando que la petición no falle.

Archivos clave

calculateCost es pura aritmética: precio por tramo dividido por un millón × uso:

typescript
// packages/ai/src/models.ts:39-46
export function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage["cost"] {
	usage.cost.input = (model.cost.input / 1000000) * usage.input;
	usage.cost.output = (model.cost.output / 1000000) * usage.output;
	usage.cost.cacheRead = (model.cost.cacheRead / 1000000) * usage.cacheRead;
	usage.cost.cacheWrite = (model.cost.cacheWrite / 1000000) * usage.cacheWrite;
	usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
	return usage.cost;
}

clampThinkingLevel busca primero hacia abajo y luego hacia arriba, garantizando devolver siempre un valor legal:

typescript
// packages/ai/src/models.ts:71-79
	for (let i = requestedIndex; i < EXTENDED_THINKING_LEVELS.length; i++) {
		const candidate = EXTENDED_THINKING_LEVELS[i];
		if (availableLevels.includes(candidate)) return candidate;
	}
	for (let i = requestedIndex - 1; i >= 0; i--) {
		const candidate = EXTENDED_THINKING_LEVELS[i];
		if (availableLevels.includes(candidate)) return candidate;
	}
	return availableLevels[0] ?? "off";

Flujo de datos

Consulta de modelo y resolución de nivel de thinking:

Límites y fallos

  • Modelo inexistente: getModel devuelve undefined; el llamador debe gestionarlo. Ver packages/ai/src/models.ts:24-25.
  • Sin reasoning: getSupportedThinkingLevels devuelve ["off"] si !model.reasoning. Ver packages/ai/src/models.ts:51-51.
  • Caso especial xhigh: sólo se soporta si thinkingLevelMap.xhigh !== undefined, a diferencia del filtro null del resto de niveles. Ver packages/ai/src/models.ts:56-57.
  • Nivel pedido fuera de la lista: cuando requestedIndex === -1, clampThinkingLevel devuelve availableLevels[0] ?? "off". Ver packages/ai/src/models.ts:68-69.
  • calculateCost muta in place: usage.cost se escribe directamente y se devuelve el mismo objeto; el llamador no debe asumir inmutabilidad.

Resumen

models.ts es la capa de consulta/cálculo; los datos del catálogo están en models.generated.ts. getModel consulta, calculateCost calcula coste, y getSupportedThinkingLevels / clampThinkingLevel gestionan el mapeo de niveles de thinking. Una vez obtenido el Model, cómo se pasa al provider en fachada stream/complete; cómo usa Anthropic el thinkingLevelMap en implementación SSE de Anthropic.