Skip to main content
The cost estimation CLI shows you costs after a workflow finishes. Metering and cost events expose similar data while a workflow runs:
  • llm:generation:metering - recommended for LLM integrations. It carries normalized usage and cost, including calls without available pricing.
  • cost:llm:request - the compatible legacy LLM cost event. Existing handlers can keep using it.
  • cost:http:request - emitted when you attach a dollar cost to an HTTP response with addRequestCost from @outputai/http.
Use them to log spend and usage to your observability stack, trigger alerts, or aggregate costs 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 llm:generation:metering, cost:llm:request, cost:http:request, or a combination.
  3. Add the file path to outputai.hookFiles in package.json.
See Error Hooks - Setup for the hook file registration pattern.
src/llm_metering_hooks.ts
src/http_cost_hooks.ts
Handler errors are caught and logged by the framework - they never affect the workflow or the request that triggered them.

LLM generation metering

When events fire

llm:generation:metering is emitted after generation completes for text, image, and Agent calls whenever usage is available. For direct streamText() and Agent.stream() usage, it fires when the stream finishes, not when it starts.

Payload

The handler receives the standard event envelope. payload.usage is always present; payload.cost is null when pricing data is unavailable:

Normalized usage

LLMGenerationUsage records usage independently from pricing. Aggregate input, output, and total fields make common reads direct; items preserve the provider breakdown. When the detailed counts do not reconcile with the aggregate count, Output keeps the aggregate input or output item instead of recording a misleading breakdown. Per-request items are recorded independently from tokens and are excluded from the input, output, and total token aggregates above.

Normalized cost

LLMGenerationCost contains one item for every normalized usage item. Cost is computed from per-million-token pricing fetched from the built-in pricing source and cached for 24 hours. Each item total is (amount / 1_000_000) * ppm.

Legacy LLM request cost

cost:llm:request is emitted on the same completion path when a legacy-priced payload can be calculated. The same payload is retained on the LLM trace as attributes["llm:usage"]. Both remain supported with the same payload shape, so existing event handlers and trace readers do not need to migrate immediately. Its LLMUsageEvent type is deprecated only to steer new integrations toward the normalized event and attributes.
src/legacy_llm_cost_hooks.ts
The legacy payload represents priced usage through the historical input, input_cached, output, and reasoning line types only. It intentionally retains the old cache-write folding and reasoning fallback behavior, and its shape is frozen: per-request charges such as grounding are never added as new usage line types, since existing consumers reduce or map over that array and a new type could break them. Grounding costs are only available on the normalized llm:generation:cost attribute and llm:generation:metering event described above. The cost:llm:request payload and llm:usage trace attribute are generated from the same object. Use llm:generation:metering for new integrations. It is more faithful to the provider response because usage is independent from pricing, cache writes and reasoning remain explicit, provider identity is included, and fallback or missing prices are represented rather than hidden.

HTTP request cost

Events fire only when your code calls addRequestCost( response, total ) with a response returned by outputFetch or createKyClient. 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 an event envelope. The HTTP request cost attribute is available under payload: