Skip to content

OpenTelemetry

View as Markdown

OpenTelemetry is a common standard for tracing, which means recording what your app does. A trace is made of spans, and a span is one traced unit of work, like one AI call. If your app already sends traces, point them at ai-tally. It reads the AI calls in them and works out what each one cost.

Set these where your app sends its traces:

Terminal window
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://ingest.ai-tally.com/v1/otlp/traces"
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/json"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="authorization=Bearer ${TALLY_KEY}"

Three things trip people up:

  • Use the _TRACES_ settings. The shorter OTEL_EXPORTER_OTLP_ENDPOINT adds /v1/traces to the URL, which is the wrong address.
  • The format must be JSON. ai-tally does not accept the binary protobuf format or gRPC. The Python OpenTelemetry exporter only sends protobuf, so from Python use the Python SDK instead.
  • Collector users: use the otlphttp exporter with encoding: json and compression: none.

The API key needs write scope.

These attributes, the named values on a span, tell ai-tally what a call was:

Attribute Example Why
gen_ai.system openai The AI provider. Needed for a price.
gen_ai.request.model or gen_ai.response.model gpt-4o-mini The model. Needed for a price.
gen_ai.operation.name chat The kind of call (see below).
gen_ai.usage.input_tokens 12 Tokens sent in. Needed for a price.
gen_ai.usage.output_tokens 5 Tokens sent back. Needed for a price, except for embeddings.
gen_ai.usage.cached_input_tokens 8 Input tokens the provider served from its cache, which cost less.
gen_ai.feature_tag support-chat Which feature made the call.
gen_ai.account_id_hash 64-character code Which customer, as a hashed customer id that cannot be traced back. Make it with python -m tally.hash_account your-customer-id.
gen_ai.session_id sess_42 Groups calls from one user session.
  • The provider attribute. ai-tally reads gen_ai.system, not the newer gen_ai.provider.name. Set gen_ai.system.
  • Missing values. If a price value is missing, the call is still saved, but its cost shows as a blank, never $0.

gen_ai.operation.name decides where a call is counted in the dashboard:

Value Counted as
chat LLM
embeddings Embeddings
tool Tool calls. Send the cost in gen_ai.tool.cost_micro_usd, in whole micro-dollars.
vector Vector DB. Send the cost in gen_ai.tool.cost_micro_usd.
  • Text. Attributes that hold text are dropped before anything is saved: any whose name ends in prompt, completion, messages, content, text or body.
  • Personal details. A span containing an email address, or attributes named like email, user_id or phone, is refused. Hash ids before you send them.
  • Span size. Each span can be up to 64 KiB.
  • Sending too fast. A 429 reply means slow down. Wait the number of seconds in its Retry-After header, then send again.