Skip to content

feat: ✨ Cost emission on event stream - #907

Merged
evaline-ju merged 4 commits into
rossoctl:mainfrom
evaline-ju:cost-emission
Sep 8, 2026
Merged

feat: ✨ Cost emission on event stream#907
evaline-ju merged 4 commits into
rossoctl:mainfrom
evaline-ju:cost-emission

Conversation

@evaline-ju

@evaline-ju evaline-ju commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

The litellm-budget-track plugin already computes each response's cost but keeps it inside the plugin. This change surfaces the cost value on the session-event stream and how it was derived (gateway header vs. token-fallback), the post-add daily total, and the configured cap.

Enables:

  • /v1/usage Pricer can be wired to this stream
  • Any consumer of SessionEvent.Plugins (abctl, external observability, future summary endpoints) sees per-response USD
  • Cost provenance is explicit — displays can distinguish authoritative gateway figures from usage-fallback estimates.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

Related issue(s)

(Optional) Testing Instructions

Summary by CodeRabbit

  • New Features

    • Added cost tracking events for priced responses, including the response cost, pricing source, updated daily total, and configured daily limit.
    • Supports pricing from gateway-provided costs or usage-based fallback calculations.
    • Unpriced responses do not generate cost events.
  • Documentation

    • Added guidance describing cost event fields, availability, and how the information appears in session event data.

Signed-off-by: Evaline Ju <69598118+evaline-ju@users.noreply.github.com>
Signed-off-by: Evaline Ju <69598118+evaline-ju@users.noreply.github.com>
@evaline-ju
evaline-ju requested a review from a team as a code owner September 8, 2026 19:32
@evaline-ju evaline-ju changed the title feature: ✨ Cost emission on event stream feat: ✨ Cost emission on event stream Sep 8, 2026
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a7b81df3-6d65-49da-8547-2c213d0a6078

📥 Commits

Reviewing files that changed from the base of the PR and between fba160f and d914746.

📒 Files selected for processing (2)
  • authbridge/authlib/plugins/litellm_budgettrack/plugin.go
  • authbridge/authlib/plugins/litellm_budgettrack/plugin_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • authbridge/authlib/plugins/litellm_budgettrack/plugin.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The LiteLLM budget tracking plugin emits a costEvent for each priced response. Events include cost, pricing source, cumulative daily total, and configured daily cap. Tests and documentation cover buffered pricing, streaming fallback, and unpriced responses.

Changes

LiteLLM cost event emission

Layer / File(s) Summary
Cost event contract and emission
authbridge/authlib/plugins/litellm_budgettrack/plugin.go
The plugin records pricing results, identifies gateway-header and usage-fallback sources, and writes costEvent data to pctx.Extensions.Custom.
Cost event validation and documentation
authbridge/authlib/plugins/litellm_budgettrack/plugin_test.go, authbridge/docs/litellm-budgettrack-plugin.md
Tests validate event fields, cumulative totals, configured limits, and no-event cases. The design document describes session-event fields and promotion behavior.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to d9147

Priced LiteLLM responses now expose per-response cost, pricing source, daily total, and configured cap to session-event consumers. The supported pricing and no-cost paths are covered, with no concrete merge-blocking risk identified.

Sequence Diagram(s)

sequenceDiagram
  participant ResponseHandler
  participant BudgetLedger
  participant SessionExtensions
  participant SessionEvent
  ResponseHandler->>BudgetLedger: Accumulate response cost
  BudgetLedger-->>ResponseHandler: Return daily total and added status
  ResponseHandler->>SessionExtensions: Store costEvent
  SessionExtensions->>SessionEvent: Promote plugin event
Loading

Suggested reviewers: aslom

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the primary change: emitting cost data on the event stream. The feature prefix and emoji add minor noise but do not prevent the title from being clear and relevant.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@huang195 huang195 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Clean, well-scoped change. Surfacing the already-computed cost is the right move, and making provenance explicit (gateway-header vs usage-fallback) is genuinely new information for consumers — a display can now distinguish an authoritative gateway figure from a token-derived estimate rather than presenting both as equally trustworthy.

What I verified:

  • Convention: the pctx.Extensions.Custom[p.Name()+pipeline.PluginEventSuffix] keying matches toolprune's pattern exactly, and a struct value marshals cleanly through SnapshotPlugins.
  • Correctness of source: traced all four paths. source is only reassigned inside the !present || isEventStream(pctx) fallback branch, so a header-priced response keeps gateway-header and a present-0 non-streamed response (genuine free call) still emits nothing. The doc's claim that unpriced responses produce no event holds on every path.
  • Concurrency: no new race. accumulate captures the post-add total inside the existing mutex, p.cfg is read-only after Configure, and TestConcurrentOnResponse gives each goroutine its own pipeline.Context — so the new unsynchronized Extensions.Custom write isn't shared. CI runs go test -race and is green.
  • Docs: the plugin-reference.md#emitting-session-events anchor resolves.

The added bool return threading emission through the same chokepoint that guards the ledger is a nice touch — it means the event stream and the ledger can't disagree about whether a call was charged.

One suggestion worth considering (below) about the 429 deny path emitting nothing; the other two comments are minor.

Author: evaline-ju (MEMBER — maintainer)
Areas reviewed: Go (authlib plugin + tests), Docs
Agent/IDE config (.claude/.vscode): none
Commits: 2 commits, all signed-off: yes
CI status: passing (23 checks pass, Spellcheck skipped)

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com


// emitCost writes the costEvent to pctx.Extensions.Custom; the listener
// forwards it to SessionEvent.Plugins under the plugin name.
func (p *BudgetTrack) emitCost(pctx *pipeline.Context, cost float64, source string, dailyTotal float64) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

suggestion — This plugin calls pctx.Record(pipeline.Invocation{...}) nowhere, unlike every other gate plugin (ibac, jwtvalidation, cpex all record explicitly). Two consequences:

  1. litellm-budget-track never appears in the per-plugin invocation timeline, so abctl shows the cost payload under Plugins but no row saying this plugin ran.
  2. More pointedly: the OnRequest 429 budget.exceeded deny path emits neither an Invocation nor a cost event. Per the session-API contract, rejected requests only land as phase: "denied" events when at least one plugin appended an Invocation before rejecting — so the single moment an operator most wants the spend number ("why was I cut off, and at what total?") is the one moment nothing is published.

Both are pre-existing and not introduced by this PR, but (2) is directly adjacent to its stated goal of making per-response USD visible to SessionEvent.Plugins consumers. A pctx.Record(pipeline.Invocation{Action: pipeline.ActionDeny, Reason: "budget.exceeded"}) alongside the DenyStatus — and an ActionObserve record here in emitCost — would close it. Reasonable as a follow-up if you'd rather keep this PR to the success path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

tracking in follow-up issue #908

Comment thread authbridge/authlib/plugins/litellm_budgettrack/plugin.go Outdated
Comment thread authbridge/authlib/plugins/litellm_budgettrack/plugin_test.go
Signed-off-by: Evaline Ju <69598118+evaline-ju@users.noreply.github.com>
Signed-off-by: Evaline Ju <69598118+evaline-ju@users.noreply.github.com>
@evaline-ju
evaline-ju merged commit db3197d into rossoctl:main Sep 8, 2026
24 checks passed
@github-project-automation github-project-automation Bot moved this from New/ToDo to Done in Rossoctl Issue Prioritization Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants