From 81f9ad322bd28b424269558ec6535cbb007d8fa6 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:39:52 -0700 Subject: [PATCH] fix(scripts): keep the weekly price sync alive past unrepresentable rows Vercel now lists video and embedding models without token pricing or token limits, and the first of them KeyError'd the whole weekly run before any provider was synced. Those rows are skipped, and a failed catalog fetch for any provider now yields an empty transform instead of a crash. --- ...to_update_price_and_context_window_file.py | 9 +++++ ...to_update_price_and_context_window_file.py | 33 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/.github/scripts/auto_update_price_and_context_window_file.py b/.github/scripts/auto_update_price_and_context_window_file.py index df1ac738681..e7d7e465f8f 100644 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ b/.github/scripts/auto_update_price_and_context_window_file.py @@ -171,6 +171,8 @@ def write_to_file(file_path, data): # Update the existing models and add the missing models for OpenRouter def transform_openrouter_data(data): transformed = {} + if not data: + return transformed for row in data: # Add the fields 'max_tokens' and 'input_cost_per_token' obj = { @@ -209,7 +211,14 @@ def transform_openrouter_data(data): # Update the existing models and add the missing models for Vercel AI Gateway def transform_vercel_ai_gateway_data(data): transformed = {} + if not data: + return transformed for row in data: + # Rows without token pricing or token limits (video/embedding models) previously KeyError'd the whole sync + if any(row.get(k) is None for k in ("context_window", "max_tokens")) or any( + row.get("pricing", {}).get(k) is None for k in ("input", "output") + ): + continue obj = { "max_tokens": row["context_window"], "input_cost_per_token": float(row["pricing"]["input"]), diff --git a/tests/test_litellm/test_auto_update_price_and_context_window_file.py b/tests/test_litellm/test_auto_update_price_and_context_window_file.py index 7dbd24a1b81..8dc43100544 100644 --- a/tests/test_litellm/test_auto_update_price_and_context_window_file.py +++ b/tests/test_litellm/test_auto_update_price_and_context_window_file.py @@ -115,9 +115,40 @@ def test_transform_modalities_set_vision_image_and_video_flags(sync_module): assert entry_text["supports_video_input"] is False -def test_transform_survives_failed_fetch(sync_module): +def test_transforms_survive_failed_fetch(sync_module): assert sync_module.transform_friendli_data(None, {}) == {} assert sync_module.transform_friendli_data([], {}) == {} + assert sync_module.transform_openrouter_data(None) == {} + assert sync_module.transform_vercel_ai_gateway_data(None) == {} + + +def test_vercel_transform_skips_rows_without_token_pricing_or_limits(sync_module): + rows = [ + { + "id": "wan-video", + "pricing": {"video_duration_pricing": [{"resolution": "720p", "cost_per_second": "0.1"}]}, + }, + { + "id": "qwen3-embedding", + "context_window": 32768, + "max_tokens": 32768, + "pricing": {"input": "0.00000001"}, + }, + { + "id": "no-limits-chat", + "pricing": {"input": "0.000001", "output": "0.000002"}, + }, + { + "id": "good-chat", + "context_window": 128000, + "max_tokens": 8192, + "pricing": {"input": "0.000001", "output": "0.000002"}, + }, + ] + transformed = sync_module.transform_vercel_ai_gateway_data(rows) + assert list(transformed) == ["vercel_ai_gateway/good-chat"] + assert transformed["vercel_ai_gateway/good-chat"]["input_cost_per_token"] == 1e-06 + assert transformed["vercel_ai_gateway/good-chat"]["output_cost_per_token"] == 2e-06 def test_transform_inherits_allowlisted_keys_from_base_model_entry(sync_module):