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 withaddRequestCostfrom@outputai/http.
Setup
Cost events use the same hooks system as error hooks:- Create a hook file and import
onfrom@outputai/core/hooks. - Register a handler for
llm:generation:metering,cost:llm:request,cost:http:request, or a combination. - Add the file path to
outputai.hookFilesinpackage.json.
src/llm_metering_hooks.ts
src/http_cost_hooks.ts
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
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 callsaddRequestCost( 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 underpayload: