Skip to content

fix(accounting): remove heuristic chat token usage - #975

Merged
seonghobae merged 1 commit into
fix/provider-embedding-current-mainfrom
fix/chat-authoritative-token-accounting
Aug 31, 2026
Merged

fix(accounting): remove heuristic chat token usage#975
seonghobae merged 1 commit into
fix/provider-embedding-current-mainfrom
fix/chat-authoritative-token-accounting

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • use valid provider usage or exact Rust raw-text counts for ADR-declared full model identifiers
  • keep unreconstructible chat prompt, tool, multimodal, stream, unknown-model, and missing-native usage explicitly unavailable
  • route token-threshold requests synchronously when prompt counts are unavailable, fail enabled budgets closed, and expose nullable usage/cost with explicit status
  • add ADR 0006, library research, changelog, OpenAPI, SSE, budget, routing, parity, and regression coverage

Stack

This PR is stacked on #970 and must merge only after that base lands. Prepared against exact #970 head 028bebc394b3ebf7c66b626c0efbe46190a0afe5.

Validation

  • uv run --locked --extra api --extra db --extra queue --group dev python -m pytest -q — 2907 passed, 2 skipped
  • focused accounting/routing/gateway/embedding/SSE suite — 408 passed, 1 skipped
  • cargo test --manifest-path rust/token_counter/Cargo.toml — 10 passed
  • cargo fmt --manifest-path rust/token_counter/Cargo.toml -- --check
  • packaged native Python parity for cl100k, o200k, and pack_cl100k — 1 passed
  • git diff --check

No provider credentials or real request data are included.


Devin Review

Use provider-reported counts or exact native raw-text tokenizers for declared models. Keep unreconstructible chat usage nullable, route conservatively, and fail enabled budgets closed when measurement is unavailable.

Commit-Message-Assisted-by: Claude (via Claude Code)
Signed-off-by: Seongho Bae <me@seonghobae.me>
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d2ad8ae-046d-4695-bbb1-8549faca72a7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae
seonghobae merged commit b2a2607 into fix/provider-embedding-current-main Aug 31, 2026
1 of 2 checks passed
@seonghobae
seonghobae deleted the fix/chat-authoritative-token-accounting branch August 31, 2026 14:07

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 8 potential issues.

Devin Review

