Skip to content
Color theme

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;
}
Field Description
inputTokens Prompt tokens sent to the model
outputTokens Completion tokens produced
cacheCreationInputTokens Tokens written to the prompt cache (provider-dependent)
cacheReadInputTokens Tokens served from the prompt cache
reasoningTokens Internal 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

const result = await this.llmGenerateText.call(
{ prompt: 'Write a haiku about coffee' },
{ config: { provider: 'claude', model: 'claude-sonnet-4-6' } },
);
const { provider, model, usage } = result.metadata;
if (usage) {
console.log(
`${provider}/${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) {
const result = await this.llmGenerateText.call(
{ prompt: 'Write a haiku' },
{ config: { provider: 'claude', model: 'claude-sonnet-4-6' } },
);
this.assignState({
llmResult: result.data,
llmMeta: result.metadata,
});
}

See also