Skip to content

fix(budgets): report budget spend from LiteLLM - #207

Open
saurya wants to merge 16 commits into
mainfrom
fix/litellm-budget-reporting-source-of-truth
Open

fix(budgets): report budget spend from LiteLLM#207
saurya wants to merge 16 commits into
mainfrom
fix/litellm-budget-reporting-source-of-truth

Conversation

@saurya

@saurya saurya commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • treat LiteLLM financial data as the source of truth for organization budget reporting
  • compute organization and member cycle spend from cumulative LiteLLM spend minus persisted cycle-start baselines
  • use the same LiteLLM-derived spend for maintenance alert thresholds
  • verify every LiteLLM team/member budget write with a fresh financial-data read before recording synchronization success
  • report missing members, shared-vs-individual cap mismatches, incorrect values, empty verification responses, and verification fetch failures as synchronization errors

Existing override reconciliation

Periodic maintenance already loads every stored override for each enabled non-personal organization and rewrites every LiteLLM team membership. Once this stack is deployed, an existing override whose individual LiteLLM cap is absent or stale is repaired automatically on the next maintenance cycle. No one-time database migration or manual re-save is required.

The main limitation is a stored override whose user has no LiteLLM team membership: synchronization cannot create or verify a member it cannot see. Separate membership repair would be needed if the member was absent before synchronization began.

Stack

Validation

  • PYTHONPATH=. poetry run pytest tests/unit/test_org_budget_service.py -q from enterprise/ — 18 passed
  • poetry run ruff check enterprise/server/services/org_budget_service.py enterprise/tests/unit/test_org_budget_service.py
  • OpenHands-Cloud PR #1117 now applies a real override, clears its real LiteLLM member cap, and verifies periodic maintenance restores the exact stable cap

This pull request was updated by an AI agent (OpenHands) on behalf of the user.


Enterprise server image for this PR:

ghcr.io/openhands/enterprise-server:sha-52e99f5

saurya and others added 11 commits August 11, 2026 18:03
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Renumber the personal organization budget cleanup migration to follow the current revision 147 head and update its regression-test import.

Co-authored-by: openhands <openhands@all-hands.dev>
Persist each governed member's cumulative LiteLLM spend at cycle start and derive their absolute cap from that stable baseline instead of current spend on every sync.

Co-authored-by: openhands <openhands@all-hands.dev>
Use LiteLLM cumulative team and membership spend minus stored cycle baselines for budget UI and alert calculations. Keep per-member baselines for all LiteLLM members so direct SDK/API usage is visible in reporting and threshold checks.\n\nCo-authored-by: openhands <openhands@all-hands.dev>
@github-actions github-actions Bot added the type: fix A bug fix label Aug 19, 2026
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  enterprise/server/services
  org_budget_service.py 70, 74, 81-84, 92-96, 107, 111-113, 121-152, 156, 193-198, 234-251, 309-323, 428-429, 432-439, 442-445, 453-455, 458-481, 493-546, 549-554, 569-573, 605-648, 661-688, 705-735
Project Total  

This report was generated by python-coverage-comment-action

Base automatically changed from fix/user-budget-cycle-baselines to main August 20, 2026 14:04
Resolve billing behavior in favor of the latest main hotfix while preserving LiteLLM budget reporting and synchronization verification.

Co-authored-by: openhands <openhands@all-hands.dev>
@ak684

ak684 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Ok, so looking through at the code some more, I think with some iterations we can thread the needle here with the idea LiteLLM as the source of truth for spend metering and enforcement.

(1) I agree the LiteLLM response contract is a risk, but I think we can account for it. One approach is to put a strict adapter at the boundary: validate the fields and types we actually depend on, isolate that contract in one place, and treat missing or malformed data as unavailable rather than silently as $0 (which is what can happen now).

(2) Catching network errors at the boundary is reasonable, but I think the current fallback behavior in #207 could cause issues because it allows a failed or empty fetch to appear as $0 spend. During a reset, that can persist a zero baseline, and the next sync can set an absolute LiteLLM cap below existing cumulative spend. I think reads should use a persisted last-known-good snapshot and explicitly report live/stale/unavailable, while rollover or other baseline-changing writes should require a fresh validated snapshot and abort if one isn't available.

(3) I think the TOCTOU gap is inherent if LiteLLM remains the enforcement point. The dashboard should be treated as an eventually consistent projection, not a promise that the next request will be admitted. We can make that honest by displaying the snapshot timestamp/freshness and explaining that enforcement happens in LiteLLM. Pub/sub would reduce lag, but it wouldn't make a UI read atomic with a later inference request, so I don't think we need it here even in an ideal solution.

Overall, my core concern with #207 as written isn't that LiteLLM is the spend source of truth, it's that unavailable data is currently indistinguishable from real zero spend, and the same unvalidated data can influence durable cycle baselines and absolute caps. There are also a few implicit refetches that could make the app both slow and semantically unsafe during an outage.

I drafted a PR on top of the really solid parts of #207 to demonstrate some additional safeguards: #242. Feel free to check it out and let's continue discussing so we can get to a better version of budgets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants