Challenge 2.2: Usage Tracking
Was kostet ein einzelner LLM-Call — und wie findest Du das heraus?
OVERVIEW
Abschnitt betitelt „OVERVIEW“Jeder generateText-Call gibt ein usage-Objekt zurück, das Dir genau sagt, wie viele Tokens verbraucht wurden. Mit den Preisen des Providers rechnest Du daraus die Kosten pro Call.
Ohne Usage Tracking: Du hast keine Ahnung, was Deine AI-Anwendung kostet. Am Monatsende kommt die Provider-Rechnung und Du kannst nicht nachvollziehen, welcher Endpoint wie viel verbraucht hat. Kostenexplosion ohne Vorwarnung.
Mit Usage Tracking: Du weisst nach jedem Call, was er gekostet hat. Du kannst Budgets setzen, Alerts bei Ueberschreitung ausloesen und gezielt optimieren — z.B. den teuersten Endpoint identifizieren und auf ein guenstigeres Modell umstellen.
WALKTHROUGH
Abschnitt betitelt „WALKTHROUGH“Schicht 1: Das usage-Objekt
Abschnitt betitelt „Schicht 1: Das usage-Objekt“Jeder generateText- und streamText-Call liefert automatisch ein usage-Objekt mit drei Feldern:
import { generateText } from 'ai';import { anthropic } from '@ai-sdk/anthropic';
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), system: 'Du bist ein hilfreicher Assistent.', prompt: 'Was ist TypeScript?',});
console.log(result.usage);// → {// promptTokens: 22, ← Was Du geschickt hast (system + prompt)// completionTokens: 85, ← Was das LLM generiert hat// totalTokens: 107 ← Summe// }promptTokens enthaelt alles, was zum LLM geht: system, prompt, messages, Tool-Definitionen. completionTokens ist die Antwort des LLM. Die Unterscheidung ist wichtig, weil Input und Output unterschiedlich bepreist sind.
Schicht 2: Kosten berechnen
Abschnitt betitelt „Schicht 2: Kosten berechnen“Die Formel ist einfach — Tokens geteilt durch 1 Million, mal Preis pro 1M Tokens:
interface ModelPricing { inputPerMillion: number; // Preis pro 1M Input-Tokens in USD outputPerMillion: number; // Preis pro 1M Output-Tokens in USD}
// Preise der gaengigen Modelle (Stand: Maerz 2026)const PRICING: Record<string, ModelPricing> = { 'claude-sonnet-4-5-20250514': { inputPerMillion: 3.0, outputPerMillion: 15.0 }, 'gpt-4o': { inputPerMillion: 2.5, outputPerMillion: 10.0 }, 'gemini-2.5-flash': { inputPerMillion: 0.15, outputPerMillion: 0.60 },};
function calculateCost( // ← Wiederverwendbare Funktion usage: { promptTokens: number; completionTokens: number }, modelId: string,): { inputCost: number; outputCost: number; totalCost: number } { const pricing = PRICING[modelId]; if (!pricing) throw new Error(`Unbekanntes Modell: ${modelId}`);
const inputCost = (usage.promptTokens / 1_000_000) * pricing.inputPerMillion; const outputCost = (usage.completionTokens / 1_000_000) * pricing.outputPerMillion;
return { inputCost, outputCost, totalCost: inputCost + outputCost, // ← Gesamtkosten pro Call };}Beispiel: 22 Prompt-Tokens + 85 Completion-Tokens mit Claude Sonnet:
- Input: 22 / 1.000.000 * $3.00 = $0.000066
- Output: 85 / 1.000.000 * $15.00 = $0.001275
- Gesamt: $0.001341 pro Call
Schicht 3: Extended Usage Details
Abschnitt betitelt „Schicht 3: Extended Usage Details“Ab AI SDK v6 liefern manche Provider erweiterte Token-Details. Diese findest Du in result.usage oder im providerMetadata:
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), system: 'Du bist ein hilfreicher Assistent für TypeScript-Fragen.', prompt: 'Erklaere Generics in TypeScript.',});
// Standard-Usage (immer verfügbar)console.log('Prompt Tokens:', result.usage.promptTokens);console.log('Completion Tokens:', result.usage.completionTokens);
// Erweiterte Details (provider-abhaengig)// Bei Anthropic:// - cacheReadTokens: Tokens aus dem Cache gelesen (guenstiger!)// - cacheCreationTokens: Tokens in den Cache geschrieben// Bei OpenAI mit Reasoning-Modellen:// - reasoningTokens: Tokens für internes NachdenkenDiese erweiterten Details sind wichtig für die Kostenoptimierung. Wenn cacheReadTokens > 0, zahlst Du weniger für diese Tokens — das lernst Du in Challenge 2.4 (Prompt Caching).
Schicht 4: Session-Tracking über mehrere Calls
Abschnitt betitelt „Schicht 4: Session-Tracking über mehrere Calls“Für eine Anwendung mit mehreren Calls brauchst Du einen Gesamtzaehler. Bei generateText ist das Ergebnis nach dem await direkt verfügbar — Du greifst einfach auf result.usage zu:
import { generateText } from 'ai';import { anthropic } from '@ai-sdk/anthropic';
let sessionTokens = { prompt: 0, completion: 0, total: 0 };let sessionCost = 0;
async function trackedGenerate(prompt: string) { const modelId = 'claude-sonnet-4-5-20250514';
const result = await generateText({ model: anthropic(modelId), prompt, });
// Nach dem await: Usage direkt aus result.usage lesen const { usage } = result; sessionTokens.prompt += usage.promptTokens; sessionTokens.completion += usage.completionTokens; sessionTokens.total += usage.totalTokens;
const cost = calculateCost(usage, modelId); sessionCost += cost.totalCost;
console.log(`[Track] Call: ${cost.totalCost.toFixed(6)} USD | Session: ${sessionCost.toFixed(6)} USD`);
return result;}
// Mehrere Calls — Session-Kosten summieren sichawait trackedGenerate('Was ist TypeScript?');await trackedGenerate('Erklaere async/await.');await trackedGenerate('Was sind Generics?');
console.log(`\nSession gesamt: ${sessionTokens.total} Tokens, ${sessionCost.toFixed(6)} USD`);Hinweis: Bei
streamTextgibt es einenonFinish-Callback, der aufgerufen wird wenn der Stream endet. BeigenerateTextbrauchst Du keinen Callback — das Ergebnis ist nach demawaitsofort verfügbar.
Aufgabe: Baue einen Cost Calculator — mache einen generateText-Call, lies usage aus und berechne die Kosten basierend auf der Modell-Preisliste.
Erstelle eine Datei challenge-2-2.ts:
import { generateText } from 'ai';import { anthropic } from '@ai-sdk/anthropic';
// TODO 1: Definiere eine PRICING-Tabelle mit mindestens 2 Modellen// Tipp: { inputPerMillion: number, outputPerMillion: number }
// TODO 2: Implementiere calculateCost(usage, modelId)// Formel: tokens / 1_000_000 * preisProMillion
// TODO 3: Mache einen generateText-Call// const result = await generateText({// model: anthropic('claude-sonnet-4-5-20250514'),// prompt: 'Erklaere in 3 Saetzen, was Machine Learning ist.',// });
// TODO 4: Berechne und logge die Kosten// console.log('Usage:', result.usage);// const cost = calculateCost(result.usage, 'claude-sonnet-4-5-20250514');// console.log('Input-Kosten:', cost.inputCost.toFixed(6), 'USD');// console.log('Output-Kosten:', cost.outputCost.toFixed(6), 'USD');// console.log('Gesamt:', cost.totalCost.toFixed(6), 'USD');Checkliste:
- PRICING-Tabelle mit mindestens 2 Modellen
-
calculateCostberechnet Input- und Output-Kosten separat -
result.usagewird korrekt ancalculateCostuebergeben - Kosten werden mit 6 Dezimalstellen geloggt (USD-Bereich für einzelne Calls)
Lösung anzeigen
import { generateText } from 'ai';import { anthropic } from '@ai-sdk/anthropic';
interface ModelPricing { inputPerMillion: number; outputPerMillion: number;}
const PRICING: Record<string, ModelPricing> = { 'claude-sonnet-4-5-20250514': { inputPerMillion: 3.0, outputPerMillion: 15.0 }, 'gpt-4o': { inputPerMillion: 2.5, outputPerMillion: 10.0 },};
function calculateCost( usage: { promptTokens: number; completionTokens: number }, modelId: string,): { inputCost: number; outputCost: number; totalCost: number } { const pricing = PRICING[modelId]; if (!pricing) throw new Error(`Unbekanntes Modell: ${modelId}`);
const inputCost = (usage.promptTokens / 1_000_000) * pricing.inputPerMillion; const outputCost = (usage.completionTokens / 1_000_000) * pricing.outputPerMillion;
return { inputCost, outputCost, totalCost: inputCost + outputCost };}
const result = await generateText({ model: anthropic('claude-sonnet-4-5-20250514'), prompt: 'Erklaere in 3 Saetzen, was Machine Learning ist.',});
console.log('Usage:', result.usage);
const cost = calculateCost(result.usage, 'claude-sonnet-4-5-20250514');console.log('Input-Kosten:', cost.inputCost.toFixed(6), 'USD');console.log('Output-Kosten:', cost.outputCost.toFixed(6), 'USD');console.log('Gesamt:', cost.totalCost.toFixed(6), 'USD');Ausfuehren mit:
npx tsx challenge-2-2.tsErwarteter Output (ungefaehr):
Usage: { promptTokens: 20, completionTokens: 78, totalTokens: 98 }Input-Kosten: 0.000060 USDOutput-Kosten: 0.001170 USDGesamt: 0.001230 USDErklärung: Die Funktion trennt Input- und Output-Kosten, weil sie unterschiedlich bepreist sind. Bei Claude Sonnet kostet Output 5x mehr als Input — deshalb lohnt es sich, die Antwortlaenge zu kontrollieren (z.B. “Antworte in maximal 3 Saetzen”).
COMBINE
Abschnitt betitelt „COMBINE“Uebung: Erweitere die selectModel-Funktion aus Level 1.2 um automatisches Cost-Tracking. Jeder Call soll seine Kosten loggen.
- Kopiere die
selectModel-Funktion aus Deinerchallenge-1-2.tsin die aktuelle Datei (oder reimplementiere sie) - Wrape den
generateText-Call in einetrackedGenerate-Funktion - Lies nach jedem
await generateText(...)die Kosten ausresult.usageund logge sie - Teste mit verschiedenen Aufgaben — vergleiche die Kosten zwischen Flash- und Pro-Modell
Optional Stretch Goal: Baue ein Session-Tracking, das die kumulierten Kosten über mehrere Calls summiert und am Ende eine Zusammenfassung ausgibt (“3 Calls, 450 Tokens, $0.003420 USD”).