How do I get LLM cost with the OpenTelemetry JavaScript SDK?
Rius computes LLM cost for you when each model-call span from the OpenTelemetry JavaScript SDK carries gen_ai.request.model and token counts. The plain SDK does not set these attributes itself, so you add them to the span in your own code. You never send a cost value, because Rius works it out from the model name and the tokens.
How it works
Rius prices a span on the server. It reads the requested model from gen_ai.request.model and the token counts from the gen_ai.usage.* attributes, then prices each count at its own rate:
gen_ai.usage.input_tokens: prompt tokens, including cached ones.gen_ai.usage.output_tokens: completion tokens, including reasoning ones.gen_ai.usage.cache_read.input_tokensandgen_ai.usage.cache_write.input_tokens: prompt-cache reads and writes, which are subsets of the input count.
A span needs the model and at least one count. A count you leave out is read as 0. A span with no model, or a model the price catalog does not know, gets an empty cost, not zero. Costs are list-price estimates in US dollars from LiteLLM’s public model-price catalog, not billed amounts.
The OpenTelemetry JavaScript SDK and its exporter only carry spans to Rius. They do not create model-call spans or fill in a model or tokens. What a span carries depends on the attributes your instrumentation or your own code sets. If an OpenInference instrumentor emits the model and its llm.token_count.prompt and completion values, Rius maps them onto the keys above at ingest, and cost works the same way. Never add a cost attribute of your own.
Set it up
- Register a tracer provider that exports to Rius. This is the shared setup, covered in full on Vanilla OpenTelemetry SDKs. Install
@opentelemetry/sdk-node,@opentelemetry/exporter-trace-otlp-protoand@opentelemetry/api. SetOTEL_EXPORTER_OTLP_ENDPOINTtohttps://ingest.eu.console.rius-glassflow.comandOTEL_EXPORTER_OTLP_HEADERStoauthorization=Bearerfollowed by your API key. The protocol comes from the exporter package you import,exporter-trace-otlp-proto, soOTEL_EXPORTER_OTLP_PROTOCOLis not needed here. Also setOTEL_SERVICE_NAME, and setOTEL_SPAN_ATTRIBUTE_COUNT_LIMITto4096so long LLM spans keep their attributes. Then start the SDK and shut it down before the process exits, or queued spans are never sent:
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
const sdk = new NodeSDK({
// No url or headers in code, so the exporter reads OTEL_EXPORTER_OTLP_*
traceExporter: new OTLPTraceExporter(),
// Rius ingests traces only
metricReaders: [],
logRecordProcessors: [],
})
sdk.start()
// ... your agent code ...
// Flushes queued spans before the process exits (top-level await needs an ES module)
await sdk.shutdown()- Wrap each model call in a span. Set the model before the call, and set the token counts from the provider’s response after it.
callTheModelstands for your own function that calls the provider:
import { trace } from '@opentelemetry/api'
const tracer = trace.getTracer('my-agent')
const span = tracer.startSpan('chat gpt-4o')
span.setAttributes({
'openinference.span.kind': 'LLM',
'gen_ai.operation.name': 'chat',
'gen_ai.request.model': 'gpt-4o',
'gen_ai.provider.name': 'openai',
})
const response = await callTheModel()
span.setAttributes({
'gen_ai.usage.input_tokens': response.usage.prompt_tokens,
'gen_ai.usage.output_tokens': response.usage.completion_tokens,
'gen_ai.response.model': response.model,
})
span.end()-
Use a model name the price catalog knows, and set both token counts from the provider’s usage fields. If the provider reports cached prompt tokens, send them in the cache read and cache write attributes as well, so each is priced at its own rate.
-
Run one call, open Traces in the console, and open the new trace. The span shows the model, the token counts and the cost. If the cost is empty, check the model name and the token attributes. The full attribute list is in the span attribute reference.
What you get
- A cost on every model-call span, computed by Rius from the model and the tokens, with cache reads and writes priced at their own rates.
- Cost totals and a per-model cost breakdown across your traces.
- Spans that count as LLM calls in the console and in alerts, because the span kind and operation are set.
- No cost or latency value to calculate or send from your code.
The Trace LLM calls with OpenTelemetry guide covers the other ways to send traces.
When something else fits
- If you use TypeScript and want model and token attributes set for you, the Rius SDK auto-instruments supported libraries, so you do not write the attributes by hand.
- If your model is not in LiteLLM’s public price catalog, its cost stays empty. Rius does not take custom prices or negotiated discounts, so the figure it shows is always a list-price estimate.