From f13325aca92ed23f30911e9c8eafa230d8f8dab2 Mon Sep 17 00:00:00 2001 From: Lee-Si-Yoon Date: Sun, 26 Jul 2026 20:23:14 +0900 Subject: [PATCH] feat: add Friendli serverless models to auto price/context sync Adds transform_friendli_data() to auto_update_price_and_context_window_file.py and registers https://api.friendli.ai/serverless/v1/models as a sync source. Also adds the current 7 Friendli serverless models to the backup JSON: - friendliai/Qwen/Qwen3-235B-A22B-Instruct-2507 - friendliai/LGAI-EXAONE/K-EXAONE-236B-A23B - friendliai/MiniMaxAI/MiniMax-M2.5 - friendliai/deepseek-ai/DeepSeek-V3.2 - friendliai/zai-org/GLM-5.1 - friendliai/google/gemma-4-31B-it - friendliai/zai-org/GLM-5.2 --- ...to_update_price_and_context_window_file.py | 65 +++++++++++++- ...odel_prices_and_context_window_backup.json | 90 +++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto_update_price_and_context_window_file.py b/.github/workflows/auto_update_price_and_context_window_file.py index 461d8d347d9..c07acf61079 100644 --- a/.github/workflows/auto_update_price_and_context_window_file.py +++ b/.github/workflows/auto_update_price_and_context_window_file.py @@ -110,6 +110,61 @@ def transform_vercel_ai_gateway_data(data): return transformed +def transform_friendli_data(data): + transformed = {} + for row in data: + f = row.get("functionality", {}) or {} + p = row.get("pricing", {}) or {} + inp = row.get("input_modalities", []) or [] + out = row.get("output_modalities", []) or [] + + obj = { + "max_input_tokens": row.get("context_length"), + "max_output_tokens": row.get("max_completion_tokens"), + "max_tokens": row.get("max_completion_tokens") or row.get("context_length"), + "input_cost_per_token": float(p.get("input", 0)), + "output_cost_per_token": float(p.get("output", 0)), + "supports_function_calling": bool(f.get("tool_call")), + "supports_parallel_function_calling": bool(f.get("parallel_tool_call")), + "supports_response_schema": bool(f.get("structured_output")), + "litellm_provider": "friendliai", + "mode": row.get("mode", "chat"), + } + + # cache pricing (optional) + if p.get("input_cache_read") is not None: + obj["cache_read_input_token_cost"] = float(p["input_cache_read"]) + if p.get("cache_write") is not None: + obj["cache_creation_input_token_cost"] = float(p["cache_write"]) + + # only set these when the API actually returns them — don't overwrite + # an existing true with false just because the field is absent. + if f.get("tool_choice") is not None: + obj["supports_tool_choice"] = bool(f["tool_choice"]) + if f.get("system_messages") is not None: + obj["supports_system_messages"] = bool(f["system_messages"]) + + # reasoning + if row.get("reasoning"): + obj["supports_reasoning"] = True + + # deprecation + if v := row.get("deprecation_date"): + obj["deprecation_date"] = v[:10] + + # vision modality + if "image" in inp or "video" in inp: + obj["supports_vision"] = True + if inp: + obj["supported_modalities"] = inp + if out: + obj["supported_output_modalities"] = out + + transformed[f'friendliai/{row["id"]}'] = obj + + return transformed + + # Load local data from a specified file def load_local_data(file_path): try: @@ -130,6 +185,7 @@ def main(): local_file_path = "model_prices_and_context_window.json" # Path to the local data file openrouter_url = "https://openrouter.ai/api/v1/models" # URL to fetch OpenRouter data vercel_ai_gateway_url = "https://ai-gateway.vercel.sh/v1/models" # URL to fetch Vercel AI Gateway data + friendli_url = "https://api.friendli.ai/serverless/v1/models" # URL to fetch Friendli serverless data # Load local data from file local_data = load_local_data(local_file_path) @@ -143,9 +199,14 @@ def main(): vercel_data = asyncio.run(fetch_data(vercel_ai_gateway_url)) # Transform the fetched Vercel AI Gateway data vercel_data = transform_vercel_ai_gateway_data(vercel_data) + + # Fetch Friendli serverless data + friendli_data = asyncio.run(fetch_data(friendli_url)) + # Transform the fetched Friendli data + friendli_data = transform_friendli_data(friendli_data) - # Combine both datasets - all_remote_data = {**openrouter_data, **vercel_data} + # Combine all datasets + all_remote_data = {**openrouter_data, **vercel_data, **friendli_data} # If both local and openrouter data are available, synchronize and save if local_data and all_remote_data: diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 2cef600ea32..fca0f833ba6 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -46194,5 +46194,95 @@ } } ] + }, + "friendliai/Qwen/Qwen3-235B-A22B-Instruct-2507": { + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "input_cost_per_token": 2e-07, + "output_cost_per_token": 8e-07, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat", + "deprecation_date": "2026-08-05" + }, + "friendliai/LGAI-EXAONE/K-EXAONE-236B-A23B": { + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "input_cost_per_token": 2e-07, + "output_cost_per_token": 8e-07, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat", + "cache_read_input_token_cost": 1e-07 + }, + "friendliai/MiniMaxAI/MiniMax-M2.5": { + "max_input_tokens": 196608, + "max_output_tokens": 196608, + "max_tokens": 196608, + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat", + "cache_read_input_token_cost": 6e-08 + }, + "friendliai/deepseek-ai/DeepSeek-V3.2": { + "max_input_tokens": 163840, + "max_output_tokens": 163840, + "max_tokens": 163840, + "input_cost_per_token": 5e-07, + "output_cost_per_token": 1.5e-06, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat", + "cache_read_input_token_cost": 2.5e-07 + }, + "friendliai/zai-org/GLM-5.1": { + "max_input_tokens": 202752, + "max_output_tokens": 202752, + "max_tokens": 202752, + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat", + "cache_read_input_token_cost": 2.6e-07 + }, + "friendliai/google/gemma-4-31B-it": { + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 4e-07, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat" + }, + "friendliai/zai-org/GLM-5.2": { + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "litellm_provider": "friendliai", + "mode": "chat", + "cache_read_input_token_cost": 2.6e-07 } }