fix(vertex-ai): fix zero cost/usage on completed Vertex AI batch jobs - #27912
Conversation
Vertex batch jobs recorded 0 spend and 0 tokens after PR #25627 added automatic transformation of GCS predictions.jsonl to OpenAI format. Two bugs fixed: 1. batch_utils.py: the Vertex-specific cost/usage reader (calculate_vertex_ai_batch_cost_and_usage) was always invoked and reads raw usageMetadata fields that no longer exist in the OpenAI-shaped output. Now the reader is only used when disable_vertex_batch_output_transformation=True; otherwise the generic path handles the already-transformed OpenAI-shaped content. 2. cost_calculator.py: batch_cost_calculator skipped the global litellm.get_model_info() lookup when a model_info dict was passed in, even when that dict had no pricing fields (e.g. deployment metadata with only id/db_model). It now falls back to the global pricing table when the provided model_info has no pricing data. Co-authored-by: Cursor <cursoragent@cursor.com>
Greptile SummaryFixes Vertex AI batch jobs recording zero cost and usage by gating the Vertex-specific
Confidence Score: 5/5Safe to merge; the routing change is backward-compatible and the cost-fallback is gated behind a precise is-not-None check. Both fixes are tightly scoped: the batch_utils.py change is a one-line condition addition that only affects the Vertex AI code path when the flag is absent (default), and the cost_calculator.py fallback fires only when every pricing key is None, leaving all existing behavior unchanged. The new tests correctly exercise both code paths and would catch regression in either direction. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/batches/batch_utils.py | Gates Vertex-specific usageMetadata reader behind disable_vertex_batch_output_transformation flag; default (False) falls through to the generic OpenAI-shaped path, fixing zero-cost attribution. |
| litellm/cost_calculator.py | Adds elif fallback to global pricing when model_info carries no pricing fields; uses is not None correctly so explicit 0.0 pricing is not mistaken for absent pricing. |
| tests/batches_tests/test_batch_custom_pricing.py | Adds test verifying explicit 0.0 pricing prevents fallback to global table; monkeypatched global returns non-zero so the assertion correctly detects accidental fallback. |
| tests/test_litellm/proxy/pass_through_endpoints/test_vertex_ai_batch_passthrough.py | Adds two pure unit regression tests (no real network calls) covering OpenAI-shaped Vertex output and raw Vertex output with transformation disabled; both use hardcoded mock data. |
Reviews (4): Last reviewed commit: "fix(cost-calculator): treat explicit zer..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
|
@greptile re review |
…_cost_calculator Co-authored-by: Cursor <cursoragent@cursor.com>
|
@greptile re review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is ON, but it could not run because the spend limit has been reached. To enable Bugbot Autofix, have a team admin raise the spend limit in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f77b5c3. Configure here.
…el_info The fallback to litellm.get_model_info() used truthy checks on pricing fields, so 0.0 was treated as missing and replaced by global rates. Use `is not None` like elsewhere in cost calculation. Add regression test. Co-authored-by: Sameer Kankute <Sameerlite@users.noreply.github.com>
|
@greptile re review |

Problem
Vertex AI batch jobs completed successfully but always recorded
spend = 0,prompt_tokens = 0,completion_tokens = 0in proxy spend logs and the UI.OpenAI batch jobs in the same proxy attributed cost and tokens correctly.
Fixes #27891
Fixes LIT-3060
Root Cause
Bug 1 —
batch_utils.py_batch_cost_calculatorand_get_batch_job_total_usage_from_file_contentalways routedvertex_aithroughcalculate_vertex_ai_batch_cost_and_usage, which reads rawresponse.usageMetadata.*fields. After #25627, those fields no longer exist — the file content is now OpenAI-shaped (response.body.usage.*) — so every line evaluated to 0 tokens.Bug 2 —
cost_calculator.pybatch_cost_calculatorskipped the globallitellm.get_model_info()lookup when amodel_infodict was passed in.CheckBatchCostalways passesdeployment_info.model_info.model_dump(), which for a standard deployment only containsidanddb_model— no pricing fields. The non-Nonedict blocked the global lookup → all pricing fieldsNone→ returned(0.0, 0.0).Fix
litellm/batches/batch_utils.py— Gate the Vertex-specific reader behinddisable_vertex_batch_output_transformation:False(default): content is already OpenAI-shaped; fall through to the generic path that readsresponse.body.usage.*— same as OpenAI/Azure.True: content is raw Vertex shape; use the specializedusageMetadatareader.Cost tracking working with openai format

Cost tracking working with vertex ai format

Note
Medium Risk
Changes batch spend/tokens attribution logic for
vertex_aiand modifies pricing resolution whenmodel_infois provided, which can affect billing calculations across deployments.Overview
Fixes Vertex AI batch jobs recording zero cost/usage by only using the Vertex-specific
usageMetadatareader whenlitellm.disable_vertex_batch_output_transformation=True; otherwise Vertex batch outputs are treated as OpenAI-shaped and flow through the generic cost/usage path.Updates
batch_cost_calculatorto fall back to globallitellm.get_model_info()when a providedmodel_infolacks pricing fields (while preserving explicit0.0pricing), and adds regression tests covering both Vertex output shapes and the pricing fallback behavior.Reviewed by Cursor Bugbot for commit 642269a. Bugbot is set up for automated code reviews on this repo. Configure here.