Skip to main content
The cost estimation CLI shows you costs after a workflow finishes. Cost events expose similar data while a workflow runs:
  • cost:llm:request — emitted when an LLM call finishes (generateText / streamText), with an LLM usage attribute.
  • cost:http:request — emitted when you attach a dollar cost to an HTTP response with addRequestCost from @outputai/http, with an HTTP request cost attribute.
Use either or both to log spend to your observability stack, trigger alerts, or aggregate cost per workflow over time.

Setup

Cost events use the same hooks system as error hooks:
  1. Create a hook file and import on from @outputai/core/hooks.
  2. Register a handler for cost:llm:request, cost:http:request, or both.
  3. Add the file path to outputai.hookFiles in package.json.
See Error Hooks — Setup for the hook file registration pattern.
src/llm_cost_hooks.ts
import { on } from '@outputai/core/hooks';
import type { LLMUsageEvent } from '@outputai/llm';

on<LLMUsageEvent>( 'cost:llm:request', async ( { eventId, eventDate, workflowDetails, activityInfo, modelId, total, tokensUsed, usage } ) => {
  console.log( 'LLM call', {
    eventId,
    eventDate,
    workflowId: workflowDetails.workflowId,
    activityId: activityInfo.activityId,
    modelId,
    tokens: tokensUsed,
    cost: total,
    usage
  } );
} );
src/http_cost_hooks.ts
import { on } from '@outputai/core/hooks';
import type { HttpRequestCostEvent } from '@outputai/http';

on<HttpRequestCostEvent>( 'cost:http:request', async ( { eventId, eventDate, workflowDetails, activityInfo, requestId, url, total } ) => {
  console.log( 'HTTP request', { eventId, eventDate, workflowId: workflowDetails.workflowId, activityId: activityInfo.activityId, requestId, url, total } );
} );
Handler errors are caught and logged by the framework — they never affect the workflow or the request that triggered them.

LLM request cost

When events fire

An cost:llm:request event is emitted after every generateText and streamText call completes. For streaming, the event fires when the stream finishes, not when it starts.

Payload

The handler receives a single LLM usage attribute object:
{
  "eventId": "550e8400-e29b-41d4-a716-446655440000",
  "eventDate": 1780401600000,
  "type": "llm:usage",
  "activityInfo": {
    "activityId": "activity-1",
    "activityType": "generateSummary"
  },
  "workflowDetails": {
    "workflowId": "workflow-123",
    "runId": "run-123",
    "workflowType": "lead_enrichment"
  },
  "outputActivityKind": "step",
  "modelId": "gpt-4o",
  "usage": [
    { "type": "input", "ppm": 5, "amount": 217, "total": 0.001085 },
    { "type": "output", "ppm": 15, "amount": 9, "total": 0.000135 }
  ],
  "total": 0.00122,
  "tokensUsed": 226
}
FieldTypeDescription
eventIdstringUUID v4 stamped per emit. Use as an idempotency key — cost:llm:request and http:request for the same fetch get distinct eventIds.
eventDatenumberMillisecond epoch timestamp for when the event was emitted.
type"llm:usage"Attribute type.
activityInfoobjectTemporal activity.Info for the activity that made the call.
workflowDetailsobjectOutput’s serializable subset of Temporal workflow.WorkflowInfo.
outputActivityKindstringOutput activity kind. Possible values are step, evaluator, and internal_step.
modelIdstringModel identifier (e.g. claude-sonnet-4-20250514, gpt-4o). From the prompt file config.
usagearrayPriced usage entries for this call. See Usage entries.
totalnumberTotal estimated cost in dollars.
tokensUsednumberTotal number of tokens represented by usage.

Usage entries

Cost is computed from the model’s per-million-token pricing, fetched from a built-in pricing source and cached for 24 hours. For each priced dimension, the dollar amount is (tokens / 1_000_000) * pricePerMillion. Non-cached input tokens use inputTokens - (cachedInputTokens ?? 0).
FieldTypeDescription
typestringUsage dimension.
ppmnumberPrice per million tokens for this dimension.
amountnumberToken count for this dimension.
totalnumberCost for this dimension.
usage[].type values:
typeDescription
inputNon-cached prompt tokens.
input_cachedCached prompt read tokens (cachedInputTokens).
outputCompletion tokens.
reasoningReasoning tokens, only when the model defines separate reasoning pricing.
Only available, finite usage dimensions are included. For example, reasoning is omitted when the model does not define separate reasoning pricing, and input_cached is omitted when there are no cached input tokens.

HTTP request cost

Events fire only when your code calls addRequestCost( response, total ) with a response object created by @outputai/http (or its exported fetch). The SDK attaches the cost to the existing HTTP trace event and emits cost:http:request. If the response did not originate from this package, addRequestCost no-ops (with a console warning) and no hook event is emitted.

Payload

The handler receives a single HTTP request cost attribute object:
{
  "eventId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "eventDate": 1780401600000,
  "type": "http:request:cost",
  "activityInfo": {
    "activityId": "activity-1",
    "activityType": "searchVendors"
  },
  "workflowDetails": {
    "workflowId": "workflow-123",
    "runId": "run-123",
    "workflowType": "lead_enrichment"
  },
  "outputActivityKind": "step",
  "requestId": "req-123",
  "url": "https://api.vendor.com/search",
  "total": 0.42
}
FieldTypeDescription
eventIdstringUUID v4 stamped per emit. Use as an idempotency key — cost:http:request and http:request for the same fetch get distinct eventIds, so consumers keying by eventId won’t collapse the two.
eventDatenumberMillisecond epoch timestamp for when the event was emitted.
type"http:request:cost"Attribute type.
activityInfoobjectTemporal activity.Info for the activity that made the request.
workflowDetailsobjectOutput’s serializable subset of Temporal workflow.WorkflowInfo.
outputActivityKindstringOutput activity kind. Possible values are step, evaluator, and internal_step.
requestIdstringInternal id linking this payload to the HTTP trace event for that request.
urlstringFinal response URL (same as response.url).
totalnumberDollar cost passed to addRequestCost.