feat(mavvrik): add Mavvrik integration for automatic LLM spend export#26573
Conversation
Greptile SummaryThis PR adds a complete Mavvrik integration that streams LiteLLM spend data from Postgres to GCS as gzip-compressed CSV, with encrypted credential persistence, a Redis pod lock, catch-up date backfill, and six new admin REST endpoints. The implementation follows the CloudZero/Vantage patterns in the codebase and the router + startup job are correctly registered. Remaining findings are all P2: routine pipeline progress messages are logged at Confidence Score: 5/5Safe to merge — all findings are P2 style/UX issues with no impact on correctness or data integrity. No P0 or P1 bugs found in the new code. The three flagged issues are: informational log messages at the wrong severity level, a minor GzipFile resource management inconsistency (no real leak), and a missing pre-check guard that produces a generic 500 instead of a clear error message for an edge-case deployment topology. All critical concerns raised in prior review rounds have been addressed in the current HEAD. No files require special attention; the three P2 findings are spread across orchestrator.py, uploader.py, and integrations/mavvrik/init.py.
|
| Filename | Overview |
|---|---|
| litellm/integrations/mavvrik/init.py | Service facade wiring all Mavvrik endpoints; update_settings may surface an opaque 500 on env-var-only + no-DB deployments. |
| litellm/integrations/mavvrik/orchestrator.py | Pod-lock + date-loop pipeline; routine status messages logged at WARNING instead of INFO throughout. |
| litellm/integrations/mavvrik/uploader.py | GCS resumable upload implementation; GzipFile not closed in the no-data early-return path (minor resource management inconsistency). |
| litellm/integrations/mavvrik/exporter.py | DB → polars DataFrame → CSV with OFFSET pagination; correctly handles DB-unavailable paths with warnings. |
| litellm/integrations/mavvrik/client.py | Mavvrik REST API client wrapping register, advance_marker, report_error, and get_signed_url; well-structured with best-effort error reporting. |
| litellm/integrations/mavvrik/settings.py | AES-encrypted credential persistence via LiteLLM_Config; correct encryption/decryption with appropriate error handling for key mismatches. |
| litellm/proxy/spend_tracking/mavvrik_endpoints.py | FastAPI router with thin dispatchers over Service; centralised exception mapping and admin-only access control applied consistently. |
| litellm/proxy/proxy_server.py | Adds mavvrik_router registration and startup scheduling; correctly falls back to env vars when no DB row exists during startup. |
| litellm/integrations/mavvrik/_http.py | Shared async HTTP transport with 3-attempt retry and exponential backoff; reuses the LiteLLM shared httpx client pool correctly. |
| litellm/types/proxy/mavvrik_endpoints.py | Pydantic request/response models with appropriate field validators; correctly marks api_key as non-repr for logging safety. |
Sequence Diagram
sequenceDiagram
participant EP as mavvrik_endpoints.py
participant SVC as Service
participant ORCH as Orchestrator
participant EXP as Exporter
participant UPL as Uploader
participant CLI as Client (Mavvrik API)
participant GCS as GCS
Note over EP,GCS: POST /mavvrik/init — store creds + schedule background job
EP->>SVC: initialize(api_key, endpoint, connection_id)
SVC->>SVC: Settings.save() [encrypt + upsert DB]
SVC->>ORCH: scheduler.add_job(orchestrator.run, interval=60min)
Note over EP,GCS: Scheduled export run (APScheduler)
ORCH->>ORCH: acquire Redis pod lock
ORCH->>CLI: register() → metricsMarker (start date)
loop for each date in [start, yesterday]
ORCH->>EXP: _stream_pages(date, page_size=10k)
EXP->>EXP: DB query_raw LIMIT/OFFSET
EXP-->>ORCH: AsyncGenerator[csv_chunk]
ORCH->>UPL: _stream_upload(pages, date)
UPL->>CLI: get_signed_url(date)
CLI-->>UPL: GCS signed URL
UPL->>GCS: POST signed_url (initiate resumable)
GCS-->>UPL: session_uri
loop per 256KB chunk
UPL->>GCS: PUT session_uri (chunk)
end
UPL->>GCS: PUT session_uri (final chunk)
ORCH->>CLI: advance_marker(next_date_epoch)
end
ORCH->>ORCH: release Redis pod lock
Note over EP,GCS: POST /mavvrik/export — manual trigger
EP->>SVC: export(date_str, limit)
SVC->>EXP: export(date_str, limit) → DataFrame + CSV
SVC->>UPL: upload(csv, date_str) [bulk path]
UPL->>CLI: get_signed_url(date_str)
UPL->>GCS: POST + PUT (initiate + finalize)
Reviews (14): Last reviewed commit: "feat(mavvrik): add Mavvrik integration f..." | Re-trigger Greptile
e1afc1f to
b5fef0a
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
b5fef0a to
17360fe
Compare
2fb17ab to
b40e754
Compare
Mavvrik spend export integration addedThis PR adds admin-gated Mavvrik configuration endpoints, encrypted settings storage, scheduled/manual spend export, and a GCS upload client. I reviewed the new auth gates, raw SQL parameterization, outbound HTTP flow, settings encryption/decryption, and response models and did not identify security issues in the changes introduced by this PR. Status: 6 open |
b40e754 to
39c9211
Compare
39c9211 to
0984720
Compare
0984720 to
d1a1628
Compare
d1a1628 to
4b99e0c
Compare
4b99e0c to
78e2e0c
Compare
f84425b to
01f7668
Compare
01f7668 to
fb59310
Compare
|
branch is out of sync with the base branch and must be rebased before it can even be considered |
|
please remove any docs on this pr - and make the relevant pr on litellm-docs repo |
3466ab1 to
60a785d
Compare
85fd9cf to
bfc95a1
Compare
Adds a complete integration with Mavvrik (https://help.mavvrik.ai/) for automatically exporting LiteLLM proxy spend data to the Mavvrik AI cost management platform. - Zero-config startup — set MAVVRIK_API_KEY, MAVVRIK_API_ENDPOINT, MAVVRIK_CONNECTION_ID env vars; proxy begins exporting automatically - Streaming export — fetches DB page-by-page (10k rows/page via OFFSET pagination with stable ORDER BY), gzip-compresses on-the-fly, uploads via GCS chunked resumable upload (256KB chunks). No row limit, ~2MB peak RAM. Validated: 500k rows → 13MB / 84s. - Idempotent by date — each calendar date has its own GCS object - Catch-up on restart — back-fills all missed days from Mavvrik marker - Pod-safe — Redis distributed lock for multi-replica deployments - Admin endpoints — init, settings, update, delete, dry-run, export litellm/integrations/mavvrik/ _http.py Shared HTTP transport (retry + exponential backoff) client.py Mavvrik REST API (register, advance_marker, signed URL) uploader.py GCS resumable upload (bulk + streaming, session cleanup) exporter.py DB fetch (4-table JOIN, stable pagination) + CSV orchestrator.py Pipeline sequencing + pod lock settings.py AES-encrypted credential persistence logger.py CustomLogger marker for callback registry __init__.py Service facade + lazy import to avoid polars at startup - update_settings reschedules background job with new credentials - delete works for env-var-only deployments (no DB row required) - dry_run guards NULL spend and missing completion_tokens column - GCS session cancelled on mid-stream _put_chunk failure - DB connectivity guard in Service.export/dry_run - Stable OFFSET pagination (ORDER BY includes dus.id as tiebreaker) - mavvrik_endpoints uses lazy import to keep polars out of startup path 164 mock-based unit tests across 10 files covering all components. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
bfc95a1 to
548254e
Compare
cf6fd9d
into
BerriAI:litellm_agent_oss_staging_05_11_2026
|
🤖 litellm-agent: Squash-merged into staging branch Triage Summary 5676 lines across 27 files (+5675 / -1) Merge Confidence: 5/5 ✅ READY All checks green. Greptile 5/5, no blocking pattern findings, no CircleCI runs (OSS-typical). |
… upload (from BerriAI#26573 pattern)
… upload (from BerriAI#26573 pattern)
… upload (from BerriAI#26573 pattern)
What is Mavvrik?
Mavvrik is an AI cost management platform. This PR adds a native LiteLLM integration that automatically exports daily LLM spend data from the LiteLLM proxy to Mavvrik for cost tracking, attribution, and analysis.
Key Features
callbacks: ["mavvrik"]and set three env vars; the proxy begins exporting automaticallyModule Layout
litellm/integrations/mavvrik/__init__.pyServicefacade — business logic for each endpointlitellm/integrations/mavvrik/client.pylitellm/integrations/mavvrik/exporter.pylitellm/integrations/mavvrik/uploader.pylitellm/integrations/mavvrik/orchestrator.pylitellm/integrations/mavvrik/logger.pyCustomLoggersubclass — per-request cost logginglitellm/integrations/mavvrik/settings.pylitellm/integrations/mavvrik/_http.pylitellm/proxy/spend_tracking/mavvrik_endpoints.pyServicelitellm/types/proxy/mavvrik_endpoints.pyAPI Endpoints Added
All endpoints require
PROXY_ADMINrole.POST/mavvrik/initGET/mavvrik/settingsPUT/mavvrik/settingsDELETE/mavvrik/deletePOST/mavvrik/dry-runPOST/mavvrik/exportQuickstart
Step 1: Add
mavvrikto yourconfig.yamlStep 2: Set env vars
Step 3: Start the proxy
LiteLLM will automatically export spend data to Mavvrik on an hourly schedule.
Test Coverage
Unit tests in
tests/test_litellm/integrations/mavvrik/:test_client.py— REST calls + retry logictest_exporter.py— DB query + CSV serializationtest_orchestrator.py— date-loop + pod-lock logictest_settings.py— credential load/save/masktest_transform.py— CSV field transformstest_upload.py— GCS protocol stepstest_uploader.py— streaming chunked uploadTest Plan
make test-unitpasses/mavvrik/initstores creds, background job schedules,/mavvrik/exportuploads to GCS/mavvrik/dry-runreturns CSV preview without uploadingRelated
🤖 Generated with Claude Code