From b0ad97deb24bd32eec6c0f871a23e4b326289caa Mon Sep 17 00:00:00 2001 From: KimiSwitch Dev Date: Sun, 18 Oct 2026 01:21:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(providers):=20=E6=92=9E=E5=90=8D=E9=98=B2?= =?UTF-8?q?=E6=8A=A4=E5=8A=A0=E5=9B=BA=EF=BC=88=E5=A4=9A=E6=A7=BD=E6=9A=82?= =?UTF-8?q?=E5=AD=98/=E7=94=A8=E9=87=8F=E5=BC=B9=E7=AA=97=E5=AE=88?= =?UTF-8?q?=E5=8D=AB/=E9=87=8D=E8=BD=BD=E5=A4=B1=E8=B4=A5=E4=BF=9D?= =?UTF-8?q?=E6=9A=82=E5=AD=98/trim=20=E9=87=8D=E5=90=8D=E6=8B=A6=E6=88=AA/?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E6=8B=A6=E6=88=AA=EF=BC=89=E2=80=94=20bump?= =?UTF-8?q?=20to=20v0.8.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 14 + docs/release-notes/v0.8.7.md | 30 + package.json | 2 +- public/models-dev.json | 2024 +++++++++++++++++++++++----- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- src/App.tsx | 241 ++-- src/components/ProviderEdit.tsx | 9 +- src/hooks/useConfig.ts | 11 +- src/lib/models-dev-full.json | 2174 ++++++++++++++++++++++++++----- src/lib/models-dev.json | 2024 +++++++++++++++++++++++----- src/lib/validation.test.ts | 54 +- src/lib/validation.ts | 36 +- tsconfig.json | 5 +- 15 files changed, 5595 insertions(+), 1035 deletions(-) create mode 100644 docs/release-notes/v0.8.7.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f589e3..3e05c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ --- +## v0.8.7 (2026-10-18) + +撞名防护加固版:对 v0.8.6 防线的一轮对抗性审查后补齐六处缺口,三个高危路径(连续撞名丢第二个受害者、用量面板绕过守卫落盘、重载失败守卫自溃)全部关闭。 + +- **多槽暂存**:被撞名顶掉的条目原为单槽暂存,连续两次撞名时第二个受害者无记录可找回;现任意多次撞名的受害者都会被记住,改名 / 删除 / JSON 应用时逐一还原 +- **用量面板保存守卫**:「用量配置」弹窗的静默保存此前不经过撞名校验,撞名态下一次保存即把丢失状态落盘;现与 Ctrl+S 共用同一组硬阻止检查 +- **重载失败不清暂存**:仅重载成功时清理被顶掉条目的暂存,失败时守卫保持有效 +- **trim 重名拦截**:`TokenHub` / `TokenHub ` 变体不再能经 Ctrl+S 落盘(落盘后编辑页会永久红框) +- **撞名态导航拦截**:撞名未解决时顶栏导航弹窗提示先解决;普通未保存修改的导航不变 +- **`Object.hasOwn` 替换 `in`**:命名 `constructor` 等原型键名不再幽灵撞名;类型库 ES2020→ES2022(仅类型声明,编译目标不变) +- 零 Rust 改动、无迁移成本;`subagent-settings.ts` 的 4 处 `in` 留待后续 +- models.dev 快照同步至 2026-10-17(8,994 模型,+102) +- 测试:前端 162 项(新增 5 项 trim 重名扫描);六条路径经三路对抗性审查复核 + ## v0.8.6 (2026-10-16) 供应商名称唯一性防护版:修复同一厂商配置第二把 Key 时,因名称撞车导致第一个供应商条目被静默覆盖、API Key 丢失的问题。 diff --git a/docs/release-notes/v0.8.7.md b/docs/release-notes/v0.8.7.md new file mode 100644 index 0000000..ee8a720 --- /dev/null +++ b/docs/release-notes/v0.8.7.md @@ -0,0 +1,30 @@ +# KimiSwitch v0.8.7 + +撞名防护加固版:v0.8.6 建立了「同名供应商不再静默丢失」的基本防线,本版是针对该防线的一轮**对抗性审查**后补齐的六处缺口——审查发现的三个高危路径(连续撞名丢第二个受害者、用量面板绕过守卫落盘、重载失败后守卫自溃)全部关闭。 + +## 高危缺口修复 + +- **连续撞名不再丢第二个受害者**:被顶掉条目的暂存保护原来是单槽——先撞 `TokenHub`、未解除又去编辑别的供应商再撞一次时,第二个被顶掉的条目没有暂存记录、无人能找回。现改为多槽暂存,任意多次撞名的受害者都会被记住并在改名/删除时逐一还原 +- **用量面板不再绕过守卫**:供应商列表的「用量配置」弹窗保存走的是静默保存通道,此前不经过撞名校验——撞名态下打开它点一次保存,整份丢失状态的配置就直接落盘了。现与 Ctrl+S 共用同一组硬阻止检查(撞名未解除、空名、重名变体),命中即弹窗阻止 +- **重载失败不再自溃守卫**:Ctrl+R / 重新加载失败时(如配置文件暂时不可读),旧逻辑会把「被顶掉条目」的暂存记录清空而内存里仍是撞名态——守卫形同虚设,随后一次保存即丢数据。现仅在重载**成功**时清理暂存 + +## 其余加固 + +- **重名变体拦截**:`TokenHub` 与 `TokenHub `(尾随空格)这类 trim 后同名、存储键不同的变体,编辑页会红框但 Ctrl+S 原来拦不住;落盘后两条目在编辑页永久红框。现保存闸门增加 trim 级重名扫描,这类状态不再能落盘 +- **撞名态导航拦截**:撞名未解决时点击顶栏「供应商 / 仪表盘 / 会话 / 高级设置 / 设置」会弹窗提示先回编辑页解决,防止带着不完整暂存游走触发边角路径;普通未保存修改的导航体验完全不变 +- **JSON 应用接入还原协议**:撞名暂存态下通过「配置 JSON」改名应用后,被顶掉的条目现在同样会自动还原,保存不再被永久阻断 +- **原型链误命中修复**:存在性判断全部改用 `Object.hasOwn`,把供应商命名为 `constructor` 之类的原型键名不再产生幽灵撞名 +- 顺带修复:编辑页红框判定与落盘闸门口径不一致(trim 对全等)、复制供应商循环取号的 `in` 判断同源问题 + +## 其他 + +- models.dev 快照同步至 2026-10-17(8,994 模型,较上版 +102) +- TypeScript 类型库从 ES2020 提升至 ES2022(仅为 `Object.hasOwn` 的类型声明;编译目标不变,运行时 WebView2 原生支持) +- 零 Rust 改动,存储层与 config.toml 格式完全不变,升级 / 回退无迁移成本 +- `src/lib/subagent-settings.ts` 尚有 4 处 `in` 判断(不在撞名落盘路径上),留待后续独立修复 + +## 测试 + +- 前端 162 项(新增 5 项 trim 重名扫描:无重复 / 空白变体成对 / 多组重复排序 / 大小写不判重 / 空名不计入) +- 六条修复路径经对抗性审查复核:三路独立审查(算法 / React 状态 / 交付质量)发现的 8 项结论逐一核实,误报 2 项已驳回 +- tsc / vite build / check-version 全部通过 diff --git a/package.json b/package.json index b581f33..4d094b5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "kimiswitch", "private": true, - "version": "0.8.6", + "version": "0.8.7", "type": "module", "scripts": { "dev": "vite", diff --git a/public/models-dev.json b/public/models-dev.json index 9f2e871..bd01e04 100644 --- a/public/models-dev.json +++ b/public/models-dev.json @@ -1,5 +1,5 @@ { - "last_updated": "2026-10-16", + "last_updated": "2026-10-17", "deepinfra/anthropic/claude-fable-5": { "name": "Claude Fable 5", "context": 1000000, @@ -667,7 +667,7 @@ "cost": { "input": 1, "output": 2.7, - "cache_read": 0.05 + "cache_read": 0.1 } }, "deepinfra/MiniMaxAI/MiniMax-M3": { @@ -7155,9 +7155,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.44, - "output": 1.32, - "cache_read": 0.014 + "input": 0.22, + "output": 0.66, + "cache_read": 0.007 } }, "nano-gpt/deepseek/deepseek-v3.2:thinking": { @@ -7173,6 +7173,19 @@ "cache_read": 0.0216 } }, + "nano-gpt/deepseek/deepseek-v4.1-flash-1017:thinking": { + "name": "DeepSeek V4.1 Flash 1017 Thinking", + "context": 1048576, + "output": 393216, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, "nano-gpt/deepseek/deepseek-latest": { "name": "DeepSeek Latest", "context": 1048576, @@ -7266,15 +7279,15 @@ }, "nano-gpt/deepseek/deepseek-v4-flash-latest": { "name": "DeepSeek V4 Flash Latest", - "context": 1000000, - "output": 131072, + "context": 1048576, + "output": 393216, "reasoning": true, "tool_call": true, - "structured_output": true, + "image": true, "cost": { - "input": 0.05, - "output": 0.16, - "cache_read": 0.013 + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 } }, "nano-gpt/deepseek/deepseek-v3.2": { @@ -7289,6 +7302,19 @@ "cache_read": 0.0216 } }, + "nano-gpt/deepseek/deepseek-v4.1-flash-1017": { + "name": "DeepSeek V4.1 Flash 1017", + "context": 1048576, + "output": 393216, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, "nano-gpt/deepseek/deepseek-v4-flash": { "name": "DeepSeek V4 Flash", "context": 1048576, @@ -8103,6 +8129,19 @@ "cache_read": 0.2 } }, + "nano-gpt/reflection/beam-1.0": { + "name": "Beam 1.0 501B A23B", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 0.05 + } + }, "nano-gpt/inference-net/schematron-v2-small": { "name": "Schematron V2 Small", "context": 128000, @@ -8264,6 +8303,20 @@ "cache_read": 0.002 } }, + "nano-gpt/meta/muse-spark-open": { + "name": "Muse Spark Open", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 1.25, + "output": 4.25, + "cache_read": 0.15 + } + }, "nano-gpt/mlabonne/NeuralDaredevil-8B-abliterated": { "name": "Neural Daredevil 8B abliterated", "context": 8192, @@ -8493,6 +8546,21 @@ "cache_read": 0.5 } }, + "nano-gpt/elorian/glimpse": { + "name": "Elorian Glimpse", + "context": 262144, + "output": 256000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.63, + "output": 3.15, + "cache_read": 0.063 + } + }, "nano-gpt/Envoid/Llama-3.05-Nemotron-Tenyxchat-Storybreaker-70B": { "name": "Nemotron Tenyxchat Storybreaker 70b", "context": 16384, @@ -8513,6 +8581,19 @@ "cache_read": 0.2465 } }, + "nano-gpt/heabsy/qwen3.8-27b-uncensored-fast": { + "name": "Qwen 3.8 27B Uncensored Fast by Heabsy", + "context": 524288, + "output": 235929, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.22 + } + }, "nano-gpt/heabsy/cyberheabsy": { "name": "CyberHeabsy", "context": 1048576, @@ -9445,6 +9526,28 @@ "cache_read": 0.05015 } }, + "nano-gpt/moondream/moondream3-preview": { + "name": "Moondream 3 Preview", + "context": 32000, + "output": 4096, + "reasoning": true, + "image": true, + "cost": { + "input": 0.3, + "output": 2.5 + } + }, + "nano-gpt/moondream/moondream3.1-9b-a2b": { + "name": "Moondream 3.1 9B A2B", + "context": 32000, + "output": 4096, + "reasoning": true, + "image": true, + "cost": { + "input": 0.3, + "output": 2.5 + } + }, "nano-gpt/ibm-granite/granite-4.2-8b": { "name": "Granite 4.2 8B", "context": 131072, @@ -9572,6 +9675,17 @@ "cache_read": 0.1003 } }, + "nano-gpt/TheDrummer/orion-26b-a4b-v1.1": { + "name": "Orion 26B A4B v1.1", + "context": 65536, + "output": 8192, + "reasoning": true, + "cost": { + "input": 0.1, + "output": 0.45, + "cache_read": 0.05 + } + }, "nano-gpt/deepseek-ai/DeepSeek-V3.1": { "name": "DeepSeek V3.1", "context": 128000, @@ -14915,6 +15029,19 @@ "cache_read": 0.71 } }, + "wandb/reflection/Beam-1.0-501B-A23B": { + "name": "Beam 1.0", + "context": 1048576, + "output": 1048576, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.1 + } + }, "wandb/google/gemma-4-31B-it": { "name": "Gemma 4 31B", "context": 262144, @@ -15105,6 +15232,19 @@ "cache_read": 0.02 } }, + "wandb/zai-org/GLM-5.3": { + "name": "GLM 5.3", + "context": 1048576, + "output": 1048576, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.2, + "output": 4, + "cache_read": 0.19 + } + }, "wandb/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B": { "name": "Nemotron 3.5 Super VL", "context": 1048576, @@ -15115,7 +15255,7 @@ "image": true, "cost": { "input": 0.15, - "output": 0.75, + "output": 0.5, "cache_read": 0.02 } }, @@ -15239,6 +15379,21 @@ "cache_read": 0.25 } }, + "databricks/databricks-gemini-3-5-flash": { + "name": "Gemini 3.5 Flash", + "context": 1048576, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.5, + "output": 9, + "cache_read": 0.15 + } + }, "databricks/databricks-kimi-k2-7-code": { "name": "Kimi K2.7 Code", "context": 262144, @@ -18391,38 +18546,6 @@ "cache_read": 0.2 } }, - "llmgateway-providers/google-ai-studio/gemini-3.5-flash": { - "name": "Gemini 3.5 Flash (Google AI Studio)", - "context": 1048576, - "output": 65536, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "image": true, - "video": true, - "cost": { - "input": 1.5, - "output": 9, - "cache_read": 0.15, - "cache_write": 0.08333 - } - }, - "llmgateway-providers/google-ai-studio/gemini-3.7-flash": { - "name": "Gemini 3.7 Flash (Google AI Studio)", - "context": 1048576, - "output": 65536, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "image": true, - "video": true, - "cost": { - "input": 0.75, - "output": 3.75, - "cache_read": 0.075, - "cache_write": 0.08333 - } - }, "llmgateway-providers/google-ai-studio/gemini-3-flash-preview": { "name": "Gemini 3 Flash (Preview) (Google AI Studio)", "context": 1048576, @@ -18765,6 +18888,19 @@ "cache_read": 0.003625 } }, + "llmgateway-providers/deepseek/deepseek-v4.1-flash-1017": { + "name": "DeepSeek V4.1 Flash 1017 (DeepSeek)", + "context": 1050000, + "output": 393216, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, "llmgateway-providers/runpod/kimi-k3": { "name": "Kimi K3 (Runpod)", "context": 1048576, @@ -21658,6 +21794,20 @@ "cache_read": 0.15 } }, + "llmgateway-providers/inference.net/deepseek-v4.1-flash-1017": { + "name": "DeepSeek V4.1 Flash 1017 (Inference.net)", + "context": 262144, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.3, + "output": 1, + "cache_read": 0.007 + } + }, "llmgateway-providers/azure-anthropic/claude-opus-5-5": { "name": "Claude Opus 5.5 (Azure Anthropic)", "context": 1000000, @@ -23221,9 +23371,9 @@ "image": true, "video": true, "cost": { - "input": 0.294, - "output": 28, - "cache_read": 0.284 + "input": 0.27, + "output": 14, + "cache_read": 0.27 } }, "kilo/poolside/laguna-s-2.1:free": { @@ -23633,7 +23783,7 @@ "kilo/deepseek/deepseek-v4.1-flash": { "name": "DeepSeek V4.1 Flash", "context": 1048576, - "output": 943718, + "output": 393216, "reasoning": true, "tool_call": true, "structured_output": true, @@ -23672,13 +23822,13 @@ }, "kilo/deepseek/deepseek-chat": { "name": "DeepSeek Chat", - "context": 128000, - "output": 16000, + "context": 163840, + "output": 16384, "tool_call": true, "structured_output": true, "cost": { - "input": 0.2574, - "output": 1.0287 + "input": 0.32, + "output": 0.89 } }, "kilo/deepseek/deepseek-v4-pro-0813": { @@ -23757,15 +23907,29 @@ }, "kilo/deepseek/deepseek-v3.2": { "name": "DeepSeek V3.2", - "context": 163840, - "output": 147456, + "context": 131072, + "output": 65536, "reasoning": true, "tool_call": true, "structured_output": true, "cost": { - "input": 0.259, - "output": 0.8, - "cache_read": 0.14 + "input": 0.28, + "output": 0.42, + "cache_read": 0.028 + } + }, + "kilo/deepseek/deepseek-v4.1-flash-1017": { + "name": "DeepSeek: DeepSeek V4.1 Flash 1017", + "context": 1048576, + "output": 393216, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 } }, "kilo/deepseek/deepseek-v4-flash": { @@ -23843,9 +24007,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.132, - "output": 0.528, - "cache_read": 0.033 + "input": 0.0825, + "output": 0.33, + "cache_read": 0.020625 } }, "kilo/tencent/hy-mt2-7b": { @@ -23866,9 +24030,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.801, - "output": 2.48, - "cache_read": 0.038 + "input": 0.7506, + "output": 2.2509, + "cache_read": 0.0378 } }, "kilo/anthracite-org/magnum-v4-72b": { @@ -23883,8 +24047,8 @@ }, "kilo/meta-llama/llama-4-scout": { "name": "Meta: Llama 4 Scout", - "context": 131072, - "output": 117964, + "context": 327680, + "output": 16384, "tool_call": true, "structured_output": true, "image": true, @@ -23919,7 +24083,7 @@ "kilo/meta-llama/llama-3.3-70b-instruct": { "name": "Llama-3.3-70B-Instruct", "context": 131072, - "output": 32768, + "output": 16384, "tool_call": true, "structured_output": true, "cost": { @@ -24093,8 +24257,8 @@ }, "kilo/z-ai/glm-5.3-flash": { "name": "GLM-5.3-Flash", - "context": 1048576, - "output": 131072, + "context": 1048575, + "output": 943717, "reasoning": true, "tool_call": true, "structured_output": true, @@ -24247,6 +24411,30 @@ "cache_read": 0.26 } }, + "kilo/reflection/beam-1.0-501b-a23b": { + "name": "Reflection: Beam 1.0 501B A23B", + "context": 1048576, + "output": 943718, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.25, + "output": 1 + } + }, + "kilo/reflection/beam-free": { + "name": "Reflection: Beam 1.0 (free)", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, "kilo/inference-net/schematron-v2-small": { "name": "Inference.net: Schematron V2 Small", "context": 128000, @@ -24589,16 +24777,16 @@ "kilo/~z-ai/glm-flash-latest": { "name": "Z.ai: GLM Flash Latest", "context": 1048576, - "output": 131072, + "output": 943718, "reasoning": true, "tool_call": true, "structured_output": true, "image": true, "video": true, "cost": { - "input": 0.0131, - "output": 0.8228, - "cache_read": 0.0125 + "input": 0.06, + "output": 1.6, + "cache_read": 0.04 } }, "kilo/~z-ai/glm-latest": { @@ -24609,9 +24797,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.0608, + "input": 0.0632, "output": 4.4, - "cache_read": 0.0565 + "cache_read": 0.0588 } }, "kilo/arcee-ai/trinity-large-thinking": { @@ -24838,6 +25026,21 @@ "cache_read": 0.5 } }, + "kilo/elorian/glimpse": { + "name": "Elorian: Glimpse", + "context": 262144, + "output": 256000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.06 + } + }, "kilo/~anthropic/claude-opus-latest": { "name": "Anthropic: Claude Opus Latest", "context": 1000000, @@ -24946,22 +25149,22 @@ "structured_output": true, "image": true, "cost": { - "input": 0.036, - "output": 0.684, - "cache_read": 0.0054 + "input": 0.09, + "output": 0.55, + "cache_read": 0.008 } }, "kilo/~deepseek/deepseek-pro-latest": { "name": "DeepSeek: DeepSeek Pro Latest", "context": 1048576, - "output": 943718, + "output": 393216, "reasoning": true, "tool_call": true, "structured_output": true, "cost": { - "input": 0.25, - "output": 5, - "cache_read": 0.249 + "input": 0.12, + "output": 8, + "cache_read": 0.12 } }, "kilo/~deepseek/deepseek-v4-flash-latest": { @@ -24972,9 +25175,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.0049, + "input": 0.0119, "output": 1.28, - "cache_read": 0.0049 + "cache_read": 0.0119 } }, "kilo/~x-ai/grok-latest": { @@ -26991,8 +27194,8 @@ }, "kilo/qwen/qwen3.5-35b-a3b": { "name": "Qwen3.5 35B-A3B", - "context": 256000, - "output": 16384, + "context": 262144, + "output": 235929, "reasoning": true, "tool_call": true, "structured_output": true, @@ -27173,6 +27376,18 @@ "cache_write": 2.5 } }, + "kilo/xingchen-agi/xing4.0-29b-a4b:free": { + "name": "XingChen: Xing4.0 29B A4B (free)", + "context": 262144, + "output": 235929, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0, + "output": 0 + } + }, "kilo/morph/morph-v3-large": { "name": "Morph: Morph V3 Large", "context": 262144, @@ -30172,6 +30387,1014 @@ "output": 0.4 } }, + "commandcode/gpt-5.4": { + "name": "GPT-5.4", + "context": 400000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25, + "cache_write": 0 + } + }, + "commandcode/gpt-5.3-codex": { + "name": "GPT-5.3 Codex", + "context": 400000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5, + "cache_write": 0 + } + }, + "commandcode/claude-opus-5-5": { + "name": "Claude Opus 5.5", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 4, + "output": 20, + "cache_read": 0.2, + "cache_write": 5 + } + }, + "commandcode/claude-fable-5-1": { + "name": "Claude Fable 5.1", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 10, + "output": 50, + "cache_read": 0.25, + "cache_write": 12.5 + } + }, + "commandcode/gpt-6.1-sol": { + "name": "GPT-6.1 Sol", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 10, + "cache_read": 0.1, + "cache_write": 2.5 + } + }, + "commandcode/gpt-6-astra": { + "name": "GPT-6 Astra", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 10, + "output": 50, + "cache_read": 1, + "cache_write": 12.5 + } + }, + "commandcode/claude-opus-5": { + "name": "Claude Opus 5", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "commandcode/claude-fable-5": { + "name": "Claude Fable 5", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 10, + "output": 50, + "cache_read": 1, + "cache_write": 12.5 + } + }, + "commandcode/gpt-5.4-mini": { + "name": "GPT-5.4 mini", + "context": 400000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075, + "cache_write": 0 + } + }, + "commandcode/gpt-5.6-luna": { + "name": "GPT-5.6 Luna", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.2, + "output": 1.2, + "cache_read": 0.02, + "cache_write": 0.25 + } + }, + "commandcode/claude-opus-4-8": { + "name": "Claude Opus 4.8", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "commandcode/gpt-5.5": { + "name": "GPT-5.5", + "context": 400000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "cache_write": 0 + } + }, + "commandcode/claude-sonnet-5": { + "name": "Claude Sonnet 5", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 2, + "output": 10, + "cache_read": 0.2, + "cache_write": 2.5 + } + }, + "commandcode/claude-haiku-4-5-20251001": { + "name": "Claude Haiku 4.5", + "context": 200000, + "output": 64000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + } + }, + "commandcode/claude-haiku-5-5": { + "name": "Claude Haiku 5.5", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.1, + "output": 0.5, + "cache_read": 0.01, + "cache_write": 0.125 + } + }, + "commandcode/gpt-6-luna": { + "name": "GPT-6 Luna", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.1, + "output": 0.5, + "cache_read": 0.01, + "cache_write": 0.125 + } + }, + "commandcode/claude-sonnet-4-6": { + "name": "Claude Sonnet 4.6", + "context": 1000000, + "output": 64000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + "commandcode/claude-sonnet-5-5": { + "name": "Claude Sonnet 5.5", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 2, + "output": 10, + "cache_read": 0.1, + "cache_write": 2.5 + } + }, + "commandcode/gpt-5.6-terra": { + "name": "GPT-5.6 Terra", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "cache_write": 2.5 + } + }, + "commandcode/claude-opus-4-7": { + "name": "Claude Opus 4.7", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "commandcode/gpt-5.6-sol": { + "name": "GPT-5.6 Sol", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "cache_write": 6.25 + } + }, + "commandcode/gpt-6-sol": { + "name": "GPT-6 Sol", + "context": 1050000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 10, + "cache_read": 0.2, + "cache_write": 2.5 + } + }, + "commandcode/deepseek/deepseek-v4.1-flash": { + "name": "DeepSeek V4.1 Flash", + "context": 1000000, + "output": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, + "commandcode/deepseek/deepseek-v4-flash-vision-exp": { + "name": "DeepSeek V4 Flash Vision Exp", + "context": 1000000, + "output": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, + "commandcode/deepseek/deepseek-v4.1-flash-fast": { + "name": "DeepSeek V4.1 Flash Fast", + "context": 1000000, + "output": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.16, + "output": 0.58, + "cache_read": 0.016 + } + }, + "commandcode/deepseek/deepseek-v4-pro": { + "name": "DeepSeek V4 Pro", + "context": 1000000, + "output": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.66, + "output": 1.98, + "cache_read": 0.022 + } + }, + "commandcode/deepseek/deepseek-v4-flash-fast": { + "name": "DeepSeek V4 Flash Fast", + "context": 1000000, + "output": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.28, + "output": 0.56, + "cache_read": 0.07 + } + }, + "commandcode/deepseek/deepseek-v4-flash": { + "name": "DeepSeek V4 Flash", + "context": 1000000, + "output": 384000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, + "commandcode/tencent/hy3-paid": { + "name": "Hy3", + "context": 256000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.14, + "output": 0.58, + "cache_read": 0.035 + } + }, + "commandcode/tencent/hy4-preview": { + "name": "Hy4 preview", + "context": 1024000, + "output": 64000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.834, + "output": 2.501, + "cache_read": 0.042 + } + }, + "commandcode/z-ai/glm-5.3-flash": { + "name": "GLM-5.3-Flash", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.15, + "output": 0.5, + "cache_read": 0.03 + } + }, + "commandcode/z-ai/glm-5.3-flashx": { + "name": "GLM-5.3-FlashX", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.37, + "output": 1.25, + "cache_read": 0.075 + } + }, + "commandcode/thinkingmachines/inkling-small": { + "name": "Inkling Small", + "context": 1000000, + "output": 1000000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.5, + "output": 1.2, + "cache_read": 0.1 + } + }, + "commandcode/thinkingmachines/inkling": { + "name": "Inkling", + "context": 256000, + "output": 256000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 1, + "output": 4.05, + "cache_read": 0.17 + } + }, + "commandcode/meta/muse-spark-1.3": { + "name": "Muse Spark 1.3", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.25, + "output": 4.25, + "cache_read": 0.15 + } + }, + "commandcode/meta/muse-spark-1.1": { + "name": "Muse Spark 1.1", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.25, + "output": 4.25, + "cache_read": 0.15 + } + }, + "commandcode/meta/muse-spark-1.2": { + "name": "Muse Spark 1.2", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.25, + "output": 4.25, + "cache_read": 0.15 + } + }, + "commandcode/meta/muse-spark-1.2-contributor": { + "name": "Muse Spark 1.2 (Contributor)", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.1, + "output": 0.2, + "cache_read": 0.002 + } + }, + "commandcode/meta/muse-wonder-1.0": { + "name": "Muse Wonder", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 4, + "output": 20, + "cache_read": 0.2 + } + }, + "commandcode/meta/muse-spark-1.3-contributor": { + "name": "Muse Spark 1.3 (Contributor)", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.1, + "output": 0.2, + "cache_read": 0.002 + } + }, + "commandcode/google/gemini-3.6-flash": { + "name": "Gemini 3.6 Flash", + "context": 1000000, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.5, + "output": 7.5, + "cache_read": 0.15 + } + }, + "commandcode/google/gemini-3.5-flash-lite": { + "name": "Gemini 3.5 Flash Lite", + "context": 1000000, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.03 + } + }, + "commandcode/google/gemini-3.5-flash": { + "name": "Gemini 3.5 Flash", + "context": 1000000, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.5, + "output": 9, + "cache_read": 0.15 + } + }, + "commandcode/google/gemini-3.7-flash": { + "name": "Gemini 3.7 Flash", + "context": 1048576, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.5, + "output": 7.5, + "cache_read": 0.15, + "cache_write": 0.08334 + } + }, + "commandcode/google/gemini-3.8-flash": { + "name": "Gemini 3.8 Flash", + "context": 1000000, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.5, + "output": 7.5, + "cache_read": 0.15 + } + }, + "commandcode/google/gemini-4-argon": { + "name": "Gemini 4 Argon", + "context": 1000000, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 2, + "output": 10, + "cache_read": 0.1 + } + }, + "commandcode/google/gemini-3.1-flash-lite": { + "name": "Gemini 3.1 Flash Lite", + "context": 1000000, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_read": 0.03 + } + }, + "commandcode/Qwen/Qwen3.8-27B": { + "name": "Qwen3.8 27B", + "context": 262144, + "output": 32768, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.4, + "output": 3, + "cache_read": 0.04 + } + }, + "commandcode/Qwen/Qwen3.8-Max": { + "name": "Qwen3.8 Max", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.25, + "cache_write": 2.5 + } + }, + "commandcode/Qwen/Qwen3.8-Max-0902": { + "name": "Qwen3.8 Max 0902", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.25 + } + }, + "commandcode/Qwen/Qwen3.8-Flash": { + "name": "Qwen3.8 Flash", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.16, + "output": 0.47, + "cache_read": 0.016 + } + }, + "commandcode/Qwen/Qwen3.8-Omni-Flash": { + "name": "Qwen3.8 Omni Flash", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.15, + "output": 0.47, + "cache_read": 0.016 + } + }, + "commandcode/xai/grok-4.7": { + "name": "Grok 4.7", + "context": 500000, + "output": 500000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.5 + } + }, + "commandcode/xai/grok-4.5": { + "name": "Grok 4.5", + "context": 500000, + "output": 500000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.5 + } + }, + "commandcode/xai/grok-4.6": { + "name": "Grok 4.6", + "context": 500000, + "output": 500000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.5 + } + }, + "commandcode/MiniMaxAI/MiniMax-M3": { + "name": "MiniMax-M3", + "context": 1000000, + "output": 512000, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } + }, + "commandcode/MiniMaxAI/MiniMax-M2.5": { + "name": "MiniMax-M2.5", + "context": 200000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.03 + } + }, + "commandcode/MiniMaxAI/MiniMax-M2.7": { + "name": "MiniMax-M2.7", + "context": 200000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + } + }, + "commandcode/sakana/fugu-ultra": { + "name": "Fugu Ultra", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + } + }, + "commandcode/inclusionai/ling-3.1-flash:free": { + "name": "Ling 3.1 Flash", + "context": 262144, + "output": 32768, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, + "commandcode/moonshotai/Kimi-K2.7-Code-Highspeed": { + "name": "Kimi K2.7 Code Highspeed", + "context": 262000, + "output": 262000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1.9, + "output": 8, + "cache_read": 0.38 + } + }, + "commandcode/moonshotai/Kimi-K3": { + "name": "Kimi K3", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + } + }, + "commandcode/moonshotai/Kimi-K2.7-Code": { + "name": "Kimi K2.7 Code", + "context": 256000, + "output": 256000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.95, + "output": 4, + "cache_read": 0.19 + } + }, + "commandcode/zai-org/GLM-5.2": { + "name": "GLM-5.2", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } + }, + "commandcode/zai-org/GLM-5.2-Fast": { + "name": "GLM-5.2-Fast", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 3, + "output": 10.25, + "cache_read": 0.5 + } + }, + "commandcode/zai-org/GLM-5.3": { + "name": "GLM-5.3", + "context": 1000000, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26 + } + }, + "commandcode/nvidia/nemotron-3-ultra-550b-a55b": { + "name": "Nemotron 3 Ultra 550B A55B", + "context": 1000000, + "output": 128000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.6, + "output": 2.4, + "cache_read": 0.12 + } + }, + "commandcode/stepfun/Step-3.7-Flash": { + "name": "Step 3.7 Flash", + "context": 256000, + "output": 256000, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 0.2, + "output": 1.15, + "cache_read": 0.04 + } + }, + "commandcode/stepfun/Step-5-Preview": { + "name": "Step 5 Preview", + "context": 1000000, + "output": 1000000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1, + "output": 2.7, + "cache_read": 0.05 + } + }, + "commandcode/stepfun/Step-3.5-Flash": { + "name": "Step 3.5 Flash", + "context": 256000, + "output": 256000, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0.09, + "output": 0.3, + "cache_read": 0.02 + } + }, + "commandcode/mistral/mistral-large-4": { + "name": "Mistral Large 4", + "context": 524288, + "output": 262144, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 1.36, + "output": 4.18, + "cache_read": 0.14 + } + }, "lowrouter/auto/mistralai/ministral-8b-2512": { "name": "Ministral 3 8B", "context": 262144, @@ -30593,6 +31816,19 @@ "cache_write": 2.5 } }, + "reflection/Beam-1.0-501B-A23B": { + "name": "Beam 1.0 501B A23B", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, "inference/meta/llama-3.1-8b-instruct": { "name": "Llama 3.1 8B Instruct", "context": 16000, @@ -35575,9 +36811,9 @@ "image": true, "video": true, "cost": { - "input": 0.294, - "output": 28, - "cache_read": 0.284 + "input": 0.27, + "output": 14, + "cache_read": 0.27 } }, "openrouter/poolside/laguna-s-2.1:free": { @@ -35979,23 +37215,23 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.0049, + "input": 0.0119, "output": 1.28, - "cache_read": 0.0049 + "cache_read": 0.0119 } }, "openrouter/deepseek/deepseek-v4.1-flash": { "name": "DeepSeek V4.1 Flash", "context": 1048576, - "output": 943718, + "output": 393216, "reasoning": true, "tool_call": true, "structured_output": true, "image": true, "cost": { - "input": 0.036, - "output": 0.684, - "cache_read": 0.0054 + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 } }, "openrouter/deepseek/deepseek-v4-flash-vision-exp": { @@ -36027,12 +37263,12 @@ "openrouter/deepseek/deepseek-chat": { "name": "DeepSeek Chat", "context": 163840, - "output": 16000, + "output": 16384, "tool_call": true, "structured_output": true, "cost": { - "input": 0.2574, - "output": 1.0287 + "input": 0.32, + "output": 0.89 } }, "openrouter/deepseek/deepseek-v4-pro-0813": { @@ -36043,9 +37279,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 1.32, - "output": 3.96, - "cache_read": 0.044 + "input": 0.66, + "output": 1.98, + "cache_read": 0.022 } }, "openrouter/deepseek/deepseek-v3.2-exp": { @@ -36091,9 +37327,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.95526, - "output": 1.91052, - "cache_read": 0.079605 + "input": 0.2088, + "output": 0.4176, + "cache_read": 0.0174 } }, "openrouter/deepseek/deepseek-r1-0528": { @@ -36112,14 +37348,28 @@ "openrouter/deepseek/deepseek-v3.2": { "name": "DeepSeek V3.2", "context": 163840, - "output": 147456, + "output": 65536, "reasoning": true, "tool_call": true, "structured_output": true, "cost": { - "input": 0.259, - "output": 0.8, - "cache_read": 0.14 + "input": 0.28, + "output": 0.42, + "cache_read": 0.028 + } + }, + "openrouter/deepseek/deepseek-v4.1-flash-1017": { + "name": "DeepSeek V4.1 Flash 1017", + "context": 1048576, + "output": 393216, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 } }, "openrouter/deepseek/deepseek-v4-flash": { @@ -36197,9 +37447,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.132, - "output": 0.528, - "cache_read": 0.033 + "input": 0.0825, + "output": 0.33, + "cache_read": 0.020625 } }, "openrouter/tencent/hy-mt2-7b": { @@ -36220,9 +37470,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.834, - "output": 2.501, - "cache_read": 0.042 + "input": 0.7506, + "output": 2.2509, + "cache_read": 0.0378 } }, "openrouter/anthracite-org/magnum-v4-72b": { @@ -36238,13 +37488,13 @@ "openrouter/meta-llama/llama-4-scout": { "name": "Llama 4 Scout", "context": 1310720, - "output": 117964, + "output": 16384, "tool_call": true, "structured_output": true, "image": true, "cost": { - "input": 0.18, - "output": 0.59 + "input": 0.1, + "output": 0.3 } }, "openrouter/meta-llama/llama-guard-4-12b": { @@ -36273,13 +37523,12 @@ "openrouter/meta-llama/llama-3.3-70b-instruct": { "name": "Llama-3.3-70B-Instruct", "context": 131072, - "output": 32768, + "output": 16384, "tool_call": true, "structured_output": true, "cost": { - "input": 0.59, - "output": 0.79, - "cache_read": 0.295 + "input": 0.1, + "output": 0.32 } }, "openrouter/meta-llama/llama-3.1-8b-instruct": { @@ -36450,16 +37699,16 @@ "openrouter/z-ai/glm-5.3-flash": { "name": "GLM-5.3-Flash", "context": 1048576, - "output": 943718, + "output": 943717, "reasoning": true, "tool_call": true, "structured_output": true, "image": true, "video": true, "cost": { - "input": 0.01179, - "output": 0.9, - "cache_read": 0.01125 + "input": 0.15, + "output": 0.5, + "cache_read": 0.03 } }, "openrouter/z-ai/glm-4.6": { @@ -36548,9 +37797,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.037, - "output": 9, - "cache_read": 0.036 + "input": 0.62, + "output": 2.42, + "cache_read": 0.14 } }, "openrouter/z-ai/glm-5.1": { @@ -36603,6 +37852,19 @@ "cache_read": 0.078 } }, + "openrouter/reflection/beam-1.0-501b-a23b": { + "name": "Beam 1.0 501B A23B", + "context": 1048576, + "output": 943718, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.1 + } + }, "openrouter/inference-net/schematron-v2-small": { "name": "Schematron V2 Small", "context": 128000, @@ -36958,9 +38220,9 @@ "image": true, "video": true, "cost": { - "input": 0.01179, - "output": 0.9, - "cache_read": 0.01125 + "input": 0.06, + "output": 1.6, + "cache_read": 0.04 } }, "openrouter/~z-ai/glm-latest": { @@ -36971,9 +38233,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.0608, + "input": 0.0632, "output": 4.4, - "cache_read": 0.0565 + "cache_read": 0.0588 } }, "openrouter/arcee-ai/trinity-large-thinking": { @@ -37200,6 +38462,21 @@ "cache_read": 0.5 } }, + "openrouter/elorian/glimpse": { + "name": "Glimpse", + "context": 262144, + "output": 256000, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.06 + } + }, "openrouter/~anthropic/claude-opus-latest": { "name": "Claude Opus Latest", "context": 1000000, @@ -37308,22 +38585,22 @@ "structured_output": true, "image": true, "cost": { - "input": 0.036, - "output": 0.684, - "cache_read": 0.0054 + "input": 0.09, + "output": 0.55, + "cache_read": 0.008 } }, "openrouter/~deepseek/deepseek-pro-latest": { "name": "DeepSeek Pro Latest", "context": 1048576, - "output": 943718, + "output": 393216, "reasoning": true, "tool_call": true, "structured_output": true, "cost": { - "input": 0.25, - "output": 5, - "cache_read": 0.249 + "input": 0.12, + "output": 8, + "cache_read": 0.12 } }, "openrouter/~deepseek/deepseek-v4-flash-latest": { @@ -37334,9 +38611,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.00441, - "output": 1.152, - "cache_read": 0.00441 + "input": 0.0119, + "output": 1.28, + "cache_read": 0.0119 } }, "openrouter/~x-ai/grok-latest": { @@ -37514,9 +38791,9 @@ "image": true, "video": true, "cost": { - "input": 0.09, - "output": 0.3, - "cache_read": 0.05 + "input": 0.0675, + "output": 0.225, + "cache_read": 0.0375 } }, "openrouter/google/gemini-3-pro-image-preview": { @@ -38451,9 +39728,9 @@ "image": true, "video": true, "cost": { - "input": 0.15, - "output": 0.5, - "cache_read": 0.02 + "input": 0.1, + "output": 0.4, + "cache_read": 0.08 } }, "openrouter/nvidia/nemotron-3-ultra-550b-a55b:free": { @@ -39306,7 +40583,7 @@ "openrouter/qwen/qwen3.5-35b-a3b": { "name": "Qwen3.5 35B-A3B", "context": 262144, - "output": 16384, + "output": 235929, "reasoning": true, "tool_call": true, "structured_output": true, @@ -39489,6 +40766,18 @@ "cache_read": 0.25 } }, + "openrouter/xingchen-agi/xing4.0-29b-a4b:free": { + "name": "Xing4.0 29B A4B (free)", + "context": 262144, + "output": 235929, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0, + "output": 0 + } + }, "openrouter/morph/morph-v3-large": { "name": "Morph V3 Large", "context": 262144, @@ -50912,9 +52201,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.378235, - "output": 3.02588, - "cache_read": 0.34385 + "input": 0.325, + "output": 2.6, + "cache_read": 0.0325 } }, "aimlapi/openai/gpt-5.2-pro": { @@ -50989,9 +52278,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.075647, - "output": 0.605176, - "cache_read": 0.06877 + "input": 0.065, + "output": 0.52, + "cache_read": 0.0065 } }, "aimlapi/openai/o1": { @@ -51340,9 +52629,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1.891175, - "output": 15.1294, - "cache_read": 1.71925 + "input": 1.625, + "output": 13, + "cache_read": 0.1625 } }, "aimlapi/openai/gpt-5.6-sol": { @@ -56208,6 +57497,19 @@ "cache_read": 0.1 } }, + "cloudflare-ai-gateway/reflection-ai/beam-1.0-501b-a23b": { + "name": "Beam 1.0 501B A23B", + "context": 262144, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, "cloudflare-ai-gateway/xai/grok-4.7": { "name": "Grok 4.7", "context": 500000, @@ -60071,6 +61373,20 @@ "output": 0 } }, + "scaleway/deepseek-v4.1-flash": { + "name": "DeepSeek V4.1 Flash", + "context": 256000, + "output": 32768, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "cost": { + "input": 0.672, + "output": 2.016, + "cache_read": 0.134 + } + }, "scaleway/bge-multilingual-gemma2": { "name": "BGE Multilingual Gemma2", "context": 8191, @@ -61180,6 +62496,19 @@ "output": 1.85 } }, + "vercel/deepseek/deepseek-v4.1-flash-1017": { + "name": "DeepSeek V4.1 Flash 1017", + "context": 1048576, + "output": 1048576, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.14, + "output": 0.42, + "cache_read": 0.0042 + } + }, "vercel/deepseek/deepseek-v4-flash": { "name": "DeepSeek V4 Flash", "context": 1000000, @@ -61331,6 +62660,28 @@ "name": "HeyGen Video", "image": true }, + "vercel/reflection/beam": { + "name": "Beam 1.0", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0, + "output": 0 + } + }, + "vercel/reflection/beam-free": { + "name": "Beam 1.0 (Free)", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "cost": { + "input": 0, + "output": 0 + } + }, "vercel/inference-net/schematron-v2-small": { "name": "Schematron V2 Small", "context": 128000, @@ -61967,6 +63318,18 @@ "cache_read": 0.036 } }, + "vercel/elorian/glimpse": { + "name": "Glimpse", + "context": 220000, + "output": 4096, + "reasoning": true, + "image": true, + "cost": { + "input": 0.6, + "output": 3, + "cache_read": 0.06 + } + }, "vercel/klingai/kling-v2.6-t2v": { "name": "Kling v2.6 Text-to-Video" }, @@ -62948,7 +64311,7 @@ "cost": { "input": 1, "output": 2.7, - "cache_read": 0.05 + "cache_read": 0.1 } }, "vercel/stepfun/step-3.7-flash": { @@ -69617,9 +70980,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.308, - "output": 0.924, - "cache_read": 0.0098 + "input": 0.44, + "output": 1.32, + "cache_read": 0.014 } }, "ofox/deepseek/deepseek-v4-flash-0423": { @@ -69644,9 +71007,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.21, - "output": 0.84, - "cache_read": 0.0042 + "input": 0.3, + "output": 1.2, + "cache_read": 0.006 } }, "ofox/deepseek/deepseek-v4-flash-vision-exp": { @@ -69671,9 +71034,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.924, - "output": 2.772, - "cache_read": 0.0308 + "input": 1.32, + "output": 3.96, + "cache_read": 0.044 } }, "ofox/deepseek/deepseek-v4-pro-0423": { @@ -70866,9 +72229,9 @@ "structured_output": true, "image": true, "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2 + "input": 2.5, + "output": 15, + "cache_read": 0.25 } }, "ofox/openai/gpt-5.4-pro": { @@ -70879,8 +72242,8 @@ "tool_call": true, "image": true, "cost": { - "input": 24, - "output": 144 + "input": 30, + "output": 180 } }, "ofox/openai/gpt-5.4-nano": { @@ -70892,9 +72255,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.16, - "output": 1, - "cache_read": 0.016 + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 } }, "ofox/openai/gpt-5.2-codex": { @@ -70906,9 +72269,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1.4, - "output": 11.2, - "cache_read": 0.144 + "input": 1.75, + "output": 14, + "cache_read": 0.18 } }, "ofox/openai/gpt-4o": { @@ -70919,9 +72282,9 @@ "structured_output": true, "image": true, "cost": { - "input": 2, - "output": 8, - "cache_read": 1 + "input": 2.5, + "output": 10, + "cache_read": 1.25 } }, "ofox/openai/gpt-5-mini": { @@ -70933,9 +72296,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.2, - "output": 1.6, - "cache_read": 0.024 + "input": 0.25, + "output": 2, + "cache_read": 0.03 } }, "ofox/openai/gpt-5.3-codex": { @@ -70947,9 +72310,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1.4, - "output": 11.2, - "cache_read": 0.144 + "input": 1.75, + "output": 14, + "cache_read": 0.18 } }, "ofox/openai/gpt-5-nano": { @@ -70960,9 +72323,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.04, - "output": 0.32, - "cache_read": 0.008 + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 } }, "ofox/openai/gpt-6.1-sol": { @@ -70989,10 +72352,10 @@ "structured_output": true, "image": true, "cost": { - "input": 8, - "output": 40, - "cache_read": 0.8, - "cache_write": 10 + "input": 10, + "output": 50, + "cache_read": 1, + "cache_write": 12.5 } }, "ofox/openai/gpt-5.1": { @@ -71004,9 +72367,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1, - "output": 8, - "cache_read": 0.104 + "input": 1.25, + "output": 10, + "cache_read": 0.13 } }, "ofox/openai/gpt-4o-mini": { @@ -71017,9 +72380,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.12, - "output": 0.48, - "cache_read": 0.06 + "input": 0.15, + "output": 0.6, + "cache_read": 0.075 } }, "ofox/openai/gpt-5.4-mini": { @@ -71031,9 +72394,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.6, - "output": 3.6, - "cache_read": 0.06 + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 } }, "ofox/openai/gpt-5.6-luna": { @@ -71060,9 +72423,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1, - "output": 8, - "cache_read": 0.104 + "input": 1.25, + "output": 10, + "cache_read": 0.13 } }, "ofox/openai/gpt-5.2": { @@ -71074,9 +72437,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1.4, - "output": 11.2, - "cache_read": 0.144 + "input": 1.75, + "output": 14, + "cache_read": 0.18 } }, "ofox/openai/gpt-5.5": { @@ -71088,9 +72451,9 @@ "structured_output": true, "image": true, "cost": { - "input": 4, - "output": 24, - "cache_read": 0.4 + "input": 5, + "output": 30, + "cache_read": 0.5 } }, "ofox/openai/gpt-4.1": { @@ -71101,9 +72464,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1.6, - "output": 6.4, - "cache_read": 0.4 + "input": 2, + "output": 8, + "cache_read": 0.5 } }, "ofox/openai/gpt-4.1-mini": { @@ -71114,9 +72477,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.32, - "output": 1.28, - "cache_read": 0.08 + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 } }, "ofox/openai/gpt-6-luna": { @@ -71128,10 +72491,10 @@ "structured_output": true, "image": true, "cost": { - "input": 0.08, - "output": 0.4, - "cache_read": 0.008, - "cache_write": 0.1 + "input": 0.1, + "output": 0.5, + "cache_read": 0.01, + "cache_write": 0.125 } }, "ofox/openai/gpt-5.6-terra": { @@ -71158,9 +72521,9 @@ "structured_output": true, "image": true, "cost": { - "input": 0.2, - "output": 1.6, - "cache_read": 0.024 + "input": 0.25, + "output": 2, + "cache_read": 0.03 } }, "ofox/openai/gpt-5": { @@ -71172,9 +72535,9 @@ "structured_output": true, "image": true, "cost": { - "input": 1, - "output": 8, - "cache_read": 0.104 + "input": 1.25, + "output": 10, + "cache_read": 0.13 } }, "ofox/openai/gpt-5.6-sol": { @@ -71201,10 +72564,10 @@ "structured_output": true, "image": true, "cost": { - "input": 1.6, - "output": 8, - "cache_read": 0.16, - "cache_write": 2 + "input": 2, + "output": 10, + "cache_read": 0.2, + "cache_write": 2.5 } }, "neon/gpt-5-4-mini": { @@ -74685,14 +76048,13 @@ }, "merge-gateway/deepseek/deepseek-v3.1": { "name": "DeepSeek V3.1", - "context": 164000, - "output": 41000, + "context": 131072, + "output": 32768, "reasoning": true, - "tool_call": true, - "structured_output": true, "cost": { - "input": 0.5, - "output": 1.5 + "input": 0.56, + "output": 1.68, + "cache_read": 0.13 } }, "merge-gateway/deepseek/deepseek-r1": { @@ -74708,14 +76070,14 @@ "merge-gateway/deepseek/deepseek-v4-pro": { "name": "DeepSeek V4 Pro", "context": 1000000, - "output": 262000, + "output": 384000, "reasoning": true, "tool_call": true, "structured_output": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 1.32, + "output": 3.96, + "cache_read": 0.044 } }, "merge-gateway/deepseek/deepseek-r1-0528": { @@ -80028,6 +81390,21 @@ "cache_write": 2.1 } }, + "pioneer/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4": { + "name": "Nemotron 3.5 Super VL 120B A12B", + "context": 262144, + "output": 4096, + "reasoning": true, + "tool_call": true, + "image": true, + "video": true, + "cost": { + "input": 0.08, + "output": 0.45, + "cache_read": 0.04, + "cache_write": 0.08 + } + }, "pioneer/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16": { "name": "Nemotron 3 Nano 30B A3B", "context": 262144, @@ -81524,6 +82901,19 @@ "cache_read": 0.002 } }, + "opencode-go/beam-free": { + "name": "Beam Free", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, "opencode-go/claude-haiku-5-5": { "name": "Claude Haiku 5.5", "context": 1000000, @@ -82727,7 +84117,7 @@ "cost": { "input": 1, "output": 2.7, - "cache_read": 0.05 + "cache_read": 0.1 } }, "edenai/deepinfra/stepfun-ai/Step-3.5-Flash": { @@ -84352,6 +85742,7 @@ "output": 384000, "reasoning": true, "tool_call": true, + "structured_output": true, "image": true, "cost": { "input": 0.3, @@ -84415,9 +85806,9 @@ "reasoning": true, "tool_call": true, "cost": { - "input": 0.44832, - "output": 0.89664, - "cache_read": 0.089664 + "input": 0.45012, + "output": 0.90024, + "cache_read": 0.090024 } }, "edenai/scaleway/qwen3-235b-a22b-instruct-2507": { @@ -84426,8 +85817,8 @@ "output": 16384, "tool_call": true, "cost": { - "input": 0.8406, - "output": 2.5218 + "input": 0.843975, + "output": 2.531925 } }, "edenai/scaleway/deepseek-v4.1-flash": { @@ -84438,9 +85829,9 @@ "tool_call": true, "image": true, "cost": { - "input": 0.67248, - "output": 2.01744, - "cache_read": 0.134496 + "input": 0.67518, + "output": 2.02554, + "cache_read": 0.135036 } }, "edenai/scaleway/qwen3-coder-30b-a3b-instruct": { @@ -84449,8 +85840,8 @@ "output": 65536, "tool_call": true, "cost": { - "input": 0.22416, - "output": 0.89664 + "input": 0.22506, + "output": 0.90024 } }, "edenai/scaleway/llama-3.3-70b-instruct": { @@ -84459,8 +85850,8 @@ "output": 4096, "tool_call": true, "cost": { - "input": 1.00872, - "output": 1.00872 + "input": 1.01277, + "output": 1.01277 } }, "edenai/scaleway/gpt-oss-120b": { @@ -84470,8 +85861,8 @@ "reasoning": true, "tool_call": true, "cost": { - "input": 0.16812, - "output": 0.67248 + "input": 0.168795, + "output": 0.67518 } }, "edenai/ionos/meta-llama/Llama-3.3-70B-Instruct": { @@ -84481,8 +85872,8 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.72852, - "output": 0.72852 + "input": 0.731445, + "output": 0.731445 } }, "edenai/ionos/Qwen/Qwen3-Coder-Next": { @@ -84492,8 +85883,8 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.16812, - "output": 0.89664 + "input": 0.168795, + "output": 0.90024 } }, "edenai/ionos/openai/gpt-oss-120b": { @@ -84504,8 +85895,8 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.16812, - "output": 0.72852 + "input": 0.168795, + "output": 0.731445 } }, "edenai/vertex/gemini-4-argon@us": { @@ -84978,9 +86369,9 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.156912, - "output": 0.39228, - "cache_read": 0.044832 + "input": 0.157542, + "output": 0.393855, + "cache_read": 0.045012 } }, "edenai/greenpt/qwen3-235b-a22b-instruct-2507": { @@ -84990,8 +86381,8 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 1.00872, - "output": 3.026159 + "input": 1.01277, + "output": 3.03831 } }, "edenai/greenpt/deepseek-v4.1-flash": { @@ -85002,9 +86393,9 @@ "tool_call": true, "image": true, "cost": { - "input": 0.246576, - "output": 1.23288, - "cache_read": 0.012329 + "input": 0.247566, + "output": 1.23783, + "cache_read": 0.012378 } }, "edenai/greenpt/llama-3.3-70b-instruct": { @@ -85014,8 +86405,8 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 1.23288, - "output": 1.23288 + "input": 1.23783, + "output": 1.23783 } }, "edenai/greenpt/gpt-oss-120b": { @@ -85026,8 +86417,8 @@ "tool_call": true, "structured_output": true, "cost": { - "input": 0.22416, - "output": 0.78456 + "input": 0.22506, + "output": 0.78771 } }, "edenai/greenference/deepseek-v4.1-flash": { @@ -85531,13 +86922,13 @@ }, "edenai/amazon/qwen.qwen3-coder-next": { "name": "Qwen3 Coder Next (Amazon Bedrock)", - "context": 262144, + "context": 256000, "output": 65536, "tool_call": true, "structured_output": true, "cost": { - "input": 0.6, - "output": 1.44 + "input": 0.5, + "output": 1.2 } }, "edenai/amazon/moonshotai.kimi-k2.5": { @@ -86664,8 +88055,8 @@ "structured_output": true, "image": true, "cost": { - "input": 0.33624, - "output": 0.44832 + "input": 0.33759, + "output": 0.45012 } }, "edenai/moonshot/kimi-k3": { @@ -89691,6 +91082,19 @@ "cache_read": 0 } }, + "opencode/beam-free": { + "name": "Beam Free", + "context": 1048576, + "output": 131072, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0 + } + }, "opencode/ling-3.0-flash-free": { "name": "Ling-3.0-flash Free", "context": 262144, @@ -92501,6 +93905,21 @@ "output": 0 } }, + "requesty/step-5-preview": { + "name": "Step 5 Preview", + "context": 1048576, + "output": 65536, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "image": true, + "video": true, + "cost": { + "input": 1, + "output": 2.7, + "cache_read": 0.05 + } + }, "requesty/claude-opus-5-5@eu": { "name": "Claude Opus 5.5 (EU)", "context": 1000000, @@ -100049,6 +101468,19 @@ "output": 1.1 } }, + "llmgateway/deepseek-v4.1-flash-1017": { + "name": "DeepSeek V4.1 Flash 1017", + "context": 1050000, + "output": 1050000, + "reasoning": true, + "tool_call": true, + "image": true, + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.003 + } + }, "llmgateway/gpt-5.6-sol": { "name": "GPT-5.6 Sol", "context": 1050000, @@ -101549,7 +102981,7 @@ "prime/meta-llama/llama-3.3-70b-instruct": { "name": "Llama 3.3 70B Instruct", "context": 131072, - "output": 32768, + "output": 16384, "tool_call": true, "structured_output": true, "cost": { @@ -105327,17 +106759,6 @@ "cache_write": 0.038 } }, - "cortecs/mistral-7b-instruct-v0.3": { - "name": "mistral-7b-instruct-v0.3", - "context": 127000, - "output": 127000, - "tool_call": true, - "structured_output": true, - "cost": { - "input": 0.111, - "output": 0.111 - } - }, "cortecs/qwen3-coder-30b-a3b-instruct": { "name": "Qwen3-Coder 30B-A3B Instruct", "context": 262144, @@ -105652,21 +107073,6 @@ "cache_write": 6.645 } }, - "cortecs/claude-sonnet-4": { - "name": "Claude Sonnet 4 (latest)", - "context": 200000, - "output": 65000, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "image": true, - "cost": { - "input": 2.989, - "output": 14.945, - "cache_read": 0.296, - "cache_write": 3.707 - } - }, "cortecs/minimax-m3": { "name": "MiniMax-M3", "context": 1048576, @@ -109275,8 +110681,6 @@ "reasoning": true, "tool_call": true, "structured_output": true, - "image": true, - "video": true, "cost": { "input": 0.15, "output": 0.5, diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 33f6e0c..e5df215 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1978,7 +1978,7 @@ dependencies = [ [[package]] name = "kimiswitch" -version = "0.8.6" +version = "0.8.7" dependencies = [ "anyhow", "chrono", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 554d2a8..38ff655 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kimiswitch" -version = "0.8.6" +version = "0.8.7" description = "Kimi Switch - model config manager" authors = ["codingplan.site"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e560934..665a444 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Kimi Switch", - "version": "0.8.6", + "version": "0.8.7", "identifier": "com.kimiswitch.app", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/App.tsx b/src/App.tsx index 2f2f4b3..2117e3f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,7 +19,7 @@ import { presetToProviderAndModels, type ProviderPreset, } from "./config/providerPresets"; -import { validateProviders } from "./lib/validation"; +import { findTrimDuplicateProviderNames, validateProviders } from "./lib/validation"; import { copyModelsOnProviderDuplicate, rekeyModelsOnProviderRename, @@ -32,6 +32,15 @@ import type { Agent, Model, Provider, UsageConfig } from "./types"; const AGENT_STORAGE_KEY = "kimi-switch-agent"; +type View = + | "list" + | "edit" + | "subagent" + | "dashboard" + | "sessions" + | "plugins" + | "settings"; + function getProviderDefaultModel(provider: Provider): string | null { const raw = provider.raw_other as Record | undefined; const value = raw?.default_model; @@ -80,9 +89,7 @@ export default function App() { updateConfig, } = useConfig(agent); - const [view, setView] = useState< - "list" | "edit" | "subagent" | "dashboard" | "sessions" | "plugins" | "settings" - >("list"); + const [view, setView] = useState("list"); const [editingProvider, setEditingProvider] = useState(""); const [loadTimeout, setLoadTimeout] = useState(false); const [switchMessage, setSwitchMessage] = useState(null); @@ -99,19 +106,76 @@ export default function App() { const [pendingNewProviders, setPendingNewProviders] = useState>( () => new Set(), ); - // The provider an earlier name collision overwrote in memory, held so it - // can be put back. Non-null means the in-memory config is missing an entry, - // so trySave hard-blocks on it; the rename / delete paths restore it. - const displacedRef = useRef<{ key: string; provider: Provider } | null>(null); + // Providers an earlier name collision overwrote in memory, keyed by the + // provider name the colliding entry squatted on, held so they can be put + // back. A non-empty map means the in-memory config is missing entries, so + // trySave and the usage-config save hard-block on it; the rename / delete / + // JSON-apply paths restore whatever they free. Multiple slots are possible + // (rename A→B, then rename C→D without resolving the first) — a single slot + // would drop the older victim on the floor. + const displacedRef = useRef>(new Map()); + + // Put every stashed provider back whose key is free again (mutates the map + // it is given, and drops the restored slots). Must run AFTER a rename has + // moved the edited entry off the squatted key: while the edited entry still + // holds it, the key is present and nothing can be restored. Running it also + // empties the restored slots, so a second collision in a row can stash its + // own victim. + const restoreDisplaced = useCallback( + (providers: Record) => { + const stash = displacedRef.current; + for (const [key, provider] of [...stash]) { + if (!Object.hasOwn(providers, key)) { + providers[key] = provider; + stash.delete(key); + } + } + }, + [], + ); + + // The reasons a config must not reach disk at all, shared by every write + // path (trySave for whole-config saves, handleSaveUsageConfig for the + // usage panel's silent save). Both describe a config that loses data the + // moment it is persisted, so there is nothing here for the user to confirm. + // Returns the alert message, or null when nothing blocks the save. + const hardBlockMessage = useCallback((): string | null => { + if (!config) return null; + if (displacedRef.current.size > 0) { + return t("saveBlockedDisplaced"); + } + const blankNames = Object.keys(config.providers).filter( + (key) => key.trim() === "" + ); + if (blankNames.length > 0) { + // JSON.stringify keeps a blank name readable in the message — joining + // the raw keys renders as nothing after the colon. + return t("saveBlockedNameConflict", { + names: blankNames.map((k) => JSON.stringify(k)).join(", "), + }); + } + // Names that collide once trimmed ("TokenHub" vs "TokenHub ") are + // invisible to the blank-name check above and to the edit page's per-name + // hint (which excludes the entry being edited), but both are on disk and + // the edit page paints both red with no way out. Block them here. + const trimDuplicates = findTrimDuplicateProviderNames(config.providers); + if (trimDuplicates.length > 0) { + return t("saveBlockedNameConflict", { + names: trimDuplicates.map((k) => JSON.stringify(k)).join(", "), + }); + } + return null; + }, [config, t]); // Every "abandon the in-memory config" path goes through here. The reload // brings back the on-disk truth, which includes whatever a collision // displaced, so the stash is obsolete once the reload has actually - // returned. Clearing it only afterwards keeps a *failed* reload (config - // unchanged, still collided) from looking clean to trySave. + // returned. Clearing it only on a *successful* reload keeps a failed one + // (config unchanged, still collided) from looking clean to trySave. const reloadFromDisk = useCallback(async () => { - await refresh(); - displacedRef.current = null; + if (await refresh()) { + displacedRef.current.clear(); + } }, [refresh]); useEffect(() => { @@ -176,31 +240,17 @@ export default function App() { // provider being switched to — only that one needs to be // complete; sibling providers being merely listed in the config // must not block the switch. - // Returns `true` when the config was actually persisted, `false` when - // the user cancelled the validation confirm (callers can react to this - // to roll back in-memory mutations made before the save). + // Returns `true` when the config was actually persisted, `false` when a + // hard block (see hardBlockMessage) or the validation confirm stopped it + // — callers can react to this to roll back in-memory mutations made before + // the save. const trySave = useCallback( async (target?: string): Promise => { if (!config) return false; if (dirty) { - // Hard blocks ahead of the completeness confirm. Both describe a - // config that loses data the moment it reaches disk, so there is - // nothing here for the user to confirm. - if (displacedRef.current) { - alert(t("saveBlockedDisplaced")); - return false; - } - const blankNames = Object.keys(config.providers).filter( - (key) => key.trim() === "" - ); - if (blankNames.length > 0) { - // JSON.stringify keeps a blank name readable in the message — - // joining the raw keys renders as nothing after the colon. - alert( - t("saveBlockedNameConflict", { - names: blankNames.map((k) => JSON.stringify(k)).join(", "), - }) - ); + const blocked = hardBlockMessage(); + if (blocked) { + alert(blocked); return false; } const issues = validateProviders(config, config.models, tGeneric, target); @@ -219,7 +269,7 @@ export default function App() { setPendingNewProviders(new Set()); return true; }, - [config, dirty, save, t], + [config, dirty, hardBlockMessage, save, t], ); // Keyboard shortcuts: Ctrl+S save, Ctrl+R reload, Ctrl+O open config dir @@ -266,7 +316,7 @@ export default function App() { // overwrite an existing provider before the user types anything. let n = providers.length + 1; let name = `provider-${n}`; - while (name in config.providers) { + while (Object.hasOwn(config.providers, name)) { n++; name = `provider-${n}`; } @@ -337,7 +387,7 @@ export default function App() { // Judged against the pre-change map: the name this write lands on is // exactly the entry the overwrite below would destroy without a trace. const displacedKey = - provider.name !== oldKey && provider.name in cfg.providers + provider.name !== oldKey && Object.hasOwn(cfg.providers, provider.name) ? provider.name : null; // Both branches assign `next`; the initial value is only there so @@ -373,30 +423,24 @@ export default function App() { providers[provider.name] = provider; next = { ...cfg, providers }; } - // Put back the entry an earlier collision displaced. This must run - // AFTER the rename above moved the edited provider off that key: while - // the edited entry still holds it, the key is present in `providers` - // and the condition would never fire. Running it before the stashing - // below also empties the ref, so a second collision in a row can stash - // its own victim instead of being skipped. - if ( - displacedRef.current && - provider.name !== displacedRef.current.key && - !(displacedRef.current.key in providers) - ) { - providers[displacedRef.current.key] = displacedRef.current.provider; - displacedRef.current = null; - } + // Put back every entry an earlier collision displaced whose key is free + // again. This must run AFTER the rename above moved the edited provider + // off that key: while the edited entry still holds it, the key is + // present in `providers` and nothing can be restored. Running it before + // the stashing below also drops the restored slots, so a second + // collision in a row can stash its own victim instead of being skipped. + restoreDisplaced(providers); // Stash whatever this write is about to overwrite. The write itself // still goes through: the name input is controlled and reports every // keystroke, so rejecting a colliding one would make the text jump back // as the user types. Keeping the loss out of config.toml is trySave's // job, not the input's. - if (displacedKey && displacedRef.current == null) { - displacedRef.current = { - key: displacedKey, - provider: cfg.providers[displacedKey], - }; + // + // One slot per squatted name, and the first snapshot wins: re-colliding + // on the same name overwrites that slot's squatter again, so the + // original victim — the only copy of it — must not be replaced. + if (displacedKey && !displacedRef.current.has(displacedKey)) { + displacedRef.current.set(displacedKey, cfg.providers[displacedKey]); } return next; }); @@ -407,14 +451,11 @@ export default function App() { updateConfig((cfg) => { const providers = { ...cfg.providers }; delete providers[name]; - // The stash holds a provider an earlier collision overwrote in memory. - // Deleting frees the key the edited entry was squatting on, so the - // displaced provider can go back instead of staying stranded — a - // stranded stash blocks every later save until the user discards. - if (displacedRef.current && !(displacedRef.current.key in providers)) { - providers[displacedRef.current.key] = displacedRef.current.provider; - displacedRef.current = null; - } + // The stash holds providers an earlier collision overwrote in memory. + // Deleting frees the keys they were squatting on, so they can go back + // instead of staying stranded — a stranded stash blocks every later + // save until the user discards. + restoreDisplaced(providers); const models = { ...cfg.models }; const removedAliases: string[] = []; for (const key of Object.keys(models)) { @@ -450,6 +491,18 @@ export default function App() { }, }; }); + // Same hard blocks as trySave: this save is silent (no loading screen, no + // completeness confirm), but "silent" must not mean "unchecked" — a + // collided or blank-named config would otherwise reach disk through the + // usage panel, which the whole-config save path refuses to do. + // + // Returns without throwing on purpose: the panel's test flow awaits this + // and would report a rejection as a network error. + const blocked = hardBlockMessage(); + if (blocked) { + alert(blocked); + return Promise.resolve(); + } // Silent save: a normal save flips App to its loading screen, which // unmounts this panel and swallows the in-flight test query's result. // Returned so the panel's test can await persistence before querying. @@ -606,7 +659,7 @@ export default function App() { alert(t("validationNameEmpty")); return; } - if (provider.name !== editingProvider && provider.name in config.providers) { + if (provider.name !== editingProvider && Object.hasOwn(config.providers, provider.name)) { alert(t("applyJsonNameConflict")); return; } @@ -629,7 +682,7 @@ export default function App() { for (const m of models) { let alias = m.alias; let n = 1; - while (alias in updatedModels) { + while (Object.hasOwn(updatedModels, alias)) { alias = `${m.alias}-${n}`; n++; } @@ -645,7 +698,7 @@ export default function App() { } let default_model = cfg.default_model; - if (default_model && !(default_model in updatedModels)) { + if (default_model && !Object.hasOwn(updatedModels, default_model)) { default_model = null; } @@ -653,7 +706,8 @@ export default function App() { // (provider renamed, models dropped) must not survive as zombies. const removedAliases = Object.keys(cfg.models).filter( (key) => - cfg.models[key].provider === oldName && !(key in updatedModels) + cfg.models[key].provider === oldName && + !Object.hasOwn(updatedModels, key) ); const raw_other = purgeSubagentPoolAliases(cfg.raw_other, removedAliases); @@ -664,6 +718,13 @@ export default function App() { providers[provider.name] = setProviderDefaultModel(updatedProvider, default_model); } + // Applying JSON is another way to move the edited entry off a name it + // squatted on, so the same restore applies: without it a stash left + // over from an earlier collision would be stranded, and every later + // save hard-blocked forever (the panel cannot be left with the stash + // intact — reloading clears it only via an explicit discard). + restoreDisplaced(providers); + return { ...cfg, providers, models: updatedModels, default_model, raw_other }; }); if (provider.name !== editingProvider) { @@ -697,13 +758,27 @@ export default function App() { const currentProvider = config.providers[editingProvider]; + // Top-bar navigation guard. Only an *unresolved* collision blocks it: the + // stash is the one state the user can only fix from the provider edit page + // (rename away, delete the squatter, or discard), so wandering off to the + // dashboard leaves a config that refuses every save. Plain unsaved edits + // deliberately keep navigating — that flow is unchanged. + const guardedSetView = (next: View, prepare?: () => void) => { + if (displacedRef.current.size > 0) { + alert(t("saveBlockedDisplaced")); + return; + } + prepare?.(); + setView(next); + }; + return (