Commit graph

235 commits

Author SHA1 Message Date
devin-ai-integration[bot]
996ee5635a
perf(proxy): pipeline spend counter increments into one Redis call per request (#40371)
* perf(proxy): pipeline spend counter increments into one redis call

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): apply surviving spend increments before raising scope error

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* style(proxy): ruff format spend counter helpers

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): settle inner spend counter gathers and fall back per key on pipeline failure

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): suppress BLE001 on pipeline fallback catch

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): invalidate all batched spend counters on pipeline failure

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-09 10:35:50 -07:00
Mateo Wang
f8e456d105
Merge pull request #40275 from BerriAI/litellm_lit6852_spend_attribution
fix(spend-tracking): recover key alias for session tokens from spend logs
2026-09-08 19:05:43 -07:00
Mateo Wang
599daea985
Merge pull request #36718 from BerriAI/litellm_fix_count_tokens_budget_reservation_leak
fix(budget_reservation): don't reserve budget on token counting routes
2026-09-08 17:29:27 -07:00
mateo-berri
268b944081 fix(spend-tracking): bound the spend-log scan with a statement timeout and name only unanimous alias, team, and owner 2026-09-08 17:24:22 -07:00
mateo-berri
7a6c0cbf08 fix(spend-tracking): drop the owner of a digest shared by several users and back off failed scans 2026-09-08 16:43:28 -07:00
Mateo Wang
5b2b5420af
Merge pull request #39626 from BerriAI/litellm_batch_ui_logs
Some checks failed
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests / caching-local (push) Waiting to run
Unit Tests / core-utils (push) Waiting to run
Unit Tests / enterprise-package (push) Waiting to run
Unit Tests / enterprise-routing (push) Waiting to run
Unit Tests / integrations (push) Waiting to run
Unit Tests / All Other Providers (push) Waiting to run
Unit Tests / Vertex AI (push) Waiting to run
Unit Tests / misc (push) Waiting to run
Unit Tests / proxy-auth (push) Waiting to run
Unit Tests / proxy-endpoints (push) Waiting to run
Unit Tests / proxy-extras (push) Waiting to run
Unit Tests / proxy-infra (push) Waiting to run
Unit Tests / proxy-server (push) Waiting to run
Unit Tests / responses-caching-types (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
feat(ui): batch observability on the logs page
2026-09-08 15:19:25 -07:00
mateo-berri
907c200b31 fix(spend-tracking): run one spend-log scan at a time and back off repeated misses
Concurrent usage reads on one worker now share a single spend-log query
instead of each scanning the same window, and a digest that comes back
nameless a second time is remembered for the full ten minutes rather than
thirty seconds, so a key that never resolves costs at most two scans per
worker per window per ten minutes. The first miss still expires after
thirty seconds so a read that lands between the daily spend flush and the
spend-log flush recovers on the next read
2026-09-08 15:00:56 -07:00
mateo-berri
0c2d0f4777 fix(spend-tracking): forget an empty spend-log lookup after thirty seconds
The daily spend rows and the spend logs of one batch are written a moment apart, so a usage read landing between them used to remember the session as nameless for ten minutes on that worker. Found identities keep the ten minute entry
2026-09-08 13:28:13 -07:00
mateo-berri
550e733bf3 perf(spend-tracking): recover key identity in one bounded spend-log pass with a per-worker cache
The spend-log lookup for permanently unresolvable digests is back to a single DISTINCT ON scan over the requested window, keeping only rows that carry an alias, user, or team so a newer nameless row cannot hide an older named one. Results and misses are cached per worker for ten minutes keyed by digest and window, failed queries are not cached, and JWT rows keyed hashed-jwt-<sha256> now pass the digest gate. Tests cover the JWT gate, cache reuse and partial misses, window changes, error handling, the with-window guard, and the daily activity wiring
2026-09-08 13:09:43 -07:00
mateo-berri
cf0488316e fix(spend-tracking): take each unresolved key's newest spend row and drop docstrings 2026-09-08 12:40:28 -07:00
mateo-berri
2285640eea fix(spend-tracking): recover key alias for session tokens from spend logs
CLI session tokens are in-memory only and never get a LiteLLM_VerificationToken
row, so the usage APIs could not resolve key_alias, team_id, or user_email for
their spend rows: the exact join and the reverse-hash recovery both miss. The
owner is written to LiteLLM_SpendLogs.metadata at request time under the same
hashed api_key, so read it back from there for keys still unresolved after the
token-table passes.

The lookup is sha256-gated like the existing reverse-hash recovery and bounded
to the records' startTime window (min date minus one day, max date plus two) so
it stays on the startTime index. No migration.

Also guard the window parser against the date=None rollup rows GROUPING SETS
aggregation emits, which raised TypeError from strptime and turned the
aggregated usage endpoints into HTTP 500s.
2026-09-08 12:02:39 -07:00
tin-berri
1a6aa98230
fix(spend): compare auto-router targets by deployment identity (#40206)
Preserve deployment identity through savings calculation, with canonical model fallback only when either ID is absent. Cover negotiated rates, unchanged deployments, alias/base-model cache accounting and missing IDs.

Fixes #38811. Based on the deployment-identity approach proposed by @QuantumBreakz in #38834.

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-07 23:29:42 -07:00
mateo-berri
9c980b96d6 fix(budget_reservation): exempt vertex and bedrock count-tokens routes from budget reservation
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-09-07 18:44:04 -07:00
mateo-berri
fb7d06da4b test(budget_reservation): type the tiny-budget reservation helper 2026-09-07 17:45:53 -07:00
mateo-berri
31bd00dad9 fix(budget_reservation): merge staging and cover count_tokens routes in the mapped test 2026-09-07 17:11:33 -07:00
tin-berri
c5ec2eedc1
fix(spend): price caching savings on the billed request basis (#40160)
Resolves LIT-7137

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-07 21:41:05 +00:00
Mateo Wang
2cfe106f9f
Merge pull request #39660 from BerriAI/litellm_bedrock_passthrough_model_access
fix(proxy): enforce key and team model access on Bedrock passthrough routes (internal copy of #34244)
2026-09-07 12:11:15 -07:00
Mateo Wang
9b64f8367e
Merge branch 'litellm_internal_staging' into litellm_batch_ui_logs 2026-09-04 20:07:32 -07:00
mateo-berri
01dd000ccb Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_spend_logs_keep_service_account_key_readable 2026-09-04 16:33:45 -07:00
mateo
93abc3a0cd Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_registry_audit_2026_09_02 2026-09-04 19:02:44 +00:00
mateo-berri
7e51fbc819 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_batch_ui_logs
# Conflicts:
#	tests/proxy_unit_tests/test_check_batch_cost.py
2026-09-04 12:00:14 -07:00
Mateo Wang
04a198e3e3
Merge pull request #39568 from BerriAI/litellm_fix-batch-spend-key-double-hash-bcae
fix(spend-tracking): keep batch spend keys joinable after v1.99 provenance gate
2026-09-04 10:47:34 -07:00
mateo
08bfdadb10 chore: merge litellm_internal_staging into litellm_registry_audit_2026_09_02, drop the deleted ocr ledger
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-04 04:18:09 +00:00
Cursor Agent
048499cdf5
fix(spend): only reverse-hash export rows whose key alias join missed
Team and service keys often have no user_email after a successful token
join. Treating empty email as a miss hashed every verification token on
routine CloudZero and Focus exports.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-04 01:44:18 +00:00
Cursor Agent
33815682bc
fix(spend): keep CloudZero and Focus spend-user email when filling alias
Export rows already join user_email from DailyUserSpend.user_id. Recovered
key-owner email must not replace that when only the alias join missed.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-04 01:38:26 +00:00
mateo-berri
ce95afe2bd fix(spend-tracking): reverse-hash dirty spend keys in Postgres instead of paging token tables 2026-09-03 17:58:17 -07:00
mateo-berri
533a1c959c fix(proxy): reserve budget for the Bedrock Converse prompt, not the context window
Resolving the model from the /bedrock path sends passthrough calls through
optimistic budget reservation, whose tokenizer cannot walk Converse content
blocks and so fell back to the model's max_input_tokens. Count those messages
as text and read inferenceConfig.maxTokens so a budgeted key reserves the
request's cost.
2026-09-03 17:42:46 -07:00
devin-ai-integration[bot]
cf3af0f486
perf(spend): group /spend/logs summary by day in Postgres instead of per-row Prisma group_by (#39351)
* perf(spend): group /spend/logs summary by day in Postgres instead of per-row Prisma group_by

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(spend): simplify /spend/logs daily summary aggregation

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend): preserve spend logs response schema

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend): compare spend log range bounds as naive UTC timestamps

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: cover spend logs summary edge cases

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: cover spend summary request filters

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet lint budgets after merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 23:53:59 +00:00
mubashir1osmani
bb5a960317 Merge remote-tracking branch 'berri/litellm_internal_staging' into litellm_batch_ui_logs 2026-09-03 19:45:38 -04:00
mateo-berri
f1f0294796 Merge branch 'litellm_internal_staging' of https://github.com/BerriAI/litellm into litellm_fix-batch-spend-key-double-hash-bcae 2026-09-03 16:36:12 -07:00
ryan-crabbe-berri
a53c550951
Merge pull request #39661 from BerriAI/litellm_lit_4741_copy_id_search
feat(ui): find rows by a pasted ID on keys, agents, memory, audit, and request logs
2026-09-03 16:17:59 -07:00
ryan-crabbe-berri
9464888ee9 test(proxy): pass search=None in direct ui_view_spend_logs calls
Calling the endpoint without going through FastAPI leaves the new search param set to its Query default object, which is not None, so the grouped-session and request_id lookup tests started taking the search branch

Claude-Session: https://claude.ai/code/session_01Q5sbiogJzPcCRmYSbaHxZf
2026-09-03 16:04:41 -07:00
ryan-crabbe-berri
9baa19c7d1 fix(proxy): stop hashing raw sk- values in list searches
The search= param on /key/list, /audit, and /spend/logs/ui, plus key_hash= on /key/list, now compare the pasted value verbatim. Only a copied key ID (the hash) matches, so a raw virtual key never needs to travel in a GET query string

Claude-Session: https://claude.ai/code/session_01Q5sbiogJzPcCRmYSbaHxZf
2026-09-03 15:54:42 -07:00
ryan-crabbe-berri
4811041048 feat(proxy): add a search param to key, memory, audit, and spend log listings
GET /key/list?search= matches the key hash (a raw sk- key is hashed
first) or a case-insensitive alias substring, and key_hash= now hashes a
raw sk- value too. GET /v1/memory?search= matches a key prefix or an
exact memory_id. GET /audit?search= matches id, object_id, changed_by,
or changed_by_api_key. GET /spend/logs/ui?search= matches request_id
across all time and api_key, team_id, user, end_user, session_id, or
model_id inside the date window; session grouping is skipped while a
search is active.

Claude-Session: https://claude.ai/code/session_01Q5sbiogJzPcCRmYSbaHxZf
2026-09-03 15:20:13 -07:00
Mateo Wang
117ef33a41
Merge pull request #39541 from BerriAI/litellm_spend_logs_bridged_streaming_message_id
fix(anthropic_messages): key bridged streaming spend rows on the streamed msg_ id
2026-09-03 14:36:51 -07:00
Mateo Wang
0c8510d47c
Merge pull request #39511 from BerriAI/litellm_spend_logs_provider_response_id
fix(spend_tracking): key /v1/messages spend rows on the msg_ id the client received
2026-09-03 14:35:50 -07:00
mubashir1osmani
c276813cb4 feat(batches): enrich batch cost rows with breakdown, identity, session, and org spend 2026-09-03 17:23:53 -04:00
mateo
5a3a2f3d0a Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_registry_audit_2026_09_02 2026-09-03 21:09:30 +00:00
ryan-crabbe-berri
1c14ded0e4
Merge pull request #39598 from BerriAI/litellm_lit_4929_session_token_aggregation
fix(ui): aggregate session token usage in the logs table
2026-09-03 12:57:27 -07:00
mateo
2c63095e83 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_registry_audit_2026_09_02 2026-09-03 19:02:19 +00:00
devin-ai-integration[bot]
e046aee3d5
fix(spend_tracking): add missing_session_id: omit to leave SpendLogs.session_id null without a client session (#39458)
* fix(spend_tracking): leave SpendLogs.session_id null when no client session id was established

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore(lint): ratchet basedpyright budget after session_id fix

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend_tracking): ignore trace ids as session ids

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend_tracking): gate null SpendLogs.session_id behind missing_session_id: omit

Unset, generate and reject keep the legacy trace id fallback. omit records only
metadata.session_id, the key Langfuse reads, so a trace id copied into
litellm_session_id by get_litellm_params never becomes a session.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend_tracking): stamp the omit decision on the request so a config reload cannot fabricate a session

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend_tracking): keep omit covering requests the pre-call stamp never reaches

Router-model provider pass-through calls allm_passthrough_route directly and
skips add_litellm_data_to_request, so those requests never run the pre-call
helper and carry no omit stamp. Reading only the stamp made POST
/anthropic/v1/messages write a fabricated uuid into SpendLogs.session_id under
missing_session_id: omit while its Langfuse trace had no session, the exact
divergence the policy exists to remove.

The stamp now only pins omit on, and an unstamped request falls back to the
configured policy, so a config reload still cannot fabricate a session for a
request that was decided pre-call.

* fix(spend_tracking): make the session-omission marker proxy-owned so clients cannot forge it

* fix(spend_tracking): strip the client-sent omission marker from both metadata buckets before they merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(spend_tracking): strip the session-omission marker from both metadata buckets

The pre-call policy ran before litellm_metadata is merged into metadata, so a
client that planted the marker in litellm_metadata had it copied back into the
route's own bucket after the strip and still got a null SpendLogs.session_id.

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-09-03 11:57:56 -07:00
ryan-crabbe-berri
b3325750ae fix(ui): aggregate session token usage in the logs table
The logs table already rolled up cost per session but the Tokens column only showed the representative call's usage. The per-session aggregate query now also sums prompt, completion and total tokens, and the Tokens cell switches to those sums for multi-call sessions the same way the Cost cell does.

Claude-Session: https://claude.ai/code/session_01CNasFqyjnLN3Rqman25vde
2026-09-03 11:09:44 -07:00
devin-ai-integration[bot]
29c37141c3
feat(ui): keyset-paginate request logs by session trace (#38794)
* feat(ui): keyset-paginate request logs by session trace

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): keep session grouping within type discipline budget

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* style(proxy): ruff format session grouping helpers

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): only group sessions when group_by_session is an explicit true

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ui): reset session cursor on custom range and live tail toggles

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(ui): cover cursor reset on custom range toggle

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ui): ignore next page clicks while the grouped page is still fetching

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ui): only block next page while grouped placeholder data is shown

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yassin <yassin@berri.ai>
2026-09-03 17:27:05 +00:00
Mateo Wang
34d4f7f8ae
fix: 1.99.0-rc2 UI bug batch (empty org on key create, session pagination, access group rename/delete) (#39436)
* fix(ui): clearing the organization picker no longer sends organization_id="" on key create

* fix(proxy): paginate Request Logs by conversation and aggregate session type counts and models server-side

* fix(proxy): keep access groups in sync when a model is renamed or deleted

* fix(proxy): cap the Request Logs conversation total like the row total

* fix(proxy): judge access group backing by the database for db models

A worker whose router has not polled the database yet still lists a sibling under its old
name, so a delete or rename handled there kept the stale name in every access group. Only
config-sourced deployments count as router backing now; db models are counted in the table.

* fix(ui): keep the conversation badge when an MCP call represents a conversation

A conversation that straddles the bounded page window can be represented by one of its MCP
rows, which showed a plain MCP badge and hid the session counts. The badge now reads the
server aggregates whenever the conversation has more than one call.

* fix(proxy): list every model of a conversation in Request Logs and keep the conversation badge for MCP representatives

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): type session spend aggregates

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ui): satisfy request logs lint budget

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(proxy): cap per-session model aggregation in request logs

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: ratchet type-discipline budget after staging merge

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(ui): send an explicit null when the key edit form clears the organization