Comment on lines +7644 to +7649
candidate_prices_available = (
self.budget_max_cost_usd is None
or all(agent.model in self.price_per_million for agent in self.agents)
)
measurement_available = (
not self._budget_unavailable_run_ids and candidate_prices_available

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Unrelated agents disable chat budgets

With a cost budget, budget_status requires prices for every enabled agent, including agents that cannot serve chat. One unpriced embedding-only agent blocks all chat requests despite fully priced eligible agents.

Prompt for agents
Restrict cost-budget price-availability checks to models that can actually participate in the pending chat execution. The current global check in TaskOrchestrator.budget_status includes every enabled capability agent, so unrelated embedding, image, audio, or other non-chat agents can disable chat. Account for exact requested-model restrictions and valid failover/model-group candidates where request context is available, while preserving fail-closed behavior for any model that can actually be selected.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +554 to +556
if measurement_status == "unavailable":
provider_response["usage"] = None
provider_response["usage_measurement_status"] = measurement_status

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Valid winner usage is discarded

When an endpoint race has one loser without usage, complete replaces valid winner usage with null. Clients lose authoritative counts even though only aggregate duplicate cost is unknown.

Prompt for agents
Separate client-facing winner usage from aggregate race cost status in CostRoutingCoordinator.complete's provider_request branch. Preserve and validate the winning provider_response usage independently, as the ordinary completion branch does with client_usage_records. Keep aggregate cost unavailable when any completed race call lacks usage, and retain unavailable ledger evidence for that loser.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

self.token_counter = token_counter or (
build_token_counter(postgres_dsn) if postgres_dsn else HeuristicTokenCounter()
)
self.token_counter = token_counter or build_token_counter(postgres_dsn)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Configured counters miss spend accounting

Passing an exact counter or PostgreSQL DSN to CostRoutingCoordinator does not configure the orchestrator’s new counter. Spend analytics remain unavailable, and enabled budgets can block after the first request.

Prompt for agents
Unify the authoritative raw-output counter used by CostRoutingCoordinator and TaskOrchestrator. A token_counter or postgres_dsn supplied to the coordinator must also drive TaskOrchestrator._trace_budget_spend, _run_budget_output_by_model, and spend_analytics, without overriding an explicitly configured orchestrator counter unexpectedly. Add coverage through build_server for injected and PostgreSQL-backed counters.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

const price = row.price_per_million_usd == null ? "&mdash;" : escapeHtml(row.price_per_million_usd);
const cost = row.estimated_cost_usd == null ? `<span class="chip" title="${escapeHtml(t("spend_no_price_action"))}">${escapeHtml(t("spend_no_price"))}</span>` : ("$" + escapeHtml(row.estimated_cost_usd));
return `<tr><td>${escapeHtml(row.model)}</td><td>${escapeHtml(row.estimated_output_tokens)}</td><td>${escapeHtml(row.step_count)}</td><td>${price}</td><td>${cost}</td></tr>`;
const cost = row.cost_usd == null ? `<span class="chip" title="${escapeHtml(t("spend_no_price_action"))}">${escapeHtml(t("spend_no_price"))}</span>` : ("$" + escapeHtml(row.cost_usd));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Unavailable usage looks unpriced

When token counts are unavailable for a priced model, renderSpend labels its null cost as “No price set.” Operators receive the wrong remediation and can overwrite valid pricing.

Prompt for agents
Render null per-model cost according to both pricing and usage availability. Use the existing price_per_million_usd and usage_source fields to distinguish an unpriced model from unavailable token evidence, and add localized text for the unavailable-cost state in both supported locales.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +5178 to +5185
if (
type(prompt_tokens) is int
and prompt_tokens >= 0
and type(completion_tokens) is int
and completion_tokens >= 0
):
usage = {**reported_usage, "usage_source": "reported"}
measurement_status = "measured"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Malformed totals become measured usage

With valid component counts, _chat_response_sse_chunks copies malformed total_tokens unchanged and marks usage measured. Streaming clients can receive negative, nonnumeric, or inconsistent totals.

Suggested change
if (
type(prompt_tokens) is int
and prompt_tokens >= 0
and type(completion_tokens) is int
and completion_tokens >= 0
):
usage = {**reported_usage, "usage_source": "reported"}
measurement_status = "measured"
if (
type(prompt_tokens) is int
and prompt_tokens >= 0
and type(completion_tokens) is int
and completion_tokens >= 0
):
usage = {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": prompt_tokens + completion_tokens,
"usage_source": "reported",
}
measurement_status = "measured"
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +87 to +100
## References

OpenAI. (n.d.). *Tiktoken model mappings*.
https://github.com/openai/tiktoken/blob/main/tiktoken/model.py

OpenAI. (n.d.). *Chat Completions API reference*.
https://platform.openai.com/docs/api-reference/chat/create

PyO3 Project. (n.d.). *Python modules*.
https://pyo3.rs/main/module

ContextualWisdomLab. (2026). *Cost-aware sync-versus-batch routing*
(ADR 0003).
https://github.com/ContextualWisdomLab/contextual-orchestrator/blob/main/docs/adr/0003-cost-aware-sync-batch-routing.md

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Research artifacts need follow-up

This substantive accounting change adds vendor references but no paper artifact or redistribution note. The repository’s research-grounding rule requires reviewer follow-up.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +7558 to +7563
measurement_status = (
"unavailable"
if not output_available or not prompt_available
else "measured"
if all(row["usage_source"] == "reported" for row in rows)
else "exact_tokenizer"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Partial output evidence stays distinguishable

Exact output counts remain visible when prompt usage is unavailable. Aggregate status and cost still fail closed, so the partial count is not presented as complete usage.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +890 to +891
prompt_tokens=item.prompt_tokens,
completion_tokens=item.completion_tokens,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Zero counts remain measured

Batch retrieval now preserves literal zero counts instead of converting them to missing values. Empty measured results therefore remain distinct from unavailable usage.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

1 participant