Bill eval-driven generation as evaluation spend - #3993
Conversation
Eval runs deliberately write no Trace rows, but the spend is real. See ADR-0049. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The eval channel gets a UsageOnlyTracer as its sole tracer, so generation spend is recorded as evaluation spend without resurrecting per-message traces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
📝 WalkthroughWalkthroughEvaluation-driven bot generation now receives an evaluation-specific usage tracer that records Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/evaluations/tests/test_generation_usage.py (1)
14-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLazy-load the LangChain test dependency.
langchain_corematches the prohibited module-levellangchain_*import pattern and is only needed by_generation_service.Proposed fix
-from langchain_core.messages import AIMessage - def _generation_service(input_tokens: int = 1000, output_tokens: int = 500): + from langchain_core.messages import AIMessage +As per coding guidelines, local imports are allowed to “reduce startup time”; as per path instructions, heavy
langchain_*imports must be lazy-loaded.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/evaluations/tests/test_generation_usage.py` at line 14, Remove the module-level AIMessage import and lazy-load AIMessage inside _generation_service, where it is used. Keep the existing message construction behavior unchanged while ensuring no langchain_* dependency is imported during module initialization.Sources: Coding guidelines, Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/adr/0049-eval-driven-generation-is-evaluation-spend.md`:
- Around line 1-5: Add YAML frontmatter to ADR-0049 with status: active while
the ADR is proposed and under development; update it to stable after shipping
and before extraction, preserving the existing HTML status marker.
---
Nitpick comments:
In `@apps/evaluations/tests/test_generation_usage.py`:
- Line 14: Remove the module-level AIMessage import and lazy-load AIMessage
inside _generation_service, where it is used. Keep the existing message
construction behavior unchanged while ensuring no langchain_* dependency is
imported during module initialization.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 9908b496-5e3b-48e4-9e80-5b9ed0ac1b16
📒 Files selected for processing (18)
apps/admin/queries.pyapps/channels/evaluation_channel.pyapps/channels/tasks.pyapps/channels/tests/channels/concrete/test_evaluation_channel.pyapps/cost_tracking/models.pyapps/cost_tracking/services/recorder.pyapps/cost_tracking/services/reporting.pyapps/evaluations/tasks.pyapps/evaluations/tests/test_evaluation_tasks.pyapps/evaluations/tests/test_generation_usage.pyapps/evaluations/usage.pyapps/service_providers/tracing/const.pyapps/service_providers/tracing/ocs_tracer.pyapps/service_providers/tracing/tests/test_ocs_tracer_cost.pyapps/service_providers/tracing/usage_tracer.pydocs/adr/0049-eval-driven-generation-is-evaluation-spend.mddocs/adr/index.mdmkdocs.yml
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Renumber ADR-0049 to ADR-0050; main took 0049. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes #3985.
Product Description
Cost reporting now includes what an evaluation run's bot generation spent. Team totals rise for teams running generation evals — on money that was previously not recorded at all. No chatbot, participant or conversation gets more expensive.
Technical Description
The issue assumed generation was already recorded as
source=chatand that this PR would reclassify it. It wasn't recorded at all:EvaluationChannelhas run with no tracers since Oct 2025, so generation wrote noTraceand therefore noUsageRecord. The fix is a write-side addition, not a reclassification — hence no backfill.Turning
OCSTracerback on for eval channels would resurrect what was deliberately switched off (oneTraceper evaluated message, outliving the pruned eval session, plus error notifications on eval failures). Instead there's aUsageOnlyTracer— the billing half ofOCSTracerwith the rest dropped — installed as the eval channel's sole tracer. ADR-0050 has the full reasoning and rejected alternatives.Worth review attention:
_usage_source()onOCSTracerclassifies bysession.platform, which the issue settled as the right signal. It's belt-and-braces: eval generation never reaches this tracer, but other paths reach an eval session (a static trigger firing on one). Read at finalisation, sinceset_sessionback-fills mid-trace.usage_tracer: Tracer | Noneonhandle_evaluation_messageis deliberately the general type rather thanUsageOnlyTracer, to keepapps/channelsoff cost tracking. It means nothing stops a caller passing anOCSTracerand reintroducing the trace rows this avoids._create_trace_serviceonEvaluationChannelsetsself._usage_tracerbeforesuper().__init__(), which builds the service from it.An eval session's cost page now reports $0 (both halves are
source=EVALUATION,session_usageis chat-only) — #3981. The admin report's token and cost halves diverge further until #3984 moves token reporting ontoUsageRecord.Migrations
N/A — no schema change.
Docs and Changelog
Changelog note: per-team cost totals now include evaluation bot generation, which was previously unrecorded. Existing rows are unchanged.