Clearing the Organization picker in the key edit form wrote undefined into
the form value, and JSON.stringify drops undefined-valued keys, so
/key/update never saw the field and the key kept its old organization.
Writing null instead survives serialization, and the backend's
model_dump(exclude_unset=True) preserves it, so the column is set to NULL.

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 08:58:48 -07:00
Cursor Agent
d426b99f56
fix(spend): page reverse-hash recovery past the first 10k keys
Historical dirty spend on large installs was still unlabeled when the
matching token sat past the first page. Keep scanning until the digest
matches or the table ends.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-03 15:44:18 +00:00
Devin AI
35c6a768c0 fix(spend-tracking): keep internal service-account key names readable in spend logs
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-03 15:42:55 +00:00
Cursor Agent
d3c839147e
fix(spend): keep CloudZero export and spend-log snapshots compatible with email recovery
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-03 15:21:35 +00:00
Cursor Agent
eeb8b4f7f3
test(spend): assert batch spend metadata keeps user email
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-03 15:00:31 +00:00
Cursor Agent
bbf4d1dc30
fix(spend): catch PrismaError instead of bare Exception in key recovery
The Usage recovery path was adding four BLE001 hits and failing the
strict-rule budget. Soft-fail only on PrismaError so a down token table
still falls through to SpendLogs.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-03 14:44:02 +00:00
Cursor Agent
6f0f2fcc8d
fix(spend): restore user_email for double-hashed keys and persist it in spend logs
Recovery now resolves the key owner's email from UserTable via the
recovered token user_id, and SpendLogsMetadata keeps user_api_key_user_email
so new batch/export consumers see email without a separate user join.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-09-03 14:27:12 +00:00