From e8311b586cd932422c87e9110ea5e2285c4d9d83 Mon Sep 17 00:00:00 2001 From: siyoon Date: Wed, 5 Aug 2026 15:08:09 +0900 Subject: [PATCH 1/4] feat(friendli): auto-sync Friendli model metadata into price registry Add Friendli /serverless/v1/models as a data source to the weekly auto_update_price_and_context_window_file.py sync. For each Friendli model we emit a friendliai/{id} entry. When the model's base_model already exists in litellm (under any provider prefix), capability flags (supports_prompt_caching, supports_vision, reasoning effort flags, etc.) are inherited from that curated entry; Friendli-supplied pricing, context/output limits, modalities, and reasoning options always override. Reasoning option type=effort values (none/minimal/low/medium/high/ xhigh/max) are mapped to the matching litellm supports_*_reasoning_effort boolean flags. budget_tokens with min=-1 means 'no budget limit' and is left implicit -- litellm has no equivalent field. 6 new entries added (K-EXAONE-236B, MiniMax-M2.5, DeepSeek-V3.2, GLM-5.1, gemma-4-31B-it, GLM-5.2); schema re-validated. backup JSON intentionally untouched: ci_cd/check_files_match.py keeps it in sync with main during CI, so it stays out of this diff. --- ...to_update_price_and_context_window_file.py | 205 +++++++++++++++++- model_prices_and_context_window.json | 122 ++++++++++- 2 files changed, 325 insertions(+), 2 deletions(-) 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 461d8d347d9..dc8f367fa38 100644 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ b/.github/scripts/auto_update_price_and_context_window_file.py @@ -1,6 +1,7 @@ import asyncio import aiohttp import json +from typing import Any, Optional # Asynchronously fetch data from a given URL async def fetch_data(url): @@ -21,6 +22,203 @@ async def fetch_data(url): print("Error fetching data from URL:", e) return None + +# --------------------------------------------------------------------------- +# Friendli +# --------------------------------------------------------------------------- + +FRIENDLI_API_URL = "https://api.friendli.ai/serverless/v1/models" +FRIENDLI_PROVIDER = "friendliai" + +# litellm model-entry keys that Friendli API provides directly. +# These override the base model's value when set. +FRIENDLI_OVERRIDE_KEYS = ( + "context_length", + "max_completion_tokens", + "pricing", + "reasoning_options", + "input_modalities", + "output_modalities", + "interleaved", + "description", +) + +# Keys copied from the base-model entry (if one exists in litellm) to the +# Friendli entry, so that manually-curated capability metadata is inherited. +INHERITABLE_BASE_KEYS = ( + "supports_reasoning", + "supports_function_calling", + "supports_parallel_function_calling", + "supports_response_schema", + "supports_system_messages", + "supports_tool_choice", + "supports_vision", + "supports_pdf_input", + "supports_prompt_caching", + "supports_assistant_prefill", + "supports_low_reasoning_effort", + "supports_minimal_reasoning_effort", + "supports_max_reasoning_effort", + "supports_xhigh_reasoning_effort", + "supports_none_reasoning_effort", + "supports_adaptive_thinking", + "supports_output_config", + "supports_native_structured_output", +) + +# litellm reasoning-effort boolean flags keyed by the effort string returned by +# the Friendli API ("none", "minimal", "low", "medium", "high", "xhigh", "max"). +EFFORT_FLAG_MAP = { + "none": "supports_none_reasoning_effort", + "minimal": "supports_minimal_reasoning_effort", + "low": "supports_low_reasoning_effort", + "medium": "supports_low_reasoning_effort", # ponytail: litellm has no "medium" flag; medium implies low. Upgrade when a medium flag is added. + "high": "supports_max_reasoning_effort", # ponytail: litellm only has low/minimal/none/xhigh/max, not a standalone "high". Map high→max. + "xhigh": "supports_xhigh_reasoning_effort", + "max": "supports_max_reasoning_effort", +} + + +def _find_base_model_entry(base_model: str, local_data: dict) -> Optional[str]: + """Return the litellm key for ``base_model`` if one already exists. + + Friendli ``base_model`` is a canonical model id like ``zhipuai/glm-5.2`` or + ``minimax/minimax-m2.5``. litellm stores the same model under various provider + prefixes (``zai/glm-5.2``, ``cloudflare/@cf/zai-org/glm-5.2`` etc). We match the + tail of the base_model against every existing key so capability flags are + inherited from whichever provider entry is already curated. + """ + if not base_model: + return None + bm_tail = base_model.split("/")[-1].lower() + # Exact key match (base_model itself could be a litellm key). + if base_model in local_data: + return base_model + # Tail match against every key's last segment. + for key in local_data: + if key.startswith("sample_spec") or key == "fallback_generalizations": + continue + if key.split("/")[-1].lower() == bm_tail: + return key + return None + + +def _effort_flags(reasoning_options: list) -> dict: + """Map Friendli ``reasoning_options`` effort values to litellm boolean flags.""" + flags: dict[str, bool] = {} + for opt in reasoning_options or []: + if opt.get("type") == "effort": + for val in opt.get("values", []): + flag = EFFORT_FLAG_MAP.get(val) + if flag: + flags[flag] = True + return flags + + +def _pricing(pricing: dict) -> dict: + """Convert Friendli pricing dict → litellm cost fields.""" + out: dict[str, Any] = {} + if not pricing: + return out + if "input" in pricing: + out["input_cost_per_token"] = float(pricing["input"]) + if "output" in pricing: + out["output_cost_per_token"] = float(pricing["output"]) + if "input_cache_read" in pricing and pricing["input_cache_read"] is not None: + out["cache_read_input_token_cost"] = float(pricing["input_cache_read"]) + return out + + +def _modalities(input_mods: list, output_mods: list) -> dict: + """Convert Friendli modality lists to litellm capability flags.""" + out: dict[str, Any] = {} + if "image" in (input_mods or []): + out["supports_vision"] = True + out["supports_image_input"] = True + return out + + +def transform_friendli_data(data: list, local_data: dict) -> dict: + """Transform the Friendli /models response into litellm model entries. + + For each Friendli model we build a ``friendliai/{id}`` entry. When the + model's ``base_model`` already exists in litellm (under any provider prefix) + we inherit capability flags; Friendli-supplied pricing/limits/modalities/ + reasoning always override. + """ + transformed: dict[str, dict] = {} + for model in data: + model_id = model["id"] + base_model = model.get("base_model") or "" + entry: dict[str, Any] = { + "litellm_provider": FRIENDLI_PROVIDER, + } + + # --- Inherit capability flags from an existing base-model entry --- + base_key = _find_base_model_entry(base_model, local_data) + if base_key: + base_entry = local_data[base_key] + for k in INHERITABLE_BASE_KEYS: + if k in base_entry: + entry[k] = base_entry[k] + + # --- Override with Friendli-supplied values --- + ctx = model.get("context_length") + if ctx is not None: + entry["max_input_tokens"] = int(ctx) + entry["max_tokens"] = int(ctx) + max_out = model.get("max_completion_tokens") + if max_out is not None: + entry["max_output_tokens"] = int(max_out) + + # Pricing + entry.update(_pricing(model.get("pricing", {}))) + + # Reasoning + if model.get("reasoning") is True: + entry["supports_reasoning"] = True + # Effort flags (if present) override inherited ones. + entry.update(_effort_flags(model.get("reasoning_options", []))) + + # Functionality + func = model.get("functionality", {}) + if func.get("tool_call") is True: + entry["supports_function_calling"] = True + if func.get("parallel_tool_call") is True: + entry["supports_parallel_function_calling"] = True + if func.get("structured_output") is True: + entry["supports_response_schema"] = True + entry["supports_native_structured_output"] = True + if func.get("system_messages") is True: + entry["supports_system_messages"] = True + if func.get("tool_choice") is True: + entry["supports_tool_choice"] = True + + # Modalities + entry.update(_modalities( + model.get("input_modalities", []), + model.get("output_modalities", []), + )) + + # Mode + entry["mode"] = model.get("mode", "chat") + + # Description → comment (free-form) + desc = model.get("description") + if desc: + entry["comment"] = desc + + # Deprecation + dep = model.get("deprecation_date") + if dep: + entry["deprecation_date"] = dep.split("T")[0] + + # Source URL for traceability + entry["source"] = FRIENDLI_API_URL + + transformed[f"{FRIENDLI_PROVIDER}/{model_id}"] = entry + return transformed + # Synchronize local data with remote data def sync_local_data_with_remote(local_data, remote_data): # Update existing keys in local_data with values from remote_data @@ -143,9 +341,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 data (no auth required for the public /models endpoint) + friendli_data = asyncio.run(fetch_data(FRIENDLI_API_URL)) + # Transform Friendli data, inheriting capability flags from existing base-model entries + friendli_data = transform_friendli_data(friendli_data, local_data) # Combine both datasets - all_remote_data = {**openrouter_data, **vercel_data} + 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/model_prices_and_context_window.json b/model_prices_and_context_window.json index 3af7d9e5019..ff0b48e8083 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -50691,5 +50691,125 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true + }, + "friendliai/LGAI-EXAONE/K-EXAONE-236B-A23B": { + "litellm_provider": "friendliai", + "max_input_tokens": 262144, + "max_tokens": 262144, + "max_output_tokens": 262144, + "input_cost_per_token": 2e-07, + "output_cost_per_token": 8e-07, + "cache_read_input_token_cost": 1e-07, + "supports_reasoning": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "mode": "chat", + "comment": "Open multilingual MoE model for reasoning, agentic tool use, and long-context work with strong Korean capabilities", + "deprecation_date": "2026-08-20", + "source": "https://api.friendli.ai/serverless/v1/models" + }, + "friendliai/MiniMaxAI/MiniMax-M2.5": { + "litellm_provider": "friendliai", + "max_input_tokens": 196608, + "max_tokens": 196608, + "max_output_tokens": 196608, + "input_cost_per_token": 3e-07, + "output_cost_per_token": 1.2e-06, + "cache_read_input_token_cost": 6e-08, + "supports_reasoning": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "mode": "chat", + "comment": "Prior MiniMax coding model for agent workflows, office edits, and automation", + "source": "https://api.friendli.ai/serverless/v1/models" + }, + "friendliai/deepseek-ai/DeepSeek-V3.2": { + "litellm_provider": "friendliai", + "supports_reasoning": true, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_prompt_caching": true, + "supports_assistant_prefill": true, + "max_input_tokens": 163840, + "max_tokens": 163840, + "max_output_tokens": 163840, + "input_cost_per_token": 5e-07, + "output_cost_per_token": 1.5e-06, + "cache_read_input_token_cost": 2.5e-07, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "mode": "chat", + "comment": "DeepSeek chat model for instruction following, coding, and analysis", + "source": "https://api.friendli.ai/serverless/v1/models" + }, + "friendliai/zai-org/GLM-5.1": { + "litellm_provider": "friendliai", + "supports_reasoning": true, + "supports_function_calling": true, + "supports_tool_choice": true, + "supports_prompt_caching": true, + "max_input_tokens": 202752, + "max_tokens": 202752, + "max_output_tokens": 202752, + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "cache_read_input_token_cost": 2.6e-07, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "mode": "chat", + "comment": "Strong GLM coding model for agentic engineering, terminals, and repository generation", + "source": "https://api.friendli.ai/serverless/v1/models" + }, + "friendliai/google/gemma-4-31B-it": { + "litellm_provider": "friendliai", + "supports_vision": true, + "max_input_tokens": 262144, + "max_tokens": 262144, + "max_output_tokens": 262144, + "input_cost_per_token": 1.4e-07, + "output_cost_per_token": 4e-07, + "supports_reasoning": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_image_input": true, + "mode": "chat", + "comment": "Largest Gemma 4 instruction model for open, self-hosted chat and reasoning", + "source": "https://api.friendli.ai/serverless/v1/models" + }, + "friendliai/zai-org/GLM-5.2": { + "litellm_provider": "friendliai", + "supports_reasoning": true, + "supports_function_calling": true, + "max_input_tokens": 1048576, + "max_tokens": 1048576, + "max_output_tokens": 1048576, + "input_cost_per_token": 1.4e-06, + "output_cost_per_token": 4.4e-06, + "cache_read_input_token_cost": 2.6e-07, + "supports_max_reasoning_effort": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "mode": "chat", + "comment": "Open flagship GLM for long-horizon coding agents and million-token context work", + "source": "https://api.friendli.ai/serverless/v1/models" } -} +} \ No newline at end of file From 8c72296855252929e5f454df298a90ef4178c479 Mon Sep 17 00:00:00 2001 From: siyoon Date: Wed, 5 Aug 2026 15:20:14 +0900 Subject: [PATCH 2/4] fix(friendli): emit explicit False for capabilities; drop comments Address Greptile review: - P1: transform_friendli_data now writes a concrete True/False for every capability it owns (tool_call, vision, image_input, response_schema, reasoning, etc.) so a later sync never leaves a stale True from a previous API response when the provider drops that capability - P2: removed section banners, docstrings, and inline comments added by this change per CLAUDE.md (no new comments unless explicitly asked) --- ...to_update_price_and_context_window_file.py | 98 ++++--------------- model_prices_and_context_window.json | 22 +++-- 2 files changed, 35 insertions(+), 85 deletions(-) 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 dc8f367fa38..6d197aadb78 100644 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ b/.github/scripts/auto_update_price_and_context_window_file.py @@ -23,28 +23,9 @@ async def fetch_data(url): return None -# --------------------------------------------------------------------------- -# Friendli -# --------------------------------------------------------------------------- - FRIENDLI_API_URL = "https://api.friendli.ai/serverless/v1/models" FRIENDLI_PROVIDER = "friendliai" -# litellm model-entry keys that Friendli API provides directly. -# These override the base model's value when set. -FRIENDLI_OVERRIDE_KEYS = ( - "context_length", - "max_completion_tokens", - "pricing", - "reasoning_options", - "input_modalities", - "output_modalities", - "interleaved", - "description", -) - -# Keys copied from the base-model entry (if one exists in litellm) to the -# Friendli entry, so that manually-curated capability metadata is inherited. INHERITABLE_BASE_KEYS = ( "supports_reasoning", "supports_function_calling", @@ -66,35 +47,23 @@ INHERITABLE_BASE_KEYS = ( "supports_native_structured_output", ) -# litellm reasoning-effort boolean flags keyed by the effort string returned by -# the Friendli API ("none", "minimal", "low", "medium", "high", "xhigh", "max"). EFFORT_FLAG_MAP = { "none": "supports_none_reasoning_effort", "minimal": "supports_minimal_reasoning_effort", "low": "supports_low_reasoning_effort", - "medium": "supports_low_reasoning_effort", # ponytail: litellm has no "medium" flag; medium implies low. Upgrade when a medium flag is added. - "high": "supports_max_reasoning_effort", # ponytail: litellm only has low/minimal/none/xhigh/max, not a standalone "high". Map high→max. + "medium": "supports_low_reasoning_effort", + "high": "supports_max_reasoning_effort", "xhigh": "supports_xhigh_reasoning_effort", "max": "supports_max_reasoning_effort", } def _find_base_model_entry(base_model: str, local_data: dict) -> Optional[str]: - """Return the litellm key for ``base_model`` if one already exists. - - Friendli ``base_model`` is a canonical model id like ``zhipuai/glm-5.2`` or - ``minimax/minimax-m2.5``. litellm stores the same model under various provider - prefixes (``zai/glm-5.2``, ``cloudflare/@cf/zai-org/glm-5.2`` etc). We match the - tail of the base_model against every existing key so capability flags are - inherited from whichever provider entry is already curated. - """ if not base_model: return None bm_tail = base_model.split("/")[-1].lower() - # Exact key match (base_model itself could be a litellm key). if base_model in local_data: return base_model - # Tail match against every key's last segment. for key in local_data: if key.startswith("sample_spec") or key == "fallback_generalizations": continue @@ -104,7 +73,6 @@ def _find_base_model_entry(base_model: str, local_data: dict) -> Optional[str]: def _effort_flags(reasoning_options: list) -> dict: - """Map Friendli ``reasoning_options`` effort values to litellm boolean flags.""" flags: dict[str, bool] = {} for opt in reasoning_options or []: if opt.get("type") == "effort": @@ -116,7 +84,6 @@ def _effort_flags(reasoning_options: list) -> dict: def _pricing(pricing: dict) -> dict: - """Convert Friendli pricing dict → litellm cost fields.""" out: dict[str, Any] = {} if not pricing: return out @@ -129,23 +96,15 @@ def _pricing(pricing: dict) -> dict: return out -def _modalities(input_mods: list, output_mods: list) -> dict: - """Convert Friendli modality lists to litellm capability flags.""" - out: dict[str, Any] = {} - if "image" in (input_mods or []): - out["supports_vision"] = True - out["supports_image_input"] = True - return out +def _modality_flags(input_mods: list) -> dict: + has_image = "image" in (input_mods or []) + return { + "supports_vision": has_image, + "supports_image_input": has_image, + } def transform_friendli_data(data: list, local_data: dict) -> dict: - """Transform the Friendli /models response into litellm model entries. - - For each Friendli model we build a ``friendliai/{id}`` entry. When the - model's ``base_model`` already exists in litellm (under any provider prefix) - we inherit capability flags; Friendli-supplied pricing/limits/modalities/ - reasoning always override. - """ transformed: dict[str, dict] = {} for model in data: model_id = model["id"] @@ -154,7 +113,6 @@ def transform_friendli_data(data: list, local_data: dict) -> dict: "litellm_provider": FRIENDLI_PROVIDER, } - # --- Inherit capability flags from an existing base-model entry --- base_key = _find_base_model_entry(base_model, local_data) if base_key: base_entry = local_data[base_key] @@ -162,7 +120,6 @@ def transform_friendli_data(data: list, local_data: dict) -> dict: if k in base_entry: entry[k] = base_entry[k] - # --- Override with Friendli-supplied values --- ctx = model.get("context_length") if ctx is not None: entry["max_input_tokens"] = int(ctx) @@ -171,49 +128,34 @@ def transform_friendli_data(data: list, local_data: dict) -> dict: if max_out is not None: entry["max_output_tokens"] = int(max_out) - # Pricing entry.update(_pricing(model.get("pricing", {}))) - # Reasoning - if model.get("reasoning") is True: - entry["supports_reasoning"] = True - # Effort flags (if present) override inherited ones. + reasoning = model.get("reasoning") is True + entry["supports_reasoning"] = reasoning + if reasoning: entry.update(_effort_flags(model.get("reasoning_options", []))) - # Functionality func = model.get("functionality", {}) - if func.get("tool_call") is True: - entry["supports_function_calling"] = True - if func.get("parallel_tool_call") is True: - entry["supports_parallel_function_calling"] = True - if func.get("structured_output") is True: - entry["supports_response_schema"] = True - entry["supports_native_structured_output"] = True - if func.get("system_messages") is True: - entry["supports_system_messages"] = True - if func.get("tool_choice") is True: - entry["supports_tool_choice"] = True + entry["supports_function_calling"] = func.get("tool_call") is True + entry["supports_parallel_function_calling"] = func.get("parallel_tool_call") is True + is_struct = func.get("structured_output") is True + entry["supports_response_schema"] = is_struct + entry["supports_native_structured_output"] = is_struct + entry["supports_system_messages"] = func.get("system_messages") is True + entry["supports_tool_choice"] = func.get("tool_choice") is True - # Modalities - entry.update(_modalities( - model.get("input_modalities", []), - model.get("output_modalities", []), - )) + entry.update(_modality_flags(model.get("input_modalities", []))) - # Mode entry["mode"] = model.get("mode", "chat") - # Description → comment (free-form) desc = model.get("description") if desc: entry["comment"] = desc - # Deprecation dep = model.get("deprecation_date") if dep: entry["deprecation_date"] = dep.split("T")[0] - # Source URL for traceability entry["source"] = FRIENDLI_API_URL transformed[f"{FRIENDLI_PROVIDER}/{model_id}"] = entry @@ -342,9 +284,7 @@ def main(): # Transform the fetched Vercel AI Gateway data vercel_data = transform_vercel_ai_gateway_data(vercel_data) - # Fetch Friendli data (no auth required for the public /models endpoint) friendli_data = asyncio.run(fetch_data(FRIENDLI_API_URL)) - # Transform Friendli data, inheriting capability flags from existing base-model entries friendli_data = transform_friendli_data(friendli_data, local_data) # Combine both datasets diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index ff0b48e8083..bfa8116c52b 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -50710,7 +50710,9 @@ "mode": "chat", "comment": "Open multilingual MoE model for reasoning, agentic tool use, and long-context work with strong Korean capabilities", "deprecation_date": "2026-08-20", - "source": "https://api.friendli.ai/serverless/v1/models" + "source": "https://api.friendli.ai/serverless/v1/models", + "supports_vision": false, + "supports_image_input": false }, "friendliai/MiniMaxAI/MiniMax-M2.5": { "litellm_provider": "friendliai", @@ -50729,7 +50731,9 @@ "supports_tool_choice": true, "mode": "chat", "comment": "Prior MiniMax coding model for agent workflows, office edits, and automation", - "source": "https://api.friendli.ai/serverless/v1/models" + "source": "https://api.friendli.ai/serverless/v1/models", + "supports_vision": false, + "supports_image_input": false }, "friendliai/deepseek-ai/DeepSeek-V3.2": { "litellm_provider": "friendliai", @@ -50750,7 +50754,9 @@ "supports_system_messages": true, "mode": "chat", "comment": "DeepSeek chat model for instruction following, coding, and analysis", - "source": "https://api.friendli.ai/serverless/v1/models" + "source": "https://api.friendli.ai/serverless/v1/models", + "supports_vision": false, + "supports_image_input": false }, "friendliai/zai-org/GLM-5.1": { "litellm_provider": "friendliai", @@ -50770,7 +50776,9 @@ "supports_system_messages": true, "mode": "chat", "comment": "Strong GLM coding model for agentic engineering, terminals, and repository generation", - "source": "https://api.friendli.ai/serverless/v1/models" + "source": "https://api.friendli.ai/serverless/v1/models", + "supports_vision": false, + "supports_image_input": false }, "friendliai/google/gemma-4-31B-it": { "litellm_provider": "friendliai", @@ -50810,6 +50818,8 @@ "supports_tool_choice": true, "mode": "chat", "comment": "Open flagship GLM for long-horizon coding agents and million-token context work", - "source": "https://api.friendli.ai/serverless/v1/models" + "source": "https://api.friendli.ai/serverless/v1/models", + "supports_vision": false, + "supports_image_input": false } -} \ No newline at end of file +} From 82b9ba8c77f3e6ac04dac23055ae233e808d183c Mon Sep 17 00:00:00 2001 From: siyoon Date: Tue, 11 Aug 2026 14:33:32 +0900 Subject: [PATCH 3/4] chore(friendli): sync price registry with updated /v1/models Re-run auto_update against the current Friendli /serverless/v1/models: - add friendliai/LGAI-EXAONE/K-EXAONE-2.0-750B-A37B (frontier EXAONE, 262K ctx, tool/parallel-tool/structured-output/system-messages/tool-choice, reasoning toggle + budget) - friendliai/google/gemma-4-31B-it max_output_tokens 262144 -> 8192 (provider lowered serverless output cap) No script changes; transform_friendli_data already emits these correctly. --- model_prices_and_context_window.json | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index bfa8116c52b..4417e81729b 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -50783,9 +50783,9 @@ "friendliai/google/gemma-4-31B-it": { "litellm_provider": "friendliai", "supports_vision": true, - "max_input_tokens": 262144, - "max_tokens": 262144, - "max_output_tokens": 262144, + "max_input_tokens": 8192, + "max_tokens": 8192, + "max_output_tokens": 8192, "input_cost_per_token": 1.4e-07, "output_cost_per_token": 4e-07, "supports_reasoning": true, @@ -50821,5 +50821,26 @@ "source": "https://api.friendli.ai/serverless/v1/models", "supports_vision": false, "supports_image_input": false + }, + "friendliai/LGAI-EXAONE/K-EXAONE-2.0-750B-A37B": { + "litellm_provider": "friendliai", + "max_input_tokens": 262144, + "max_tokens": 262144, + "max_output_tokens": 262144, + "input_cost_per_token": 6e-07, + "output_cost_per_token": 2.4e-06, + "cache_read_input_token_cost": 1.2e-07, + "supports_reasoning": true, + "supports_function_calling": true, + "supports_parallel_function_calling": true, + "supports_response_schema": true, + "supports_native_structured_output": true, + "supports_system_messages": true, + "supports_tool_choice": true, + "supports_vision": false, + "supports_image_input": false, + "mode": "chat", + "comment": "Frontier-scale multilingual language model developed by LG AI Research", + "source": "https://api.friendli.ai/serverless/v1/models" } } From 471440eb79ba24400b021ae35988f1565db6178c Mon Sep 17 00:00:00 2001 From: siyoon Date: Sat, 22 Aug 2026 12:47:13 +0900 Subject: [PATCH 4/4] fix(friendli): emit explicit False for removed reasoning-effort flags Greptile P1: _effort_flags only wrote True for enabled levels, so sync_local_data_with_remote's merge left stale True flags when Friendli dropped an effort level from reasoning_options. --- .github/scripts/auto_update_price_and_context_window_file.py | 2 +- 1 file changed, 1 insertion(+), 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 6d197aadb78..523ec148fc6 100644 --- a/.github/scripts/auto_update_price_and_context_window_file.py +++ b/.github/scripts/auto_update_price_and_context_window_file.py @@ -73,7 +73,7 @@ def _find_base_model_entry(base_model: str, local_data: dict) -> Optional[str]: def _effort_flags(reasoning_options: list) -> dict: - flags: dict[str, bool] = {} + flags: dict[str, bool] = {flag: False for flag in EFFORT_FLAG_MAP.values()} for opt in reasoning_options or []: if opt.get("type") == "effort": for val in opt.get("values", []):