Skip to content

feat: record token usage, cost and latency per agent and per run - #78

Merged
JNK234 merged 1 commit into
mainfrom
feat/token-usage-49
Sep 8, 2026
Merged

JNK234 merged 1 commit into
mainfrom
feat/token-usage-49

Conversation

@JNK234

@JNK234 JNK234 commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Closes #49.

What

Every provider returns token usage and the extension threw it away. This PR reads it, normalises it across providers, and exposes it to the modeler.

  • Usage record on ChatResponse: input, output, total, plus optional reasoning, cache-read, cache-write, cost, latency.
  • Parsed in the four provider parsers. The OpenAI-compatible parser covers OpenAI, Groq, OpenRouter and Together.
  • Field semantics follow the OpenTelemetry GenAI conventions so the headline numbers compare across providers: input counts every prompt token including cached ones (Anthropic reports those separately, so they are summed); output counts every generated token including reasoning (Gemini reports thoughts separately, so they are added).
  • cost is passed through only when a provider reports it (OpenRouter). Nothing is estimated and there is no price table to go stale.
  • latency-ms is stamped in BaseHttpProvider for every call, throttle queueing and rate-limit retries included.
  • Two reporters, same [key value] shape as structured output so llm:get reads them: llm:usage (calling agent) and llm:usage-total (whole run). Both reset on clear-all.
  • A reply the extension then rejects (schema parse failure, unmatched llm:choose) still counts: the provider billed it. A call that never produced a reply records nothing.

Tests

  • UsageParsingSpec: 13 tests, one per provider field mapping plus absent-usage cases, Usage.plus, and two latency tests (stamped without usage; covers a 429 backoff).
  • tests.txt: 8 cases via a __TEST_USAGE:in,out[,reasoning] marker in DeterministicTestProvider: starts at zero, accumulates, per-agent isolation, resets on clear-all, async, every primitive, rejected reply counted, failed call not counted.
  • Live: T9 added to demos/e2e-tests. Against Groq openai/gpt-oss-20b, 34/34 pass. Groq reports reasoning tokens for this model and the parser picks them up.

Deterministic suite: 224 passing.

Docs

docs/API-REFERENCE.md: new Token Usage section with the key table, the per-provider mapping, and the cost is "" when unknown rule.

Every provider reports token usage and the extension discarded it. This
reads it into a Usage record on ChatResponse, normalised across providers
after the OpenTelemetry GenAI conventions: input counts every prompt token
including cached ones, output counts every generated token including
reasoning, and provider-specific extras (reasoning, cache read/write,
OpenRouter's reported cost) are kept where reported. The HTTP layer stamps
wall-clock latency on each call, retries and throttle waits included.

Two reporters expose it in the same [key value] shape as structured output:
llm:usage for the calling agent and llm:usage-total for the run. Both reset
on clear-all. A reply the extension then rejects still counts, since the
provider billed it; a call with no reply records nothing.

Tests: UsageParsingSpec covers each provider's field mapping and the
latency stamp; eight tests.txt cases cover accumulation, per-agent
isolation, async, every primitive, rejected replies and failed calls via a
__TEST_USAGE marker in the test provider. T9 in the live harness verifies
against Groq: 34/34 pass.

Closes #49
@JNK234
JNK234 merged commit c82178d into main Sep 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: token usage and cost tracking

1 participant