fix(cost_router): record unavailable race-loser usage instead of dropping it - #955
Conversation
…ping it _record_race_endpoint_usage silently discarded ledger evidence for a completed (billable) race-losing endpoint call whenever its usage payload could not be parsed (missing/malformed usage field), unlike every other completion path in this module. record_stream_usage already handles the analogous "call happened, can't measure it" case by writing a measurement_status="unavailable" row with zero counts; apply the same pattern here so duplicate-cost race calls are never invisible to cost_report()/rollup()/total(). Resolves agent lookup before branching on parsed usage (needed by both branches) and reuses the resulting provider/model tuple for both the new "unavailable" ledger write and the existing "measured" path.
|
Warning Review limit reachedNext included review available in 57 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthrough레이스 엔드포인트의 사용량 payload를 파싱할 수 없을 때 비용 원장에 Changes비용 원장 기록
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: 🟡 Moderate · up to The PR records previously missing race-loser usage, but a completion can still be reported as fully measured when one call’s cost is unavailable, potentially misleading cost audits. Merge should wait for that status to propagate correctly or for explicit owner acceptance. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
|
Generated by Claude Code |
|
@opencode-agent please review this draft PR. Generated by Claude Code |
…ge-unavailable-record
Devin review on #955: this PR's own fix writes a measurement_status="unavailable" ledger row for a race-loser call whose usage couldn't be parsed, but complete()'s two cost-aggregation blocks (the provider_request/race-proxy path and the ordinary orchestrator.run() sync path) only checked for "estimated" when rolling records up into the response's cost dict. A measured winner plus this new "unavailable" loser row still reported the whole completion as confidently "measured" and silently summed the loser's unknown cost as 0. Both paths now use the same unavailable-outranks-estimated-outranks- measured precedence record_stream_usage() already uses, and cost_amount becomes None rather than a partial sum whenever any contributing record is unavailable. New end-to-end regression tests exercise both aggregation paths through complete() with an unparseable race-loser usage payload.
|
Root cause is fixed in #979 (draft, fully validated), not yet merged so it doesn't help this specific re-run. Re-ran the failed job once (first occurrence of this cause on this PR); watching #979 to landing, which should let this self-heal. Generated by Claude Code |
|
Exact-head audit found the completion-response fixes correct but two downstream false-zero surfaces remained. An unavailable race-loser row was still summed as Fixed in de6c995: ledger aggregates use unavailable > estimated > measured precedence and null cost when any contributing usage is unavailable; telemetry always exports the record counter and measurement-status attribute, but omits token/cost metrics whose values are unknown. Measured and estimated numeric behavior is unchanged. Validation: 126 focused router/ledger/metering tests passed; Ruff passed; interrogate 100%; diff check passed. Existing review threads were already resolved. Previous Noema failed on the 120-second LLM review call timeout; coverage-evidence remained queued with no runner or steps. Hosted gates must rerun on this exact head; no bypass or merge was attempted. |
|
Cross-PR integration contract: routing identity is provider-neutral |
|
Acknowledged. This PR's diff ( Generated by Claude Code |
|
Contextual-Orchestrator와 관계한 것들을 같이 손보든 어쩌든 해결하세요. Bypass merge 필요하면 가능 (chicken and eggs 상황이라면) + NVIDIA NIM 만 쓰는 건 허용하지 않아요. Contextual-Orchestrator를 쓰세요. Timeout은 적어도 3시간으로 잡으세요. 120초 같은 건 당황스럽군요. Opencode와 Noema 는 Coderabbitai 및 Devin 수준으로 실제로 리뷰를 하게 하시오. Strix도 보안 리뷰를 꼼꼼하게 하도록 하시오. 특히 보안 리뷰는 전체 코드로 수행하는 것입니다. Contextual-Orchestrator는 실시간으로 빠르면서 능력이 좋은 모델에 요청을 보내어 시간을 당기시오. |
|
The Generated by Claude Code |
|
The job ran its normal single-shot sequence: queried Reviews API for an Expected resolution: once the dispatched review posts a verdict against Generated by Claude Code |
| statuses = {record.measurement_status for record in records} | ||
| aggregate_measurement_status = ( | ||
| "unavailable" if "unavailable" in statuses | ||
| else "estimated" if "estimated" in statuses | ||
| else "measured" |
There was a problem hiding this comment.
🔴 Late losers leave costs measured
When a loser finishes after winner publication, statuses excludes its unavailable record. The response reports measured cost before the loser enters the ledger.
Prompt for agents
The sync and provider-request completion paths aggregate race records immediately after race_first_valid returns, but endpoint_race.py deliberately safe-drains uncancellable loser futures without waiting. A loser can invoke _record_race_endpoint_usage after race_records and statuses have already been copied, causing the response to omit its cost and retain a measured status. Coordinate race finalization with cost aggregation so every started loser is either drained into the current response's records or represented as unavailable before the response cost is built. Preserve the existing deadline and cancellation behavior, and apply the fix to both complete() branches.
Was this helpful? React with 👍 or 👎 to provide feedback.
Bug
This repo's
CLAUDE.md/AGENTS.mdmandate: "Equivalent model-group endpoints may race only through the normalized, explicit endpoint-equivalence contract. Preserve modality validation, bounded concurrency, deadline, cancellation/drain provenance, and honest duplicate-cost evidence."CostRoutingCoordinator._record_race_endpoint_usage()(contextual_orchestrator/cost_router.py) is the sink the orchestrator calls for every completed non-winning endpoint in a multi-endpoint model-group race (wired viaorchestrator._race_usage_sink, and invoked only for race participants whose HTTP call to the provider actually completed and returned a response body —_race_attempt_collector.emit()inorchestrator.pyreturns early whenerror is not None or value is None). A losing race branch is, by definition, a duplicate paid call.Previously, when
self._provider_usage(usage)returnedNone(usage payload missing or malformed — a real provider quirk, not hypothetical:usagenot a dict, orprompt_tokens/completion_tokensnot non-negative ints), the function returned immediately with no ledger row created at all. That spend became permanently invisible tocost_report()/rollup()/total()— no row, no "unavailable" label, nothing a cost audit could catch.This was inconsistent with every other completion path in the same class:
complete()'s ordinary sync/provider-proxy paths always call_record_completioneven when usage can't be parsed, falling back to heuristic estimation (measurement_status="estimated").record_stream_usage()always creates a row, even whencountsis falsy (prompt_tokens=counts[0] if counts else 0,measurement_status="measured" if counts else "unavailable") — this is exactly the "we know a call happened but can't measure it" case._record_race_endpoint_usagewas the only one of these sinks that could return without ever callingself.ledger.record_usage(directly or via_record_completion).Fix
Mirrors
record_stream_usage's pattern: when_provider_usage()returnsNonefor a race-loser call, write ameasurement_status="unavailable"ledger row withprompt_tokens=0/completion_tokens=0instead of returning silently. Deliberately does not route this case through_record_completionwithNonecounts — that path falls back to heuristic estimation frommessages/answer, which are empty in this context, and would silently produce a fake near-zero "estimated" cost instead of an honest "unavailable" one. Callsself.ledger.record_usage(...)directly instead, same asrecord_stream_usage.Also resolves
agentbefore branching on parsedcounts(needed by both branches either way) and reuses the resulting(provider, model)tuple for both the new "unavailable" write and the existing "measured" path — avoids computing it twice.Scope is intentionally minimal — only this one function changed.
Test
Added
test_race_loser_with_unparseable_usage_is_recorded_as_unavailableintests/test_cost_router.py, alongside the existing race-usage tests. It sets usage toNonefor a race-loser call and asserts the ledger now contains exactly one row withmeasurement_status == "unavailable",prompt_tokens == 0,completion_tokens == 0, and correct provider/model/workflow_run_id — previously this scenario produced an empty ledger.Evidence
python -m pytest tests/test_cost_router.py -q→ 26 passed (including the new regression test).python -m pytest tests -q(full suite) → 2832 passed, 1 skipped, 1 pre-existing failure (test_fast_mlsirm_fit_uses_judge_acceptance_item_for_context_score,ModuleNotFoundError: No module named 'fast_mlsirm'— unrelated to this change; also called out as a pre-existing environment gap in fix(privacy): enforce OpenRouter ZDR at request time #953's own test plan).interrogate -v→ 100.0% (RESULT: PASSED (minimum: 100.0%, actual: 100.0%)).cost_router.pyfromtest_cost_router.pyalone confirms the new "unavailable" branch (the added lines) is fully exercised — none of its line numbers appear incoverage report's Missing column.No production routing/defaults changed; this only affects what gets written to the cost ledger when a race-loser's usage payload is unparseable.
Generated by Claude Code
Summary by CodeRabbit
버그 수정
테스트