Skip to content

Modell-Metadaten und Kostenberechnung

源码版本v0.73.1

models.ts ist der Modellkatalog-Eingang von pi-ai — getModel sucht in der Registrierung nach Model, calculateCost rechnet Kosten aus Usage, getSupportedThinkingLevels listet die Thinking-Stufen eines Modells, und clampThinkingLevel zieht eine angefragte Stufe auf den nächsten gültigen Wert. Die Modelldaten selbst liegen nicht in dieser Datei, sondern in models.generated.ts (automatisch generiert; hier nicht vertieft); models.ts macht nur Lookup und Berechnung.

Verantwortung

  1. Katalog-Initialisierung: beim Modulladen wird MODELS (aus models.generated.ts) zu einem Map<provider, Map<id, Model>> flachgemacht; siehe packages/ai/src/models.ts:4-13.
  2. Modell-Lookup: getModel(provider, modelId) liefert anhand von Provider + id das Model und bewahrt über einen generischen Parameter den api-Typ. Siehe packages/ai/src/models.ts:20-26.
  3. Provider / Modelle auflisten: getProviders() und getModels(provider) für UI-Listen. Siehe packages/ai/src/models.ts:28-37.
  4. Kostenberechnung: calculateCost(model, usage) rechnet Millionen-Token-Preis × Verbrauch und füllt die Felder in usage.cost. Siehe packages/ai/src/models.ts:39-46.
  5. Thinking-Stufen: getSupportedThinkingLevels filtert null-Stufen aus model.thinkingLevelMap; clampThinkingLevel zieht eine angefragte Stufe auf einen gültigen Wert. Siehe packages/ai/src/models.ts:48-80.
  6. Modell-Gleichheit: modelsAreEqual vergleicht id + provider; genutzt beim UI-Wechsel. Siehe packages/ai/src/models.ts:86-92.

Entwurfsmotivation

Warum models.generated.ts und models.ts trennen? Weil der Modellkatalog (Preise, Kontextlänge, Thinking-Unterstützung) hochfrequent geänderte Daten sind — Anbieter ändern Preise, bringen neue Modelle, passen API-Felder an. Die Trennung von der Lookup-/Berechnungslogik in models.ts erlaubt dem Generator, einmalig statische Daten zu produzieren, während die Lookup-Logik stabil bleibt. Diese Seite behandelt nicht den Generator, sondern nur die Lookup-Seite.

Warum verwendet ModelThinkingLevel sechs Stufen off | minimal | low | medium | high | xhigh? Weil die Thinking-Steuerung der Provider unterschiedlich granular ist — Anthropic gibt Budget-Tokens, OpenAI Responses gibt effort, Google gibt ein Thinking-Budget. Die Abstraktion auf 6 Stufen erfolgt über thinkingLevelMap, das jede Stufe auf provider-spezifische Parameter abbildet; null heißt „diese Stufe wird von diesem Modell nicht unterstützt". clampThinkingLevel behandelt Fälle wie „angefragt high, Modell geht nur bis medium", indem es den nächstgelegenen gültigen Wert nach unten sucht, damit der Request nicht fehlschlägt.

Wichtige Dateien

calculateCost ist reine Arithmetik — vier Preise durch eine Million mal Verbrauch:

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 sucht erst nach unten, dann nach oben, und liefert immer einen gültigen Wert:

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";

Datenfluss

Modell-Lookup und Auflösung der Thinking-Stufe:

Grenzen und Fehler

  • Modell nicht vorhanden: getModel gibt undefined zurück; der Aufrufer muss selbst behandeln; siehe packages/ai/src/models.ts:24-25.
  • Kein Reasoning: getSupportedThinkingLevels gibt bei !model.reasoning direkt ["off"] zurück; siehe packages/ai/src/models.ts:51-51.
  • xhigh-Sonderfall: nur unterstützt, wenn thinkingLevelMap.xhigh !== undefined; anders als das null-Filtern der übrigen Stufen; siehe packages/ai/src/models.ts:56-57.
  • Angefragte Stufe außerhalb der Liste: wenn in clampThinkingLevel requestedIndex === -1, wird availableLevels[0] ?? "off" zurückgegeben; siehe packages/ai/src/models.ts:68-69.
  • calculateCost mutiert vor Ort: usage.cost wird direkt geschrieben und dasselbe Objekt zurückgegeben; der Aufrufer sollte keine Unveränderlichkeit erwarten.

Zusammenfassung

models.ts ist die Lookup-/Berechnungsschicht; die Katalogdaten liegen in models.generated.ts. getModel sucht, calculateCost rechnet Kosten, getSupportedThinkingLevels / clampThinkingLevel behandeln die Thinking-Stufen-Abbildung. Wie die Model-Instanz nach dem Lookup an den Provider geht, steht in stream/complete-Fassade; wie Anthropic thinkingLevelMap nutzt, in Anthropic SSE-Implementierung.