Skip to Content
DocumentationBuildAIAI Token Usage Tracking

AI Token Usage Tracking

Every LLM tool call returns token usage on result.metadata. The shape is the same regardless of which tool produced the result — LlmGenerateTextTool, LlmGenerateObjectTool, and any other LLM-backed tool all return LlmResultMeta with a usage?: LlmUsage. Read it to log costs, enforce budgets, or report consumption back to users.

Result metadata shape

type LlmResultMeta = { provider: string; // e.g. 'claude', 'openai' model: string; // e.g. 'claude-sonnet-4-6' usage?: LlmUsage; }; interface LlmUsage { inputTokens: number; outputTokens: number; cacheCreationInputTokens?: number; cacheReadInputTokens?: number; reasoningTokens?: number; }
FieldDescription
inputTokensPrompt tokens sent to the model
outputTokensCompletion tokens produced
cacheCreationInputTokensTokens written to the prompt cache (provider-dependent)
cacheReadInputTokensTokens served from the prompt cache
reasoningTokensInternal reasoning tokens (e.g. Claude thinking, OpenAI o-series)

usage is optional — providers that don’t report token counts will omit it.

Reading usage from a tool call

import type { LlmResultMeta } from '@loopstack/llm-provider-module'; const result = await this.llmGenerateText.call( { prompt: 'Write a haiku about coffee' }, { config: { provider: 'claude', model: 'claude-sonnet-4-6' } }, ); const meta = result.metadata as LlmResultMeta | undefined; const usage = meta?.usage; if (usage) { console.log( `${meta!.provider}/${meta!.model}:`, `in=${usage.inputTokens}`, `out=${usage.outputTokens}`, `cacheRead=${usage.cacheReadInputTokens ?? 0}`, ); }

The same access pattern works for LlmGenerateObjectTool and any other LLM-backed tool that returns LlmResultMeta.

Persisting usage in workflow state

If you need usage downstream (for billing, reporting, or aggregation across multiple calls), store result.metadata in the workflow state alongside the result:

interface PromptState { llmResult?: LlmGenerateTextResult; llmMeta?: LlmResultMeta; } @Transition({ to: 'prompt_executed' }) async prompt(state: PromptState, ctx: RunContext): Promise<PromptState> { const result = await this.llmGenerateText.call( { prompt: 'Write a haiku' }, { config: { provider: 'claude', model: 'claude-sonnet-4-6' } }, ); return { llmResult: result.data, llmMeta: result.metadata as LlmResultMeta | undefined, }; }

See also

Last updated on