feat(agents): opt-in usage tracking and per-round generation ids #5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/agent-usage-tracking"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes the cost gap Orihon's run monitor hit (TeamAI/Orihon Phase 2, cut 5):
AgentResult.AggregatedUsage.Costwas always null because the agent loop never requested the usage block, andRoundDetailcarried no generation id to fetch cost post-hoc.AgentOptions.TrackUsage(default off — the usage block adds a little response latency on some providers): when set,BuildRequestaddsusage: { include: true }to every request, soRoundDetail.Usageand the aggregated usage carry token counts andCost.Clone()carries the flag.RoundDetail.ResponseId— the completion's generation id on every round, so exact cost/routing detail is reachable viaGetGenerationAsynceven with tracking off.4 new tests (flag on every request incl. multi-round, off-by-default, distinct per-round ids, clone). Full suite 143/143 green.
🤖 Generated with Claude Code
Summary
Summary
Coverage
OpenRouter.Net - 69.6%
43F954A3762D43C2CD5DCD5CDF29C19D3789DC49A7DCA1C47__Base64DataUriPattern_0
OpenRouter.Net.Agents - 81.6%
OpenRouter.Net.Imaging - 82.2%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh my~ ♪ A cost-visibility PR that's opt-in by default and threads a generation id through every round so cost is reachable even when tracking is off? Someone's been thinking about latency-sensitive callers. This is the kind of design restraint I adore — the feature is there when you want it, invisible when you don't. Delightful~
Verdict: ✅ Looks good to me~
I tore this one apart looking for a loose thread and... there isn't one. The yandere is pouting because she wanted to find something to fix~ ♡ But correctness first, feelings later.
What I verified, branch by branch:
AgentOptions.TrackUsage— defaultfalse, XML doc explains the latency trade-off honestly.Clone()carries it (line 126, right besideEnableCachingwhere it belongs). ✅BuildRequestline 568 —Usage = _options.TrackUsage ? new UsageOptions { Include = true } : null. I checkedUsageOptions— it's asealed recordwithrequired bool Includeand[JsonPropertyName("include")]. The shape is exactly right. Off-by-default meansnullon the wire → byte-identical requests to pre-PR behavior. Zero drift. ✅RoundDetail.ResponseId—string?(correctly nullable, since a provider could omit it). Doc points atIOpenRouterClient.GetGenerationAsync. I confirmedChatCompletionResponse.Idisrequired string, sosuccess.Value.Idcan never produce a wrong null. ✅new RoundDetail { ... }sites inAgent.cs(no-tool-calls arm at :223, tool-calls arm at :264) and both gotResponseId = success.Value.Id. I grepped to make sure there's no third site that was missed. There isn't. ✅On the tests — these are real tests, not tautologies, and that makes me so happy~ ♪
TrackUsage_asks_for_the_usage_block_on_every_request— enqueues a tool-call + assistant (2 rounds), then assertsr.Usage?.Includeis true on both captured requests viaAssert.All. Proves the flag hits every round, not just the first.Usage_tracking_is_off_by_default— assertsAssert.Single(...).Usageisnull. Pins the opt-in contract.Every_round_carries_its_generation_id— and here's the clever part: this test runs withTrackUsageoff (default). It provesResponseIdis populated independently of usage tracking — exactly the PR body's "even with tracking off" promise. Then assertsDistinct().Count() == 2so you know each round got its own id, not the same one duplicated. That's how you test an invariant.Clone_carries_the_tracking_flag— round-trips the flag throughClone().Coverage (local, since the coverage bot hasn't posted for
0d4e39fyet):Agent.cs: both newResponseIdsites exercised (24 hits no-tool arm, 13 hits tool arm), BuildRequest ternary hit 38×.AgentOptions.cs: 100% line / 100% branch.RoundDetail.cs: 88.8% line / 100% branch.✅ What I liked~
ResponseIddecoupled fromTrackUsage. Cost is reachable post-hoc viaGetGenerationAsynceven when the caller opted out of per-request usage blocks. Two ways to get cost, neither forces the other. Elegant.ResponseId. Most authors would've written that test withTrackUsage = trueand not noticed it muddies the invariant. You left it default-off on purpose and it makes the test mean more.Clone()consistency. Every existing field is copied member-wise; you slottedTrackUsageinto the same cluster without disrupting the pattern.Fufu~ nothing to fix, nothing to block, nothing even worth a "consider also." Go merge it~ ♡
Automated review by Jibril · 2026-07-25
CI/CD: absent for head
0d4e39f(PR just opened, no coverage bot comment yet) · Local checks: build 0/0, 143/143 tests pass, coverage inspected on changed files