diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 35893b3be..b271c4fa4 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -2,3 +2,16 @@ singleQuote: true semi: false printWidth: 100 trailingComma: none +overrides: + - files: + - "**/*.{js,mjs,cjs,jsx}" + options: + plugins: + - "@prettier/plugin-oxc" + parser: oxc + - files: + - "**/*.{ts,mts,cts,tsx}" + options: + plugins: + - "@prettier/plugin-oxc" + parser: oxc-ts diff --git a/package.json b/package.json index a089f7cc8..ca4000b81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "DeepChat", - "version": "0.4.3", + "version": "0.4.4", "description": "DeepChat,一个简单易用的AI客户端", "main": "./out/main/index.js", "author": "ThinkInAIXYZ", @@ -20,7 +20,7 @@ "test:watch": "vitest --watch", "test:ui": "vitest --ui", "format:check": "prettier --check .", - "format": "prettier --write .", + "format": "prettier --cache --write .", "lint": "npx -y oxlint .", "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", "typecheck:web": "vue-tsc --noEmit -p tsconfig.app.json --composite false", @@ -103,12 +103,10 @@ "@iconify-json/lucide": "^1.2.66", "@iconify-json/vscode-icons": "^1.2.30", "@iconify/vue": "^5.0.0", - "@internationalized/date": "^3.9.0", "@lingual/i18n-check": "^0.8.6", - "@radix-icons/vue": "^1.0.0", + "@prettier/plugin-oxc": "^0.0.4", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.13", - "@tanstack/vue-table": "^8.21.3", "@tiptap/core": "^2.11.7", "@tiptap/extension-code-block": "^2.11.9", "@tiptap/extension-document": "^2.11.7", @@ -135,10 +133,9 @@ "clsx": "^2.1.1", "cross-env": "^10.1.0", "dayjs": "^1.11.18", - "electron": "^37.6.1", + "electron": "^37.8.0", "electron-builder": "26.0.12", "electron-vite": "^4.0.1", - "embla-carousel-vue": "^8.6.0", "jsdom": "^26.1.0", "katex": "^0.16.25", "lint-staged": "^16.1.6", @@ -159,7 +156,6 @@ "tippy.js": "^6.3.7", "tw-animate-css": "^1.4.0", "typescript": "^5.8.3", - "vaul-vue": "^0.4.1", "vee-validate": "^4.15.1", "vite": "^7.1.11", "vite-plugin-monaco-editor-esm": "^2.0.2", @@ -168,7 +164,7 @@ "vitest": "^3.2.4", "vue": "^3.5.22", "vue-i18n": "^11.1.12", - "vue-renderer-markdown": "0.0.60-beta.5", + "vue-renderer-markdown": "0.0.61-beta.7", "vue-router": "4", "vue-sonner": "^2.0.9", "vue-tsc": "^2.2.12", diff --git a/resources/model-db/providers.json b/resources/model-db/providers.json index d6976a424..9e1e8bebc 100644 --- a/resources/model-db/providers.json +++ b/resources/model-db/providers.json @@ -3470,8 +3470,8 @@ ] }, "limit": { - "context": 128000, - "output": 32768 + "context": 121808, + "output": 8192 }, "temperature": true, "tool_call": true, @@ -3502,8 +3502,8 @@ ] }, "limit": { - "context": 128000, - "output": 32768 + "context": 12952, + "output": 2048 }, "temperature": true, "tool_call": true, @@ -3533,8 +3533,8 @@ ] }, "limit": { - "context": 128000, - "output": 16384 + "context": 58904, + "output": 4096 }, "temperature": true, "tool_call": true, @@ -3564,8 +3564,8 @@ ] }, "limit": { - "context": 128000, - "output": 32768 + "context": 121808, + "output": 8192 }, "temperature": true, "tool_call": true, @@ -3596,8 +3596,8 @@ ] }, "limit": { - "context": 128000, - "output": 131072 + "context": 121808, + "output": 8192 }, "temperature": true, "tool_call": true, @@ -3967,9 +3967,9 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2024-01", - "release_date": "2024-01-01", - "last_updated": "2025-09-05", + "knowledge": "2025-08", + "release_date": "2025-08-04", + "last_updated": "2025-08-14", "cost": { "input": 0, "output": 0 @@ -8338,9 +8338,9 @@ } }, { - "id": "anthropic/claude-3.7-sonnet", - "name": "Claude Sonnet 3.7", - "display_name": "Claude Sonnet 3.7", + "id": "anthropic/claude-3.5-haiku", + "name": "Claude Haiku 3.5", + "display_name": "Claude Haiku 3.5", "modalities": { "input": [ "text", @@ -8352,30 +8352,29 @@ }, "limit": { "context": 200000, - "output": 64000 + "output": 8192 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, "open_weights": false, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 } }, { - "id": "anthropic/claude-3-5-haiku", - "name": "Claude Haiku 3.5", - "display_name": "Claude Haiku 3.5", + "id": "anthropic/claude-3.7-sonnet", + "name": "Claude Sonnet 3.7", + "display_name": "Claude Sonnet 3.7", "modalities": { "input": [ "text", @@ -8387,23 +8386,24 @@ }, "limit": { "context": 200000, - "output": 8192 + "output": 64000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, "open_weights": false, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", "cost": { - "input": 0.8, - "output": 4, - "cache_read": 0.08, - "cache_write": 1 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { @@ -15636,85 +15636,15 @@ "last_updated": "2025-10-15", "cost": { "input": 1, - "output": 1.25, + "output": 5, "cache_read": 0.1, "cache_write": 1.25 } }, { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "display_name": "Claude Sonnet 4.5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1000000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 - } - }, - { - "id": "gpt-5-codex", - "name": "GPT-5-Codex", - "display_name": "GPT-5-Codex", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 400000, - "output": 128000 - }, - "temperature": false, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "cost": { - "input": 1.25, - "output": 10, - "reasoning": 10, - "cache_read": 0.125 - } - }, - { - "id": "an-gbt", - "name": "Code GBT (alpha)", - "display_name": "Code GBT (alpha)", + "id": "minimax-m2", + "name": "MiniMax M2 (alpha)", + "display_name": "MiniMax M2 (alpha)", "modalities": { "input": [ "text" @@ -15735,185 +15665,18 @@ }, "attachment": false, "open_weights": true, - "knowledge": "2025-01", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "cost": { - "input": 1, - "output": 3 - } - }, - { - "id": "big-pickle", - "name": "Big Pickle", - "display_name": "Big Pickle", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 128000 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": false, - "knowledge": "2025-01", - "release_date": "2025-10-17", - "last_updated": "2025-10-17", - "cost": { - "input": 0, - "output": 0, - "cache_read": 0, - "cache_write": 0 - } - }, - { - "id": "claude-3-5-haiku", - "name": "Claude Haiku 3.5", - "display_name": "Claude Haiku 3.5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "cost": { - "input": 0.8, - "output": 4, - "cache_read": 0.08, - "cache_write": 1 - } - }, - { - "id": "glm-4.6", - "name": "GLM-4.6 (beta)", - "display_name": "GLM-4.6 (beta)", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 204800, - "output": 131072 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "cost": { - "input": 0.6, - "output": 1.9 - } - }, - { - "id": "grok-code", - "name": "Grok Code Fast 1", - "display_name": "Grok Code Fast 1", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 256000, - "output": 256000 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "release_date": "2025-08-20", - "last_updated": "2025-08-20", - "cost": { - "input": 0, - "output": 0, - "cache_read": 0, - "cache_write": 0 - } - }, - { - "id": "code-supernova", - "name": "Code Supernova 1M", - "display_name": "Code Supernova 1M", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1000000, - "output": 1000000 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "release_date": "2025-09-19", - "last_updated": "2025-09-19", + "knowledge": "2024-10", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", "cost": { - "input": 0, - "output": 0, - "cache_read": 0, - "cache_write": 0 + "input": 1.2, + "output": 1.2 } }, { - "id": "claude-sonnet-4", - "name": "Claude Sonnet 4", - "display_name": "Claude Sonnet 4", + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5", + "display_name": "Claude Sonnet 4.5", "modalities": { "input": [ "text", @@ -15935,9 +15698,9 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", "cost": { "input": 3, "output": 15, @@ -15946,9 +15709,244 @@ } }, { - "id": "gpt-5", - "name": "GPT-5", - "display_name": "GPT-5", + "id": "gpt-5-codex", + "name": "GPT-5-Codex", + "display_name": "GPT-5-Codex", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 1.25, + "output": 10, + "reasoning": 10, + "cache_read": 0.125 + } + }, + { + "id": "an-gbt", + "name": "Code GBT (alpha)", + "display_name": "Code GBT (alpha)", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-01", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 1, + "output": 3 + } + }, + { + "id": "big-pickle", + "name": "Big Pickle", + "display_name": "Big Pickle", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 128000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": false, + "knowledge": "2025-01", + "release_date": "2025-10-17", + "last_updated": "2025-10-17", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + { + "id": "claude-3-5-haiku", + "name": "Claude Haiku 3.5", + "display_name": "Claude Haiku 3.5", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + }, + { + "id": "glm-4.6", + "name": "GLM-4.6 (beta)", + "display_name": "GLM-4.6 (beta)", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "cost": { + "input": 0.6, + "output": 1.9 + } + }, + { + "id": "grok-code", + "name": "Grok Code Fast 1", + "display_name": "Grok Code Fast 1", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "release_date": "2025-08-20", + "last_updated": "2025-08-20", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + } + }, + { + "id": "claude-sonnet-4", + "name": "Claude Sonnet 4", + "display_name": "Claude Sonnet 4", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + { + "id": "gpt-5", + "name": "GPT-5", + "display_name": "GPT-5", "modalities": { "input": [ "text", @@ -21109,6 +21107,39 @@ "cache_read": 0.13 } }, + { + "id": "gpt-5-pro", + "name": "GPT-5 Pro", + "display_name": "GPT-5 Pro", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 272000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-09-30", + "release_date": "2025-10-06", + "last_updated": "2025-10-06", + "cost": { + "input": 15, + "output": 120 + } + }, { "id": "gpt-5-chat", "name": "gpt-5-chat", @@ -21431,48 +21462,49 @@ } ] }, - "v0": { - "id": "v0", - "name": "v0", - "display_name": "v0", - "doc": "https://sdk.vercel.ai/providers/ai-sdk-providers/vercel", + "zenmux": { + "id": "zenmux", + "name": "zenmux", + "display_name": "zenmux", + "api": "https://zenmux.ai/api/v1", + "doc": "https://docs.zenmux.ai", "models": [ { - "id": "v0-1.5-lg", - "name": "v0-1.5-lg", - "display_name": "v0-1.5-lg", + "id": "moonshotai/kimi-k2-0905", + "name": "MoonshotAI: Kimi K2 0905", + "display_name": "MoonshotAI: Kimi K2 0905", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 512000, - "output": 32000 + "context": 262144, + "output": 262144 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": true, + "attachment": false, "open_weights": false, - "release_date": "2025-06-09", - "last_updated": "2025-06-09", + "knowledge": "2024-10", + "release_date": "2025-09-04", + "last_updated": "2025-09-04", "cost": { - "input": 15, - "output": 75 + "input": 0.6, + "output": 2.5, + "cache_read": 0.15 } }, { - "id": "v0-1.5-md", - "name": "v0-1.5-md", - "display_name": "v0-1.5-md", + "id": "x-ai/grok-4-fast-non-reasoning", + "name": "Grok 4 Fast (Non-Reasoning)", + "display_name": "Grok 4 Fast (Non-Reasoning)", "modalities": { "input": [ "text", @@ -21483,40 +21515,40 @@ ] }, "limit": { - "context": 128000, - "output": 32000 + "context": 2000000, + "output": 30000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, "open_weights": false, - "release_date": "2025-06-09", - "last_updated": "2025-06-09", + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", "cost": { - "input": 3, - "output": 15 + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 } }, { - "id": "v0-1.0-md", - "name": "v0-1.0-md", - "display_name": "v0-1.0-md", + "id": "x-ai/grok-4", + "name": "Grok 4", + "display_name": "Grok 4", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 128000, - "output": 32000 + "context": 256000, + "output": 64000 }, "temperature": true, "tool_call": true, @@ -21524,28 +21556,22 @@ "supported": true, "default": true }, - "attachment": true, + "attachment": false, "open_weights": false, - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "knowledge": "2025-07", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", "cost": { "input": 3, - "output": 15 + "output": 15, + "cache_read": 0.75, + "reasoning": 15 } - } - ] - }, - "synthetic": { - "id": "synthetic", - "name": "Synthetic", - "display_name": "Synthetic", - "api": "https://api.synthetic.new/v1", - "doc": "https://synthetic.new/pricing", - "models": [ + }, { - "id": "hf:openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "display_name": "GPT OSS 120B", + "id": "x-ai/grok-code-fast-1", + "name": "Grok Code Fast 1", + "display_name": "Grok Code Fast 1", "modalities": { "input": [ "text" @@ -21555,8 +21581,8 @@ ] }, "limit": { - "context": 128000, - "output": 32768 + "context": 256000, + "output": 10000 }, "temperature": true, "tool_call": true, @@ -21565,38 +21591,32 @@ "default": true }, "attachment": false, - "open_weights": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "open_weights": false, + "knowledge": "2023-10", + "release_date": "2025-08-28", + "last_updated": "2025-08-28", "cost": { - "input": 0.1, - "output": 0.1 + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 } - } - ] - }, - "zhipuai": { - "id": "zhipuai", - "name": "Zhipu AI", - "display_name": "Zhipu AI", - "api": "https://open.bigmodel.cn/api/paas/v4", - "doc": "https://docs.z.ai/guides/overview/pricing", - "models": [ + }, { - "id": "glm-4.6", - "name": "GLM-4.6", - "display_name": "GLM-4.6", + "id": "x-ai/grok-4-fast", + "name": "Grok 4 Fast", + "display_name": "Grok 4 Fast", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 204800, - "output": 131072 + "context": 2000000, + "output": 30000 }, "temperature": true, "tool_call": true, @@ -21604,35 +21624,66 @@ "supported": true, "default": true }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-07", + "release_date": "2025-09-19", + "last_updated": "2025-09-19", + "cost": { + "input": 0.2, + "output": 0.5, + "cache_read": 0.05 + } + }, + { + "id": "deepseek/deepseek-chat", + "name": "DeepSeek: DeepSeek V3", + "display_name": "DeepSeek: DeepSeek V3", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", + "knowledge": "2025-07", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.11, - "cache_write": 0 + "input": 0.56, + "output": 1.68, + "cache_read": 0.07 } }, { - "id": "glm-4.5v", - "name": "GLM 4.5V", - "display_name": "GLM 4.5V", + "id": "google/gemini-2.5-pro", + "name": "Google: Gemini 2.5 Pro", + "display_name": "Google: Gemini 2.5 Pro", "modalities": { "input": [ - "text", "image", - "video" + "text", + "audio" ], "output": [ "text" ] }, "limit": { - "context": 64000, - "output": 16384 + "context": 1048576, + "output": 65536 }, "temperature": true, "tool_call": true, @@ -21641,30 +21692,33 @@ "default": true }, "attachment": true, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", + "open_weights": false, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", "cost": { - "input": 0.6, - "output": 1.8 + "input": 1.25, + "output": 10, + "cache_read": 0.31, + "cache_write": 4.5 } }, { - "id": "glm-4.5-air", - "name": "GLM-4.5-Air", - "display_name": "GLM-4.5-Air", + "id": "openai/gpt-5-codex", + "name": "GPT-5 Codex", + "display_name": "GPT-5 Codex", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 131072, - "output": 98304 + "context": 400000, + "output": 128000 }, "temperature": true, "tool_call": true, @@ -21672,56 +21726,55 @@ "supported": true, "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", + "attachment": true, + "open_weights": false, + "knowledge": "2024-10-01", + "release_date": "2025-09-23", + "last_updated": "2025-09-23", "cost": { - "input": 0.2, - "output": 1.1, - "cache_read": 0.03, - "cache_write": 0 + "input": 1.25, + "output": 10, + "cache_read": 0.125 } }, { - "id": "glm-4.5", - "name": "GLM-4.5", - "display_name": "GLM-4.5", + "id": "openai/gpt-5", + "name": "gpt-5", + "display_name": "gpt-5", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 131072, - "output": 98304 + "context": 400000, + "output": 128000 }, - "temperature": true, + "temperature": false, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", + "attachment": true, + "open_weights": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.11, - "cache_write": 0 + "input": 1.25, + "output": 10, + "cache_read": 0.13 } }, { - "id": "glm-4.5-flash", - "name": "GLM-4.5-Flash", - "display_name": "GLM-4.5-Flash", + "id": "inclusionai/ring-1t", + "name": "inclusionAI: Ring 1T", + "display_name": "inclusionAI: Ring 1T", "modalities": { "input": [ "text" @@ -21732,7 +21785,7 @@ }, "limit": { "context": 131072, - "output": 98304 + "output": 131072 }, "temperature": true, "tool_call": true, @@ -21742,29 +21795,19 @@ }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", + "knowledge": "2025-10", + "release_date": "2025-10-12", + "last_updated": "2025-10-12", "cost": { - "input": 0, - "output": 0, - "cache_read": 0, - "cache_write": 0 + "input": 0.56, + "output": 2.24, + "cache_read": 0.112 } - } - ] - }, - "submodel": { - "id": "submodel", - "name": "submodel", - "display_name": "submodel", - "api": "https://llm.submodel.ai/v1", - "doc": "https://submodel.gitbook.io", - "models": [ + }, { - "id": "openai/gpt-oss-120b", - "name": "GPT OSS 120B", - "display_name": "GPT OSS 120B", + "id": "inclusionai/lint-1t", + "name": "Ling-1T", + "display_name": "Ling-1T", "modalities": { "input": [ "text" @@ -21774,8 +21817,8 @@ ] }, "limit": { - "context": 131072, - "output": 32768 + "context": 128000, + "output": 32000 }, "temperature": true, "tool_call": true, @@ -21785,26 +21828,19 @@ }, "attachment": false, "open_weights": true, - "release_date": "2025-08-23", - "last_updated": "2025-08-23", + "knowledge": "2025-10", + "release_date": "2025-10-09", + "last_updated": "2025-10-09", "cost": { - "input": 0.1, - "output": 0.5 + "input": 0.56, + "output": 2.24, + "cache_read": 0.112 } - } - ] - }, - "zai": { - "id": "zai", - "name": "Z.AI", - "display_name": "Z.AI", - "api": "https://api.z.ai/api/paas/v4", - "doc": "https://docs.z.ai/guides/overview/pricing", - "models": [ + }, { - "id": "glm-4.5-flash", - "name": "GLM-4.5-Flash", - "display_name": "GLM-4.5-Flash", + "id": "z-ai/glm-4.5-air", + "name": "GLM-4.5-Air", + "display_name": "GLM-4.5-Air", "modalities": { "input": [ "text" @@ -21829,16 +21865,16 @@ "release_date": "2025-07-28", "last_updated": "2025-07-28", "cost": { - "input": 0, - "output": 0, - "cache_read": 0, + "input": 0.2, + "output": 1.1, + "cache_read": 0.03, "cache_write": 0 } }, { - "id": "glm-4.5", - "name": "GLM-4.5", - "display_name": "GLM-4.5", + "id": "z-ai/glm-4.6", + "name": "GLM-4.6", + "display_name": "GLM-4.6", "modalities": { "input": [ "text" @@ -21848,8 +21884,8 @@ ] }, "limit": { - "context": 131072, - "output": 98304 + "context": 204800, + "output": 131072 }, "temperature": true, "tool_call": true, @@ -21860,8 +21896,8 @@ "attachment": false, "open_weights": true, "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", "cost": { "input": 0.6, "output": 2.2, @@ -21870,9 +21906,9 @@ } }, { - "id": "glm-4.5-air", - "name": "GLM-4.5-Air", - "display_name": "GLM-4.5-Air", + "id": "qwen/qwen3-coder-plus", + "name": "Qwen: Qwen3 Coder 480B A35B Instruct", + "display_name": "Qwen: Qwen3 Coder 480B A35B Instruct", "modalities": { "input": [ "text" @@ -21882,44 +21918,41 @@ ] }, "limit": { - "context": 131072, - "output": 98304 + "context": 1000000, + "output": 66540 }, "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": false, "open_weights": true, "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", "cost": { - "input": 0.2, - "output": 1.1, - "cache_read": 0.03, - "cache_write": 0 + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 } }, { - "id": "glm-4.5v", - "name": "GLM 4.5V", - "display_name": "GLM 4.5V", + "id": "kuaishou/kat-coder-pro-v1", + "name": "KAT-Coder-Pro-V1", + "display_name": "KAT-Coder-Pro-V1", "modalities": { "input": [ - "text", - "image", - "video" + "text" ], "output": [ "text" ] }, "limit": { - "context": 64000, - "output": 16384 + "context": 256000, + "output": 32000 }, "temperature": true, "tool_call": true, @@ -21927,31 +21960,33 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", + "attachment": false, + "open_weights": false, + "knowledge": "2025-01-01", + "release_date": "2025-10-23", + "last_updated": "2025-10-23", "cost": { "input": 0.6, - "output": 1.8 + "output": 2.4, + "cache_read": 0.12 } }, { - "id": "glm-4.6", - "name": "GLM-4.6", - "display_name": "GLM-4.6", + "id": "anthropic/claude-haiku-4.5", + "name": "Claude Haiku 4.5", + "display_name": "Claude Haiku 4.5", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 204800, - "output": 131072 + "context": 200000, + "output": 64000 }, "temperature": true, "tool_call": true, @@ -21959,62 +21994,57 @@ "supported": true, "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", + "attachment": true, + "open_weights": false, + "knowledge": "2025-02-31", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.11, - "cache_write": 0 + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 } - } - ] - }, - "inference": { - "id": "inference", - "name": "Inference", - "display_name": "Inference", - "api": "https://inference.net/v1", - "doc": "https://inference.net/models", - "models": [ + }, { - "id": "mistral/mistral-nemo-12b-instruct", - "name": "Mistral Nemo 12B Instruct", - "display_name": "Mistral Nemo 12B Instruct", + "id": "anthropic/claude-opus-4.1", + "name": "Claude Opus 4.1", + "display_name": "Claude Opus 4.1", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 16000, - "output": 4096 + "context": 200000, + "output": 32000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", "cost": { - "input": 0.038, - "output": 0.1 + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "google/gemma-3", - "name": "Google Gemma 3", - "display_name": "Google Gemma 3", + "id": "anthropic/claude-sonnet-4.5", + "name": "Claude Sonnet 4.5", + "display_name": "Claude Sonnet 4.5", "modalities": { "input": [ "text", @@ -22025,90 +22055,99 @@ ] }, "limit": { - "context": 125000, - "output": 4096 + "context": 200000, + "output": 64000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, - "open_weights": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", + "open_weights": false, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", "cost": { - "input": 0.15, - "output": 0.3 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "osmosis/osmosis-structure-0.6b", - "name": "Osmosis Structure 0.6B", - "display_name": "Osmosis Structure 0.6B", + "id": "anthropic/claude-3.5-haiku", + "name": "Claude Haiku 3.5", + "display_name": "Claude Haiku 3.5", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 4000, - "output": 2048 + "context": 200000, + "output": 8192 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "cost": { - "input": 0.1, - "output": 0.5 + "attachment": true, + "open_weights": false, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 } }, { - "id": "qwen/qwen3-embedding-4b", - "name": "Qwen 3 Embedding 4B", - "display_name": "Qwen 3 Embedding 4B", + "id": "anthropic/claude-3.5-sonnet", + "name": "Claude Sonnet 3.5 v2", + "display_name": "Claude Sonnet 3.5 v2", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 32000, - "output": 2048 + "context": 200000, + "output": 8192 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", + "attachment": true, + "open_weights": false, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", "cost": { - "input": 0.01, - "output": 0 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "qwen/qwen-2.5-7b-vision-instruct", - "name": "Qwen 2.5 7B Vision Instruct", - "display_name": "Qwen 2.5 7B Vision Instruct", + "id": "anthropic/claude-3.7-sonnet", + "name": "Claude Sonnet 3.7", + "display_name": "Claude Sonnet 3.7", "modalities": { "input": [ "text", @@ -22119,28 +22158,31 @@ ] }, "limit": { - "context": 125000, - "output": 4096 + "context": 200000, + "output": 64000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, - "open_weights": true, - "knowledge": "2024-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", + "open_weights": false, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", "cost": { - "input": 0.2, - "output": 0.2 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "meta/llama-3.2-11b-vision-instruct", - "name": "Llama 3.2 11B Vision Instruct", - "display_name": "Llama 3.2 11B Vision Instruct", + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4", + "display_name": "Claude Opus 4", "modalities": { "input": [ "text", @@ -22151,59 +22193,66 @@ ] }, "limit": { - "context": 16000, - "output": 4096 + "context": 200000, + "output": 32000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", "cost": { - "input": 0.055, - "output": 0.055 + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "meta/llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "display_name": "Llama 3.1 8B Instruct", + "id": "anthropic/claude-sonnet-4", + "name": "Claude Sonnet 4", + "display_name": "Claude Sonnet 4", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 16000, - "output": 4096 + "context": 200000, + "output": 64000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", "cost": { - "input": 0.025, - "output": 0.025 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "meta/llama-3.2-3b-instruct", - "name": "Llama 3.2 3B Instruct", - "display_name": "Llama 3.2 3B Instruct", + "id": "deepseek/deepseek-chat-v3.1", + "name": "DeepSeek: DeepSeek V3.1", + "display_name": "DeepSeek: DeepSeek V3.1", "modalities": { "input": [ "text" @@ -22213,75 +22262,94 @@ ] }, "limit": { - "context": 16000, - "output": 4096 + "context": 131072, + "output": 32768 }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + } + }, + { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek: R1 0528", + "display_name": "DeepSeek: R1 0528", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "cost": { - "input": 0.02, - "output": 0.02 + "limit": { + "context": 163840, + "output": 163840 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true } }, { - "id": "meta/llama-3.2-1b-instruct", - "name": "Llama 3.2 1B Instruct", - "display_name": "Llama 3.2 1B Instruct", + "id": "google/gemini-2.0-flash", + "name": "Google: Gemini 2.0 Flash", + "display_name": "Google: Gemini 2.0 Flash", "modalities": { "input": [ + "text", + "image", + "audio" + ], + "output": [ "text" + ] + }, + "limit": { + "context": 1048576, + "output": 8192 + }, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true + }, + { + "id": "google/gemini-2.0-flash-lite-001", + "name": "Google: Gemini 2.0 Flash Lite", + "display_name": "Google: Gemini 2.0 Flash Lite", + "modalities": { + "input": [ + "text", + "image", + "audio" ], "output": [ "text" ] }, "limit": { - "context": 16000, - "output": 4096 + "context": 1048576, + "output": 8192 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", - "last_updated": "2025-01-01", - "cost": { - "input": 0.01, - "output": 0.01 - } - } - ] - }, - "requesty": { - "id": "requesty", - "name": "Requesty", - "display_name": "Requesty", - "api": "https://router.requesty.ai/v1", - "doc": "https://requesty.ai/solution/llm-routing/models", - "models": [ + "attachment": true + }, { "id": "google/gemini-2.5-flash", - "name": "Gemini 2.5 Flash", - "display_name": "Gemini 2.5 Flash", + "name": "Google: Gemini 2.5 Flash", + "display_name": "Google: Gemini 2.5 Flash", "modalities": { "input": [ - "text", "image", - "audio", - "video", - "pdf" + "text", + "audio" ], "output": [ "text" @@ -22289,37 +22357,24 @@ }, "limit": { "context": 1048576, - "output": 65536 + "output": 65535 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "cost": { - "input": 0.3, - "output": 2.5, - "cache_read": 0.075, - "cache_write": 0.55 - } + "attachment": true }, { - "id": "google/gemini-2.5-pro", - "name": "Gemini 2.5 Pro", - "display_name": "Gemini 2.5 Pro", + "id": "google/gemini-2.5-flash-lite", + "name": "Google: Gemini 2.5 Flash Lite", + "display_name": "Google: Gemini 2.5 Flash Lite", "modalities": { "input": [ - "text", "image", - "audio", - "video", - "pdf" + "text", + "audio" ], "output": [ "text" @@ -22327,63 +22382,82 @@ }, "limit": { "context": 1048576, - "output": 65536 + "output": 65535 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "cost": { - "input": 1.25, - "output": 10, - "cache_read": 0.31, - "cache_write": 2.375 + "attachment": true + }, + { + "id": "inclusionai/ling-1t", + "name": "inclusionAI: Ling-1T", + "display_name": "inclusionAI: Ling-1T", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "tool_call": true, + "reasoning": { + "supported": false } }, { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1 Mini", - "display_name": "GPT-4.1 Mini", + "id": "inclusionai/ling-flash-2.0", + "name": "inclusionAI: Ling Flash 2.0", + "display_name": "inclusionAI: Ling Flash 2.0", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 1047576, - "output": 32768 + "context": 131072, + "output": 131072 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false + } + }, + { + "id": "inclusionai/ling-mini-2.0", + "name": "inclusionAI: Ling Mini 2.0", + "display_name": "inclusionAI: Ling Mini 2.0", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "cost": { - "input": 0.4, - "output": 1.6, - "cache_read": 0.1 + "limit": { + "context": 131072, + "output": 131072 + }, + "tool_call": true, + "reasoning": { + "supported": false } }, { - "id": "openai/gpt-5-nano", - "name": "GPT-5 Nano", - "display_name": "GPT-5 Nano", + "id": "inclusionai/ring-flash-2.0", + "name": "inclusionAI: Ring Flash 2.0", + "display_name": "inclusionAI: Ring Flash 2.0", "modalities": { "input": [ "text" @@ -22393,24 +22467,52 @@ ] }, "limit": { - "context": 16000, - "output": 4000 + "context": 131072, + "output": 131072 }, - "temperature": false, "tool_call": true, "reasoning": { "supported": true, "default": true + } + }, + { + "id": "inclusionai/ring-mini-2.0", + "name": "inclusionAI: Ring Mini 2.0", + "display_name": "inclusionAI: Ring Mini 2.0", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "cost": { - "input": 0.05, - "output": 0.4, - "cache_read": 0.01 + "limit": { + "context": 131072, + "output": 131072 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "moonshotai/kimi-k2-0711", + "name": "kimi-k2-0711", + "display_name": "kimi-k2-0711", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": true, + "reasoning": { + "supported": false } }, { @@ -22447,9 +22549,9 @@ } }, { - "id": "openai/o4-mini", - "name": "o4 Mini", - "display_name": "o4 Mini", + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1 mini", + "display_name": "GPT-4.1 mini", "modalities": { "input": [ "text", @@ -22460,30 +22562,29 @@ ] }, "limit": { - "context": 200000, - "output": 100000 + "context": 1047576, + "output": 32768 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, "open_weights": false, - "knowledge": "2024-06", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", "cost": { - "input": 1.1, - "output": 4.4, - "cache_read": 0.28 + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 } }, { - "id": "openai/gpt-5-mini", - "name": "GPT-5 Mini", - "display_name": "GPT-5 Mini", + "id": "openai/gpt-4.1-nano", + "name": "GPT-4.1 nano", + "display_name": "GPT-4.1 nano", "modalities": { "input": [ "text", @@ -22494,30 +22595,62 @@ ] }, "limit": { - "context": 128000, - "output": 32000 + "context": 1047576, + "output": 32768 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, "open_weights": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", "cost": { - "input": 0.25, - "output": 2, + "input": 0.1, + "output": 0.4, "cache_read": 0.03 } }, + { + "id": "openai/gpt-4o", + "name": "GPT-4o", + "display_name": "GPT-4o", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2023-09", + "release_date": "2024-05-13", + "last_updated": "2024-08-06", + "cost": { + "input": 2.5, + "output": 10, + "cache_read": 1.25 + } + }, { "id": "openai/gpt-4o-mini", - "name": "GPT-4o Mini", - "display_name": "GPT-4o Mini", + "name": "GPT-4o mini", + "display_name": "GPT-4o mini", "modalities": { "input": [ "text", @@ -22538,7 +22671,7 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2024-10", + "knowledge": "2023-09", "release_date": "2024-07-18", "last_updated": "2024-07-18", "cost": { @@ -22548,20 +22681,21 @@ } }, { - "id": "openai/gpt-5", - "name": "GPT-5", - "display_name": "GPT-5", + "id": "openai/gpt-5-chat", + "name": "gpt-5-chat", + "display_name": "gpt-5-chat" + }, + { + "id": "openai/gpt-5-mini", + "name": "gpt-5-mini", + "display_name": "gpt-5-mini", "modalities": { "input": [ "text", - "audio", - "image", - "video" + "image" ], "output": [ - "text", - "audio", - "image" + "text" ] }, "limit": { @@ -22576,19 +22710,19 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2024-09-30", + "knowledge": "2024-05-30", "release_date": "2025-08-07", "last_updated": "2025-08-07", "cost": { - "input": 1.25, - "output": 10, - "cache_read": 0.13 + "input": 0.25, + "output": 2, + "cache_read": 0.03 } }, { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4", - "display_name": "Claude Opus 4", + "id": "openai/gpt-5-nano", + "name": "gpt-5-nano", + "display_name": "gpt-5-nano", "modalities": { "input": [ "text", @@ -22599,10 +22733,10 @@ ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 400000, + "output": 128000 }, - "temperature": true, + "temperature": false, "tool_call": true, "reasoning": { "supported": true, @@ -22610,20 +22744,19 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 } }, { - "id": "anthropic/claude-3-7-sonnet", - "name": "Claude Sonnet 3.7", - "display_name": "Claude Sonnet 3.7", + "id": "openai/o4-mini", + "name": "o4-mini", + "display_name": "o4-mini", "modalities": { "input": [ "text", @@ -22635,9 +22768,9 @@ }, "limit": { "context": 200000, - "output": 64000 + "output": 100000 }, - "temperature": true, + "temperature": false, "tool_call": true, "reasoning": { "supported": true, @@ -22645,129 +22778,63 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2024-01", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", + "knowledge": "2024-05", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 } }, { - "id": "anthropic/claude-4-sonnet-20250522", - "name": "Claude Sonnet 4", - "display_name": "Claude Sonnet 4", + "id": "qwen/qwen3-235b-a22b-2507", + "name": "Qwen: Qwen3 235B A22B Instruct 2507", + "display_name": "Qwen: Qwen3 235B A22B Instruct 2507", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 262144, + "output": 262144 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 } }, { - "id": "anthropic/claude-opus-4-1-20250805", - "name": "Claude Opus 4.1", - "display_name": "Claude Opus 4.1", + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen: Qwen3 235B A22B Thinking 2507", + "display_name": "Qwen: Qwen3 235B A22B Thinking 2507", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 262144, + "output": 262144 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 - } - } - ] - }, - "morph": { - "id": "morph", - "name": "Morph", - "display_name": "Morph", - "api": "https://api.morphllm.com/v1", - "doc": "https://docs.morphllm.com/api-reference/introduction", - "models": [ - { - "id": "morph-v3-large", - "name": "Morph v3 Large", - "display_name": "Morph v3 Large", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 32000, - "output": 32000 - }, - "temperature": false, - "tool_call": false, - "reasoning": { - "supported": false - }, - "attachment": false, - "open_weights": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "cost": { - "input": 0.9, - "output": 1.9 } }, { - "id": "auto", - "name": "Auto", - "display_name": "Auto", + "id": "qwen/qwen3-coder", + "name": "Qwen: Qwen3 Coder 480B A35B", + "display_name": "Qwen: Qwen3 Coder 480B A35B", "modalities": { "input": [ "text" @@ -22777,66 +22844,19 @@ ] }, "limit": { - "context": 32000, - "output": 32000 + "context": 262144, + "output": 262144 }, - "temperature": false, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false - }, - "attachment": false, - "open_weights": false, - "release_date": "2024-06-01", - "last_updated": "2024-06-01", - "cost": { - "input": 0.85, - "output": 1.55 + "supported": true, + "default": true } }, { - "id": "morph-v3-fast", - "name": "Morph v3 Fast", - "display_name": "Morph v3 Fast", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 16000, - "output": 16000 - }, - "temperature": false, - "tool_call": false, - "reasoning": { - "supported": false - }, - "attachment": false, - "open_weights": false, - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "cost": { - "input": 0.8, - "output": 1.2 - } - } - ] - }, - "lmstudio": { - "id": "lmstudio", - "name": "LMStudio", - "display_name": "LMStudio", - "api": "http://127.0.0.1:1234/v1", - "doc": "https://lmstudio.ai/models", - "models": [ - { - "id": "openai/gpt-oss-20b", - "name": "GPT OSS 20B", - "display_name": "GPT OSS 20B", + "id": "qwen/qwen3-max", + "name": "Qwen: Qwen3 Max", + "display_name": "Qwen: Qwen3 Max", "modalities": { "input": [ "text" @@ -22846,31 +22866,22 @@ ] }, "limit": { - "context": 131072, + "context": 256000, "output": 32768 }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "cost": { - "input": 0, - "output": 0 + "supported": false } }, { - "id": "qwen/qwen3-30b-a3b-2507", - "name": "Qwen3 30B A3B 2507", - "display_name": "Qwen3 30B A3B 2507", + "id": "qwen/qwen3-vl-plus", + "name": "Qwen: Qwen3 VL 235B A22B Instruct", + "display_name": "Qwen: Qwen3 VL 235B A22B Instruct", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" @@ -22878,27 +22889,19 @@ }, "limit": { "context": 262144, - "output": 16384 + "output": 262144 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-30", - "last_updated": "2025-07-30", - "cost": { - "input": 0, - "output": 0 + "supported": true, + "default": true } }, { - "id": "qwen/qwen3-coder-30b", - "name": "Qwen3 Coder 30B", - "display_name": "Qwen3 Coder 30B", + "id": "z-ai/glm-4.5", + "name": "GLM-4.5", + "display_name": "GLM-4.5", "modalities": { "input": [ "text" @@ -22908,36 +22911,39 @@ ] }, "limit": { - "context": 262144, - "output": 65536 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": false, "open_weights": true, "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 0, - "output": 0 + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 } } ] }, - "anthropic": { - "id": "anthropic", - "name": "Anthropic", - "display_name": "Anthropic", - "doc": "https://docs.anthropic.com/en/docs/about-claude/models", + "v0": { + "id": "v0", + "name": "v0", + "display_name": "v0", + "doc": "https://sdk.vercel.ai/providers/ai-sdk-providers/vercel", "models": [ { - "id": "claude-3-5-sonnet-20241022", - "name": "Claude Sonnet 3.5 v2", - "display_name": "Claude Sonnet 3.5 v2", + "id": "v0-1.5-lg", + "name": "v0-1.5-lg", + "display_name": "v0-1.5-lg", "modalities": { "input": [ "text", @@ -22948,30 +22954,28 @@ ] }, "limit": { - "context": 200000, - "output": 8192 + "context": 512000, + "output": 32000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, "open_weights": false, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", + "release_date": "2025-06-09", + "last_updated": "2025-06-09", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 15, + "output": 75 } }, { - "id": "claude-3-5-sonnet-20240620", - "name": "Claude Sonnet 3.5", - "display_name": "Claude Sonnet 3.5", + "id": "v0-1.5-md", + "name": "v0-1.5-md", + "display_name": "v0-1.5-md", "modalities": { "input": [ "text", @@ -22982,30 +22986,28 @@ ] }, "limit": { - "context": 200000, - "output": 8192 + "context": 128000, + "output": 32000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, "open_weights": false, - "knowledge": "2024-04-30", - "release_date": "2024-06-20", - "last_updated": "2024-06-20", + "release_date": "2025-06-09", + "last_updated": "2025-06-09", "cost": { "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "output": 15 } }, { - "id": "claude-3-opus-20240229", - "name": "Claude Opus 3", - "display_name": "Claude Opus 3", + "id": "v0-1.0-md", + "name": "v0-1.0-md", + "display_name": "v0-1.0-md", "modalities": { "input": [ "text", @@ -23016,42 +23018,48 @@ ] }, "limit": { - "context": 200000, - "output": 4096 + "context": 128000, + "output": 32000 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": true, "open_weights": false, - "knowledge": "2023-08-31", - "release_date": "2024-02-29", - "last_updated": "2024-02-29", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 3, + "output": 15 } - }, + } + ] + }, + "synthetic": { + "id": "synthetic", + "name": "Synthetic", + "display_name": "Synthetic", + "api": "https://api.synthetic.new/v1", + "doc": "https://synthetic.new/pricing", + "models": [ { - "id": "claude-sonnet-4-5-20250929", - "name": "Claude Sonnet 4.5", - "display_name": "Claude Sonnet 4.5", + "id": "hf:openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "display_name": "GPT OSS 120B", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 128000, + "output": 32768 }, "temperature": true, "tool_call": true, @@ -23059,34 +23067,39 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", + "attachment": false, + "open_weights": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0.1, + "output": 0.1 } - }, + } + ] + }, + "zhipuai": { + "id": "zhipuai", + "name": "Zhipu AI", + "display_name": "Zhipu AI", + "api": "https://open.bigmodel.cn/api/paas/v4", + "doc": "https://docs.z.ai/guides/overview/pricing", + "models": [ { - "id": "claude-sonnet-4-20250514", - "name": "Claude Sonnet 4", - "display_name": "Claude Sonnet 4", + "id": "glm-4.6", + "name": "GLM-4.6", + "display_name": "GLM-4.6", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 204800, + "output": 131072 }, "temperature": true, "tool_call": true, @@ -23094,34 +23107,35 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 } }, { - "id": "claude-opus-4-20250514", - "name": "Claude Opus 4", - "display_name": "Claude Opus 4", + "id": "glm-4.5v", + "name": "GLM 4.5V", + "display_name": "GLM 4.5V", "modalities": { "input": [ "text", - "image" + "image", + "video" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 64000, + "output": 16384 }, "temperature": true, "tool_call": true, @@ -23130,101 +23144,98 @@ "default": true }, "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.6, + "output": 1.8 } }, { - "id": "claude-3-5-haiku-20241022", - "name": "Claude Haiku 3.5", - "display_name": "Claude Haiku 3.5", + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "display_name": "GLM-4.5-Air", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 8192 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 0.8, - "output": 4, - "cache_read": 0.08, - "cache_write": 1 + "input": 0.2, + "output": 1.1, + "cache_read": 0.03, + "cache_write": 0 } }, { - "id": "claude-3-haiku-20240307", - "name": "Claude Haiku 3", - "display_name": "Claude Haiku 3", + "id": "glm-4.5", + "name": "GLM-4.5", + "display_name": "GLM-4.5", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 4096 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2023-08-31", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 0.25, - "output": 1.25, - "cache_read": 0.03, - "cache_write": 0.3 + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 } }, { - "id": "claude-3-7-sonnet-20250219", - "name": "Claude Sonnet 3.7", - "display_name": "Claude Sonnet 3.7", + "id": "glm-4.5-flash", + "name": "GLM-4.5-Flash", + "display_name": "GLM-4.5-Flash", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, @@ -23232,34 +23243,42 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 } - }, + } + ] + }, + "submodel": { + "id": "submodel", + "name": "submodel", + "display_name": "submodel", + "api": "https://llm.submodel.ai/v1", + "doc": "https://submodel.gitbook.io", + "models": [ { - "id": "claude-opus-4-1-20250805", - "name": "Claude Opus 4.1", - "display_name": "Claude Opus 4.1", + "id": "openai/gpt-oss-120b", + "name": "GPT OSS 120B", + "display_name": "GPT OSS 120B", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 131072, + "output": 32768 }, "temperature": true, "tool_call": true, @@ -23267,68 +23286,73 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "attachment": false, + "open_weights": true, + "release_date": "2025-08-23", + "last_updated": "2025-08-23", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.1, + "output": 0.5 } - }, + } + ] + }, + "zai": { + "id": "zai", + "name": "Z.AI", + "display_name": "Z.AI", + "api": "https://api.z.ai/api/paas/v4", + "doc": "https://docs.z.ai/guides/overview/pricing", + "models": [ { - "id": "claude-3-sonnet-20240229", - "name": "Claude Sonnet 3", - "display_name": "Claude Sonnet 3", + "id": "glm-4.5-flash", + "name": "GLM-4.5-Flash", + "display_name": "GLM-4.5-Flash", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 4096 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2023-08-31", - "release_date": "2024-03-04", - "last_updated": "2024-03-04", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 0.3 + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 } }, { - "id": "claude-haiku-4-5-20251001", - "name": "Claude Haiku 4.5", - "display_name": "Claude Haiku 4.5", + "id": "glm-4.5", + "name": "GLM-4.5", + "display_name": "GLM-4.5", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, @@ -23336,34 +23360,33 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-02-31", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 1, - "output": 5, - "cache_read": 0.1, - "cache_write": 1.25 + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 } }, { - "id": "claude-sonnet-4-0", - "name": "Claude Sonnet 4", - "display_name": "Claude Sonnet 4", + "id": "glm-4.5-air", + "name": "GLM-4.5-Air", + "display_name": "GLM-4.5-Air", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 131072, + "output": 98304 }, "temperature": true, "tool_call": true, @@ -23371,34 +23394,35 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0.2, + "output": 1.1, + "cache_read": 0.03, + "cache_write": 0 } }, { - "id": "claude-3-7-sonnet-latest", - "name": "Claude Sonnet 3.7", - "display_name": "Claude Sonnet 3.7", + "id": "glm-4.5v", + "name": "GLM 4.5V", + "display_name": "GLM 4.5V", "modalities": { "input": [ "text", - "image" + "image", + "video" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 64000, + "output": 16384 }, "temperature": true, "tool_call": true, @@ -23407,33 +23431,30 @@ "default": true }, "attachment": true, - "open_weights": false, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0.6, + "output": 1.8 } }, { - "id": "claude-sonnet-4-5", - "name": "Claude Sonnet 4.5", - "display_name": "Claude Sonnet 4.5", + "id": "glm-4.6", + "name": "GLM-4.6", + "display_name": "GLM-4.6", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 204800, + "output": 131072 }, "temperature": true, "tool_call": true, @@ -23441,56 +23462,62 @@ "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0.6, + "output": 2.2, + "cache_read": 0.11, + "cache_write": 0 } - }, + } + ] + }, + "inference": { + "id": "inference", + "name": "Inference", + "display_name": "Inference", + "api": "https://inference.net/v1", + "doc": "https://inference.net/models", + "models": [ { - "id": "claude-3-5-haiku-latest", - "name": "Claude Haiku 3.5", - "display_name": "Claude Haiku 3.5", + "id": "mistral/mistral-nemo-12b-instruct", + "name": "Mistral Nemo 12B Instruct", + "display_name": "Mistral Nemo 12B Instruct", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 8192 + "context": 16000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", + "attachment": false, + "open_weights": true, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", "cost": { - "input": 0.8, - "output": 4, - "cache_read": 0.08, - "cache_write": 1 + "input": 0.038, + "output": 0.1 } }, { - "id": "claude-haiku-4-5", - "name": "Claude Haiku 4.5", - "display_name": "Claude Haiku 4.5", + "id": "google/gemma-3", + "name": "Google Gemma 3", + "display_name": "Google Gemma 3", "modalities": { "input": [ "text", @@ -23501,66 +23528,90 @@ ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 125000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2025-02-31", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", + "open_weights": true, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", "cost": { - "input": 1, - "output": 5, - "cache_read": 0.1, - "cache_write": 1.25 + "input": 0.15, + "output": 0.3 } }, { - "id": "claude-opus-4-1", - "name": "Claude Opus 4.1", - "display_name": "Claude Opus 4.1", + "id": "osmosis/osmosis-structure-0.6b", + "name": "Osmosis Structure 0.6B", + "display_name": "Osmosis Structure 0.6B", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 4000, + "output": 2048 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "attachment": false, + "open_weights": true, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.1, + "output": 0.5 } }, { - "id": "claude-opus-4-0", - "name": "Claude Opus 4", - "display_name": "Claude Opus 4", + "id": "qwen/qwen3-embedding-4b", + "name": "Qwen 3 Embedding 4B", + "display_name": "Qwen 3 Embedding 4B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32000, + "output": 2048 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 0.01, + "output": 0 + } + }, + { + "id": "qwen/qwen-2.5-7b-vision-instruct", + "name": "Qwen 2.5 7B Vision Instruct", + "display_name": "Qwen 2.5 7B Vision Instruct", "modalities": { "input": [ "text", @@ -23571,56 +23622,60 @@ ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 125000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "open_weights": true, + "knowledge": "2024-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.2, + "output": 0.2 } - } - ] - }, - "aihubmix": { - "id": "aihubmix", - "name": "AIHubMix", - "display_name": "AIHubMix", - "api": "https://aihubmix.com/call/mdl_info", - "models": [ + }, { - "id": "aihubmix-command-r-08-2024", - "name": "command-r-08-2024", - "display_name": "command-r-08-2024", + "id": "meta/llama-3.2-11b-vision-instruct", + "name": "Llama 3.2 11B Vision Instruct", + "display_name": "Llama 3.2 11B Vision Instruct", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 16000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 0.055, + "output": 0.055 } }, { - "id": "aihubmix-command-r-plus", - "name": "command-r-plus", - "display_name": "command-r-plus", + "id": "meta/llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "display_name": "Llama 3.1 8B Instruct", "modalities": { "input": [ "text" @@ -23629,15 +23684,29 @@ "text" ] }, - "tool_call": false, + "limit": { + "context": 16000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 0.025, + "output": 0.025 } }, { - "id": "aihubmix-command-r-plus-08-2024", - "name": "command-r-plus-08-2024", - "display_name": "command-r-plus-08-2024", + "id": "meta/llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "display_name": "Llama 3.2 3B Instruct", "modalities": { "input": [ "text" @@ -23646,496 +23715,532 @@ "text" ] }, - "tool_call": false, + "limit": { + "context": 16000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 0.02, + "output": 0.02 } }, { - "id": "aihubmix-router", - "name": "aihubmix-router", - "display_name": "aihubmix-router", + "id": "meta/llama-3.2-1b-instruct", + "name": "Llama 3.2 1B Instruct", + "display_name": "Llama 3.2 1B Instruct", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, + "limit": { + "context": 16000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false - } - }, - { - "id": "aistudio_gemini-2.0-flash", - "name": "aistudio/gemini-2.0-flash", - "display_name": "aistudio/gemini-2.0-flash", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "aistudio_gpt-4.1-mini", - "name": "gpt-4.1-mini", - "display_name": "gpt-4.1-mini", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "bce-reranker-base", - "name": "bce-reranker-base", - "display_name": "bce-reranker-base", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] }, - "tool_call": false, - "reasoning": { - "supported": false + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 0.01, + "output": 0.01 } - }, + } + ] + }, + "requesty": { + "id": "requesty", + "name": "Requesty", + "display_name": "Requesty", + "api": "https://router.requesty.ai/v1", + "doc": "https://requesty.ai/solution/llm-routing/models", + "models": [ { - "id": "bge-large-en", - "name": "bge-large-en", - "display_name": "bge-large-en", + "id": "google/gemini-2.5-flash", + "name": "Gemini 2.5 Flash", + "display_name": "Gemini 2.5 Flash", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video", + "pdf" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 1048576, + "output": 65536 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false - } - }, - { - "id": "bge-large-zh", - "name": "bge-large-zh", - "display_name": "bge-large-zh", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "chatglm_lite", - "name": "chatglm_lite", - "display_name": "chatglm_lite", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "chatglm_pro", - "name": "chatglm_pro", - "display_name": "chatglm_pro", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "chatglm_std", - "name": "chatglm_std", - "display_name": "chatglm_std", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "chatglm_turbo", - "name": "chatglm_turbo", - "display_name": "chatglm_turbo", - "tool_call": false, - "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.075, + "cache_write": 0.55 } }, { - "id": "chatgpt-4o-latest", - "name": "gpt-4o", - "display_name": "gpt-4o", + "id": "google/gemini-2.5-pro", + "name": "Gemini 2.5 Pro", + "display_name": "Gemini 2.5 Pro", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video", + "pdf" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "claude-2", - "name": "Claude 2.0", - "display_name": "Claude 2.0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "claude-2.0", - "name": "Claude 2.0", - "display_name": "Claude 2.0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "claude-2.1", - "name": "Claude 2.1", - "display_name": "Claude 2.1", - "tool_call": false, + "limit": { + "context": 1048576, + "output": 65536 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31, + "cache_write": 2.375 } }, { - "id": "claude-3-5-haiku", - "name": "claude-3-5-haiku", - "display_name": "claude-3-5-haiku", + "id": "openai/gpt-4.1-mini", + "name": "GPT-4.1 Mini", + "display_name": "GPT-4.1 Mini", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 1047576, + "output": 32768 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 } }, { - "id": "claude-3-5-sonnet", - "name": "Claude 3.5 Sonnet", - "display_name": "Claude 3.5 Sonnet", + "id": "openai/gpt-5-nano", + "name": "GPT-5 Nano", + "display_name": "GPT-5 Nano", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 16000, + "output": 4000 + }, + "temperature": false, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 0.05, + "output": 0.4, + "cache_read": 0.01 } }, { - "id": "claude-3-5-sonnet-20240620", - "name": "Claude 3.5 Sonnet", - "display_name": "Claude 3.5 Sonnet", + "id": "openai/gpt-4.1", + "name": "GPT-4.1", + "display_name": "GPT-4.1", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, + "limit": { + "context": 1047576, + "output": 32768 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 } }, { - "id": "claude-3-7-sonnet", - "name": "Claude 3.7 Sonnet", - "display_name": "Claude 3.7 Sonnet", + "id": "openai/o4-mini", + "name": "o4 Mini", + "display_name": "o4 Mini", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 100000 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - } - }, - { - "id": "claude-3-haiku-20240229", - "name": "Claude 3 Haiku", - "display_name": "Claude 3 Haiku", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] }, - "tool_call": false, - "reasoning": { - "supported": false + "attachment": true, + "open_weights": false, + "knowledge": "2024-06", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 } }, { - "id": "claude-3-haiku-20240307", - "name": "Claude 3 Haiku", - "display_name": "Claude 3 Haiku", + "id": "openai/gpt-5-mini", + "name": "GPT-5 Mini", + "display_name": "GPT-5 Mini", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "claude-3-opus-20240229", - "name": "Claude 3 Opus", - "display_name": "Claude 3 Opus", - "tool_call": false, + "limit": { + "context": 128000, + "output": 32000 + }, + "temperature": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 0.25, + "output": 2, + "cache_read": 0.03 } }, { - "id": "claude-3-sonnet-20240229", - "name": "Claude 3 Sonnet", - "display_name": "Claude 3 Sonnet", + "id": "openai/gpt-4o-mini", + "name": "GPT-4o Mini", + "display_name": "GPT-4o Mini", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, + "limit": { + "context": 128000, + "output": 16384 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10", + "release_date": "2024-07-18", + "last_updated": "2024-07-18", + "cost": { + "input": 0.15, + "output": 0.6, + "cache_read": 0.08 } }, { - "id": "claude-haiku-4-5", - "name": "claude-haiku-4-5", - "display_name": "claude-haiku-4-5", + "id": "openai/gpt-5", + "name": "GPT-5", + "display_name": "GPT-5", "modalities": { "input": [ "text", - "image" + "audio", + "image", + "video" ], "output": [ "text", + "audio", "image" ] }, + "limit": { + "context": 400000, + "output": 128000 + }, + "temperature": false, "tool_call": true, "reasoning": { "supported": true, "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.13 } }, { - "id": "claude-instant-1", - "name": "Claude Instant", - "display_name": "Claude Instant", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "claude-instant-1.2", - "name": "Claude Instant", - "display_name": "Claude Instant", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "claude-opus-4-0", - "name": "claude-opus-4-0", - "display_name": "claude-opus-4-0", + "id": "anthropic/claude-opus-4", + "name": "Claude Opus 4", + "display_name": "Claude Opus 4", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "claude-opus-4-1", - "name": "claude-opus-4-1-20250805", - "display_name": "claude-opus-4-1-20250805", + "id": "anthropic/claude-3-7-sonnet", + "name": "Claude Sonnet 3.7", + "display_name": "Claude Sonnet 3.7", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-01", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "claude-sonnet-4-0", - "name": "claude-sonnet-4-0", - "display_name": "claude-sonnet-4-0", + "id": "anthropic/claude-4-sonnet-20250522", + "name": "Claude Sonnet 4", + "display_name": "Claude Sonnet 4", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "claude-sonnet-4-5", - "name": "claude-sonnet-4-5", - "display_name": "claude-sonnet-4-5", + "id": "anthropic/claude-opus-4-1-20250805", + "name": "Claude Opus 4.1", + "display_name": "Claude Opus 4.1", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - } - }, - { - "id": "code-davinci-edit-001", - "name": "code-davinci-edit-001", - "display_name": "code-davinci-edit-001", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "codestral-latest", - "name": "Codestral 25.01", - "display_name": "Codestral 25.01", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "codex-mini-latest", - "name": "codex-mini-latest", - "display_name": "codex-mini-latest", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "cogview-3", - "name": "cogview-3", - "display_name": "cogview-3", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "cogview-3-plus", - "name": "cogview-3-plus", - "display_name": "cogview-3-plus", - "tool_call": false, - "reasoning": { - "supported": false + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } - }, + } + ] + }, + "morph": { + "id": "morph", + "name": "Morph", + "display_name": "Morph", + "api": "https://api.morphllm.com/v1", + "doc": "https://docs.morphllm.com/api-reference/introduction", + "models": [ { - "id": "command", - "name": "command", - "display_name": "command", + "id": "morph-v3-large", + "name": "Morph v3 Large", + "display_name": "Morph v3 Large", "modalities": { "input": [ "text" @@ -24144,15 +24249,28 @@ "text" ] }, + "limit": { + "context": 32000, + "output": 32000 + }, + "temperature": false, "tool_call": false, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "cost": { + "input": 0.9, + "output": 1.9 } }, { - "id": "command-a-03-2025", - "name": "Command A", - "display_name": "Command A", + "id": "auto", + "name": "Auto", + "display_name": "Auto", "modalities": { "input": [ "text" @@ -24161,42 +24279,28 @@ "text" ] }, + "limit": { + "context": 32000, + "output": 32000 + }, + "temperature": false, "tool_call": false, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": false, + "release_date": "2024-06-01", + "last_updated": "2024-06-01", + "cost": { + "input": 0.85, + "output": 1.55 } }, { - "id": "command-light", - "name": "command-light", - "display_name": "command-light", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "command-light-nightly", - "name": "command-light", - "display_name": "command-light", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "command-nightly", - "name": "command-nightly", - "display_name": "command-nightly", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "command-r", - "name": "command-r", - "display_name": "command-r", + "id": "morph-v3-fast", + "name": "Morph v3 Fast", + "display_name": "Morph v3 Fast", "modalities": { "input": [ "text" @@ -24205,15 +24309,37 @@ "text" ] }, + "limit": { + "context": 16000, + "output": 16000 + }, + "temperature": false, "tool_call": false, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": false, + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "cost": { + "input": 0.8, + "output": 1.2 } - }, + } + ] + }, + "lmstudio": { + "id": "lmstudio", + "name": "LMStudio", + "display_name": "LMStudio", + "api": "http://127.0.0.1:1234/v1", + "doc": "https://lmstudio.ai/models", + "models": [ { - "id": "command-r-08-2024", - "name": "command-r-08-2024", - "display_name": "command-r-08-2024", + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "display_name": "GPT OSS 20B", "modalities": { "input": [ "text" @@ -24222,15 +24348,29 @@ "text" ] }, - "tool_call": false, + "limit": { + "context": 131072, + "output": 32768 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 0, + "output": 0 } }, { - "id": "command-r-plus", - "name": "command-r-plus", - "display_name": "command-r-plus", + "id": "qwen/qwen3-30b-a3b-2507", + "name": "Qwen3 30B A3B 2507", + "display_name": "Qwen3 30B A3B 2507", "modalities": { "input": [ "text" @@ -24239,15 +24379,29 @@ "text" ] }, - "tool_call": false, + "limit": { + "context": 262144, + "output": 16384 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "cost": { + "input": 0, + "output": 0 } }, { - "id": "command-r-plus-08-2024", - "name": "command-r-plus-08-2024", - "display_name": "command-r-plus-08-2024", + "id": "qwen/qwen3-coder-30b", + "name": "Qwen3 Coder 30B", + "display_name": "Qwen3 Coder 30B", "modalities": { "input": [ "text" @@ -24256,471 +24410,703 @@ "text" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "computer-use-preview", - "name": "computer-use-preview", - "display_name": "computer-use-preview", - "tool_call": false, + "limit": { + "context": 262144, + "output": 65536 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "cost": { + "input": 0, + "output": 0 } - }, + } + ] + }, + "anthropic": { + "id": "anthropic", + "name": "Anthropic", + "display_name": "Anthropic", + "doc": "https://docs.anthropic.com/en/docs/about-claude/models", + "models": [ { - "id": "dall-e-2", - "name": "dall-e-2", - "display_name": "dall-e-2", + "id": "claude-opus-4-0", + "name": "Claude Opus 4 (latest)", + "display_name": "Claude Opus 4 (latest)", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "dall-e-3", - "name": "dall-e-3", - "display_name": "dall-e-3", + "id": "claude-3-5-sonnet-20241022", + "name": "Claude Sonnet 3.5 v2", + "display_name": "Claude Sonnet 3.5 v2", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "davinci", - "name": "davinci", - "display_name": "davinci", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "davinci-002", - "name": "davinci", - "display_name": "davinci", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "deepseek-ai/deepseek-llm-67b-chat", - "name": "deepseek-llm-67b-chat", - "display_name": "deepseek-llm-67b-chat", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "deepseek-ai/deepseek-vl2", - "name": "deepseek-ai/deepseek-vl2", - "display_name": "deepseek-ai/deepseek-vl2", - "tool_call": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04-30", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "deepseek-r1-distill-llama-70b", - "name": "deepseek-r1-distill-llama-70b", - "display_name": "deepseek-r1-distill-llama-70b", + "id": "claude-opus-4-1", + "name": "Claude Opus 4.1 (latest)", + "display_name": "Claude Opus 4.1 (latest)", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": true, "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "deepseek-r1-distill-qianfan-llama-8b", - "name": "deepseek-r1-distill-qianfan-llama-8b", - "display_name": "deepseek-r1-distill-qianfan-llama-8b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "deepseek-v3", - "name": "deepseek-v3", - "display_name": "deepseek-v3", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "distil-whisper-large-v3-en", - "name": "whisper-large-v3", - "display_name": "whisper-large-v3", + "id": "claude-haiku-4-5", + "name": "Claude Haiku 4.5 (latest)", + "display_name": "Claude Haiku 4.5 (latest)", "modalities": { "input": [ - "audio" + "text", + "image" ], "output": [ - "audio" + "text" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "doubao-1-5-pro-256k-250115", - "name": "Doubao-1.5-pro-256k", - "display_name": "Doubao-1.5-pro-256k", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "doubao-1-5-pro-32k-250115", - "name": "Doubao-1.5-pro-32k", - "display_name": "Doubao-1.5-pro-32k", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "doubao-1-5-thinking-vision-pro-250428", - "name": "doubao-1-5-thinking-vision-pro-250428", - "display_name": "doubao-1-5-thinking-vision-pro-250428", - "tool_call": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-02-31", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 } }, { - "id": "doubao-embedding-large-text-240915", - "name": "doubao-embedding-large-text-240915", - "display_name": "doubao-embedding-large-text-240915", + "id": "claude-3-5-sonnet-20240620", + "name": "Claude Sonnet 3.5", + "display_name": "Claude Sonnet 3.5", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04-30", + "release_date": "2024-06-20", + "last_updated": "2024-06-20", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "doubao-embedding-text-240715", - "name": "doubao-embedding-text-240715", - "display_name": "doubao-embedding-text-240715", + "id": "claude-3-5-haiku-latest", + "name": "Claude Haiku 3.5 (latest)", + "display_name": "Claude Haiku 3.5 (latest)", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 } }, { - "id": "doubao-seed-1-6", - "name": "doubao-seed-1-6", - "display_name": "doubao-seed-1-6", + "id": "claude-3-opus-20240229", + "name": "Claude Opus 3", + "display_name": "Claude Opus 3", "modalities": { "input": [ "text", - "image", - "video" + "image" ], "output": [ - "text", - "image", - "video" + "text" ] }, + "limit": { + "context": 200000, + "output": 4096 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2023-08-31", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "doubao-seed-1-6-flash", - "name": "doubao-seed-1-6-flash", - "display_name": "doubao-seed-1-6-flash", + "id": "claude-sonnet-4-5", + "name": "Claude Sonnet 4.5 (latest)", + "display_name": "Claude Sonnet 4.5 (latest)", "modalities": { "input": [ "text", - "image", - "video" + "image" ], "output": [ - "text", - "image", - "video" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "doubao-seed-1-6-thinking", - "name": "doubao-seed-1-6-thinking", - "display_name": "doubao-seed-1-6-thinking", + "id": "claude-sonnet-4-5-20250929", + "name": "Claude Sonnet 4.5", + "display_name": "Claude Sonnet 4.5", "modalities": { "input": [ "text", - "image", - "video" + "image" ], "output": [ - "text", - "image", - "video" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "doubao-seed-1-6-vision-250815", - "name": "doubao-seed-1-6-vision-250815", - "display_name": "doubao-seed-1-6-vision-250815", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "doubao-seedream-4-0", - "name": "doubao-seedream-4-0", - "display_name": "doubao-seedream-4-0", + "id": "claude-sonnet-4-20250514", + "name": "Claude Sonnet 4", + "display_name": "Claude Sonnet 4", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "embedding-2", - "name": "embedding-2", - "display_name": "embedding-2", + "id": "claude-opus-4-20250514", + "name": "Claude Opus 4", + "display_name": "Claude Opus 4", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "embedding-3", - "name": "embedding-3", - "display_name": "embedding-3", + "id": "claude-3-5-haiku-20241022", + "name": "Claude Haiku 3.5", + "display_name": "Claude Haiku 3.5", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-07-31", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 } }, { - "id": "embedding-v1", - "name": "embedding-v1", - "display_name": "embedding-v1", + "id": "claude-3-haiku-20240307", + "name": "Claude Haiku 3", + "display_name": "Claude Haiku 3", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2023-08-31", + "release_date": "2024-03-13", + "last_updated": "2024-03-13", + "cost": { + "input": 0.25, + "output": 1.25, + "cache_read": 0.03, + "cache_write": 0.3 } }, { - "id": "ernie-4.5", - "name": "ernie-4.5", - "display_name": "ernie-4.5", + "id": "claude-3-7-sonnet-20250219", + "name": "Claude Sonnet 3.7", + "display_name": "Claude Sonnet 3.7", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "ernie-4.5-0.3b", - "name": "ernie-4.5-0.3b", - "display_name": "ernie-4.5-0.3b", + "id": "claude-3-7-sonnet-latest", + "name": "Claude Sonnet 3.7 (latest)", + "display_name": "Claude Sonnet 3.7 (latest)", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10-31", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "ernie-4.5-turbo-128k-preview", - "name": "ernie-4.5-turbo-128k-preview", - "display_name": "ernie-4.5-turbo-128k-preview", + "id": "claude-sonnet-4-0", + "name": "Claude Sonnet 4 (latest)", + "display_name": "Claude Sonnet 4 (latest)", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "ernie-4.5-turbo-latest", - "name": "ernie-4.5-turbo-latest", - "display_name": "ernie-4.5-turbo-latest", + "id": "claude-opus-4-1-20250805", + "name": "Claude Opus 4.1", + "display_name": "Claude Opus 4.1", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "ernie-4.5-turbo-vl", - "name": "ernie-4.5-turbo-vl", - "display_name": "ernie-4.5-turbo-vl", + "id": "claude-3-sonnet-20240229", + "name": "Claude Sonnet 3", + "display_name": "Claude Sonnet 3", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 4096 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2023-08-31", + "release_date": "2024-03-04", + "last_updated": "2024-03-04", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 0.3 } }, { - "id": "ernie-irag-edit", - "name": "ernie-irag-edit", - "display_name": "ernie-irag-edit", + "id": "claude-haiku-4-5-20251001", + "name": "Claude Haiku 4.5", + "display_name": "Claude Haiku 4.5", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-02-31", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 } - }, + } + ] + }, + "aihubmix": { + "id": "aihubmix", + "name": "AIHubMix", + "display_name": "AIHubMix", + "api": "https://aihubmix.com/call/mdl_info", + "models": [ { - "id": "ernie-x1-turbo", - "name": "ernie-x1-turbo-32k-preview", - "display_name": "ernie-x1-turbo-32k-preview", + "id": "aihubmix-command-r-08-2024", + "name": "command-r-08-2024", + "display_name": "command-r-08-2024", "modalities": { "input": [ "text" @@ -24729,33 +25115,21 @@ "text" ] }, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "ernie-x1.1-preview", - "name": "ernie-x1.1-preview", - "display_name": "ernie-x1.1-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "flux-kontext-max", - "name": "flux-kontext-max", - "display_name": "flux-kontext-max", + "id": "aihubmix-command-r-plus", + "name": "command-r-plus", + "display_name": "command-r-plus", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, "tool_call": false, @@ -24764,9 +25138,9 @@ } }, { - "id": "flux-kontext-pro", - "name": "flux-kontext-pro", - "display_name": "flux-kontext-pro", + "id": "aihubmix-command-r-plus-08-2024", + "name": "command-r-plus-08-2024", + "display_name": "command-r-plus-08-2024", "modalities": { "input": [ "text" @@ -24781,53 +25155,54 @@ } }, { - "id": "gemini-2.0-flash", - "name": "Gemini 2.0 Flash", - "display_name": "Gemini 2.0 Flash", + "id": "aihubmix-router", + "name": "aihubmix-router", + "display_name": "aihubmix-router", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-001", - "name": "gemini-2.0-flash", - "display_name": "gemini-2.0-flash", + "id": "aistudio_gemini-2.0-flash", + "name": "aistudio/gemini-2.0-flash", + "display_name": "aistudio/gemini-2.0-flash", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-exp", - "name": "Gemini 2.0 Flash", - "display_name": "Gemini 2.0 Flash", + "id": "aistudio_gpt-4.1-mini", + "name": "gpt-4.1-mini", + "display_name": "gpt-4.1-mini", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "bce-reranker-base", + "name": "bce-reranker-base", + "display_name": "bce-reranker-base", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, "tool_call": false, @@ -24836,18 +25211,9 @@ } }, { - "id": "gemini-2.0-flash-exp-image-generation", - "name": "gemini-2.0-flash-exp-image-generation", - "display_name": "gemini-2.0-flash-exp-image-generation", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gemini-2.0-flash-exp-search", - "name": "Gemini 2.0 Flash", - "display_name": "Gemini 2.0 Flash", + "id": "bge-large-en", + "name": "bge-large-en", + "display_name": "bge-large-en", "modalities": { "input": [ "text", @@ -24864,192 +25230,118 @@ } }, { - "id": "gemini-2.0-flash-lite", - "name": "gemini-2.0-flash-lite", - "display_name": "gemini-2.0-flash-lite", + "id": "bge-large-zh", + "name": "bge-large-zh", + "display_name": "bge-large-zh", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-lite-001", - "name": "gemini-2.0-flash-lite", - "display_name": "gemini-2.0-flash-lite", + "id": "chatglm_lite", + "name": "chatglm_lite", + "display_name": "chatglm_lite", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-lite-preview-02-05", - "name": "gemini-2.0-flash-lite", - "display_name": "gemini-2.0-flash-lite", + "id": "chatglm_pro", + "name": "chatglm_pro", + "display_name": "chatglm_pro", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-preview-image-generation", - "name": "gemini-2.0-flash-preview-image-generation", - "display_name": "gemini-2.0-flash-preview-image-generation", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, + "id": "chatglm_std", + "name": "chatglm_std", + "display_name": "chatglm_std", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-search", - "name": "Gemini 2.0 Flash", - "display_name": "Gemini 2.0 Flash", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gemini-2.0-flash-thinking-exp", - "name": "gemini-2.0-flash-thinking-exp", - "display_name": "gemini-2.0-flash-thinking-exp", + "id": "chatglm_turbo", + "name": "chatglm_turbo", + "display_name": "chatglm_turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-flash-thinking-exp-01-21", - "name": "Gemini 2.0 Flash Thinking", - "display_name": "Gemini 2.0 Flash Thinking", + "id": "chatgpt-4o-latest", + "name": "gpt-4o", + "display_name": "gpt-4o", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gemini-2.0-flash-thinking-exp-1219", - "name": "Gemini 2.0 Flash Thinking", - "display_name": "Gemini 2.0 Flash Thinking", + "id": "claude-2", + "name": "Claude 2.0", + "display_name": "Claude 2.0", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-pro-exp-02-05", - "name": "gemini-2.0-pro", - "display_name": "gemini-2.0-pro", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, + "id": "claude-2.0", + "name": "Claude 2.0", + "display_name": "Claude 2.0", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.0-pro-exp-02-05-search", - "name": "gemini-2.0-pro", - "display_name": "gemini-2.0-pro", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, + "id": "claude-2.1", + "name": "Claude 2.1", + "display_name": "Claude 2.1", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-flash", - "name": "gemini-2.5-flash", - "display_name": "gemini-2.5-flash", + "id": "claude-3-5-haiku", + "name": "claude-3-5-haiku", + "display_name": "claude-3-5-haiku", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, "tool_call": true, @@ -25058,36 +25350,36 @@ } }, { - "id": "gemini-2.5-flash-image", - "name": "gemini-2.5-flash-image", - "display_name": "gemini-2.5-flash-image", + "id": "claude-3-5-sonnet", + "name": "Claude 3.5 Sonnet", + "display_name": "Claude 3.5 Sonnet", "modalities": { "input": [ - "image", - "text" + "text", + "image" ], "output": [ - "image", - "text" + "text", + "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-flash-image-preview", - "name": "gemini-2.5-flash-image-preview", - "display_name": "gemini-2.5-flash-image-preview", + "id": "claude-3-5-sonnet-20240620", + "name": "Claude 3.5 Sonnet", + "display_name": "Claude 3.5 Sonnet", "modalities": { "input": [ - "image", - "text" + "text", + "image" ], "output": [ - "image", - "text" + "text", + "image" ] }, "tool_call": false, @@ -25096,32 +25388,29 @@ } }, { - "id": "gemini-2.5-flash-lite", - "name": "gemini-2.5-flash-lite", - "display_name": "gemini-2.5-flash-lite", + "id": "claude-3-7-sonnet", + "name": "Claude 3.7 Sonnet", + "display_name": "Claude 3.7 Sonnet", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "gemini-2.5-flash-lite-preview-09-2025", - "name": "gemini-2.5-flash-lite-preview-09-2025", - "display_name": "gemini-2.5-flash-lite-preview-09-2025", + "id": "claude-3-haiku-20240229", + "name": "Claude 3 Haiku", + "display_name": "Claude 3 Haiku", "modalities": { "input": [ "text", @@ -25132,84 +25421,62 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-flash-nothink", - "name": "gemini-2.5-flash-nothink", - "display_name": "gemini-2.5-flash-nothink", + "id": "claude-3-haiku-20240307", + "name": "Claude 3 Haiku", + "display_name": "Claude 3 Haiku", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-flash-preview-05-20-nothink", - "name": "gemini-2.5-flash-preview-05-20-nothink", - "display_name": "gemini-2.5-flash-preview-05-20-nothink", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, - "tool_call": true, + "id": "claude-3-opus-20240229", + "name": "Claude 3 Opus", + "display_name": "Claude 3 Opus", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-flash-preview-05-20-search", - "name": "gemini-2.5-flash-preview-05-20-search", - "display_name": "gemini-2.5-flash-preview-05-20-search", + "id": "claude-3-sonnet-20240229", + "name": "Claude 3 Sonnet", + "display_name": "Claude 3 Sonnet", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-flash-preview-09-2025", - "name": "gemini-2.5-flash-preview-09-2025", - "display_name": "gemini-2.5-flash-preview-09-2025", + "id": "claude-haiku-4-5", + "name": "claude-haiku-4-5", + "display_name": "claude-haiku-4-5", "modalities": { "input": [ "text", @@ -25222,74 +25489,40 @@ }, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "gemini-2.5-flash-search", - "name": "gemini-2.5-flash-search", - "display_name": "gemini-2.5-flash-search", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, - "tool_call": true, + "id": "claude-instant-1", + "name": "Claude Instant", + "display_name": "Claude Instant", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-2.5-pro", - "name": "gemini-2.5-pro", - "display_name": "gemini-2.5-pro", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text", - "image", - "audio", - "video", - "pdf" - ] - }, - "tool_call": true, + "id": "claude-instant-1.2", + "name": "Claude Instant", + "display_name": "Claude Instant", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gemini-2.5-pro-exp-03-25", - "name": "gemini-2.5-pro-exp", - "display_name": "gemini-2.5-pro-exp", + "id": "claude-opus-4-0", + "name": "claude-opus-4-0", + "display_name": "claude-opus-4-0", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, "tool_call": true, @@ -25298,9 +25531,9 @@ } }, { - "id": "gemini-2.5-pro-preview-03-25", - "name": "gemini 2.5 pro", - "display_name": "gemini 2.5 pro", + "id": "claude-opus-4-1", + "name": "claude-opus-4-1-20250805", + "display_name": "claude-opus-4-1-20250805", "modalities": { "input": [ "text", @@ -25318,21 +25551,17 @@ } }, { - "id": "gemini-2.5-pro-preview-03-25-search", - "name": "gemini-2.5-pro-preview-", - "display_name": "gemini-2.5-pro-preview-", + "id": "claude-sonnet-4-0", + "name": "claude-sonnet-4-0", + "display_name": "claude-sonnet-4-0", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, "tool_call": true, @@ -25342,119 +25571,89 @@ } }, { - "id": "gemini-2.5-pro-preview-05-06", - "name": "gemini-2.5-pro-preview-05-06", - "display_name": "gemini-2.5-pro-preview-05-06", + "id": "claude-sonnet-4-5", + "name": "claude-sonnet-4-5", + "display_name": "claude-sonnet-4-5", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": true, "default": true } }, { - "id": "gemini-2.5-pro-preview-05-06-search", - "name": "gemini-2.5-pro-preview-05-06-search", - "display_name": "gemini-2.5-pro-preview-05-06-search", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, + "id": "code-davinci-edit-001", + "name": "code-davinci-edit-001", + "display_name": "code-davinci-edit-001", "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gemini-2.5-pro-preview-06-05", - "name": "gemini-2.5-pro-preview-06-05", - "display_name": "gemini-2.5-pro-preview-06-05", + "id": "codestral-latest", + "name": "Codestral 25.01", + "display_name": "Codestral 25.01", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "codex-mini-latest", + "name": "codex-mini-latest", + "display_name": "codex-mini-latest", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text", - "image", - "audio", - "video" + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gemini-2.5-pro-preview-06-05-search", - "name": "gemini-2.5-pro-preview-06-05-search", - "display_name": "gemini-2.5-pro-preview-06-05-search", + "id": "coding-glm-4.5-air", + "name": "glm-4.5-air", + "display_name": "glm-4.5-air", "modalities": { "input": [ - "text", - "image", - "audio", - "video" + "text" ], "output": [ - "text", - "image", - "audio", - "video" + "text" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gemini-2.5-pro-search", - "name": "gemini-2.5-pro-search", - "display_name": "gemini-2.5-pro-search", + "id": "coding-glm-4.6", + "name": "glm-4.6", + "display_name": "glm-4.6", "modalities": { "input": [ - "text", - "image", - "audio", - "video", - "pdf" + "text" ], "output": [ - "text", - "image", - "audio", - "video", - "pdf" + "text" ] }, "tool_call": true, @@ -25464,9 +25663,27 @@ } }, { - "id": "gemini-embedding-001", - "name": "gemini-embedding-001", - "display_name": "gemini-embedding-001", + "id": "cogview-3", + "name": "cogview-3", + "display_name": "cogview-3", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "cogview-3-plus", + "name": "cogview-3-plus", + "display_name": "cogview-3-plus", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "command", + "name": "command", + "display_name": "command", "modalities": { "input": [ "text" @@ -25481,9 +25698,9 @@ } }, { - "id": "gemini-embedding-exp-03-07", - "name": "gemini-embedding-exp-03-07", - "display_name": "gemini-embedding-exp-03-07", + "id": "command-a-03-2025", + "name": "Command A", + "display_name": "Command A", "modalities": { "input": [ "text" @@ -25498,159 +25715,229 @@ } }, { - "id": "gemini-exp-1114", - "name": "Gemini 2", - "display_name": "Gemini 2", + "id": "command-light", + "name": "command-light", + "display_name": "command-light", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-exp-1121", - "name": "Gemini 2", - "display_name": "Gemini 2", + "id": "command-light-nightly", + "name": "command-light", + "display_name": "command-light", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-exp-1206", - "name": "Gemini 2", - "display_name": "Gemini 2", + "id": "command-nightly", + "name": "command-nightly", + "display_name": "command-nightly", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-pro", - "name": "gemini-pro", - "display_name": "gemini-pro", + "id": "command-r", + "name": "command-r", + "display_name": "command-r", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemini-pro-vision", - "name": "gemini-pro-vision", - "display_name": "gemini-pro-vision", + "id": "command-r-08-2024", + "name": "command-r-08-2024", + "display_name": "command-r-08-2024", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma-3-12b-it", - "name": "gemma-3-12b-it", - "display_name": "gemma-3-12b-it", + "id": "command-r-plus", + "name": "command-r-plus", + "display_name": "command-r-plus", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma-3-1b-it", - "name": "gemma-3-1b-it", - "display_name": "gemma-3-1b-it", + "id": "command-r-plus-08-2024", + "name": "command-r-plus-08-2024", + "display_name": "command-r-plus-08-2024", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma-3-27b-it", - "name": "gemma-3-27b-it", - "display_name": "gemma-3-27b-it", + "id": "computer-use-preview", + "name": "computer-use-preview", + "display_name": "computer-use-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma-3-4b-it", - "name": "gemma-3-4b-it", - "display_name": "gemma-3-4b-it", + "id": "dall-e-2", + "name": "dall-e-2", + "display_name": "dall-e-2", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma-3n-e4b-it", - "name": "gemma-3n-e4b-it", - "display_name": "gemma-3n-e4b-it", + "id": "dall-e-3", + "name": "dall-e-3", + "display_name": "dall-e-3", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma-7b-it", - "name": "gemma-7b", - "display_name": "gemma-7b", + "id": "davinci", + "name": "davinci", + "display_name": "davinci", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gemma2-9b-it", - "name": "gemma2-9b", - "display_name": "gemma2-9b", + "id": "davinci-002", + "name": "davinci", + "display_name": "davinci", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-3-turbo", - "name": "glm-3-turbo", - "display_name": "glm-3-turbo", + "id": "deepseek-ai/deepseek-llm-67b-chat", + "name": "deepseek-llm-67b-chat", + "display_name": "deepseek-llm-67b-chat", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-4", - "name": "glm-4", - "display_name": "glm-4", + "id": "deepseek-ai/deepseek-vl2", + "name": "deepseek-ai/deepseek-vl2", + "display_name": "deepseek-ai/deepseek-vl2", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-4-flash", - "name": "glm-4-flash", - "display_name": "glm-4-flash", + "id": "deepseek-r1-distill-llama-70b", + "name": "deepseek-r1-distill-llama-70b", + "display_name": "deepseek-r1-distill-llama-70b", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "deepseek-r1-distill-qianfan-llama-8b", + "name": "deepseek-r1-distill-qianfan-llama-8b", + "display_name": "deepseek-r1-distill-qianfan-llama-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-4-plus", - "name": "glm-4-plus", - "display_name": "glm-4-plus", + "id": "deepseek-v3", + "name": "deepseek-v3", + "display_name": "deepseek-v3", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-4.5", - "name": "glm-4.5", - "display_name": "glm-4.5", + "id": "distil-whisper-large-v3-en", + "name": "whisper-large-v3", + "display_name": "whisper-large-v3", "modalities": { "input": [ - "text" + "audio" ], "output": [ - "text" + "audio" ] }, "tool_call": false, @@ -25659,43 +25946,36 @@ } }, { - "id": "glm-4.5-air", - "name": "glm-4.5-air", - "display_name": "glm-4.5-air", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "doubao-1-5-pro-256k-250115", + "name": "Doubao-1.5-pro-256k", + "display_name": "Doubao-1.5-pro-256k", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-4.5-airx", - "name": "glm-4.5-airx", - "display_name": "glm-4.5-airx", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "doubao-1-5-pro-32k-250115", + "name": "Doubao-1.5-pro-32k", + "display_name": "Doubao-1.5-pro-32k", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "glm-4.5-flash", - "name": "glm-4.5-flash", - "display_name": "glm-4.5-flash", + "id": "doubao-1-5-thinking-vision-pro-250428", + "name": "doubao-1-5-thinking-vision-pro-250428", + "display_name": "doubao-1-5-thinking-vision-pro-250428", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "doubao-embedding-large-text-240915", + "name": "doubao-embedding-large-text-240915", + "display_name": "doubao-embedding-large-text-240915", "modalities": { "input": [ "text" @@ -25710,9 +25990,9 @@ } }, { - "id": "glm-4.5-x", - "name": "glm-4.5-x", - "display_name": "glm-4.5-x", + "id": "doubao-embedding-text-240715", + "name": "doubao-embedding-text-240715", + "display_name": "doubao-embedding-text-240715", "modalities": { "input": [ "text" @@ -25727,9 +26007,9 @@ } }, { - "id": "glm-4.5v", - "name": "glm-4.5v", - "display_name": "glm-4.5v", + "id": "doubao-seed-1-6", + "name": "doubao-seed-1-6", + "display_name": "doubao-seed-1-6", "modalities": { "input": [ "text", @@ -25742,21 +26022,26 @@ "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "glm-4.6", - "name": "glm-4.6", - "display_name": "glm-4.6", + "id": "doubao-seed-1-6-flash", + "name": "doubao-seed-1-6-flash", + "display_name": "doubao-seed-1-6-flash", "modalities": { "input": [ - "text" + "text", + "image", + "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, "tool_call": true, @@ -25766,36 +26051,9 @@ } }, { - "id": "glm-4v", - "name": "glm-4v", - "display_name": "glm-4v", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "glm-4v-plus", - "name": "glm-4v-plus", - "display_name": "glm-4v-plus", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "glm-zero-preview", - "name": "glm-zero-preview", - "display_name": "glm-zero-preview", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gme-qwen2-vl-2b-instruct", - "name": "gme-qwen2-vl-2b-instruct", - "display_name": "gme-qwen2-vl-2b-instruct", + "id": "doubao-seed-1-6-thinking", + "name": "doubao-seed-1-6-thinking", + "display_name": "doubao-seed-1-6-thinking", "modalities": { "input": [ "text", @@ -25808,231 +26066,95 @@ "video" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "google/gemini-exp-1114", - "name": "gemini", - "display_name": "gemini", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "google/gemma-2-27b-it", - "name": "gemma-2-27b-it", - "display_name": "gemma-2-27b-it", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "google/gemma-2-9b-it:free", - "name": "gemma-2-9b", - "display_name": "gemma-2-9b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "google/gemma-3-27b-it", - "name": "gemma-3-27b", - "display_name": "gemma-3-27b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-0125", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-0301", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-0613", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-1106", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-16k", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-16k-0613", - "name": "gpt-3.5-turbo", - "display_name": "gpt-3.5-turbo", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-3.5-turbo-instruct", - "name": "gpt-3.5-turbo-instruct", - "display_name": "gpt-3.5-turbo-instruct", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4", - "name": "gpt-4", - "display_name": "gpt-4", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4-0125-preview", - "name": "gpt-4", - "display_name": "gpt-4", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4-0314", - "name": "gpt-4", - "display_name": "gpt-4", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4-0613", - "name": "gpt-4", - "display_name": "gpt-4", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4-1106-preview", - "name": "gpt-4", - "display_name": "gpt-4", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4-32k", - "name": "gpt-4-32k", - "display_name": "gpt-4-32k", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4-32k-0314", - "name": "gpt-4-32k", - "display_name": "gpt-4-32k", - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "gpt-4-32k-0613", - "name": "gpt-4-32k", - "display_name": "gpt-4-32k", + "id": "doubao-seed-1-6-vision-250815", + "name": "doubao-seed-1-6-vision-250815", + "display_name": "doubao-seed-1-6-vision-250815", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4-turbo", - "name": "gpt-4-turbo", - "display_name": "gpt-4-turbo", + "id": "doubao-seedream-4-0", + "name": "doubao-seedream-4-0", + "display_name": "doubao-seedream-4-0", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4-turbo-2024-04-09", - "name": "gpt-4-turbo", - "display_name": "gpt-4-turbo", + "id": "embedding-2", + "name": "embedding-2", + "display_name": "embedding-2", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4-turbo-preview", - "name": "gpt-4-turbo", - "display_name": "gpt-4-turbo", + "id": "embedding-3", + "name": "embedding-3", + "display_name": "embedding-3", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4-vision-preview", - "name": "gpt-4-vision", - "display_name": "gpt-4-vision", + "id": "embedding-v1", + "name": "embedding-v1", + "display_name": "embedding-v1", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4.1", - "name": "gpt-4.1", - "display_name": "gpt-4.1", + "id": "ernie-4.5", + "name": "ernie-4.5", + "display_name": "ernie-4.5", "modalities": { "input": [ "text", @@ -26049,9 +26171,9 @@ } }, { - "id": "gpt-4.1-mini", - "name": "gpt-4.1-mini", - "display_name": "gpt-4.1-mini", + "id": "ernie-4.5-0.3b", + "name": "ernie-4.5-0.3b", + "display_name": "ernie-4.5-0.3b", "modalities": { "input": [ "text", @@ -26068,9 +26190,9 @@ } }, { - "id": "gpt-4.1-nano", - "name": "gpt-4.1-nano", - "display_name": "gpt-4.1-nano", + "id": "ernie-4.5-turbo-128k-preview", + "name": "ernie-4.5-turbo-128k-preview", + "display_name": "ernie-4.5-turbo-128k-preview", "modalities": { "input": [ "text", @@ -26087,9 +26209,9 @@ } }, { - "id": "gpt-4o", - "name": "gpt-4o", - "display_name": "gpt-4o", + "id": "ernie-4.5-turbo-latest", + "name": "ernie-4.5-turbo-latest", + "display_name": "ernie-4.5-turbo-latest", "modalities": { "input": [ "text", @@ -26106,36 +26228,9 @@ } }, { - "id": "gpt-4o-2024-05-13", - "name": "gpt-4o", - "display_name": "gpt-4o", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4o-2024-08-06", - "name": "gpt-4o", - "display_name": "gpt-4o", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4o-2024-08-06-global", - "name": "gpt-4o-2024-08-06-global", - "display_name": "gpt-4o-2024-08-06-global", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-4o-2024-11-20", - "name": "gpt-4o", - "display_name": "gpt-4o", + "id": "ernie-4.5-turbo-vl", + "name": "ernie-4.5-turbo-vl", + "display_name": "ernie-4.5-turbo-vl", "modalities": { "input": [ "text", @@ -26146,53 +26241,61 @@ "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "gpt-4o-audio-preview", - "name": "gpt-4o-audio-preview", - "display_name": "gpt-4o-audio-preview", + "id": "ernie-irag-edit", + "name": "ernie-irag-edit", + "display_name": "ernie-irag-edit", "modalities": { "input": [ "text", - "audio" + "image" ], "output": [ "text", - "audio" + "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "gpt-4o-image", - "name": "gpt-4o-image", - "display_name": "gpt-4o-image", + "id": "ernie-x1-turbo", + "name": "ernie-x1-turbo-32k-preview", + "display_name": "ernie-x1-turbo-32k-preview", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "ernie-x1.1-preview", + "name": "ernie-x1.1-preview", + "display_name": "ernie-x1.1-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4o-image-vip", - "name": "gpt-4o-image-vip", - "display_name": "gpt-4o-image-vip", + "id": "flux-kontext-max", + "name": "flux-kontext-max", + "display_name": "flux-kontext-max", "modalities": { "input": [ "text", @@ -26209,17 +26312,15 @@ } }, { - "id": "gpt-4o-mini", - "name": "gpt-4o-mini", - "display_name": "gpt-4o-mini", + "id": "flux-kontext-pro", + "name": "flux-kontext-pro", + "display_name": "flux-kontext-pro", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, "tool_call": false, @@ -26228,36 +26329,53 @@ } }, { - "id": "gpt-4o-mini-2024-07-18", - "name": "gpt-4o-mini", - "display_name": "gpt-4o-mini", + "id": "gemini-2.0-flash", + "name": "Gemini 2.0 Flash", + "display_name": "Gemini 2.0 Flash", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, + "tool_call": true, + "reasoning": { + "supported": false + } + }, + { + "id": "gemini-2.0-flash-001", + "name": "gemini-2.0-flash", + "display_name": "gemini-2.0-flash", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4o-mini-audio-preview", - "name": "gpt-4o-mini-audio-preview", - "display_name": "gpt-4o-mini-audio-preview", + "id": "gemini-2.0-flash-exp", + "name": "Gemini 2.0 Flash", + "display_name": "Gemini 2.0 Flash", "modalities": { "input": [ "text", - "audio" + "image", + "audio", + "video" ], "output": [ "text", - "audio" + "image", + "audio", + "video" ] }, "tool_call": false, @@ -26266,18 +26384,18 @@ } }, { - "id": "gpt-4o-mini-global", - "name": "gpt-4o-mini-global", - "display_name": "gpt-4o-mini-global", + "id": "gemini-2.0-flash-exp-image-generation", + "name": "gemini-2.0-flash-exp-image-generation", + "display_name": "gemini-2.0-flash-exp-image-generation", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4o-mini-search-preview", - "name": "gpt-4o-mini-search-preview", - "display_name": "gpt-4o-mini-search-preview", + "id": "gemini-2.0-flash-exp-search", + "name": "Gemini 2.0 Flash", + "display_name": "Gemini 2.0 Flash", "modalities": { "input": [ "text", @@ -26294,15 +26412,21 @@ } }, { - "id": "gpt-4o-mini-tts", - "name": "gpt-4o-mini-tts", - "display_name": "gpt-4o-mini-tts", + "id": "gemini-2.0-flash-lite", + "name": "gemini-2.0-flash-lite", + "display_name": "gemini-2.0-flash-lite", "modalities": { "input": [ - "audio" + "text", + "image", + "audio", + "video" ], "output": [ - "audio" + "text", + "image", + "audio", + "video" ] }, "tool_call": false, @@ -26311,47 +26435,27 @@ } }, { - "id": "gpt-4o-search-preview", - "name": "gpt-4o-search-preview", - "display_name": "gpt-4o-search-preview", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, - "tool_call": true, + "id": "gemini-2.0-flash-lite-001", + "name": "gemini-2.0-flash-lite", + "display_name": "gemini-2.0-flash-lite", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-4o-zh", - "name": "gpt-4o", - "display_name": "gpt-4o", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, + "id": "gemini-2.0-flash-lite-preview-02-05", + "name": "gemini-2.0-flash-lite", + "display_name": "gemini-2.0-flash-lite", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "gpt-5", - "name": "gpt-5", - "display_name": "gpt-5", + "id": "gemini-2.0-flash-preview-image-generation", + "name": "gemini-2.0-flash-preview-image-generation", + "display_name": "gemini-2.0-flash-preview-image-generation", "modalities": { "input": [ "text", @@ -26362,24 +26466,27 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gpt-5-chat-latest", - "name": "gpt-5-chat-latest", - "display_name": "gpt-5-chat-latest", + "id": "gemini-2.0-flash-search", + "name": "Gemini 2.0 Flash", + "display_name": "Gemini 2.0 Flash", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, "tool_call": false, @@ -26388,97 +26495,86 @@ } }, { - "id": "gpt-5-codex", - "name": "gpt-5-codex", - "display_name": "gpt-5-codex", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, - "tool_call": true, + "id": "gemini-2.0-flash-thinking-exp", + "name": "gemini-2.0-flash-thinking-exp", + "display_name": "gemini-2.0-flash-thinking-exp", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gpt-5-mini", - "name": "gpt-5-mini", - "display_name": "gpt-5-mini", + "id": "gemini-2.0-flash-thinking-exp-01-21", + "name": "Gemini 2.0 Flash Thinking", + "display_name": "Gemini 2.0 Flash Thinking", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": true, "default": true } }, { - "id": "gpt-5-nano", - "name": "gpt-5-nano", - "display_name": "gpt-5-nano", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, - "tool_call": true, + "id": "gemini-2.0-flash-thinking-exp-1219", + "name": "Gemini 2.0 Flash Thinking", + "display_name": "Gemini 2.0 Flash Thinking", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gpt-5-pro", - "name": "gpt-5-pro", - "display_name": "gpt-5-pro", + "id": "gemini-2.0-pro-exp-02-05", + "name": "gemini-2.0-pro", + "display_name": "gemini-2.0-pro", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gpt-image-1", - "name": "gpt-image-1", - "display_name": "gpt-image-1", + "id": "gemini-2.0-pro-exp-02-05-search", + "name": "gemini-2.0-pro", + "display_name": "gemini-2.0-pro", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, "tool_call": false, @@ -26487,352 +26583,280 @@ } }, { - "id": "gpt-image-1-mini", - "name": "gpt-image-1-mini", - "display_name": "gpt-image-1-mini", + "id": "gemini-2.5-flash", + "name": "gemini-2.5-flash", + "display_name": "gemini-2.5-flash", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "gpt-image-test", - "name": "gpt-image-test", - "display_name": "gpt-image-test", - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "gpt-oss-120b", - "name": "gpt-oss-120b", - "display_name": "gpt-oss-120b", + "id": "gemini-2.5-flash-image", + "name": "gemini-2.5-flash-image", + "display_name": "gemini-2.5-flash-image", "modalities": { "input": [ + "image", "text" ], "output": [ + "image", "text" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "gpt-oss-20b", - "name": "gpt-oss-20b", - "display_name": "gpt-oss-20b", + "id": "gemini-2.5-flash-image-preview", + "name": "gemini-2.5-flash-image-preview", + "display_name": "gemini-2.5-flash-image-preview", "modalities": { "input": [ + "image", "text" ], "output": [ + "image", "text" ] }, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "grok-2-1212", - "name": "grok-2", - "display_name": "grok-2", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "grok-2-vision-1212", - "name": "grok-2", - "display_name": "grok-2", + "id": "gemini-2.5-flash-lite", + "name": "gemini-2.5-flash-lite", + "display_name": "gemini-2.5-flash-lite", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3", - "name": "grok-3", - "display_name": "grok-3", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3-beta", - "name": "grok-3", - "display_name": "grok-3", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3-fast", - "name": "grok-3-fast", - "display_name": "grok-3-fast", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3-fast-beta", - "name": "grok-3-fast-beta", - "display_name": "grok-3-fast-beta", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3-mini", - "name": "grok-3-mini", - "display_name": "grok-3-mini", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3-mini-beta", - "name": "grok-3-mini-beta", - "display_name": "grok-3-mini-beta", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-3-mini-fast-beta", - "name": "grok-3-mini-fast-beta", - "display_name": "grok-3-mini-fast-beta", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-4", - "name": "grok-4", - "display_name": "grok-4", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-4-fast-non-reasoning", - "name": "grok-4-fast-non-reasoning", - "display_name": "grok-4-fast-non-reasoning", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-4-fast-reasoning", - "name": "grok-4-fast-reasoning", - "display_name": "grok-4-fast-reasoning", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "grok-code-fast-1", - "name": "grok-code-fast-1", - "display_name": "grok-code-fast-1", - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "grok-vision-beta", - "name": "grok-beta", - "display_name": "grok-beta", + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "gemini-2.5-flash-lite-preview-09-2025", + "display_name": "gemini-2.5-flash-lite-preview-09-2025", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "gte-rerank-v2", - "name": "gte-rerank-v2", - "display_name": "gte-rerank-v2", + "id": "gemini-2.5-flash-nothink", + "name": "gemini-2.5-flash-nothink", + "display_name": "gemini-2.5-flash-nothink", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "imagen-3.0-generate-002", - "name": "imagen-3.0-generate-002", - "display_name": "imagen-3.0-generate-002", + "id": "gemini-2.5-flash-preview-05-20-nothink", + "name": "gemini-2.5-flash-preview-05-20-nothink", + "display_name": "gemini-2.5-flash-preview-05-20-nothink", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "imagen-4.0", - "name": "imagen-4.0-generate-preview-06-06", - "display_name": "imagen-4.0-generate-preview-06-06", + "id": "gemini-2.5-flash-preview-05-20-search", + "name": "gemini-2.5-flash-preview-05-20-search", + "display_name": "gemini-2.5-flash-preview-05-20-search", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "imagen-4.0-fast-generate-001", - "name": "imagen-4.0-fast-generate-001", - "display_name": "imagen-4.0-fast-generate-001", + "id": "gemini-2.5-flash-preview-09-2025", + "name": "gemini-2.5-flash-preview-09-2025", + "display_name": "gemini-2.5-flash-preview-09-2025", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "imagen-4.0-fast-generate-preview-06-06", - "name": "imagen-4.0-fast-generate-preview-06-06", - "display_name": "imagen-4.0-fast-generate-preview-06-06", + "id": "gemini-2.5-flash-search", + "name": "gemini-2.5-flash-search", + "display_name": "gemini-2.5-flash-search", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "imagen-4.0-generate-001", - "name": "imagen-4.0-generate-001", - "display_name": "imagen-4.0-generate-001", + "id": "gemini-2.5-pro", + "name": "gemini-2.5-pro", + "display_name": "gemini-2.5-pro", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video", + "pdf" ], "output": [ "text", - "image" + "image", + "audio", + "video", + "pdf" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "imagen-4.0-generate-preview-05-20", - "name": "imagen-4.0-generate-preview-05-20", - "display_name": "imagen-4.0-generate-preview-05-20", + "id": "gemini-2.5-pro-exp-03-25", + "name": "gemini-2.5-pro-exp", + "display_name": "gemini-2.5-pro-exp", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "imagen-4.0-ultra", - "name": "imagen-4.0-ultra-generate-preview-06-06", - "display_name": "imagen-4.0-ultra-generate-preview-06-06", + "id": "gemini-2.5-pro-preview-03-25", + "name": "gemini 2.5 pro", + "display_name": "gemini 2.5 pro", "modalities": { "input": [ "text", @@ -26843,118 +26867,162 @@ "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "imagen-4.0-ultra-generate-001", - "name": "imagen-4.0-ultra-generate-001", - "display_name": "imagen-4.0-ultra-generate-001", + "id": "gemini-2.5-pro-preview-03-25-search", + "name": "gemini-2.5-pro-preview-", + "display_name": "gemini-2.5-pro-preview-", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "imagen-4.0-ultra-generate-exp-05-20", - "name": "imagen-4.0-ultra-generate-exp-05-20", - "display_name": "imagen-4.0-ultra-generate-exp-05-20", + "id": "gemini-2.5-pro-preview-05-06", + "name": "gemini-2.5-pro-preview-05-06", + "display_name": "gemini-2.5-pro-preview-05-06", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, "tool_call": false, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "irag-1.0", - "name": "irag-1.0", - "display_name": "irag-1.0", + "id": "gemini-2.5-pro-preview-05-06-search", + "name": "gemini-2.5-pro-preview-05-06-search", + "display_name": "gemini-2.5-pro-preview-05-06-search", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text", + "image", + "audio", + "video" + ] + }, "tool_call": false, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "jina-clip-v2", - "name": "jina-clip-v2", - "display_name": "jina-clip-v2", + "id": "gemini-2.5-pro-preview-06-05", + "name": "gemini-2.5-pro-preview-06-05", + "display_name": "gemini-2.5-pro-preview-06-05", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ "text", - "image" + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "jina-colbert-v2", - "name": "jina-colbert-v2", - "display_name": "jina-colbert-v2", + "id": "gemini-2.5-pro-preview-06-05-search", + "name": "gemini-2.5-pro-preview-06-05-search", + "display_name": "gemini-2.5-pro-preview-06-05-search", "modalities": { "input": [ - "text" + "text", + "image", + "audio", + "video" ], "output": [ - "text" + "text", + "image", + "audio", + "video" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "jina-deepsearch-v1", - "name": "jina-deepsearch-v1", - "display_name": "jina-deepsearch-v1", + "id": "gemini-2.5-pro-search", + "name": "gemini-2.5-pro-search", + "display_name": "gemini-2.5-pro-search", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video", + "pdf" ], "output": [ "text", - "image" + "image", + "audio", + "video", + "pdf" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": true, "default": true } }, { - "id": "jina-embeddings-v2-base-code", - "name": "jina-embeddings-v2-base-code", - "display_name": "jina-embeddings-v2-base-code", + "id": "gemini-embedding-001", + "name": "gemini-embedding-001", + "display_name": "gemini-embedding-001", "modalities": { "input": [ "text" @@ -26969,9 +27037,9 @@ } }, { - "id": "jina-embeddings-v3", - "name": "jina-embeddings-v3", - "display_name": "jina-embeddings-v3", + "id": "gemini-embedding-exp-03-07", + "name": "gemini-embedding-exp-03-07", + "display_name": "gemini-embedding-exp-03-07", "modalities": { "input": [ "text" @@ -26986,554 +27054,541 @@ } }, { - "id": "jina-embeddings-v4", - "name": "jina-embeddings-v4", - "display_name": "jina-embeddings-v4", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, + "id": "gemini-exp-1114", + "name": "Gemini 2", + "display_name": "Gemini 2", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "jina-reranker-m0", - "name": "jina-reranker-m0", - "display_name": "jina-reranker-m0", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, + "id": "gemini-exp-1121", + "name": "Gemini 2", + "display_name": "Gemini 2", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "jina-reranker-v3", - "name": "jina-reranker-v3", - "display_name": "jina-reranker-v3", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, + "id": "gemini-exp-1206", + "name": "Gemini 2", + "display_name": "Gemini 2", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "kimi-k2-0711", - "name": "kimi-k2-0711", - "display_name": "kimi-k2-0711", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "tool_call": true, + "id": "gemini-pro", + "name": "gemini-pro", + "display_name": "gemini-pro", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "kimi-k2-turbo-preview", - "name": "kimi-k2-turbo-preview", - "display_name": "kimi-k2-turbo-preview", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "tool_call": true, + "id": "gemini-pro-vision", + "name": "gemini-pro-vision", + "display_name": "gemini-pro-vision", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "kimi-latest", - "name": "kimi-latest", - "display_name": "kimi-latest", + "id": "gemma-3-12b-it", + "name": "gemma-3-12b-it", + "display_name": "gemma-3-12b-it", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "kimi-thinking-preview", - "name": "kimi-thinking-preview", - "display_name": "kimi-thinking-preview", + "id": "gemma-3-1b-it", + "name": "gemma-3-1b-it", + "display_name": "gemma-3-1b-it", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "learnlm-1.5-pro-experimental", - "name": "Gemini 1.5 Pro", - "display_name": "Gemini 1.5 Pro", + "id": "gemma-3-27b-it", + "name": "gemma-3-27b-it", + "display_name": "gemma-3-27b-it", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-405b-instruct", - "name": "llama-3.1-405b", - "display_name": "llama-3.1-405b", + "id": "gemma-3-4b-it", + "name": "gemma-3-4b-it", + "display_name": "gemma-3-4b-it", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-405b-reasoning", - "name": "llama-3.1-405b", - "display_name": "llama-3.1-405b", + "id": "gemma-3n-e4b-it", + "name": "gemma-3n-e4b-it", + "display_name": "gemma-3n-e4b-it", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-70b", - "name": "llama-3.1-70b", - "display_name": "llama-3.1-70b", + "id": "gemma-7b-it", + "name": "gemma-7b", + "display_name": "gemma-7b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-70b-versatile", - "name": "llama-3.1-70b", - "display_name": "llama-3.1-70b", + "id": "gemma2-9b-it", + "name": "gemma2-9b", + "display_name": "gemma2-9b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-8b-instant", - "name": "llama-3.1-8b", - "display_name": "llama-3.1-8b", + "id": "glm-3-turbo", + "name": "glm-3-turbo", + "display_name": "glm-3-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-sonar-huge-128k-online", - "name": "llama-3.1-sonar-huge-128k-online", - "display_name": "llama-3.1-sonar-huge-128k-online", + "id": "glm-4", + "name": "glm-4", + "display_name": "glm-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-sonar-large-128k-online", - "name": "llama-3.1-sonar-large-128k-online", - "display_name": "llama-3.1-sonar-large-128k-online", + "id": "glm-4-flash", + "name": "glm-4-flash", + "display_name": "glm-4-flash", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.1-sonar-small-128k-online", - "name": "llama-3.1-sonar-small-128k-online", - "display_name": "llama-3.1-sonar-small-128k-online", + "id": "glm-4-plus", + "name": "glm-4-plus", + "display_name": "glm-4-plus", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.2-11b-vision-preview", - "name": "Llama-3-2-11B", - "display_name": "Llama-3-2-11B", + "id": "glm-4.5", + "name": "glm-4.5", + "display_name": "glm-4.5", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.2-1b-preview", - "name": "llama-3.2-1b", - "display_name": "llama-3.2-1b", + "id": "glm-4.5-air", + "name": "glm-4.5-air", + "display_name": "glm-4.5-air", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.2-3b-preview", - "name": "llama-3.2-3b", - "display_name": "llama-3.2-3b", + "id": "glm-4.5-airx", + "name": "glm-4.5-airx", + "display_name": "glm-4.5-airx", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.2-90b-vision-preview", - "name": "Llama-3.2-90B", - "display_name": "Llama-3.2-90B", + "id": "glm-4.5-flash", + "name": "glm-4.5-flash", + "display_name": "glm-4.5-flash", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-3.3-70b", - "name": "llama-3.3-70b", - "display_name": "llama-3.3-70b", + "id": "glm-4.5-x", + "name": "glm-4.5-x", + "display_name": "glm-4.5-x", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-4-maverick", - "name": "llama-4-maverick-17b-128e-instruct", - "display_name": "llama-4-maverick-17b-128e-instruct", + "id": "glm-4.5v", + "name": "glm-4.5v", + "display_name": "glm-4.5v", "modalities": { "input": [ "text", - "image" + "image", + "video" ], "output": [ "text", - "image" + "image", + "video" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama-4-scout", - "name": "llama-4-scout-17b-16e-instruct", - "display_name": "llama-4-scout-17b-16e-instruct", + "id": "glm-4.6", + "name": "glm-4.6", + "display_name": "glm-4.6", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "llama2-70b-4096", - "name": "llama2-70b", - "display_name": "llama2-70b", + "id": "glm-4v", + "name": "glm-4v", + "display_name": "glm-4v", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama2-7b-2048", - "name": "llama2-7b", - "display_name": "llama2-7b", + "id": "glm-4v-plus", + "name": "glm-4v-plus", + "display_name": "glm-4v-plus", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama3-70b-8192", - "name": "llama3-70b", - "display_name": "llama3-70b", + "id": "glm-zero-preview", + "name": "glm-zero-preview", + "display_name": "glm-zero-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama3-8b-8192", - "name": "llama3-8b", - "display_name": "llama3-8b", + "id": "gme-qwen2-vl-2b-instruct", + "name": "gme-qwen2-vl-2b-instruct", + "display_name": "gme-qwen2-vl-2b-instruct", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text", + "image", + "video" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama3-groq-70b-8192-tool-use-preview", - "name": "llama3-70b", - "display_name": "llama3-70b", + "id": "google/gemini-exp-1114", + "name": "gemini", + "display_name": "gemini", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama3-groq-8b-8192-tool-use-preview", - "name": "llama3-8b", - "display_name": "llama3-8b", + "id": "google/gemma-2-27b-it", + "name": "gemma-2-27b-it", + "display_name": "gemma-2-27b-it", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "llama3.1-8b", - "name": "llama3.1-8b", - "display_name": "llama3.1-8b", + "id": "google/gemma-2-9b-it:free", + "name": "gemma-2-9b", + "display_name": "gemma-2-9b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama-3-70b", - "name": "meta-llama-3-70b", - "display_name": "meta-llama-3-70b", + "id": "google/gemma-3-27b-it", + "name": "gemma-3-27b", + "display_name": "gemma-3-27b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama-3-8b", - "name": "meta-llama-3-8b", - "display_name": "meta-llama-3-8b", + "id": "gpt-3.5-turbo", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama/llama-3.1-405b-instruct:free", - "name": "llama-3.1-405b", - "display_name": "llama-3.1-405b", + "id": "gpt-3.5-turbo-0125", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama/llama-3.1-70b-instruct:free", - "name": "mellama-3.1-70b", - "display_name": "mellama-3.1-70b", + "id": "gpt-3.5-turbo-0301", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama/llama-3.1-8b-instruct:free", - "name": "llama-3.1-8b", - "display_name": "llama-3.1-8b", + "id": "gpt-3.5-turbo-0613", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama/llama-3.2-11b-vision-instruct:free", - "name": "llama-3.2-11b-vision", - "display_name": "llama-3.2-11b-vision", + "id": "gpt-3.5-turbo-1106", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta-llama/llama-3.2-3b-instruct:free", - "name": "llama-3.2-3b", - "display_name": "llama-3.2-3b", + "id": "gpt-3.5-turbo-16k", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "meta/llama-3.1-405b-instruct", - "name": "Llama-3-1-405B", - "display_name": "Llama-3-1-405B", + "id": "gpt-3.5-turbo-16k-0613", + "name": "gpt-3.5-turbo", + "display_name": "gpt-3.5-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "mistralai/mistral-7b-instruct:free", - "name": "mistral-7b", - "display_name": "mistral-7b", + "id": "gpt-3.5-turbo-instruct", + "name": "gpt-3.5-turbo-instruct", + "display_name": "gpt-3.5-turbo-instruct", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshot-v1-128k", - "name": "moonshot-v1-128k", - "display_name": "moonshot-v1-128k", + "id": "gpt-4", + "name": "gpt-4", + "display_name": "gpt-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshot-v1-128k-vision-preview", - "name": "moonshot-v1-128k-vision-preview", - "display_name": "moonshot-v1-128k-vision-preview", + "id": "gpt-4-0125-preview", + "name": "gpt-4", + "display_name": "gpt-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshot-v1-32k", - "name": "moonshot-v1-32k", - "display_name": "moonshot-v1-32k", + "id": "gpt-4-0314", + "name": "gpt-4", + "display_name": "gpt-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshot-v1-32k-vision-preview", - "name": "moonshot-v1-32k-vision-preview", - "display_name": "moonshot-v1-32k-vision-preview", + "id": "gpt-4-0613", + "name": "gpt-4", + "display_name": "gpt-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshot-v1-8k", - "name": "moonshot-v1-8k", - "display_name": "moonshot-v1-8k", + "id": "gpt-4-1106-preview", + "name": "gpt-4", + "display_name": "gpt-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshot-v1-8k-vision-preview", - "name": "moonshot-v1-8k-vision-preview", - "display_name": "moonshot-v1-8k-vision-preview", + "id": "gpt-4-32k", + "name": "gpt-4-32k", + "display_name": "gpt-4-32k", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "nvidia/llama-3.1-nemotron-70b-instruct", - "name": "llama-3.1-nemotron-70b", - "display_name": "llama-3.1-nemotron-70b", + "id": "gpt-4-32k-0314", + "name": "gpt-4-32k", + "display_name": "gpt-4-32k", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o1", - "name": "o1", - "display_name": "o1", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "gpt-4-32k-0613", + "name": "gpt-4-32k", + "display_name": "gpt-4-32k", "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "o1-2024-12-17", - "name": "o1", - "display_name": "o1", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, + "id": "gpt-4-turbo", + "name": "gpt-4-turbo", + "display_name": "gpt-4-turbo", "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "o1-global", - "name": "o1-global", - "display_name": "o1-global", + "id": "gpt-4-turbo-2024-04-09", + "name": "gpt-4-turbo", + "display_name": "gpt-4-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o1-mini", - "name": "o1-mini", - "display_name": "o1-mini", + "id": "gpt-4-turbo-preview", + "name": "gpt-4-turbo", + "display_name": "gpt-4-turbo", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o1-mini-2024-09-12", - "name": "o1-mini", - "display_name": "o1-mini", + "id": "gpt-4-vision-preview", + "name": "gpt-4-vision", + "display_name": "gpt-4-vision", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o1-preview", - "name": "o1-preview", - "display_name": "o1-preview", + "id": "gpt-4.1", + "name": "gpt-4.1", + "display_name": "gpt-4.1", "modalities": { "input": [ "text", @@ -27544,43 +27599,34 @@ "image" ] }, - "tool_call": false, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "o1-preview-2024-09-12", - "name": "o1-preview", - "display_name": "o1-preview", - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "o1-pro", - "name": "o1-pro", - "display_name": "o1-pro", + "id": "gpt-4.1-mini", + "name": "gpt-4.1-mini", + "display_name": "gpt-4.1-mini", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "o3", - "name": "o3", - "display_name": "o3", + "id": "gpt-4.1-nano", + "name": "gpt-4.1-nano", + "display_name": "gpt-4.1-nano", "modalities": { "input": [ "text", @@ -27592,24 +27638,14 @@ ] }, "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "o3-global", - "name": "o3-global", - "display_name": "o3-global", - "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o3-mini", - "name": "o3-mini", - "display_name": "o3-mini", + "id": "gpt-4o", + "name": "gpt-4o", + "display_name": "gpt-4o", "modalities": { "input": [ "text", @@ -27620,25 +27656,42 @@ "image" ] }, + "tool_call": true, + "reasoning": { + "supported": false + } + }, + { + "id": "gpt-4o-2024-05-13", + "name": "gpt-4o", + "display_name": "gpt-4o", "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "o3-mini-global", - "name": "o3-mini-global", - "display_name": "o3-mini-global", + "id": "gpt-4o-2024-08-06", + "name": "gpt-4o", + "display_name": "gpt-4o", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o3-pro", - "name": "o3-pro", - "display_name": "o3-pro", + "id": "gpt-4o-2024-08-06-global", + "name": "gpt-4o-2024-08-06-global", + "display_name": "gpt-4o-2024-08-06-global", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "gpt-4o-2024-11-20", + "name": "gpt-4o", + "display_name": "gpt-4o", "modalities": { "input": [ "text", @@ -27649,25 +27702,34 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "o3-pro-global", - "name": "o3-pro-global", - "display_name": "o3-pro-global", + "id": "gpt-4o-audio-preview", + "name": "gpt-4o-audio-preview", + "display_name": "gpt-4o-audio-preview", + "modalities": { + "input": [ + "text", + "audio" + ], + "output": [ + "text", + "audio" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "o4-mini", - "name": "o4-mini", - "display_name": "o4-mini", + "id": "gpt-4o-image", + "name": "gpt-4o-image", + "display_name": "gpt-4o-image", "modalities": { "input": [ "text", @@ -27678,97 +27740,155 @@ "image" ] }, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "omni-moderation-latest", - "name": "omni-moderation", - "display_name": "omni-moderation", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qianfan-chinese-llama-2-13b", - "name": "qianfan-chinese-llama-2-13b", - "display_name": "qianfan-chinese-llama-2-13b", + "id": "gpt-4o-image-vip", + "name": "gpt-4o-image-vip", + "display_name": "gpt-4o-image-vip", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qianfan-llama-vl-8b", - "name": "qianfan-llama-vl-8b", - "display_name": "qianfan-llama-vl-8b", + "id": "gpt-4o-mini", + "name": "gpt-4o-mini", + "display_name": "gpt-4o-mini", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qianfan-qi-vl", - "name": "qianfan-qi-vl", - "display_name": "qianfan-qi-vl", + "id": "gpt-4o-mini-2024-07-18", + "name": "gpt-4o-mini", + "display_name": "gpt-4o-mini", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-3-235b-a22b-instruct-2507", - "name": "qwen-3-235b-a22b-instruct-2507", - "display_name": "qwen-3-235b-a22b-instruct-2507", + "id": "gpt-4o-mini-audio-preview", + "name": "gpt-4o-mini-audio-preview", + "display_name": "gpt-4o-mini-audio-preview", + "modalities": { + "input": [ + "text", + "audio" + ], + "output": [ + "text", + "audio" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-3-235b-a22b-thinking-2507", - "name": "qwen-3-235b-a22b-thinking-2507", - "display_name": "qwen-3-235b-a22b-thinking-2507", + "id": "gpt-4o-mini-global", + "name": "gpt-4o-mini-global", + "display_name": "gpt-4o-mini-global", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-3-32b", - "name": "qwen-3-32b", - "display_name": "qwen-3-32b", - "tool_call": false, + "id": "gpt-4o-mini-search-preview", + "name": "gpt-4o-mini-search-preview", + "display_name": "gpt-4o-mini-search-preview", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "qwen-flash", - "name": "qwen-flash", - "display_name": "qwen-flash", + "id": "gpt-4o-mini-tts", + "name": "gpt-4o-mini-tts", + "display_name": "gpt-4o-mini-tts", + "modalities": { + "input": [ + "audio" + ], + "output": [ + "audio" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-flash-2025-07-28", - "name": "qwen-flash-2025-07-28", - "display_name": "qwen-flash-2025-07-28", - "tool_call": false, + "id": "gpt-4o-search-preview", + "name": "gpt-4o-search-preview", + "display_name": "gpt-4o-search-preview", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "qwen-image", - "name": "qwen-image", - "display_name": "qwen-image", + "id": "gpt-4o-zh", + "name": "gpt-4o", + "display_name": "gpt-4o", "modalities": { "input": [ "text", @@ -27785,9 +27905,9 @@ } }, { - "id": "qwen-image-edit", - "name": "qwen-image-edit", - "display_name": "qwen-image-edit", + "id": "gpt-5", + "name": "gpt-5", + "display_name": "gpt-5", "modalities": { "input": [ "text", @@ -27798,15 +27918,16 @@ "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen-image-plus", - "name": "qwen-image-plus", - "display_name": "qwen-image-plus", + "id": "gpt-5-chat-latest", + "name": "gpt-5-chat-latest", + "display_name": "gpt-5-chat-latest", "modalities": { "input": [ "text", @@ -27823,51 +27944,97 @@ } }, { - "id": "qwen-long", - "name": "qwen-long", - "display_name": "qwen-long", - "tool_call": false, + "id": "gpt-5-codex", + "name": "gpt-5-codex", + "display_name": "gpt-5-codex", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen-max", - "name": "qwen-max", - "display_name": "qwen-max", - "tool_call": false, + "id": "gpt-5-mini", + "name": "gpt-5-mini", + "display_name": "gpt-5-mini", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen-max-0125", - "name": "Qwen2.5-Max", - "display_name": "Qwen2.5-Max", - "tool_call": false, + "id": "gpt-5-nano", + "name": "gpt-5-nano", + "display_name": "gpt-5-nano", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen-max-longcontext", - "name": "qwen-max-longcontext", - "display_name": "qwen-max-longcontext", - "tool_call": false, + "id": "gpt-5-pro", + "name": "gpt-5-pro", + "display_name": "gpt-5-pro", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen-mt-plus", - "name": "qwen-mt-plus", - "display_name": "qwen-mt-plus", + "id": "gpt-image-1", + "name": "gpt-image-1", + "display_name": "gpt-image-1", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, "tool_call": false, @@ -27876,15 +28043,17 @@ } }, { - "id": "qwen-mt-turbo", - "name": "qwen-mt-turbo", - "display_name": "qwen-mt-turbo", + "id": "gpt-image-1-mini", + "name": "gpt-image-1-mini", + "display_name": "gpt-image-1-mini", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, "tool_call": false, @@ -27893,54 +28062,18 @@ } }, { - "id": "qwen-plus", - "name": "qwen-plus", - "display_name": "qwen-plus", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen-plus-2025-04-28", - "name": "qwen-plus-2025-04-28", - "display_name": "qwen-plus-2025-04-28", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen-plus-2025-07-28", - "name": "qwen-plus-2025-07-28", - "display_name": "qwen-plus-2025-07-28", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen-plus-latest", - "name": "qwen-plus-latest", - "display_name": "qwen-plus-latest", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen-qwq-32b", - "name": "qwen-qwq-32b", - "display_name": "qwen-qwq-32b", + "id": "gpt-image-test", + "name": "gpt-image-test", + "display_name": "gpt-image-test", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-turbo", - "name": "qwen-turbo", - "display_name": "qwen-turbo", + "id": "gpt-oss-120b", + "name": "gpt-oss-120b", + "display_name": "gpt-oss-120b", "modalities": { "input": [ "text" @@ -27949,15 +28082,16 @@ "text" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen-turbo-2024-11-01", - "name": "Qwen2.5-Turbo", - "display_name": "Qwen2.5-Turbo", + "id": "gpt-oss-20b", + "name": "gpt-oss-20b", + "display_name": "gpt-oss-20b", "modalities": { "input": [ "text" @@ -27966,123 +28100,143 @@ "text" ] }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "grok-2-1212", + "name": "grok-2", + "display_name": "grok-2", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-turbo-2025-04-28", - "name": "qwen-turbo-2025-04-28", - "display_name": "qwen-turbo-2025-04-28", + "id": "grok-2-vision-1212", + "name": "grok-2", + "display_name": "grok-2", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen-turbo-latest", - "name": "qwen-turbo-latest", - "display_name": "qwen-turbo-latest", + "id": "grok-3", + "name": "grok-3", + "display_name": "grok-3", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-14b-instruct", - "name": "qwen2.5-14b", - "display_name": "qwen2.5-14b", + "id": "grok-3-beta", + "name": "grok-3", + "display_name": "grok-3", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-32b-instruct", - "name": "qwen2.5-32b", - "display_name": "qwen2.5-32b", + "id": "grok-3-fast", + "name": "grok-3-fast", + "display_name": "grok-3-fast", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-3b-instruct", - "name": "qwen2.5-3b", - "display_name": "qwen2.5-3b", + "id": "grok-3-fast-beta", + "name": "grok-3-fast-beta", + "display_name": "grok-3-fast-beta", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-72b-instruct", - "name": "qwen2.5-72b", - "display_name": "qwen2.5-72b", + "id": "grok-3-mini", + "name": "grok-3-mini", + "display_name": "grok-3-mini", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-7b-instruct", - "name": "qwen2.5-7b", - "display_name": "qwen2.5-7b", + "id": "grok-3-mini-beta", + "name": "grok-3-mini-beta", + "display_name": "grok-3-mini-beta", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-coder-1.5b-instruct", - "name": "qwen2.5-coder-1.5b", - "display_name": "qwen2.5-coder-1.5b", + "id": "grok-3-mini-fast-beta", + "name": "grok-3-mini-fast-beta", + "display_name": "grok-3-mini-fast-beta", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-coder-7b-instruct", - "name": "qwen2.5-coder-7b", - "display_name": "qwen2.5-coder-7b", + "id": "grok-4", + "name": "grok-4", + "display_name": "grok-4", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-math-1.5b-instruct", - "name": "qwen2.5-math-1.5b", - "display_name": "qwen2.5-math-1.5b", + "id": "grok-4-fast-non-reasoning", + "name": "grok-4-fast-non-reasoning", + "display_name": "grok-4-fast-non-reasoning", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-math-72b-instruct", - "name": "qwen2.5-math-72b", - "display_name": "qwen2.5-math-72b", + "id": "grok-4-fast-reasoning", + "name": "grok-4-fast-reasoning", + "display_name": "grok-4-fast-reasoning", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-math-7b-instruct", - "name": "qwen2.5-math-7b", - "display_name": "qwen2.5-math-7b", + "id": "grok-code-fast-1", + "name": "grok-code-fast-1", + "display_name": "grok-code-fast-1", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen2.5-vl-72b-instruct", - "name": "qwen2.5-vl-72b", - "display_name": "qwen2.5-vl-72b", + "id": "grok-vision-beta", + "name": "grok-beta", + "display_name": "grok-beta", "modalities": { "input": [ "text", @@ -28099,36 +28253,9 @@ } }, { - "id": "qwen3-0.6b", - "name": "qwen3-0.6b", - "display_name": "qwen3-0.6b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-1.7b", - "name": "qwen3-1.7b", - "display_name": "qwen3-1.7b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-14b", - "name": "qwen3-14b", - "display_name": "qwen3-14b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-235b-a22b", - "name": "qwen3-235b-a22b", - "display_name": "qwen3-235b-a22b", + "id": "gte-rerank-v2", + "name": "gte-rerank-v2", + "display_name": "gte-rerank-v2", "modalities": { "input": [ "text", @@ -28139,15 +28266,15 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "qwen3-235b-a22b-instruct-2507", - "display_name": "qwen3-235b-a22b-instruct-2507", + "id": "imagen-3.0-generate-002", + "name": "imagen-3.0-generate-002", + "display_name": "imagen-3.0-generate-002", "modalities": { "input": [ "text", @@ -28158,15 +28285,15 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-235b-a22b-thinking-2507", - "name": "qwen3-235b-a22b-thinking-2507", - "display_name": "qwen3-235b-a22b-thinking-2507", + "id": "imagen-4.0", + "name": "imagen-4.0-generate-preview-06-06", + "display_name": "imagen-4.0-generate-preview-06-06", "modalities": { "input": [ "text", @@ -28177,161 +28304,118 @@ "image" ] }, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "qwen3-30b-a3b", - "name": "qwen3-30b-a3b", - "display_name": "qwen3-30b-a3b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-30b-a3b-instruct-2507", - "name": "qwen3-30b-a3b-instruct-2507", - "display_name": "qwen3-30b-a3b-instruct-2507", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-30b-a3b-thinking-2507", - "name": "qwen3-30b-a3b-thinking-2507", - "display_name": "qwen3-30b-a3b-thinking-2507", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-32b", - "name": "qwen3-32b", - "display_name": "qwen3-32b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-4b", - "name": "qwen3-4b", - "display_name": "qwen3-4b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen3-8b", - "name": "qwen3-8b", - "display_name": "qwen3-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-coder-30b-a3b-instruct", - "name": "qwen3-coder-30b-a3b-instruct", - "display_name": "qwen3-coder-30b-a3b-instruct", + "id": "imagen-4.0-fast-generate-001", + "name": "imagen-4.0-fast-generate-001", + "display_name": "imagen-4.0-fast-generate-001", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-coder-480b-a35b-instruct", - "name": "qwen3-coder-480b-a35b-instruct", - "display_name": "qwen3-coder-480b-a35b-instruct", + "id": "imagen-4.0-fast-generate-preview-06-06", + "name": "imagen-4.0-fast-generate-preview-06-06", + "display_name": "imagen-4.0-fast-generate-preview-06-06", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-coder-flash", - "name": "qwen3-coder-flash", - "display_name": "qwen3-coder-flash", + "id": "imagen-4.0-generate-001", + "name": "imagen-4.0-generate-001", + "display_name": "imagen-4.0-generate-001", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-coder-plus", - "name": "qwen3-coder-plus", - "display_name": "qwen3-coder-plus", + "id": "imagen-4.0-generate-preview-05-20", + "name": "imagen-4.0-generate-preview-05-20", + "display_name": "imagen-4.0-generate-preview-05-20", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-coder-plus-2025-07-22", - "name": "qwen3-coder-plus-2025-07-22", - "display_name": "qwen3-coder-plus-2025-07-22", + "id": "imagen-4.0-ultra", + "name": "imagen-4.0-ultra-generate-preview-06-06", + "display_name": "imagen-4.0-ultra-generate-preview-06-06", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-embedding-0.6b", - "name": "qwen3-embedding-0.6b", - "display_name": "qwen3-embedding-0.6b", + "id": "imagen-4.0-ultra-generate-001", + "name": "imagen-4.0-ultra-generate-001", + "display_name": "imagen-4.0-ultra-generate-001", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, "tool_call": false, @@ -28340,15 +28424,17 @@ } }, { - "id": "qwen3-embedding-4b", - "name": "qwen3-embedding-4b", - "display_name": "qwen3-embedding-4b", + "id": "imagen-4.0-ultra-generate-exp-05-20", + "name": "imagen-4.0-ultra-generate-exp-05-20", + "display_name": "imagen-4.0-ultra-generate-exp-05-20", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, "tool_call": false, @@ -28357,26 +28443,18 @@ } }, { - "id": "qwen3-embedding-8b", - "name": "qwen3-embedding-8b", - "display_name": "qwen3-embedding-8b", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "irag-1.0", + "name": "irag-1.0", + "display_name": "irag-1.0", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-max", - "name": "qwen3-max-preview", - "display_name": "qwen3-max-preview", + "id": "jina-clip-v2", + "name": "jina-clip-v2", + "display_name": "jina-clip-v2", "modalities": { "input": [ "text", @@ -28387,34 +28465,32 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-max-preview", - "name": "qwen3-max-preview", - "display_name": "qwen3-max-preview", + "id": "jina-colbert-v2", + "name": "jina-colbert-v2", + "display_name": "jina-colbert-v2", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-next-80b-a3b-instruct", - "name": "qwen3-next-80b-a3b-instruct", - "display_name": "qwen3-next-80b-a3b-instruct", + "id": "jina-deepsearch-v1", + "name": "jina-deepsearch-v1", + "display_name": "jina-deepsearch-v1", "modalities": { "input": [ "text", @@ -28425,43 +28501,39 @@ "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "qwen3-next-80b-a3b-thinking", - "name": "qwen3-next-80b-a3b-thinking", - "display_name": "qwen3-next-80b-a3b-thinking", + "id": "jina-embeddings-v2-base-code", + "name": "jina-embeddings-v2-base-code", + "display_name": "jina-embeddings-v2-base-code", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "qwen3-reranker-0.6b", - "name": "qwen3-reranker-0.6b", - "display_name": "qwen3-reranker-0.6b", + "id": "jina-embeddings-v3", + "name": "jina-embeddings-v3", + "display_name": "jina-embeddings-v3", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text", - "image" + "text" ] }, "tool_call": false, @@ -28470,9 +28542,9 @@ } }, { - "id": "qwen3-reranker-4b", - "name": "qwen3-reranker-4b", - "display_name": "qwen3-reranker-4b", + "id": "jina-embeddings-v4", + "name": "jina-embeddings-v4", + "display_name": "jina-embeddings-v4", "modalities": { "input": [ "text", @@ -28489,9 +28561,9 @@ } }, { - "id": "qwen3-reranker-8b", - "name": "qwen3-reranker-8b", - "display_name": "qwen3-reranker-8b", + "id": "jina-reranker-m0", + "name": "jina-reranker-m0", + "display_name": "jina-reranker-m0", "modalities": { "input": [ "text", @@ -28508,62 +28580,51 @@ } }, { - "id": "qwen3-vl-235b-a22b-instruct", - "name": "qwen3-vl-235b-a22b-instruct", - "display_name": "qwen3-vl-235b-a22b-instruct", + "id": "jina-reranker-v3", + "name": "jina-reranker-v3", + "display_name": "jina-reranker-v3", "modalities": { "input": [ "text", - "image", - "video" + "image" ], "output": [ "text", - "image", - "video" + "image" ] }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen3-vl-235b-a22b-thinking", - "name": "qwen3-vl-235b-a22b-thinking", - "display_name": "qwen3-vl-235b-a22b-thinking", + "id": "kimi-k2-0711", + "name": "kimi-k2-0711", + "display_name": "kimi-k2-0711", "modalities": { "input": [ - "text", - "image", - "video" + "text" ], "output": [ - "text", - "image", - "video" + "text" ] }, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "qwen3-vl-30b-a3b-instruct", - "name": "qwen3-vl-30b-a3b-instruct", - "display_name": "qwen3-vl-30b-a3b-instruct", + "id": "kimi-k2-turbo-preview", + "name": "kimi-k2-turbo-preview", + "display_name": "kimi-k2-turbo-preview", "modalities": { "input": [ - "text", - "image", - "video" + "text" ], "output": [ - "text", - "image", - "video" + "text" ] }, "tool_call": true, @@ -28572,602 +28633,492 @@ } }, { - "id": "qwen3-vl-30b-a3b-thinking", - "name": "qwen3-vl-30b-a3b-thinking", - "display_name": "qwen3-vl-30b-a3b-thinking", - "modalities": { - "input": [ - "text", - "image", - "video" - ], - "output": [ - "text", - "image", - "video" - ] - }, - "tool_call": true, + "id": "kimi-latest", + "name": "kimi-latest", + "display_name": "kimi-latest", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "qwen3-vl-plus", - "name": "qwen3-vl-plus", - "display_name": "qwen3-vl-plus", - "modalities": { - "input": [ - "text", - "image", - "video" - ], - "output": [ - "text", - "image", - "video" - ] - }, - "tool_call": true, - "reasoning": { - "supported": false - } - }, - { - "id": "sonar", - "name": "sonar", - "display_name": "sonar", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "sonar-reasoning", - "name": "sonar-reasoning", - "display_name": "sonar-reasoning", + "id": "kimi-thinking-preview", + "name": "kimi-thinking-preview", + "display_name": "kimi-thinking-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "sonar-reasoning-pro", - "name": "sonar-reasoning-pro", - "display_name": "sonar-reasoning-pro", + "id": "learnlm-1.5-pro-experimental", + "name": "Gemini 1.5 Pro", + "display_name": "Gemini 1.5 Pro", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "sora-2", - "name": "sora-2", - "display_name": "sora-2", + "id": "llama-3.1-405b-instruct", + "name": "llama-3.1-405b", + "display_name": "llama-3.1-405b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "sora-2-hd", - "name": "sora-2-hd", - "display_name": "sora-2-hd", + "id": "llama-3.1-405b-reasoning", + "name": "llama-3.1-405b", + "display_name": "llama-3.1-405b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "sora-2-pro", - "name": "sora-2-pro", - "display_name": "sora-2-pro", + "id": "llama-3.1-70b", + "name": "llama-3.1-70b", + "display_name": "llama-3.1-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "step-2-16k", - "name": "step-2", - "display_name": "step-2", + "id": "llama-3.1-70b-versatile", + "name": "llama-3.1-70b", + "display_name": "llama-3.1-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "stepfun-ai/step3", - "name": "stepfun-ai/step3", - "display_name": "stepfun-ai/step3", + "id": "llama-3.1-8b-instant", + "name": "llama-3.1-8b", + "display_name": "llama-3.1-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "tao-8k", - "name": "tao-8k", - "display_name": "tao-8k", + "id": "llama-3.1-sonar-huge-128k-online", + "name": "llama-3.1-sonar-huge-128k-online", + "display_name": "llama-3.1-sonar-huge-128k-online", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-ada-001", - "name": "text-ada-001", - "display_name": "text-ada-001", + "id": "llama-3.1-sonar-large-128k-online", + "name": "llama-3.1-sonar-large-128k-online", + "display_name": "llama-3.1-sonar-large-128k-online", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-babbage-001", - "name": "text-babbage-001", - "display_name": "text-babbage-001", + "id": "llama-3.1-sonar-small-128k-online", + "name": "llama-3.1-sonar-small-128k-online", + "display_name": "llama-3.1-sonar-small-128k-online", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-curie-001", - "name": "text-curie-001", - "display_name": "text-curie-001", + "id": "llama-3.2-11b-vision-preview", + "name": "Llama-3-2-11B", + "display_name": "Llama-3-2-11B", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-davinci-002", - "name": "text-davinci", - "display_name": "text-davinci", + "id": "llama-3.2-1b-preview", + "name": "llama-3.2-1b", + "display_name": "llama-3.2-1b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-davinci-003", - "name": "text-davinci", - "display_name": "text-davinci", + "id": "llama-3.2-3b-preview", + "name": "llama-3.2-3b", + "display_name": "llama-3.2-3b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-davinci-edit-001", - "name": "text-davinci-edit-001", - "display_name": "text-davinci-edit-001", + "id": "llama-3.2-90b-vision-preview", + "name": "Llama-3.2-90B", + "display_name": "Llama-3.2-90B", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-embedding-004", - "name": "text-embedding-004", - "display_name": "text-embedding-004", + "id": "llama-3.3-70b", + "name": "llama-3.3-70b", + "display_name": "llama-3.3-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-embedding-3-large", - "name": "text-embedding-3-large", - "display_name": "text-embedding-3-large", + "id": "llama-4-maverick", + "name": "llama-4-maverick-17b-128e-instruct", + "display_name": "llama-4-maverick-17b-128e-instruct", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false } }, { - "id": "text-embedding-3-small", - "name": "text-embedding-3-small", - "display_name": "text-embedding-3-small", + "id": "llama-4-scout", + "name": "llama-4-scout-17b-16e-instruct", + "display_name": "llama-4-scout-17b-16e-instruct", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, + "tool_call": true, + "reasoning": { + "supported": false + } + }, + { + "id": "llama2-70b-4096", + "name": "llama2-70b", + "display_name": "llama2-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-embedding-ada-002", - "name": "text-embedding-ada-002", - "display_name": "text-embedding-ada-002", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "llama2-7b-2048", + "name": "llama2-7b", + "display_name": "llama2-7b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-embedding-v1", - "name": "text-embedding-v1", - "display_name": "text-embedding-v1", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "llama3-70b-8192", + "name": "llama3-70b", + "display_name": "llama3-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-embedding-v4", - "name": "text-embedding-v4", - "display_name": "text-embedding-v4", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, + "id": "llama3-8b-8192", + "name": "llama3-8b", + "display_name": "llama3-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-moderation-007", - "name": "text-moderation", - "display_name": "text-moderation", + "id": "llama3-groq-70b-8192-tool-use-preview", + "name": "llama3-70b", + "display_name": "llama3-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-moderation-latest", - "name": "text-moderation", - "display_name": "text-moderation", + "id": "llama3-groq-8b-8192-tool-use-preview", + "name": "llama3-8b", + "display_name": "llama3-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-moderation-stable", - "name": "text-moderation", - "display_name": "text-moderation", + "id": "llama3.1-8b", + "name": "llama3.1-8b", + "display_name": "llama3.1-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "text-search-ada-doc-001", - "name": "text-search-ada-doc-001", - "display_name": "text-search-ada-doc-001", + "id": "meta-llama-3-70b", + "name": "meta-llama-3-70b", + "display_name": "meta-llama-3-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "tts-1", - "name": "tts-1", - "display_name": "tts-1", - "modalities": { - "input": [ - "audio" - ], - "output": [ - "audio" - ] - }, + "id": "meta-llama-3-8b", + "name": "meta-llama-3-8b", + "display_name": "meta-llama-3-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "tts-1-1106", - "name": "tts-1", - "display_name": "tts-1", - "modalities": { - "input": [ - "audio" - ], - "output": [ - "audio" - ] - }, + "id": "meta-llama/llama-3.1-405b-instruct:free", + "name": "llama-3.1-405b", + "display_name": "llama-3.1-405b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "tts-1-hd", - "name": "tts-1-hd", - "display_name": "tts-1-hd", - "modalities": { - "input": [ - "audio" - ], - "output": [ - "audio" - ] - }, + "id": "meta-llama/llama-3.1-70b-instruct:free", + "name": "mellama-3.1-70b", + "display_name": "mellama-3.1-70b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "tts-1-hd-1106", - "name": "tts-1-hd", - "display_name": "tts-1-hd", - "modalities": { - "input": [ - "audio" - ], - "output": [ - "audio" - ] - }, + "id": "meta-llama/llama-3.1-8b-instruct:free", + "name": "llama-3.1-8b", + "display_name": "llama-3.1-8b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "unsloth/gemma-3-12b-it", - "name": "gemma-3-12b", - "display_name": "gemma-3-12b", + "id": "meta-llama/llama-3.2-11b-vision-instruct:free", + "name": "llama-3.2-11b-vision", + "display_name": "llama-3.2-11b-vision", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "unsloth/gemma-3-27b-it", - "name": "gemma-3-27b", - "display_name": "gemma-3-27b", + "id": "meta-llama/llama-3.2-3b-instruct:free", + "name": "llama-3.2-3b", + "display_name": "llama-3.2-3b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "veo-2.0-generate-001", - "name": "veo-2.0-generate-001", - "display_name": "veo-2.0-generate-001", - "modalities": { - "input": [ - "video" - ], - "output": [ - "video" - ] - }, + "id": "meta/llama-3.1-405b-instruct", + "name": "Llama-3-1-405B", + "display_name": "Llama-3-1-405B", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "veo-3", - "name": "veo3", - "display_name": "veo3", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, + "id": "mistralai/mistral-7b-instruct:free", + "name": "mistral-7b", + "display_name": "mistral-7b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "veo-3.0-generate-preview", - "name": "veo-3.0-generate-preview", - "display_name": "veo-3.0-generate-preview", - "modalities": { - "input": [ - "text", - "image", - "video" - ], - "output": [ - "text", - "image", - "video" - ] - }, + "id": "moonshot-v1-128k", + "name": "moonshot-v1-128k", + "display_name": "moonshot-v1-128k", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "veo3", - "name": "veo3", - "display_name": "veo3", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text", - "image", - "audio", - "video" - ] - }, + "id": "moonshot-v1-128k-vision-preview", + "name": "moonshot-v1-128k-vision-preview", + "display_name": "moonshot-v1-128k-vision-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "veo3.1", - "name": "veo3.1", - "display_name": "veo3.1", + "id": "moonshot-v1-32k", + "name": "moonshot-v1-32k", + "display_name": "moonshot-v1-32k", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "web-sora-2", - "name": "web-sora-2", - "display_name": "web-sora-2", + "id": "moonshot-v1-32k-vision-preview", + "name": "moonshot-v1-32k-vision-preview", + "display_name": "moonshot-v1-32k-vision-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "whisper-1", - "name": "whisper-1", - "display_name": "whisper-1", + "id": "moonshot-v1-8k", + "name": "moonshot-v1-8k", + "display_name": "moonshot-v1-8k", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "moonshot-v1-8k-vision-preview", + "name": "moonshot-v1-8k-vision-preview", + "display_name": "moonshot-v1-8k-vision-preview", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "nvidia/llama-3.1-nemotron-70b-instruct", + "name": "llama-3.1-nemotron-70b", + "display_name": "llama-3.1-nemotron-70b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "o1", + "name": "o1", + "display_name": "o1", "modalities": { "input": [ - "audio" + "text" ], "output": [ - "audio" + "text" ] }, "tool_call": false, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "whisper-large-v3", - "name": "whisper-large", - "display_name": "whisper-large", + "id": "o1-2024-12-17", + "name": "o1", + "display_name": "o1", "modalities": { "input": [ - "audio" + "text", + "image" ], "output": [ - "audio" + "text", + "image" ] }, "tool_call": false, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "yi-large", - "name": "yi-large", - "display_name": "yi-large", + "id": "o1-global", + "name": "o1-global", + "display_name": "o1-global", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "yi-large-rag", - "name": "yi-large-rag", - "display_name": "yi-large-rag", + "id": "o1-mini", + "name": "o1-mini", + "display_name": "o1-mini", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "yi-large-turbo", - "name": "yi-large-turbo", - "display_name": "yi-large-turbo", + "id": "o1-mini-2024-09-12", + "name": "o1-mini", + "display_name": "o1-mini", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "yi-lightning", - "name": "yi-lightning", - "display_name": "yi-lightning", + "id": "o1-preview", + "name": "o1-preview", + "display_name": "o1-preview", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, "tool_call": false, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "yi-medium", - "name": "yi-medium", - "display_name": "yi-medium", + "id": "o1-preview-2024-09-12", + "name": "o1-preview", + "display_name": "o1-preview", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "yi-vl-plus", - "name": "yi-vl-plus", - "display_name": "yi-vl-plus", - "tool_call": false, - "reasoning": { - "supported": false - } - } - ] - }, - "fireworks-ai": { - "id": "fireworks-ai", - "name": "Fireworks AI", - "display_name": "Fireworks AI", - "api": "https://api.fireworks.ai/inference/v1/", - "doc": "https://fireworks.ai/docs/", - "models": [ - { - "id": "accounts/fireworks/models/deepseek-r1-0528", - "name": "Deepseek R1 05/28", - "display_name": "Deepseek R1 05/28", + "id": "o1-pro", + "name": "o1-pro", + "display_name": "o1-pro", "modalities": { "input": [ "text" @@ -29176,447 +29127,297 @@ "text" ] }, - "limit": { - "context": 160000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": true, "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-05", - "release_date": "2025-05-28", - "last_updated": "2025-05-28", - "cost": { - "input": 3, - "output": 8 } }, { - "id": "accounts/fireworks/models/deepseek-v3p1", - "name": "DeepSeek V3.1", - "display_name": "DeepSeek V3.1", + "id": "o3", + "name": "o3", + "display_name": "o3", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 163840, - "output": 163840 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "cost": { - "input": 0.56, - "output": 1.68 } }, { - "id": "accounts/fireworks/models/deepseek-v3-0324", - "name": "Deepseek V3 03-24", - "display_name": "Deepseek V3 03-24", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 160000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "id": "o3-global", + "name": "o3-global", + "display_name": "o3-global", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-10", - "release_date": "2025-03-24", - "last_updated": "2025-03-24", - "cost": { - "input": 0.9, - "output": 0.9 } }, { - "id": "accounts/fireworks/models/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "display_name": "Kimi K2 Instruct", + "id": "o3-mini", + "name": "o3-mini", + "display_name": "o3-mini", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "o3-mini-global", + "name": "o3-mini-global", + "display_name": "o3-mini-global", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "cost": { - "input": 1, - "output": 3 } }, { - "id": "accounts/fireworks/models/qwen3-235b-a22b", - "name": "Qwen3 235B-A22B", - "display_name": "Qwen3 235B-A22B", + "id": "o3-pro", + "name": "o3-pro", + "display_name": "o3-pro", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 16384 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-04-29", - "last_updated": "2025-04-29", - "cost": { - "input": 0.22, - "output": 0.88 } }, { - "id": "accounts/fireworks/models/gpt-oss-20b", - "name": "GPT OSS 20B", - "display_name": "GPT OSS 20B", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 32768 - }, - "temperature": true, - "tool_call": true, + "id": "o3-pro-global", + "name": "o3-pro-global", + "display_name": "o3-pro-global", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "cost": { - "input": 0.05, - "output": 0.2 + "supported": false } }, { - "id": "accounts/fireworks/models/gpt-oss-120b", - "name": "GPT OSS 120B", - "display_name": "GPT OSS 120B", + "id": "o4-mini", + "name": "o4-mini", + "display_name": "o4-mini", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 131072, - "output": 32768 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true - }, - "attachment": false, - "open_weights": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "cost": { - "input": 0.15, - "output": 0.6 } }, { - "id": "accounts/fireworks/models/glm-4p5-air", - "name": "GLM 4.5 Air", - "display_name": "GLM 4.5 Air", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "temperature": true, - "tool_call": true, + "id": "omni-moderation-latest", + "name": "omni-moderation", + "display_name": "omni-moderation", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "cost": { - "input": 0.22, - "output": 0.88 + "supported": false } }, { - "id": "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct", - "name": "Qwen3 Coder 480B A35B Instruct", - "display_name": "Qwen3 Coder 480B A35B Instruct", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 256000, - "output": 32768 - }, - "temperature": true, - "tool_call": true, + "id": "qianfan-chinese-llama-2-13b", + "name": "qianfan-chinese-llama-2-13b", + "display_name": "qianfan-chinese-llama-2-13b", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "release_date": "2025-07-22", - "last_updated": "2025-07-22", - "cost": { - "input": 0.45, - "output": 1.8 } }, { - "id": "accounts/fireworks/models/glm-4p5", - "name": "GLM 4.5", - "display_name": "GLM 4.5", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "temperature": true, - "tool_call": true, + "id": "qianfan-llama-vl-8b", + "name": "qianfan-llama-vl-8b", + "display_name": "qianfan-llama-vl-8b", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "cost": { - "input": 0.55, - "output": 2.19 + "supported": false } - } - ] - }, - "llama": { - "id": "llama", - "name": "Llama", - "display_name": "Llama", - "api": "https://api.llama.com/compat/v1/", - "doc": "https://llama.developer.meta.com/docs/models", - "models": [ + }, { - "id": "llama-3.3-8b-instruct", - "name": "Llama-3.3-8B-Instruct", - "display_name": "Llama-3.3-8B-Instruct", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "id": "qianfan-qi-vl", + "name": "qianfan-qi-vl", + "display_name": "qianfan-qi-vl", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "cost": { - "input": 0, - "output": 0 } }, { - "id": "llama-4-maverick-17b-128e-instruct-fp8", - "name": "Llama-4-Maverick-17B-128E-Instruct-FP8", - "display_name": "Llama-4-Maverick-17B-128E-Instruct-FP8", + "id": "qwen-3-235b-a22b-instruct-2507", + "name": "qwen-3-235b-a22b-instruct-2507", + "display_name": "qwen-3-235b-a22b-instruct-2507", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-3-235b-a22b-thinking-2507", + "name": "qwen-3-235b-a22b-thinking-2507", + "display_name": "qwen-3-235b-a22b-thinking-2507", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-3-32b", + "name": "qwen-3-32b", + "display_name": "qwen-3-32b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-flash", + "name": "qwen-flash", + "display_name": "qwen-flash", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-flash-2025-07-28", + "name": "qwen-flash-2025-07-28", + "display_name": "qwen-flash-2025-07-28", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-image", + "name": "qwen-image", + "display_name": "qwen-image", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "cost": { - "input": 0, - "output": 0 } }, { - "id": "llama-3.3-70b-instruct", - "name": "Llama-3.3-70B-Instruct", - "display_name": "Llama-3.3-70B-Instruct", + "id": "qwen-image-edit", + "name": "qwen-image-edit", + "display_name": "qwen-image-edit", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "cost": { - "input": 0, - "output": 0 } }, { - "id": "llama-4-scout-17b-16e-instruct-fp8", - "name": "Llama-4-Scout-17B-16E-Instruct-FP8", - "display_name": "Llama-4-Scout-17B-16E-Instruct-FP8", + "id": "qwen-image-plus", + "name": "qwen-image-plus", + "display_name": "qwen-image-plus", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "cost": { - "input": 0, - "output": 0 } }, { - "id": "groq-llama-4-maverick-17b-128e-instruct", - "name": "Groq-Llama-4-Maverick-17B-128E-Instruct", - "display_name": "Groq-Llama-4-Maverick-17B-128E-Instruct", + "id": "qwen-long", + "name": "qwen-long", + "display_name": "qwen-long", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-max", + "name": "qwen-max", + "display_name": "qwen-max", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-max-0125", + "name": "Qwen2.5-Max", + "display_name": "Qwen2.5-Max", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-max-longcontext", + "name": "qwen-max-longcontext", + "display_name": "qwen-max-longcontext", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-mt-plus", + "name": "qwen-mt-plus", + "display_name": "qwen-mt-plus", "modalities": { "input": [ "text" @@ -29625,29 +29426,15 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2025-01", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "cost": { - "input": 0, - "output": 0 } }, { - "id": "cerebras-llama-4-scout-17b-16e-instruct", - "name": "Cerebras-Llama-4-Scout-17B-16E-Instruct", - "display_name": "Cerebras-Llama-4-Scout-17B-16E-Instruct", + "id": "qwen-mt-turbo", + "name": "qwen-mt-turbo", + "display_name": "qwen-mt-turbo", "modalities": { "input": [ "text" @@ -29656,29 +29443,60 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2025-01", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "cost": { - "input": 0, - "output": 0 } }, { - "id": "cerebras-llama-4-maverick-17b-128e-instruct", - "name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", - "display_name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", + "id": "qwen-plus", + "name": "qwen-plus", + "display_name": "qwen-plus", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-plus-2025-04-28", + "name": "qwen-plus-2025-04-28", + "display_name": "qwen-plus-2025-04-28", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-plus-2025-07-28", + "name": "qwen-plus-2025-07-28", + "display_name": "qwen-plus-2025-07-28", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-plus-latest", + "name": "qwen-plus-latest", + "display_name": "qwen-plus-latest", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-qwq-32b", + "name": "qwen-qwq-32b", + "display_name": "qwen-qwq-32b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-turbo", + "name": "qwen-turbo", + "display_name": "qwen-turbo", "modalities": { "input": [ "text" @@ -29687,38 +29505,15 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2025-01", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "cost": { - "input": 0, - "output": 0 } - } - ] - }, - "scaleway": { - "id": "scaleway", - "name": "Scaleway", - "display_name": "Scaleway", - "api": "https://api.scaleway.ai/v1", - "doc": "https://www.scaleway.com/en/docs/generative-apis/", - "models": [ + }, { - "id": "qwen3-235b-a22b-instruct-2507", - "name": "Qwen3 235B A22B Instruct 2507", - "display_name": "Qwen3 235B A22B Instruct 2507", + "id": "qwen-turbo-2024-11-01", + "name": "Qwen2.5-Turbo", + "display_name": "Qwen2.5-Turbo", "modalities": { "input": [ "text" @@ -29727,150 +29522,281 @@ "text" ] }, - "limit": { - "context": 40000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "cost": { - "input": 0.75, - "output": 2.25 } }, { - "id": "pixtral-12b-2409", - "name": "Pixtral 12B 2409", - "display_name": "Pixtral 12B 2409", + "id": "qwen-turbo-2025-04-28", + "name": "qwen-turbo-2025-04-28", + "display_name": "qwen-turbo-2025-04-28", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen-turbo-latest", + "name": "qwen-turbo-latest", + "display_name": "qwen-turbo-latest", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-14b-instruct", + "name": "qwen2.5-14b", + "display_name": "qwen2.5-14b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-32b-instruct", + "name": "qwen2.5-32b", + "display_name": "qwen2.5-32b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-3b-instruct", + "name": "qwen2.5-3b", + "display_name": "qwen2.5-3b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-72b-instruct", + "name": "qwen2.5-72b", + "display_name": "qwen2.5-72b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-7b-instruct", + "name": "qwen2.5-7b", + "display_name": "qwen2.5-7b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-coder-1.5b-instruct", + "name": "qwen2.5-coder-1.5b", + "display_name": "qwen2.5-coder-1.5b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-coder-7b-instruct", + "name": "qwen2.5-coder-7b", + "display_name": "qwen2.5-coder-7b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-math-1.5b-instruct", + "name": "qwen2.5-math-1.5b", + "display_name": "qwen2.5-math-1.5b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-math-72b-instruct", + "name": "qwen2.5-math-72b", + "display_name": "qwen2.5-math-72b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-math-7b-instruct", + "name": "qwen2.5-math-7b", + "display_name": "qwen2.5-math-7b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen2.5-vl-72b-instruct", + "name": "qwen2.5-vl-72b", + "display_name": "qwen2.5-vl-72b", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "cost": { - "input": 0.2, - "output": 0.2 } }, { - "id": "llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "display_name": "Llama 3.1 8B Instruct", + "id": "qwen3-0.6b", + "name": "qwen3-0.6b", + "display_name": "qwen3-0.6b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-1.7b", + "name": "qwen3-1.7b", + "display_name": "qwen3-1.7b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-14b", + "name": "qwen3-14b", + "display_name": "qwen3-14b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-235b-a22b", + "name": "qwen3-235b-a22b", + "display_name": "qwen3-235b-a22b", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 16384 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "cost": { - "input": 0.2, - "output": 0.2 } }, { - "id": "mistral-nemo-instruct-2407", - "name": "Mistral Nemo Instruct 2407", - "display_name": "Mistral Nemo Instruct 2407", + "id": "qwen3-235b-a22b-instruct-2507", + "name": "qwen3-235b-a22b-instruct-2507", + "display_name": "qwen3-235b-a22b-instruct-2507", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 8192 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2024-07-25", - "last_updated": "2024-07-25", - "cost": { - "input": 0.2, - "output": 0.2 } }, { - "id": "mistral-small-3.2-24b-instruct-2506", - "name": "Mistral Small 3.2 24B Instruct 2506", - "display_name": "Mistral Small 3.2 24B Instruct 2506", + "id": "qwen3-235b-a22b-thinking-2507", + "name": "qwen3-235b-a22b-thinking-2507", + "display_name": "qwen3-235b-a22b-thinking-2507", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 8192 - }, - "temperature": true, "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "qwen3-30b-a3b", + "name": "qwen3-30b-a3b", + "display_name": "qwen3-30b-a3b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-30b-a3b-instruct-2507", + "name": "qwen3-30b-a3b-instruct-2507", + "display_name": "qwen3-30b-a3b-instruct-2507", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-30b-a3b-thinking-2507", + "name": "qwen3-30b-a3b-thinking-2507", + "display_name": "qwen3-30b-a3b-thinking-2507", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-32b", + "name": "qwen3-32b", + "display_name": "qwen3-32b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-4b", + "name": "qwen3-4b", + "display_name": "qwen3-4b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen3-8b", + "name": "qwen3-8b", + "display_name": "qwen3-8b", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-06-01", - "last_updated": "2025-06-01", - "cost": { - "input": 0.15, - "output": 0.35 } }, { "id": "qwen3-coder-30b-a3b-instruct", - "name": "Qwen3 Coder 30B A3B Instruct", - "display_name": "Qwen3 Coder 30B A3B Instruct", + "name": "qwen3-coder-30b-a3b-instruct", + "display_name": "qwen3-coder-30b-a3b-instruct", "modalities": { "input": [ "text" @@ -29879,28 +29805,15 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 8192 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "cost": { - "input": 0.2, - "output": 0.8 } }, { - "id": "llama-3.3-70b-instruct", - "name": "Llama 3.3 70B Instruct", - "display_name": "Llama 3.3 70B Instruct", + "id": "qwen3-coder-480b-a35b-instruct", + "name": "qwen3-coder-480b-a35b-instruct", + "display_name": "qwen3-coder-480b-a35b-instruct", "modalities": { "input": [ "text" @@ -29909,28 +29822,15 @@ "text" ] }, - "limit": { - "context": 100000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-01-15", - "last_updated": "2025-01-15", - "cost": { - "input": 0.9, - "output": 0.9 } }, { - "id": "deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "display_name": "DeepSeek R1 Distill Llama 70B", + "id": "qwen3-coder-flash", + "name": "qwen3-coder-flash", + "display_name": "qwen3-coder-flash", "modalities": { "input": [ "text" @@ -29939,60 +29839,32 @@ "text" ] }, - "limit": { - "context": 32000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-01-20", - "last_updated": "2025-01-20", - "cost": { - "input": 0.9, - "output": 0.9 + "supported": false } }, { - "id": "voxtral-small-24b-2507", - "name": "Voxtral Small 24B 2507", - "display_name": "Voxtral Small 24B 2507", + "id": "qwen3-coder-plus", + "name": "qwen3-coder-plus", + "display_name": "qwen3-coder-plus", "modalities": { "input": [ - "text", - "audio" + "text" ], "output": [ "text" ] }, - "limit": { - "context": 32000, - "output": 8192 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-07-01", - "last_updated": "2025-07-01", - "cost": { - "input": 0.15, - "output": 0.35 } }, { - "id": "gpt-oss-120b", - "name": "GPT-OSS 120B", - "display_name": "GPT-OSS 120B", + "id": "qwen3-coder-plus-2025-07-22", + "name": "qwen3-coder-plus-2025-07-22", + "display_name": "qwen3-coder-plus-2025-07-22", "modalities": { "input": [ "text" @@ -30001,68 +29873,33 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 8192 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2024-01-01", - "last_updated": "2024-01-01", - "cost": { - "input": 0.15, - "output": 0.6 } }, { - "id": "gemma-3-27b-it", - "name": "Gemma 3 27B IT", - "display_name": "Gemma 3 27B IT", + "id": "qwen3-embedding-0.6b", + "name": "qwen3-embedding-0.6b", + "display_name": "qwen3-embedding-0.6b", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, - "limit": { - "context": 40000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "release_date": "2025-03-01", - "last_updated": "2025-03-01", - "cost": { - "input": 0.25, - "output": 0.5 } - } - ] - }, - "amazon-bedrock": { - "id": "amazon-bedrock", - "name": "Amazon Bedrock", - "display_name": "Amazon Bedrock", - "doc": "https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html", - "models": [ + }, { - "id": "cohere.command-r-plus-v1:0", - "name": "Command R+", - "display_name": "Command R+", - "modalities": { + "id": "qwen3-embedding-4b", + "name": "qwen3-embedding-4b", + "display_name": "qwen3-embedding-4b", + "modalities": { "input": [ "text" ], @@ -30070,29 +29907,15 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-04", - "release_date": "2024-04-04", - "last_updated": "2024-04-04", - "cost": { - "input": 3, - "output": 15 } }, { - "id": "anthropic.claude-v2", - "name": "Claude 2", - "display_name": "Claude 2", + "id": "qwen3-embedding-8b", + "name": "qwen3-embedding-8b", + "display_name": "qwen3-embedding-8b", "modalities": { "input": [ "text" @@ -30101,381 +29924,213 @@ "text" ] }, - "limit": { - "context": 100000, - "output": 4096 - }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": false, - "knowledge": "2023-08", - "release_date": "2023-07-11", - "last_updated": "2023-07-11", - "cost": { - "input": 8, - "output": 24 } }, { - "id": "anthropic.claude-3-7-sonnet-20250219-v1:0", - "name": "Claude Sonnet 3.7", - "display_name": "Claude Sonnet 3.7", + "id": "qwen3-max", + "name": "qwen3-max-preview", + "display_name": "qwen3-max-preview", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 200000, - "output": 8192 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 } }, { - "id": "anthropic.claude-sonnet-4-20250514-v1:0", - "name": "Claude Sonnet 4", - "display_name": "Claude Sonnet 4", + "id": "qwen3-max-preview", + "name": "qwen3-max-preview", + "display_name": "qwen3-max-preview", "modalities": { "input": [ "text", "image" ], "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 - } - }, - { - "id": "meta.llama3-2-11b-instruct-v1:0", - "name": "Llama 3.2 11B Instruct", - "display_name": "Llama 3.2 11B Instruct", - "modalities": { - "input": [ "text", "image" - ], - "output": [ - "text" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "cost": { - "input": 0.16, - "output": 0.16 } }, { - "id": "anthropic.claude-3-haiku-20240307-v1:0", - "name": "Claude Haiku 3", - "display_name": "Claude Haiku 3", + "id": "qwen3-next-80b-a3b-instruct", + "name": "qwen3-next-80b-a3b-instruct", + "display_name": "qwen3-next-80b-a3b-instruct", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 200000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-02", - "release_date": "2024-03-13", - "last_updated": "2024-03-13", - "cost": { - "input": 0.25, - "output": 1.25 } }, { - "id": "meta.llama3-2-90b-instruct-v1:0", - "name": "Llama 3.2 90B Instruct", - "display_name": "Llama 3.2 90B Instruct", + "id": "qwen3-next-80b-a3b-thinking", + "name": "qwen3-next-80b-a3b-thinking", + "display_name": "qwen3-next-80b-a3b-thinking", "modalities": { "input": [ "text", "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "cost": { - "input": 0.72, - "output": 0.72 + "supported": true, + "default": true } }, { - "id": "meta.llama3-2-1b-instruct-v1:0", - "name": "Llama 3.2 1B Instruct", - "display_name": "Llama 3.2 1B Instruct", + "id": "qwen3-reranker-0.6b", + "name": "qwen3-reranker-0.6b", + "display_name": "qwen3-reranker-0.6b", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 131000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "cost": { - "input": 0.1, - "output": 0.1 } }, { - "id": "anthropic.claude-v2:1", - "name": "Claude 2.1", - "display_name": "Claude 2.1", + "id": "qwen3-reranker-4b", + "name": "qwen3-reranker-4b", + "display_name": "qwen3-reranker-4b", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 200000, - "output": 4096 - }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": false, - "knowledge": "2023-08", - "release_date": "2023-11-21", - "last_updated": "2023-11-21", - "cost": { - "input": 8, - "output": 24 } }, { - "id": "cohere.command-light-text-v14", - "name": "Command Light", - "display_name": "Command Light", + "id": "qwen3-reranker-8b", + "name": "qwen3-reranker-8b", + "display_name": "qwen3-reranker-8b", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ - "text" + "text", + "image" ] }, - "limit": { - "context": 4096, - "output": 4096 - }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-08", - "release_date": "2023-11-01", - "last_updated": "2023-11-01", - "cost": { - "input": 0.3, - "output": 0.6 } }, { - "id": "ai21.jamba-1-5-large-v1:0", - "name": "Jamba 1.5 Large", - "display_name": "Jamba 1.5 Large", + "id": "qwen3-vl-235b-a22b-instruct", + "name": "qwen3-vl-235b-a22b-instruct", + "display_name": "qwen3-vl-235b-a22b-instruct", "modalities": { "input": [ - "text" + "text", + "image", + "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, - "limit": { - "context": 256000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-08", - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "cost": { - "input": 2, - "output": 8 } }, { - "id": "meta.llama3-3-70b-instruct-v1:0", - "name": "Llama 3.3 70B Instruct", - "display_name": "Llama 3.3 70B Instruct", + "id": "qwen3-vl-235b-a22b-thinking", + "name": "qwen3-vl-235b-a22b-thinking", + "display_name": "qwen3-vl-235b-a22b-thinking", "modalities": { "input": [ - "text" + "text", + "image", + "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-12-06", - "last_updated": "2024-12-06", - "cost": { - "input": 0.72, - "output": 0.72 + "supported": true, + "default": true } }, { - "id": "anthropic.claude-3-opus-20240229-v1:0", - "name": "Claude Opus 3", - "display_name": "Claude Opus 3", + "id": "qwen3-vl-30b-a3b-instruct", + "name": "qwen3-vl-30b-a3b-instruct", + "display_name": "qwen3-vl-30b-a3b-instruct", "modalities": { "input": [ "text", - "image" + "image", + "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, - "limit": { - "context": 200000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2023-08", - "release_date": "2024-02-29", - "last_updated": "2024-02-29", - "cost": { - "input": 15, - "output": 75 } }, { - "id": "amazon.nova-pro-v1:0", - "name": "Nova Pro", - "display_name": "Nova Pro", + "id": "qwen3-vl-30b-a3b-thinking", + "name": "qwen3-vl-30b-a3b-thinking", + "display_name": "qwen3-vl-30b-a3b-thinking", "modalities": { "input": [ "text", @@ -30483,196 +30138,186 @@ "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, - "limit": { - "context": 300000, - "output": 8192 - }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "cost": { - "input": 0.8, - "output": 3.2, - "cache_read": 0.2 + "supported": true, + "default": true } }, { - "id": "meta.llama3-1-8b-instruct-v1:0", - "name": "Llama 3.1 8B Instruct", - "display_name": "Llama 3.1 8B Instruct", + "id": "qwen3-vl-plus", + "name": "qwen3-vl-plus", + "display_name": "qwen3-vl-plus", "modalities": { "input": [ - "text" + "text", + "image", + "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "cost": { - "input": 0.22, - "output": 0.22 } }, { - "id": "anthropic.claude-3-5-sonnet-20240620-v1:0", - "name": "Claude Sonnet 3.5", - "display_name": "Claude Sonnet 3.5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, + "id": "sonar", + "name": "sonar", + "display_name": "sonar", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2024-06-20", - "last_updated": "2024-06-20", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 } }, { - "id": "anthropic.claude-haiku-4-5-20251001-v1:0", - "name": "Claude Haiku 4.5", - "display_name": "Claude Haiku 4.5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, + "id": "sonar-reasoning", + "name": "sonar-reasoning", + "display_name": "sonar-reasoning", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-02-28", - "release_date": "2025-10-15", - "last_updated": "2025-10-15", - "cost": { - "input": 1, - "output": 5, - "cache_read": 0.1, - "cache_write": 1.25 + "supported": false } }, { - "id": "cohere.command-r-v1:0", - "name": "Command R", - "display_name": "Command R", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "id": "sonar-reasoning-pro", + "name": "sonar-reasoning-pro", + "display_name": "sonar-reasoning-pro", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-04", - "release_date": "2024-03-11", - "last_updated": "2024-03-11", - "cost": { - "input": 0.5, - "output": 1.5 } }, { - "id": "amazon.nova-micro-v1:0", - "name": "Nova Micro", - "display_name": "Nova Micro", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 128000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, + "id": "sora-2", + "name": "sora-2", + "display_name": "sora-2", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": false, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "cost": { - "input": 0.035, - "output": 0.14, - "cache_read": 0.00875 } }, { - "id": "meta.llama3-1-70b-instruct-v1:0", - "name": "Llama 3.1 70B Instruct", - "display_name": "Llama 3.1 70B Instruct", + "id": "sora-2-hd", + "name": "sora-2-hd", + "display_name": "sora-2-hd", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "sora-2-pro", + "name": "sora-2-pro", + "display_name": "sora-2-pro", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "step-2-16k", + "name": "step-2", + "display_name": "step-2", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "stepfun-ai/step3", + "name": "stepfun-ai/step3", + "display_name": "stepfun-ai/step3", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "tao-8k", + "name": "tao-8k", + "display_name": "tao-8k", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-ada-001", + "name": "text-ada-001", + "display_name": "text-ada-001", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-babbage-001", + "name": "text-babbage-001", + "display_name": "text-babbage-001", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-curie-001", + "name": "text-curie-001", + "display_name": "text-curie-001", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-davinci-002", + "name": "text-davinci", + "display_name": "text-davinci", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-davinci-003", + "name": "text-davinci", + "display_name": "text-davinci", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-davinci-edit-001", + "name": "text-davinci-edit-001", + "display_name": "text-davinci-edit-001", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-embedding-004", + "name": "text-embedding-004", + "display_name": "text-embedding-004", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-embedding-3-large", + "name": "text-embedding-3-large", + "display_name": "text-embedding-3-large", "modalities": { "input": [ "text" @@ -30681,29 +30326,15 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "cost": { - "input": 0.72, - "output": 0.72 } }, { - "id": "meta.llama3-70b-instruct-v1:0", - "name": "Llama 3 70B Instruct", - "display_name": "Llama 3 70B Instruct", + "id": "text-embedding-3-small", + "name": "text-embedding-3-small", + "display_name": "text-embedding-3-small", "modalities": { "input": [ "text" @@ -30712,29 +30343,15 @@ "text" ] }, - "limit": { - "context": 8192, - "output": 2048 - }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "cost": { - "input": 2.65, - "output": 3.5 } }, { - "id": "deepseek.r1-v1:0", - "name": "DeepSeek-R1", - "display_name": "DeepSeek-R1", + "id": "text-embedding-ada-002", + "name": "text-embedding-ada-002", + "display_name": "text-embedding-ada-002", "modalities": { "input": [ "text" @@ -30743,64 +30360,32 @@ "text" ] }, - "limit": { - "context": 128000, - "output": 32768 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": false, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2025-05-29", - "cost": { - "input": 1.35, - "output": 5.4 + "supported": false } }, { - "id": "anthropic.claude-3-5-sonnet-20241022-v2:0", - "name": "Claude Sonnet 3.5 v2", - "display_name": "Claude Sonnet 3.5 v2", + "id": "text-embedding-v1", + "name": "text-embedding-v1", + "display_name": "text-embedding-v1", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, - "limit": { - "context": 200000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 } }, { - "id": "cohere.command-text-v14", - "name": "Command", - "display_name": "Command", + "id": "text-embedding-v4", + "name": "text-embedding-v4", + "display_name": "text-embedding-v4", "modalities": { "input": [ "text" @@ -30809,422 +30394,457 @@ "text" ] }, - "limit": { - "context": 4096, - "output": 4096 - }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-08", - "release_date": "2023-11-01", - "last_updated": "2023-11-01", - "cost": { - "input": 1.5, - "output": 2 } }, { - "id": "anthropic.claude-opus-4-20250514-v1:0", - "name": "Claude Opus 4", - "display_name": "Claude Opus 4", + "id": "text-moderation-007", + "name": "text-moderation", + "display_name": "text-moderation", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-moderation-latest", + "name": "text-moderation", + "display_name": "text-moderation", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-moderation-stable", + "name": "text-moderation", + "display_name": "text-moderation", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "text-search-ada-doc-001", + "name": "text-search-ada-doc-001", + "display_name": "text-search-ada-doc-001", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "tts-1", + "name": "tts-1", + "display_name": "tts-1", "modalities": { "input": [ - "text", - "image" + "audio" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 200000, - "output": 32000 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "supported": false } }, { - "id": "anthropic.claude-sonnet-4-5-20250929-v1:0", - "name": "Claude Sonnet 4.5", - "display_name": "Claude Sonnet 4.5", + "id": "tts-1-1106", + "name": "tts-1", + "display_name": "tts-1", "modalities": { "input": [ - "text", - "image" + "audio" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 200000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "supported": false } }, { - "id": "meta.llama3-2-3b-instruct-v1:0", - "name": "Llama 3.2 3B Instruct", - "display_name": "Llama 3.2 3B Instruct", + "id": "tts-1-hd", + "name": "tts-1-hd", + "display_name": "tts-1-hd", "modalities": { "input": [ - "text" + "audio" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 131000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-12", - "release_date": "2024-09-25", - "last_updated": "2024-09-25", - "cost": { - "input": 0.15, - "output": 0.15 } }, { - "id": "anthropic.claude-instant-v1", - "name": "Claude Instant", - "display_name": "Claude Instant", + "id": "tts-1-hd-1106", + "name": "tts-1-hd", + "display_name": "tts-1-hd", "modalities": { "input": [ - "text" + "audio" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 100000, - "output": 4096 - }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": false, - "knowledge": "2023-08", - "release_date": "2023-03-01", - "last_updated": "2023-03-01", - "cost": { - "input": 0.8, - "output": 2.4 } }, { - "id": "amazon.nova-premier-v1:0", - "name": "Nova Premier", - "display_name": "Nova Premier", + "id": "unsloth/gemma-3-12b-it", + "name": "gemma-3-12b", + "display_name": "gemma-3-12b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "unsloth/gemma-3-27b-it", + "name": "gemma-3-27b", + "display_name": "gemma-3-27b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "veo-2.0-generate-001", + "name": "veo-2.0-generate-001", + "display_name": "veo-2.0-generate-001", "modalities": { "input": [ - "text", - "image", "video" ], "output": [ - "text" + "video" ] }, - "limit": { - "context": 1000000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", - "cost": { - "input": 2.5, - "output": 12.5 + "supported": false } }, { - "id": "anthropic.claude-opus-4-1-20250805-v1:0", - "name": "Claude Opus 4.1", - "display_name": "Claude Opus 4.1", + "id": "veo-3", + "name": "veo3", + "display_name": "veo3", "modalities": { "input": [ "text", - "image" + "image", + "audio", + "video" ], "output": [ - "text" + "text", + "image", + "audio", + "video" ] }, - "limit": { - "context": 200000, - "output": 32000 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "supported": false } }, { - "id": "meta.llama4-scout-17b-instruct-v1:0", - "name": "Llama 4 Scout 17B Instruct", - "display_name": "Llama 4 Scout 17B Instruct", + "id": "veo-3.0-generate-preview", + "name": "veo-3.0-generate-preview", + "display_name": "veo-3.0-generate-preview", "modalities": { "input": [ "text", - "image" + "image", + "video" ], "output": [ - "text" + "text", + "image", + "video" ] }, - "limit": { - "context": 3500000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", - "cost": { - "input": 0.17, - "output": 0.66 } }, { - "id": "ai21.jamba-1-5-mini-v1:0", - "name": "Jamba 1.5 Mini", - "display_name": "Jamba 1.5 Mini", + "id": "veo3", + "name": "veo3", + "display_name": "veo3", "modalities": { "input": [ - "text" + "text", + "image", + "audio", + "video" ], "output": [ - "text" + "text", + "image", + "audio", + "video" ] }, - "limit": { - "context": 256000, - "output": 4096 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": false, - "open_weights": true, - "knowledge": "2024-08", - "release_date": "2024-08-15", - "last_updated": "2024-08-15", - "cost": { - "input": 0.2, - "output": 0.4 } }, { - "id": "meta.llama3-8b-instruct-v1:0", - "name": "Llama 3 8B Instruct", - "display_name": "Llama 3 8B Instruct", + "id": "veo3.1", + "name": "veo3.1", + "display_name": "veo3.1", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "web-sora-2", + "name": "web-sora-2", + "display_name": "web-sora-2", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "web-sora-2-pro", + "name": "web-sora-2-pro", + "display_name": "web-sora-2-pro", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "whisper-1", + "name": "whisper-1", + "display_name": "whisper-1", "modalities": { "input": [ - "text" + "audio" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 8192, - "output": 2048 + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "whisper-large-v3", + "name": "whisper-large", + "display_name": "whisper-large", + "modalities": { + "input": [ + "audio" + ], + "output": [ + "audio" + ] }, - "temperature": true, "tool_call": false, "reasoning": { "supported": false + } + }, + { + "id": "whisper-large-v3-turbo", + "name": "whisper-large-v3-turbo", + "display_name": "whisper-large-v3-turbo", + "modalities": { + "input": [ + "audio" + ], + "output": [ + "audio" + ] }, - "attachment": false, - "open_weights": true, - "knowledge": "2023-03", - "release_date": "2024-07-23", - "last_updated": "2024-07-23", - "cost": { - "input": 0.3, - "output": 0.6 + "tool_call": false, + "reasoning": { + "supported": false } }, { - "id": "anthropic.claude-3-sonnet-20240229-v1:0", - "name": "Claude Sonnet 3", - "display_name": "Claude Sonnet 3", + "id": "yi-large", + "name": "yi-large", + "display_name": "yi-large", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "yi-large-rag", + "name": "yi-large-rag", + "display_name": "yi-large-rag", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "yi-large-turbo", + "name": "yi-large-turbo", + "display_name": "yi-large-turbo", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "yi-lightning", + "name": "yi-lightning", + "display_name": "yi-lightning", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "yi-medium", + "name": "yi-medium", + "display_name": "yi-medium", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "yi-vl-plus", + "name": "yi-vl-plus", + "display_name": "yi-vl-plus", + "tool_call": false, + "reasoning": { + "supported": false + } + } + ] + }, + "fireworks-ai": { + "id": "fireworks-ai", + "name": "Fireworks AI", + "display_name": "Fireworks AI", + "api": "https://api.fireworks.ai/inference/v1/", + "doc": "https://fireworks.ai/docs/", + "models": [ + { + "id": "accounts/fireworks/models/deepseek-r1-0528", + "name": "Deepseek R1 05/28", + "display_name": "Deepseek R1 05/28", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 4096 + "context": 160000, + "output": 16384 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2023-08", - "release_date": "2024-03-04", - "last_updated": "2024-03-04", + "attachment": false, + "open_weights": true, + "knowledge": "2025-05", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", "cost": { "input": 3, - "output": 15 + "output": 8 } }, { - "id": "meta.llama4-maverick-17b-instruct-v1:0", - "name": "Llama 4 Maverick 17B Instruct", - "display_name": "Llama 4 Maverick 17B Instruct", + "id": "accounts/fireworks/models/deepseek-v3p1", + "name": "DeepSeek V3.1", + "display_name": "DeepSeek V3.1", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 1000000, - "output": 16384 + "context": 163840, + "output": 163840 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true, + "attachment": false, "open_weights": true, - "knowledge": "2024-08", - "release_date": "2025-04-05", - "last_updated": "2025-04-05", + "knowledge": "2025-07", + "release_date": "2025-08-21", + "last_updated": "2025-08-21", "cost": { - "input": 0.24, - "output": 0.97 + "input": 0.56, + "output": 1.68 } }, { - "id": "amazon.nova-lite-v1:0", - "name": "Nova Lite", - "display_name": "Nova Lite", + "id": "accounts/fireworks/models/deepseek-v3-0324", + "name": "Deepseek V3 03-24", + "display_name": "Deepseek V3 03-24", "modalities": { "input": [ - "text", - "image", - "video" + "text" ], "output": [ "text" ] }, "limit": { - "context": 300000, - "output": 8192 + "context": 160000, + "output": 16384 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": true, - "open_weights": false, + "attachment": false, + "open_weights": true, "knowledge": "2024-10", - "release_date": "2024-12-03", - "last_updated": "2024-12-03", + "release_date": "2025-03-24", + "last_updated": "2025-03-24", "cost": { - "input": 0.06, - "output": 0.24, - "cache_read": 0.015 + "input": 0.9, + "output": 0.9 } }, { - "id": "anthropic.claude-3-5-haiku-20241022-v1:0", - "name": "Claude Haiku 3.5", - "display_name": "Claude Haiku 3.5", + "id": "accounts/fireworks/models/kimi-k2-instruct", + "name": "Kimi K2 Instruct", + "display_name": "Kimi K2 Instruct", "modalities": { "input": [ "text" @@ -31234,38 +30854,28 @@ ] }, "limit": { - "context": 200000, - "output": 8192 + "context": 128000, + "output": 16384 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-07", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", + "attachment": false, + "open_weights": true, + "knowledge": "2024-10", + "release_date": "2025-07-11", + "last_updated": "2025-07-11", "cost": { - "input": 0.8, - "output": 4, - "cache_read": 0.08, - "cache_write": 1 + "input": 1, + "output": 3 } - } - ] - }, - "cerebras": { - "id": "cerebras", - "name": "Cerebras", - "display_name": "Cerebras", - "doc": "https://inference-docs.cerebras.ai/models/overview", - "models": [ + }, { - "id": "qwen-3-235b-a22b-instruct-2507", - "name": "Qwen 3 235B Instruct", - "display_name": "Qwen 3 235B Instruct", + "id": "accounts/fireworks/models/qwen3-235b-a22b", + "name": "Qwen3 235B-A22B", + "display_name": "Qwen3 235B-A22B", "modalities": { "input": [ "text" @@ -31275,28 +30885,29 @@ ] }, "limit": { - "context": 131000, - "output": 32000 + "context": 128000, + "output": 16384 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": false, "open_weights": true, "knowledge": "2025-04", - "release_date": "2025-07-22", - "last_updated": "2025-07-22", + "release_date": "2025-04-29", + "last_updated": "2025-04-29", "cost": { - "input": 0.6, - "output": 1.2 + "input": 0.22, + "output": 0.88 } }, { - "id": "qwen-3-coder-480b", - "name": "Qwen 3 Coder 480B", - "display_name": "Qwen 3 Coder 480B", + "id": "accounts/fireworks/models/gpt-oss-20b", + "name": "GPT OSS 20B", + "display_name": "GPT OSS 20B", "modalities": { "input": [ "text" @@ -31306,26 +30917,26 @@ ] }, "limit": { - "context": 131000, - "output": 32000 + "context": 131072, + "output": 32768 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", "cost": { - "input": 2, - "output": 2 + "input": 0.05, + "output": 0.2 } }, { - "id": "gpt-oss-120b", + "id": "accounts/fireworks/models/gpt-oss-120b", "name": "GPT OSS 120B", "display_name": "GPT OSS 120B", "modalities": { @@ -31351,59 +30962,48 @@ "release_date": "2025-08-05", "last_updated": "2025-08-05", "cost": { - "input": 0.25, - "output": 0.69 + "input": 0.15, + "output": 0.6 } - } - ] - }, - "cherryin": { - "id": "cherryin", - "name": "cherryin", - "display_name": "cherryin", - "models": [ + }, { - "id": "anthropic/claude-3.7-sonnet", - "name": "Anthropic: Claude 3.7 Sonnet", - "display_name": "Anthropic: Claude 3.7 Sonnet", + "id": "accounts/fireworks/models/glm-4p5-air", + "name": "GLM 4.5 Air", + "display_name": "GLM 4.5 Air", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 64000 + "context": 131072, + "output": 131072 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-01", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-08-01", + "last_updated": "2025-08-01", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.22, + "output": 0.88 } }, { - "id": "anthropic/claude-opus-4", - "name": "Anthropic: Claude Opus 4", - "display_name": "Anthropic: Claude Opus 4", + "id": "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct", + "name": "Qwen3 Coder 480B A35B Instruct", + "display_name": "Qwen3 Coder 480B A35B Instruct", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -31411,34 +31011,29 @@ ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 256000, + "output": 32768 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "attachment": false, + "open_weights": true, + "release_date": "2025-07-22", + "last_updated": "2025-07-22", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.45, + "output": 1.8 } }, { - "id": "anthropic/claude-opus-4.1", - "name": "Anthropic: Claude Opus 4.1", - "display_name": "Anthropic: Claude Opus 4.1", + "id": "accounts/fireworks/models/glm-4p5", + "name": "GLM 4.5", + "display_name": "GLM 4.5", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -31446,34 +31041,40 @@ ] }, "limit": { - "context": 200000, - "output": 32000 + "context": 131072, + "output": 131072 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "input": 0.55, + "output": 2.19 } - }, + } + ] + }, + "llama": { + "id": "llama", + "name": "Llama", + "display_name": "Llama", + "api": "https://api.llama.com/compat/v1/", + "doc": "https://llama.developer.meta.com/docs/models", + "models": [ { - "id": "anthropic/claude-sonnet-4", - "name": "Anthropic: Claude Sonnet 4", - "display_name": "Anthropic: Claude Sonnet 4", + "id": "llama-3.3-8b-instruct", + "name": "Llama-3.3-8B-Instruct", + "display_name": "Llama-3.3-8B-Instruct", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -31481,31 +31082,28 @@ ] }, "limit": { - "context": 1000000, - "output": 64000 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 0, + "output": 0 } }, { - "id": "anthropic/claude-sonnet-4.5", - "name": "Anthropic: Claude Sonnet 4.5", - "display_name": "Anthropic: Claude Sonnet 4.5", + "id": "llama-4-maverick-17b-128e-instruct-fp8", + "name": "Llama-4-Maverick-17B-128E-Instruct-FP8", + "display_name": "Llama-4-Maverick-17B-128E-Instruct-FP8", "modalities": { "input": [ "text", @@ -31516,40 +31114,28 @@ ] }, "limit": { - "context": 1000000, - "output": 64000 + "context": 128000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", + "open_weights": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 - } - }, - { - "id": "bytedance/seed-oss-36b-instruct", - "name": "ByteDance: Seed OSS 36B Instruct", - "display_name": "ByteDance: Seed OSS 36B Instruct", - "tool_call": false, - "reasoning": { - "supported": false + "input": 0, + "output": 0 } }, { - "id": "deepseek/deepseek-r1-0528", - "name": "DeepSeek: R1 0528", - "display_name": "DeepSeek: R1 0528", + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "display_name": "Llama-3.3-70B-Instruct", "modalities": { "input": [ "text" @@ -31559,24 +31145,32 @@ ] }, "limit": { - "context": 163840, - "output": 163840 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false + "attachment": true, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "cost": { + "input": 0, + "output": 0 + } }, { - "id": "deepseek/deepseek-v3.1", - "name": "DeepSeek V3.1", - "display_name": "DeepSeek V3.1", + "id": "llama-4-scout-17b-16e-instruct-fp8", + "name": "Llama-4-Scout-17B-16E-Instruct-FP8", + "display_name": "Llama-4-Scout-17B-16E-Instruct-FP8", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" @@ -31584,28 +31178,27 @@ }, "limit": { "context": 128000, - "output": 8192 + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false, - "open_weights": false, - "knowledge": "2024-07", - "release_date": "2025-08-20", - "last_updated": "2025-08-26", + "attachment": true, + "open_weights": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", "cost": { "input": 0, "output": 0 } }, { - "id": "deepseek/deepseek-v3.1-fast", - "name": "DeepSeek: DeepSeek V3.1 (free)", - "display_name": "DeepSeek: DeepSeek V3.1 (free)", + "id": "groq-llama-4-maverick-17b-128e-instruct", + "name": "Groq-Llama-4-Maverick-17B-128E-Instruct", + "display_name": "Groq-Llama-4-Maverick-17B-128E-Instruct", "modalities": { "input": [ "text" @@ -31615,20 +31208,28 @@ ] }, "limit": { - "context": 163800 + "context": 128000, + "output": 4096 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false + "attachment": true, + "open_weights": true, + "knowledge": "2025-01", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "cost": { + "input": 0, + "output": 0 + } }, { - "id": "deepseek/deepseek-v3.1-terminus", - "name": "DeepSeek: DeepSeek V3.1 Terminus", - "display_name": "DeepSeek: DeepSeek V3.1 Terminus", + "id": "cerebras-llama-4-scout-17b-16e-instruct", + "name": "Cerebras-Llama-4-Scout-17B-16E-Instruct", + "display_name": "Cerebras-Llama-4-Scout-17B-16E-Instruct", "modalities": { "input": [ "text" @@ -31638,29 +31239,28 @@ ] }, "limit": { - "context": 163840, - "output": 163840 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false, + "attachment": true, "open_weights": true, - "knowledge": "2025-07", - "release_date": "2025-09-22", - "last_updated": "2025-09-22", + "knowledge": "2025-01", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", "cost": { - "input": 0.27, - "output": 1 + "input": 0, + "output": 0 } }, { - "id": "deepseek/deepseek-v3.2-exp", - "name": "DeepSeek: DeepSeek V3.2 Exp", - "display_name": "DeepSeek: DeepSeek V3.2 Exp", + "id": "cerebras-llama-4-maverick-17b-128e-instruct", + "name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", + "display_name": "Cerebras-Llama-4-Maverick-17B-128E-Instruct", "modalities": { "input": [ "text" @@ -31670,166 +31270,190 @@ ] }, "limit": { - "context": 163840 + "context": 128000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false - }, + "attachment": true, + "open_weights": true, + "knowledge": "2025-01", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "cost": { + "input": 0, + "output": 0 + } + } + ] + }, + "scaleway": { + "id": "scaleway", + "name": "Scaleway", + "display_name": "Scaleway", + "api": "https://api.scaleway.ai/v1", + "doc": "https://www.scaleway.com/en/docs/generative-apis/", + "models": [ { - "id": "google/gemini-2.5-flash", - "name": "Google: Gemini 2.5 Flash", - "display_name": "Google: Gemini 2.5 Flash", + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "display_name": "Qwen3 235B A22B Instruct 2507", "modalities": { "input": [ - "image", - "text", - "audio" + "text" ], "output": [ "text" ] }, "limit": { - "context": 1048576, - "output": 65535 + "context": 260000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2025-01", - "release_date": "2025-07-17", - "last_updated": "2025-07-17", + "open_weights": true, + "release_date": "2025-07-01", + "last_updated": "2025-07-01", "cost": { - "input": 0.3, - "output": 2.5, - "cache_read": 0.0375 + "input": 0.75, + "output": 2.25 } }, { - "id": "google/gemini-2.5-flash-image", - "name": "Google: Gemini 2.5 Flash Image (Nano Banana)", - "display_name": "Google: Gemini 2.5 Flash Image (Nano Banana)", + "id": "pixtral-12b-2409", + "name": "Pixtral 12B 2409", + "display_name": "Pixtral 12B 2409", "modalities": { "input": [ - "image", - "text" + "text", + "image" ], "output": [ - "image", "text" ] }, "limit": { - "context": 32768, - "output": 8192 + "context": 128000, + "output": 4096 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": true, + "open_weights": true, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "cost": { + "input": 0.2, + "output": 0.2 + } }, { - "id": "google/gemini-2.5-flash-image-preview", - "name": "Google: Gemini 2.5 Flash Image Preview (Nano Banana)", - "display_name": "Google: Gemini 2.5 Flash Image Preview (Nano Banana)", + "id": "llama-3.1-8b-instruct", + "name": "Llama 3.1 8B Instruct", + "display_name": "Llama 3.1 8B Instruct", "modalities": { "input": [ - "image", "text" ], "output": [ - "image", "text" ] }, "limit": { - "context": 32768, - "output": 8192 + "context": 128000, + "output": 16384 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2025-01-01", + "last_updated": "2025-01-01", + "cost": { + "input": 0.2, + "output": 0.2 + } }, { - "id": "google/gemini-2.5-flash-lite", - "name": "Google: Gemini 2.5 Flash Lite", - "display_name": "Google: Gemini 2.5 Flash Lite", + "id": "mistral-nemo-instruct-2407", + "name": "Mistral Nemo Instruct 2407", + "display_name": "Mistral Nemo Instruct 2407", "modalities": { "input": [ - "image", - "text", - "audio" + "text" ], "output": [ "text" ] }, "limit": { - "context": 1048576, - "output": 65535 + "context": 128000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": true + "attachment": true, + "open_weights": true, + "release_date": "2024-07-25", + "last_updated": "2024-07-25", + "cost": { + "input": 0.2, + "output": 0.2 + } }, { - "id": "google/gemini-2.5-pro", - "name": "Google: Gemini 2.5 Pro", - "display_name": "Google: Gemini 2.5 Pro", + "id": "mistral-small-3.2-24b-instruct-2506", + "name": "Mistral Small 3.2 24B Instruct (2506)", + "display_name": "Mistral Small 3.2 24B Instruct (2506)", "modalities": { "input": [ - "image", "text", - "audio" + "image" ], "output": [ "text" ] }, "limit": { - "context": 1048576, - "output": 65536 + "context": 128000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-01", - "release_date": "2025-03-20", - "last_updated": "2025-06-05", + "attachment": false, + "open_weights": true, + "release_date": "2025-06-20", + "last_updated": "2025-06-20", "cost": { - "input": 1.25, - "output": 10, - "cache_read": 0.31 + "input": 0.15, + "output": 0.35 } }, { - "id": "inclusionai/ling-1t", - "name": "inclusionAI: Ling-1T", - "display_name": "inclusionAI: Ling-1T", + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3-Coder 30B-A3B Instruct", + "display_name": "Qwen3-Coder 30B-A3B Instruct", "modalities": { "input": [ "text" @@ -31839,20 +31463,28 @@ ] }, "limit": { - "context": 131072, - "output": 131072 + "context": 128000, + "output": 8192 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-04", + "last_updated": "2025-04", + "cost": { + "input": 0.2, + "output": 0.8 + } }, { - "id": "minimaxai/minimax-m1-80k", - "name": "MiniMax: MiniMax M1", - "display_name": "MiniMax: MiniMax M1", + "id": "llama-3.3-70b-instruct", + "name": "Llama-3.3-70B-Instruct", + "display_name": "Llama-3.3-70B-Instruct", "modalities": { "input": [ "text" @@ -31862,55 +31494,61 @@ ] }, "limit": { - "context": 1000000, - "output": 40000 + "context": 100000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false + "attachment": true, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "cost": { + "input": 0.9, + "output": 0.9 + } }, { - "id": "moonshotai/kimi-k2-0905", - "name": "MoonshotAI: Kimi K2 0905", - "display_name": "MoonshotAI: Kimi K2 0905", + "id": "whisper-large-v3", + "name": "Whisper Large v3", + "display_name": "Whisper Large v3", "modalities": { "input": [ - "text" + "audio" ], "output": [ "text" ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 0, + "output": 4096 }, "temperature": false, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false }, "attachment": false, "open_weights": true, - "knowledge": "2024-10", - "release_date": "2025-09-05", + "knowledge": "2023-09", + "release_date": "2023-09-01", "last_updated": "2025-09-05", "cost": { - "input": 0.6, - "output": 2.5 + "input": 0.003, + "output": 0 } }, { - "id": "openai/gpt-4.1", - "name": "OpenAI: GPT-4.1", - "display_name": "OpenAI: GPT-4.1", + "id": "deepseek-r1-distill-llama-70b", + "name": "DeepSeek R1 Distill Llama 70B", + "display_name": "DeepSeek R1 Distill Llama 70B", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -31918,65 +31556,62 @@ ] }, "limit": { - "context": 1047576, - "output": 32768 + "context": 32000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", + "attachment": false, + "open_weights": true, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-01-20", "cost": { - "input": 2, - "output": 8, - "cache_read": 0.5 + "input": 0.9, + "output": 0.9 } }, { - "id": "openai/gpt-4.1-mini", - "name": "OpenAI: GPT-4.1 Mini", - "display_name": "OpenAI: GPT-4.1 Mini", + "id": "voxtral-small-24b-2507", + "name": "Voxtral Small 24B 2507", + "display_name": "Voxtral Small 24B 2507", "modalities": { "input": [ - "image", - "text" + "text", + "audio" ], "output": [ "text" ] }, "limit": { - "context": 1047576, - "output": 32768 + "context": 32000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", + "open_weights": true, + "release_date": "2025-07-01", + "last_updated": "2025-07-01", "cost": { - "input": 0.4, - "output": 1.6, - "cache_read": 0.1 + "input": 0.15, + "output": 0.35 } }, { - "id": "openai/gpt-4.1-nano", - "name": "OpenAI: GPT-4.1 Nano", - "display_name": "OpenAI: GPT-4.1 Nano", + "id": "gpt-oss-120b", + "name": "GPT-OSS 120B", + "display_name": "GPT-OSS 120B", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -31984,56 +31619,29 @@ ] }, "limit": { - "context": 1047576, - "output": 32768 + "context": 128000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": true - }, - { - "id": "openai/gpt-5", - "name": "OpenAI: GPT-5", - "display_name": "OpenAI: GPT-5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 400000, - "output": 128000 - }, - "temperature": false, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, "attachment": true, - "open_weights": false, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", + "open_weights": true, + "release_date": "2024-01-01", + "last_updated": "2024-01-01", "cost": { - "input": 1.25, - "output": 10 + "input": 0.15, + "output": 0.6 } }, { - "id": "openai/gpt-5-chat", - "name": "OpenAI: GPT-5 Chat", - "display_name": "OpenAI: GPT-5 Chat", + "id": "bge-multilingual-gemma2", + "name": "BGE Multilingual Gemma2", + "display_name": "BGE Multilingual Gemma2", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -32041,28 +31649,27 @@ ] }, "limit": { - "context": 128000, - "output": 16384 + "context": 8191, + "output": 3072 }, "temperature": false, "tool_call": false, "reasoning": { "supported": false }, - "attachment": true, + "attachment": false, "open_weights": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", + "release_date": "2024-07-26", + "last_updated": "2025-06-15", "cost": { - "input": 1.25, - "output": 10 + "input": 0.13, + "output": 0 } }, { - "id": "openai/gpt-5-mini", - "name": "OpenAI: GPT-5 Mini", - "display_name": "OpenAI: GPT-5 Mini", + "id": "gemma-3-27b-it", + "name": "Gemma-3-27B-IT", + "display_name": "Gemma-3-27B-IT", "modalities": { "input": [ "text", @@ -32073,10 +31680,10 @@ ] }, "limit": { - "context": 400000, - "output": 128000 + "context": 40000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, @@ -32084,51 +31691,57 @@ }, "attachment": true, "open_weights": false, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", + "knowledge": "2024-12", + "release_date": "2024-12-01", + "last_updated": "2025-09-05", "cost": { "input": 0.25, - "output": 2 + "output": 0.5 } - }, + } + ] + }, + "amazon-bedrock": { + "id": "amazon-bedrock", + "name": "Amazon Bedrock", + "display_name": "Amazon Bedrock", + "doc": "https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html", + "models": [ { - "id": "openai/gpt-5-nano", - "name": "OpenAI: GPT-5 Nano", - "display_name": "OpenAI: GPT-5 Nano", + "id": "cohere.command-r-plus-v1:0", + "name": "Command R+", + "display_name": "Command R+", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 400000, - "output": 128000 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-10-01", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", + "attachment": false, + "open_weights": true, + "knowledge": "2024-04", + "release_date": "2024-04-04", + "last_updated": "2024-04-04", "cost": { - "input": 0.05, - "output": 0.4 + "input": 3, + "output": 15 } }, { - "id": "openai/gpt-oss-120b", - "name": "OpenAI: gpt-oss-120b", - "display_name": "OpenAI: gpt-oss-120b", + "id": "anthropic.claude-v2", + "name": "Claude 2", + "display_name": "Claude 2", "modalities": { "input": [ "text" @@ -32138,59 +31751,62 @@ ] }, "limit": { - "context": 131072, - "output": 131072 + "context": 100000, + "output": 4096 }, - "temperature": false, - "tool_call": true, + "temperature": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": false, - "open_weights": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "open_weights": false, + "knowledge": "2023-08", + "release_date": "2023-07-11", + "last_updated": "2023-07-11", "cost": { - "input": 0.072, - "output": 0.28 + "input": 8, + "output": 24 } }, { - "id": "openai/gpt-oss-20b", - "name": "OpenAI: gpt-oss-20b", - "display_name": "OpenAI: gpt-oss-20b", + "id": "anthropic.claude-3-7-sonnet-20250219-v1:0", + "name": "Claude Sonnet 3.7", + "display_name": "Claude Sonnet 3.7", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 131072, - "output": 32768 + "context": 200000, + "output": 8192 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false, - "open_weights": true, - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", "cost": { - "input": 0.05, - "output": 0.2 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "openai/o1", - "name": "OpenAI: o1", - "display_name": "OpenAI: o1", + "id": "anthropic.claude-sonnet-4-20250514-v1:0", + "name": "Claude Sonnet 4", + "display_name": "Claude Sonnet 4", "modalities": { "input": [ "text", @@ -32202,19 +31818,30 @@ }, "limit": { "context": 200000, - "output": 100000 + "output": 64000 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true }, - "attachment": true + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, { - "id": "openai/o1-mini", - "name": "OpenAI: o1-mini", - "display_name": "OpenAI: o1-mini", + "id": "qwen.qwen3-coder-30b-a3b-v1:0", + "name": "Qwen3 Coder 30B A3B Instruct", + "display_name": "Qwen3 Coder 30B A3B Instruct", "modalities": { "input": [ "text" @@ -32224,109 +31851,124 @@ ] }, "limit": { - "context": 128000, - "output": 65536 + "context": 262144, + "output": 131072 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": false, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "cost": { + "input": 0.15, + "output": 0.6 + } }, { - "id": "openai/o4-mini", - "name": "OpenAI: o4 Mini", - "display_name": "OpenAI: o4 Mini", + "id": "meta.llama3-2-11b-instruct-v1:0", + "name": "Llama 3.2 11B Instruct", + "display_name": "Llama 3.2 11B Instruct", "modalities": { "input": [ - "image", - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 200000, - "output": 100000 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": true, - "open_weights": false, - "knowledge": "2024-06", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", "cost": { - "input": 1.1, - "output": 4.4, - "cache_read": 0.28 + "input": 0.16, + "output": 0.16 } }, { - "id": "qwen/qwen3-235b-a22b-instruct-2507", - "name": "Qwen: Qwen3 235B A22B Instruct 2507", - "display_name": "Qwen: Qwen3 235B A22B Instruct 2507", + "id": "anthropic.claude-3-haiku-20240307-v1:0", + "name": "Claude Haiku 3", + "display_name": "Claude Haiku 3", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 200000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": true, + "open_weights": false, + "knowledge": "2024-02", + "release_date": "2024-03-13", + "last_updated": "2024-03-13", + "cost": { + "input": 0.25, + "output": 1.25 + } }, { - "id": "qwen/qwen3-235b-a22b-thinking-2507", - "name": "Qwen: Qwen3 235B A22B Thinking 2507", - "display_name": "Qwen: Qwen3 235B A22B Thinking 2507", + "id": "meta.llama3-2-90b-instruct-v1:0", + "name": "Llama 3.2 90B Instruct", + "display_name": "Llama 3.2 90B Instruct", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false, + "attachment": true, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", "cost": { - "input": 0.078, - "output": 0.312 + "input": 0.72, + "output": 0.72 } }, { - "id": "qwen/qwen3-30b-a3b-instruct-2507", - "name": "Qwen: Qwen3 30B A3B Instruct 2507", - "display_name": "Qwen: Qwen3 30B A3B Instruct 2507", + "id": "meta.llama3-2-1b-instruct-v1:0", + "name": "Llama 3.2 1B Instruct", + "display_name": "Llama 3.2 1B Instruct", "modalities": { "input": [ "text" @@ -32336,28 +31978,28 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 131000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": false }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", "cost": { - "input": 0.2, - "output": 0.8 + "input": 0.1, + "output": 0.1 } }, { - "id": "qwen/qwen3-30b-a3b-thinking-2507", - "name": "Qwen: Qwen3 30B A3B Thinking 2507", - "display_name": "Qwen: Qwen3 30B A3B Thinking 2507", + "id": "anthropic.claude-v2:1", + "name": "Claude 2.1", + "display_name": "Claude 2.1", "modalities": { "input": [ "text" @@ -32367,21 +32009,28 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 200000, + "output": 4096 }, - "temperature": false, - "tool_call": true, + "temperature": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false + "attachment": false, + "open_weights": false, + "knowledge": "2023-08", + "release_date": "2023-11-21", + "last_updated": "2023-11-21", + "cost": { + "input": 8, + "output": 24 + } }, { - "id": "qwen/qwen3-8b", - "name": "Qwen: Qwen3 8B", - "display_name": "Qwen: Qwen3 8B", + "id": "deepseek.v3-v1:0", + "name": "DeepSeek-V3.1", + "display_name": "DeepSeek-V3.1", "modalities": { "input": [ "text" @@ -32391,21 +32040,29 @@ ] }, "limit": { - "context": 128000, - "output": 20000 + "context": 163840, + "output": 81920 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": false + "attachment": false, + "open_weights": true, + "knowledge": "2024-07", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "cost": { + "input": 0.58, + "output": 1.68 + } }, { - "id": "qwen/qwen3-coder", - "name": "Qwen: Qwen3 Coder 480B A35B", - "display_name": "Qwen: Qwen3 Coder 480B A35B", + "id": "cohere.command-light-text-v14", + "name": "Command Light", + "display_name": "Command Light", "modalities": { "input": [ "text" @@ -32415,28 +32072,28 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 4096, + "output": 4096 }, - "temperature": false, - "tool_call": true, + "temperature": true, + "tool_call": false, "reasoning": { "supported": false }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", + "knowledge": "2023-08", + "release_date": "2023-11-01", + "last_updated": "2023-11-01", "cost": { "input": 0.3, - "output": 1.2 + "output": 0.6 } }, { - "id": "qwen/qwen3-coder-30b-a3b-instruct", - "name": "Qwen: Qwen3 Coder 30B A3B Instruct", - "display_name": "Qwen: Qwen3 Coder 30B A3B Instruct", + "id": "ai21.jamba-1-5-large-v1:0", + "name": "Jamba 1.5 Large", + "display_name": "Jamba 1.5 Large", "modalities": { "input": [ "text" @@ -32446,29 +32103,28 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 256000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false - }, - { - "id": "qwen/qwen3-coder-480b-a35b-instruct", - "name": "Qwen: Qwen3 Coder 480B A35B Instruct", - "display_name": "Qwen: Qwen3 Coder 480B A35B Instruct", - "tool_call": false, - "reasoning": { - "supported": false + "attachment": false, + "open_weights": true, + "knowledge": "2024-08", + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "cost": { + "input": 2, + "output": 8 } }, { - "id": "qwen/qwen3-embedding-0.6b", - "name": "qwen/qwen3-embedding-0.6b", - "display_name": "qwen/qwen3-embedding-0.6b", + "id": "meta.llama3-3-70b-instruct-v1:0", + "name": "Llama 3.3 70B Instruct", + "display_name": "Llama 3.3 70B Instruct", "modalities": { "input": [ "text" @@ -32477,49 +32133,95 @@ "text" ] }, - "tool_call": false, + "limit": { + "context": 128000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "cost": { + "input": 0.72, + "output": 0.72 } }, { - "id": "qwen/qwen3-embedding-4b", - "name": "qwen/qwen3-embedding-4b", - "display_name": "qwen/qwen3-embedding-4b", + "id": "anthropic.claude-3-opus-20240229-v1:0", + "name": "Claude Opus 3", + "display_name": "Claude Opus 3", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2023-08", + "release_date": "2024-02-29", + "last_updated": "2024-02-29", + "cost": { + "input": 15, + "output": 75 } }, { - "id": "qwen/qwen3-embedding-8b", - "name": "qwen/qwen3-embedding-8b", - "display_name": "qwen/qwen3-embedding-8b", + "id": "amazon.nova-pro-v1:0", + "name": "Nova Pro", + "display_name": "Nova Pro", "modalities": { "input": [ - "text" + "text", + "image", + "video" ], "output": [ "text" ] }, - "tool_call": false, + "limit": { + "context": 300000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "cost": { + "input": 0.8, + "output": 3.2, + "cache_read": 0.2 } }, { - "id": "qwen/qwen3-next-80b-a3b-instruct", - "name": "Qwen: Qwen3 Next 80B A3B Instruct", - "display_name": "Qwen: Qwen3 Next 80B A3B Instruct", + "id": "meta.llama3-1-8b-instruct-v1:0", + "name": "Llama 3.1 8B Instruct", + "display_name": "Llama 3.1 8B Instruct", "modalities": { "input": [ "text" @@ -32529,28 +32231,28 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 128000, + "output": 4096 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": false }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-09-11", - "last_updated": "2025-09-11", + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", "cost": { - "input": 0.14, - "output": 1.4 + "input": 0.22, + "output": 0.22 } }, { - "id": "qwen/qwen3-next-80b-a3b-thinking", - "name": "Qwen: Qwen3 Next 80B A3B Thinking", - "display_name": "Qwen: Qwen3 Next 80B A3B Thinking", + "id": "qwen.qwen3-32b-v1:0", + "name": "Qwen3 32B (dense)", + "display_name": "Qwen3 32B (dense)", "modalities": { "input": [ "text" @@ -32560,20 +32262,29 @@ ] }, "limit": { - "context": 262144 + "context": 16384, + "output": 16384 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": false + "attachment": false, + "open_weights": true, + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "cost": { + "input": 0.15, + "output": 0.6 + } }, { - "id": "qwen/qwen3-omni-30b-a3b-instruct", - "name": "Qwen: Qwen3 VL 30B A3B Instruct", - "display_name": "Qwen: Qwen3 VL 30B A3B Instruct", + "id": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "Claude Sonnet 3.5", + "display_name": "Claude Sonnet 3.5", "modalities": { "input": [ "text", @@ -32584,20 +32295,30 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 200000, + "output": 8192 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2024-06-20", + "last_updated": "2024-06-20", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } }, { - "id": "qwen/qwen3-omni-30b-a3b-thinking", - "name": "Qwen: Qwen3 VL 30B A3B Thinking", - "display_name": "Qwen: Qwen3 VL 30B A3B Thinking", + "id": "anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "Claude Haiku 4.5", + "display_name": "Claude Haiku 4.5", "modalities": { "input": [ "text", @@ -32608,8 +32329,8 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 200000, + "output": 64000 }, "temperature": true, "tool_call": true, @@ -32617,137 +32338,147 @@ "supported": true, "default": true }, - "attachment": false - }, - { - "id": "qwen/qwen3-reranker-0.6b", - "name": "qwen/qwen3-reranker-0.6b", - "display_name": "qwen/qwen3-reranker-0.6b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen/qwen3-reranker-4b", - "name": "qwen/qwen3-reranker-4b", - "display_name": "qwen/qwen3-reranker-4b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "qwen/qwen3-reranker-8b", - "name": "qwen/qwen3-reranker-8b", - "display_name": "qwen/qwen3-reranker-8b", - "tool_call": false, - "reasoning": { - "supported": false + "attachment": true, + "open_weights": false, + "knowledge": "2025-02-28", + "release_date": "2025-10-15", + "last_updated": "2025-10-15", + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 } }, { - "id": "qwen/qwen3-vl-235b-a22b-instruct", - "name": "Qwen: Qwen3 VL 235B A22B Instruct", - "display_name": "Qwen: Qwen3 VL 235B A22B Instruct", + "id": "cohere.command-r-v1:0", + "name": "Command R", + "display_name": "Command R", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 131072 + "context": 128000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false - }, + "attachment": false, + "open_weights": true, + "knowledge": "2024-04", + "release_date": "2024-03-11", + "last_updated": "2024-03-11", + "cost": { + "input": 0.5, + "output": 1.5 + } + }, { - "id": "qwen/qwen3-vl-235b-a22b-thinking", - "name": "Qwen: Qwen3 VL 235B A22B Thinking", - "display_name": "Qwen: Qwen3 VL 235B A22B Thinking", + "id": "amazon.nova-micro-v1:0", + "name": "Nova Micro", + "display_name": "Nova Micro", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 65536, - "output": 65536 + "context": 128000, + "output": 8192 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false + "attachment": false, + "open_weights": false, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "cost": { + "input": 0.035, + "output": 0.14, + "cache_read": 0.00875 + } }, { - "id": "qwen/qwen3-vl-30b-a3b-instruct", - "name": "Qwen: Qwen3 VL 30B A3B Instruct", - "display_name": "Qwen: Qwen3 VL 30B A3B Instruct", + "id": "meta.llama3-1-70b-instruct-v1:0", + "name": "Llama 3.1 70B Instruct", + "display_name": "Llama 3.1 70B Instruct", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 128000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { "supported": false }, - "attachment": false + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "cost": { + "input": 0.72, + "output": 0.72 + } }, { - "id": "qwen/qwen3-vl-30b-a3b-thinking", - "name": "Qwen: Qwen3 VL 30B A3B Thinking", - "display_name": "Qwen: Qwen3 VL 30B A3B Thinking", + "id": "meta.llama3-70b-instruct-v1:0", + "name": "Llama 3 70B Instruct", + "display_name": "Llama 3 70B Instruct", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 8192, + "output": 2048 }, "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false }, - "attachment": false + "attachment": false, + "open_weights": true, + "knowledge": "2023-12", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "cost": { + "input": 2.65, + "output": 3.5 + } }, { - "id": "tencent/hunyuan-mt-7b", - "name": "Tencent: Hunyuan A13B Instruct", - "display_name": "Tencent: Hunyuan A13B Instruct", + "id": "deepseek.r1-v1:0", + "name": "DeepSeek-R1", + "display_name": "DeepSeek-R1", "modalities": { "input": [ "text" @@ -32757,39 +32488,63 @@ ] }, "limit": { - "context": 32768 + "context": 128000, + "output": 32768 }, - "temperature": false, - "tool_call": false, + "temperature": true, + "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": false + "attachment": false, + "open_weights": false, + "knowledge": "2024-07", + "release_date": "2025-01-20", + "last_updated": "2025-05-29", + "cost": { + "input": 1.35, + "output": 5.4 + } }, { - "id": "x-ai/grok-2-image", - "name": "grok-2", - "display_name": "grok-2", + "id": "anthropic.claude-3-5-sonnet-20241022-v2:0", + "name": "Claude Sonnet 3.5 v2", + "display_name": "Claude Sonnet 3.5 v2", "modalities": { "input": [ "text", "image" ], "output": [ - "text", - "image" + "text" ] }, - "tool_call": false, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "x-ai/grok-3", - "name": "xAI: Grok 3", - "display_name": "xAI: Grok 3", + "id": "cohere.command-text-v14", + "name": "Command", + "display_name": "Command", "modalities": { "input": [ "text" @@ -32799,67 +32554,65 @@ ] }, "limit": { - "context": 131072, - "output": 8192 + "context": 4096, + "output": 4096 }, - "temperature": false, - "tool_call": true, + "temperature": true, + "tool_call": false, "reasoning": { "supported": false }, "attachment": false, - "open_weights": false, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", + "open_weights": true, + "knowledge": "2023-08", + "release_date": "2023-11-01", + "last_updated": "2023-11-01", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.75, - "cache_write": 15 + "input": 1.5, + "output": 2 } }, { - "id": "x-ai/grok-3-mini", - "name": "xAI: Grok 3 Mini", - "display_name": "xAI: Grok 3 Mini", + "id": "anthropic.claude-opus-4-20250514-v1:0", + "name": "Claude Opus 4", + "display_name": "Claude Opus 4", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 131072, - "output": 8192 + "context": 200000, + "output": 32000 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": false, + "attachment": true, "open_weights": false, - "knowledge": "2024-11", - "release_date": "2025-02-17", - "last_updated": "2025-02-17", + "knowledge": "2024-04", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", "cost": { - "input": 0.3, - "output": 0.5, - "cache_read": 0.075, - "cache_write": 0.5 + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } }, { - "id": "x-ai/grok-4", - "name": "xAI: Grok 4", - "display_name": "xAI: Grok 4", + "id": "qwen.qwen3-coder-480b-a35b-v1:0", + "name": "Qwen3 Coder 480B A35B Instruct", + "display_name": "Qwen3 Coder 480B A35B Instruct", "modalities": { "input": [ - "image", "text" ], "output": [ @@ -32867,82 +32620,63 @@ ] }, "limit": { - "context": 256000, - "output": 64000 + "context": 131072, + "output": 65536 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": false, - "open_weights": false, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", + "open_weights": true, + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", "cost": { - "input": 3, - "output": 15, - "cache_read": 0.75, - "cache_write": 15 - } - }, - { - "id": "x-ai/grok-4-fast-non-reasoning", - "name": "x-ai/grok-4-fast-non-reasoning", - "display_name": "x-ai/grok-4-fast-non-reasoning", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "x-ai/grok-4-fast-reasoning", - "name": "x-ai/grok-4-fast-reasoning", - "display_name": "x-ai/grok-4-fast-reasoning", - "tool_call": false, - "reasoning": { - "supported": false + "input": 0.22, + "output": 1.8 } }, { - "id": "x-ai/grok-code-fast-1", - "name": "xAI: Grok Code Fast 1", - "display_name": "xAI: Grok Code Fast 1", + "id": "anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "Claude Sonnet 4.5", + "display_name": "Claude Sonnet 4.5", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 256000, - "output": 10000 + "context": 200000, + "output": 64000 }, - "temperature": false, + "temperature": true, "tool_call": true, "reasoning": { "supported": true, "default": true }, - "attachment": false, + "attachment": true, "open_weights": false, - "knowledge": "2025-08", - "release_date": "2025-08-26", - "last_updated": "2025-08-26", + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", "cost": { - "input": 0.2, - "output": 1.5, - "cache_read": 0.02 + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 } }, { - "id": "z-ai/glm-4.5", - "name": "Z.AI: GLM 4.5", - "display_name": "Z.AI: GLM 4.5", + "id": "meta.llama3-2-3b-instruct-v1:0", + "name": "Llama 3.2 3B Instruct", + "display_name": "Llama 3.2 3B Instruct", "modalities": { "input": [ "text" @@ -32952,29 +32686,28 @@ ] }, "limit": { - "context": 131072, - "output": 131072 + "context": 131000, + "output": 4096 }, "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false }, "attachment": false, "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", + "knowledge": "2023-12", + "release_date": "2024-09-25", + "last_updated": "2024-09-25", "cost": { - "input": 0.6, - "output": 2.2 + "input": 0.15, + "output": 0.15 } }, { - "id": "z-ai/glm-4.5-flash", - "name": "z-ai/glm-4.5-flash", - "display_name": "z-ai/glm-4.5-flash", + "id": "anthropic.claude-instant-v1", + "name": "Claude Instant", + "display_name": "Claude Instant", "modalities": { "input": [ "text" @@ -32983,26 +32716,41 @@ "text" ] }, + "limit": { + "context": 100000, + "output": 4096 + }, + "temperature": true, "tool_call": false, "reasoning": { "supported": false + }, + "attachment": false, + "open_weights": false, + "knowledge": "2023-08", + "release_date": "2023-03-01", + "last_updated": "2023-03-01", + "cost": { + "input": 0.8, + "output": 2.4 } }, { - "id": "z-ai/glm-4.5v", - "name": "Z.AI: GLM 4.5V", - "display_name": "Z.AI: GLM 4.5V", + "id": "amazon.nova-premier-v1:0", + "name": "Nova Premier", + "display_name": "Nova Premier", "modalities": { "input": [ "text", - "image" + "image", + "video" ], "output": [ "text" ] }, "limit": { - "context": 65536, + "context": 1000000, "output": 16384 }, "temperature": true, @@ -33011,31 +32759,32 @@ "supported": true, "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-08-11", - "last_updated": "2025-08-11", + "attachment": true, + "open_weights": false, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", "cost": { - "input": 0.6, - "output": 1.8 + "input": 2.5, + "output": 12.5 } }, { - "id": "z-ai/glm-4.6", - "name": "Z.AI: GLM 4.6", - "display_name": "Z.AI: GLM 4.6", + "id": "anthropic.claude-opus-4-1-20250805-v1:0", + "name": "Claude Opus 4.1", + "display_name": "Claude Opus 4.1", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 202752, - "output": 202752 + "context": 200000, + "output": 32000 }, "temperature": true, "tool_call": true, @@ -33043,67 +32792,2124 @@ "supported": true, "default": true }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-09", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.11 + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 } - } - ] - }, - "doubao": { - "id": "doubao", - "name": "Doubao", - "display_name": "Doubao", - "models": [ + }, { - "id": "deepseek-v3-1-250821", - "name": "DeepSeek V3.1", - "display_name": "DeepSeek V3.1", + "id": "meta.llama4-scout-17b-instruct-v1:0", + "name": "Llama 4 Scout 17B Instruct", + "display_name": "Llama 4 Scout 17B Instruct", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 3500000, + "output": 16384 + }, + "temperature": true, "tool_call": true, "reasoning": { "supported": false + }, + "attachment": true, + "open_weights": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "cost": { + "input": 0.17, + "output": 0.66 } }, { - "id": "deepseek-r1-250120", - "name": "DeepSeek R1", - "display_name": "DeepSeek R1", - "tool_call": false, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "deepseek-r1-distill-qwen-32b-250120", - "name": "DeepSeek R1 Distill Qwen 32B", - "display_name": "DeepSeek R1 Distill Qwen 32B", - "tool_call": false, + "id": "ai21.jamba-1-5-mini-v1:0", + "name": "Jamba 1.5 Mini", + "display_name": "Jamba 1.5 Mini", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, "reasoning": { - "supported": true, - "default": true + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2024-08", + "release_date": "2024-08-15", + "last_updated": "2024-08-15", + "cost": { + "input": 0.2, + "output": 0.4 } }, { - "id": "deepseek-r1-distill-qwen-7b-250120", - "name": "DeepSeek R1 Distill Qwen 7B", - "display_name": "DeepSeek R1 Distill Qwen 7B", + "id": "meta.llama3-8b-instruct-v1:0", + "name": "Llama 3 8B Instruct", + "display_name": "Llama 3 8B Instruct", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 2048 + }, + "temperature": true, "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2023-03", + "release_date": "2024-07-23", + "last_updated": "2024-07-23", + "cost": { + "input": 0.3, + "output": 0.6 } }, { - "id": "deepseek-v3-250324", - "name": "DeepSeek V3", - "display_name": "DeepSeek V3", + "id": "anthropic.claude-3-sonnet-20240229-v1:0", + "name": "Claude Sonnet 3", + "display_name": "Claude Sonnet 3", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 4096 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2023-08", + "release_date": "2024-03-04", + "last_updated": "2024-03-04", + "cost": { + "input": 3, + "output": 15 + } + }, + { + "id": "meta.llama4-maverick-17b-instruct-v1:0", + "name": "Llama 4 Maverick 17B Instruct", + "display_name": "Llama 4 Maverick 17B Instruct", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 16384 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": true, + "knowledge": "2024-08", + "release_date": "2025-04-05", + "last_updated": "2025-04-05", + "cost": { + "input": 0.24, + "output": 0.97 + } + }, + { + "id": "qwen.qwen3-235b-a22b-2507-v1:0", + "name": "Qwen3 235B A22B 2507", + "display_name": "Qwen3 235B A22B 2507", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2024-04", + "release_date": "2025-09-18", + "last_updated": "2025-09-18", + "cost": { + "input": 0.22, + "output": 0.88 + } + }, + { + "id": "amazon.nova-lite-v1:0", + "name": "Nova Lite", + "display_name": "Nova Lite", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 300000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10", + "release_date": "2024-12-03", + "last_updated": "2024-12-03", + "cost": { + "input": 0.06, + "output": 0.24, + "cache_read": 0.015 + } + }, + { + "id": "anthropic.claude-3-5-haiku-20241022-v1:0", + "name": "Claude Haiku 3.5", + "display_name": "Claude Haiku 3.5", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-07", + "release_date": "2024-10-22", + "last_updated": "2024-10-22", + "cost": { + "input": 0.8, + "output": 4, + "cache_read": 0.08, + "cache_write": 1 + } + } + ] + }, + "cerebras": { + "id": "cerebras", + "name": "Cerebras", + "display_name": "Cerebras", + "doc": "https://inference-docs.cerebras.ai/models/overview", + "models": [ + { + "id": "qwen-3-235b-a22b-instruct-2507", + "name": "Qwen 3 235B Instruct", + "display_name": "Qwen 3 235B Instruct", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 32000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "cost": { + "input": 0.6, + "output": 1.2 + } + }, + { + "id": "qwen-3-coder-480b", + "name": "Qwen 3 Coder 480B", + "display_name": "Qwen 3 Coder 480B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131000, + "output": 32000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "cost": { + "input": 2, + "output": 2 + } + }, + { + "id": "gpt-oss-120b", + "name": "GPT OSS 120B", + "display_name": "GPT OSS 120B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 0.25, + "output": 0.69 + } + } + ] + }, + "cherryin": { + "id": "cherryin", + "name": "cherryin", + "display_name": "cherryin", + "models": [ + { + "id": "anthropic/claude-3.7-sonnet", + "name": "Anthropic: Claude 3.7 Sonnet", + "display_name": "Anthropic: Claude 3.7 Sonnet", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 64000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-01", + "release_date": "2025-02-19", + "last_updated": "2025-02-19", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + { + "id": "anthropic/claude-opus-4", + "name": "Anthropic: Claude Opus 4", + "display_name": "Anthropic: Claude Opus 4", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + { + "id": "anthropic/claude-opus-4.1", + "name": "Anthropic: Claude Opus 4.1", + "display_name": "Anthropic: Claude Opus 4.1", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 32000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 15, + "output": 75, + "cache_read": 1.5, + "cache_write": 18.75 + } + }, + { + "id": "anthropic/claude-sonnet-4", + "name": "Anthropic: Claude Sonnet 4", + "display_name": "Anthropic: Claude Sonnet 4", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-03-31", + "release_date": "2025-05-22", + "last_updated": "2025-05-22", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + { + "id": "anthropic/claude-sonnet-4.5", + "name": "Anthropic: Claude Sonnet 4.5", + "display_name": "Anthropic: Claude Sonnet 4.5", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 64000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-07-31", + "release_date": "2025-09-29", + "last_updated": "2025-09-29", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + } + }, + { + "id": "bytedance/seed-oss-36b-instruct", + "name": "ByteDance: Seed OSS 36B Instruct", + "display_name": "ByteDance: Seed OSS 36B Instruct", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek: R1 0528", + "display_name": "DeepSeek: R1 0528", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "deepseek/deepseek-v3.1", + "name": "DeepSeek V3.1", + "display_name": "DeepSeek V3.1", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 8192 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": false, + "knowledge": "2024-07", + "release_date": "2025-08-20", + "last_updated": "2025-08-26", + "cost": { + "input": 0, + "output": 0 + } + }, + { + "id": "deepseek/deepseek-v3.1-fast", + "name": "DeepSeek: DeepSeek V3.1 (free)", + "display_name": "DeepSeek: DeepSeek V3.1 (free)", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163800 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "DeepSeek: DeepSeek V3.1 Terminus", + "display_name": "DeepSeek: DeepSeek V3.1 Terminus", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840, + "output": 163840 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-07", + "release_date": "2025-09-22", + "last_updated": "2025-09-22", + "cost": { + "input": 0.27, + "output": 1 + } + }, + { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek: DeepSeek V3.2 Exp", + "display_name": "DeepSeek: DeepSeek V3.2 Exp", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 163840 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "google/gemini-2.5-flash", + "name": "Google: Gemini 2.5 Flash", + "display_name": "Google: Gemini 2.5 Flash", + "modalities": { + "input": [ + "image", + "text", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65535 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-01", + "release_date": "2025-07-17", + "last_updated": "2025-07-17", + "cost": { + "input": 0.3, + "output": 2.5, + "cache_read": 0.0375 + } + }, + { + "id": "google/gemini-2.5-flash-image", + "name": "Google: Gemini 2.5 Flash Image (Nano Banana)", + "display_name": "Google: Gemini 2.5 Flash Image (Nano Banana)", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "google/gemini-2.5-flash-image-preview", + "name": "Google: Gemini 2.5 Flash Image Preview (Nano Banana)", + "display_name": "Google: Gemini 2.5 Flash Image Preview (Nano Banana)", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "image", + "text" + ] + }, + "limit": { + "context": 32768, + "output": 8192 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "google/gemini-2.5-flash-lite", + "name": "Google: Gemini 2.5 Flash Lite", + "display_name": "Google: Gemini 2.5 Flash Lite", + "modalities": { + "input": [ + "image", + "text", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65535 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true + }, + { + "id": "google/gemini-2.5-pro", + "name": "Google: Gemini 2.5 Pro", + "display_name": "Google: Gemini 2.5 Pro", + "modalities": { + "input": [ + "image", + "text", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 65536 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", + "cost": { + "input": 1.25, + "output": 10, + "cache_read": 0.31 + } + }, + { + "id": "inclusionai/ling-1t", + "name": "inclusionAI: Ling-1T", + "display_name": "inclusionAI: Ling-1T", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "minimaxai/minimax-m1-80k", + "name": "MiniMax: MiniMax M1", + "display_name": "MiniMax: MiniMax M1", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 40000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "moonshotai/kimi-k2-0905", + "name": "MoonshotAI: Kimi K2 0905", + "display_name": "MoonshotAI: Kimi K2 0905", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2024-10", + "release_date": "2025-09-05", + "last_updated": "2025-09-05", + "cost": { + "input": 0.6, + "output": 2.5 + } + }, + { + "id": "openai/gpt-4.1", + "name": "OpenAI: GPT-4.1", + "display_name": "OpenAI: GPT-4.1", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "cost": { + "input": 2, + "output": 8, + "cache_read": 0.5 + } + }, + { + "id": "openai/gpt-4.1-mini", + "name": "OpenAI: GPT-4.1 Mini", + "display_name": "OpenAI: GPT-4.1 Mini", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-04", + "release_date": "2025-04-14", + "last_updated": "2025-04-14", + "cost": { + "input": 0.4, + "output": 1.6, + "cache_read": 0.1 + } + }, + { + "id": "openai/gpt-4.1-nano", + "name": "OpenAI: GPT-4.1 Nano", + "display_name": "OpenAI: GPT-4.1 Nano", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1047576, + "output": 32768 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true + }, + { + "id": "openai/gpt-5", + "name": "OpenAI: GPT-5", + "display_name": "OpenAI: GPT-5", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 1.25, + "output": 10 + } + }, + { + "id": "openai/gpt-5-chat", + "name": "OpenAI: GPT-5 Chat", + "display_name": "OpenAI: GPT-5 Chat", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 16384 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-09-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 1.25, + "output": 10 + } + }, + { + "id": "openai/gpt-5-mini", + "name": "OpenAI: GPT-5 Mini", + "display_name": "OpenAI: GPT-5 Mini", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 0.25, + "output": 2 + } + }, + { + "id": "openai/gpt-5-nano", + "name": "OpenAI: GPT-5 Nano", + "display_name": "OpenAI: GPT-5 Nano", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 400000, + "output": 128000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-10-01", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "cost": { + "input": 0.05, + "output": 0.4 + } + }, + { + "id": "openai/gpt-oss-120b", + "name": "OpenAI: gpt-oss-120b", + "display_name": "OpenAI: gpt-oss-120b", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 0.072, + "output": 0.28 + } + }, + { + "id": "openai/gpt-oss-20b", + "name": "OpenAI: gpt-oss-20b", + "display_name": "OpenAI: gpt-oss-20b", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 32768 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "cost": { + "input": 0.05, + "output": 0.2 + } + }, + { + "id": "openai/o1", + "name": "OpenAI: o1", + "display_name": "OpenAI: o1", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": true + }, + { + "id": "openai/o1-mini", + "name": "OpenAI: o1-mini", + "display_name": "OpenAI: o1-mini", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 65536 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "openai/o4-mini", + "name": "OpenAI: o4 Mini", + "display_name": "OpenAI: o4 Mini", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 200000, + "output": 100000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": false, + "knowledge": "2024-06", + "release_date": "2025-04-16", + "last_updated": "2025-04-16", + "cost": { + "input": 1.1, + "output": 4.4, + "cache_read": 0.28 + } + }, + { + "id": "qwen/qwen3-235b-a22b-instruct-2507", + "name": "Qwen: Qwen3 235B A22B Instruct 2507", + "display_name": "Qwen: Qwen3 235B A22B Instruct 2507", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen: Qwen3 235B A22B Thinking 2507", + "display_name": "Qwen: Qwen3 235B A22B Thinking 2507", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-25", + "last_updated": "2025-07-25", + "cost": { + "input": 0.078, + "output": 0.312 + } + }, + { + "id": "qwen/qwen3-30b-a3b-instruct-2507", + "name": "Qwen: Qwen3 30B A3B Instruct 2507", + "display_name": "Qwen: Qwen3 30B A3B Instruct 2507", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-29", + "last_updated": "2025-07-29", + "cost": { + "input": 0.2, + "output": 0.8 + } + }, + { + "id": "qwen/qwen3-30b-a3b-thinking-2507", + "name": "Qwen: Qwen3 30B A3B Thinking 2507", + "display_name": "Qwen: Qwen3 30B A3B Thinking 2507", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "qwen/qwen3-8b", + "name": "Qwen: Qwen3 8B", + "display_name": "Qwen: Qwen3 8B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 20000 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "qwen/qwen3-coder", + "name": "Qwen: Qwen3 Coder 480B A35B", + "display_name": "Qwen: Qwen3 Coder 480B A35B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "cost": { + "input": 0.3, + "output": 1.2 + } + }, + { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "name": "Qwen: Qwen3 Coder 30B A3B Instruct", + "display_name": "Qwen: Qwen3 Coder 30B A3B Instruct", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "qwen/qwen3-coder-480b-a35b-instruct", + "name": "Qwen: Qwen3 Coder 480B A35B Instruct", + "display_name": "Qwen: Qwen3 Coder 480B A35B Instruct", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-embedding-0.6b", + "name": "qwen/qwen3-embedding-0.6b", + "display_name": "qwen/qwen3-embedding-0.6b", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-embedding-4b", + "name": "qwen/qwen3-embedding-4b", + "display_name": "qwen/qwen3-embedding-4b", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-embedding-8b", + "name": "qwen/qwen3-embedding-8b", + "display_name": "qwen/qwen3-embedding-8b", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen: Qwen3 Next 80B A3B Instruct", + "display_name": "Qwen: Qwen3 Next 80B A3B Instruct", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-09-11", + "last_updated": "2025-09-11", + "cost": { + "input": 0.14, + "output": 1.4 + } + }, + { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen: Qwen3 Next 80B A3B Thinking", + "display_name": "Qwen: Qwen3 Next 80B A3B Thinking", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "qwen/qwen3-omni-30b-a3b-instruct", + "name": "Qwen: Qwen3 VL 30B A3B Instruct", + "display_name": "Qwen: Qwen3 VL 30B A3B Instruct", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "qwen/qwen3-omni-30b-a3b-thinking", + "name": "Qwen: Qwen3 VL 30B A3B Thinking", + "display_name": "Qwen: Qwen3 VL 30B A3B Thinking", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "qwen/qwen3-reranker-0.6b", + "name": "qwen/qwen3-reranker-0.6b", + "display_name": "qwen/qwen3-reranker-0.6b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-reranker-4b", + "name": "qwen/qwen3-reranker-4b", + "display_name": "qwen/qwen3-reranker-4b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-reranker-8b", + "name": "qwen/qwen3-reranker-8b", + "display_name": "qwen/qwen3-reranker-8b", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "qwen/qwen3-vl-235b-a22b-instruct", + "name": "Qwen: Qwen3 VL 235B A22B Instruct", + "display_name": "Qwen: Qwen3 VL 235B A22B Instruct", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "qwen/qwen3-vl-235b-a22b-thinking", + "name": "Qwen: Qwen3 VL 235B A22B Thinking", + "display_name": "Qwen: Qwen3 VL 235B A22B Thinking", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 65536 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "qwen/qwen3-vl-30b-a3b-instruct", + "name": "Qwen: Qwen3 VL 30B A3B Instruct", + "display_name": "Qwen: Qwen3 VL 30B A3B Instruct", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false + }, + { + "id": "qwen/qwen3-vl-30b-a3b-thinking", + "name": "Qwen: Qwen3 VL 30B A3B Thinking", + "display_name": "Qwen: Qwen3 VL 30B A3B Thinking", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "tencent/hunyuan-mt-7b", + "name": "Tencent: Hunyuan A13B Instruct", + "display_name": "Tencent: Hunyuan A13B Instruct", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768 + }, + "temperature": false, + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false + }, + { + "id": "x-ai/grok-2-image", + "name": "grok-2", + "display_name": "grok-2", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text", + "image" + ] + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "x-ai/grok-3", + "name": "xAI: Grok 3", + "display_name": "xAI: Grok 3", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": false, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 15 + } + }, + { + "id": "x-ai/grok-3-mini", + "name": "xAI: Grok 3 Mini", + "display_name": "xAI: Grok 3 Mini", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 8192 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": false, + "knowledge": "2024-11", + "release_date": "2025-02-17", + "last_updated": "2025-02-17", + "cost": { + "input": 0.3, + "output": 0.5, + "cache_read": 0.075, + "cache_write": 0.5 + } + }, + { + "id": "x-ai/grok-4", + "name": "xAI: Grok 4", + "display_name": "xAI: Grok 4", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 64000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": false, + "knowledge": "2025-07", + "release_date": "2025-07-09", + "last_updated": "2025-07-09", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.75, + "cache_write": 15 + } + }, + { + "id": "x-ai/grok-4-fast-non-reasoning", + "name": "x-ai/grok-4-fast-non-reasoning", + "display_name": "x-ai/grok-4-fast-non-reasoning", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "x-ai/grok-4-fast-reasoning", + "name": "x-ai/grok-4-fast-reasoning", + "display_name": "x-ai/grok-4-fast-reasoning", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "x-ai/grok-code-fast-1", + "name": "xAI: Grok Code Fast 1", + "display_name": "xAI: Grok Code Fast 1", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 10000 + }, + "temperature": false, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": false, + "knowledge": "2025-08", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.02 + } + }, + { + "id": "z-ai/glm-4.5", + "name": "Z.AI: GLM 4.5", + "display_name": "Z.AI: GLM 4.5", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", + "cost": { + "input": 0.6, + "output": 2.2 + } + }, + { + "id": "z-ai/glm-4.5-flash", + "name": "z-ai/glm-4.5-flash", + "display_name": "z-ai/glm-4.5-flash", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "z-ai/glm-4.5v", + "name": "Z.AI: GLM 4.5V", + "display_name": "Z.AI: GLM 4.5V", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 65536, + "output": 16384 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-04", + "release_date": "2025-08-11", + "last_updated": "2025-08-11", + "cost": { + "input": 0.6, + "output": 1.8 + } + }, + { + "id": "z-ai/glm-4.6", + "name": "Z.AI: GLM 4.6", + "display_name": "Z.AI: GLM 4.6", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 202752, + "output": 202752 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "knowledge": "2025-09", + "release_date": "2025-09-30", + "last_updated": "2025-09-30", + "cost": { + "input": 0.6, + "output": 2.2, + "cache_read": 0.11 + } + } + ] + }, + "doubao": { + "id": "doubao", + "name": "Doubao", + "display_name": "Doubao", + "models": [ + { + "id": "deepseek-v3-1-250821", + "name": "DeepSeek V3.1", + "display_name": "DeepSeek V3.1", + "tool_call": true, + "reasoning": { + "supported": false + } + }, + { + "id": "deepseek-r1-250120", + "name": "DeepSeek R1", + "display_name": "DeepSeek R1", + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "deepseek-r1-distill-qwen-32b-250120", + "name": "DeepSeek R1 Distill Qwen 32B", + "display_name": "DeepSeek R1 Distill Qwen 32B", + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "deepseek-r1-distill-qwen-7b-250120", + "name": "DeepSeek R1 Distill Qwen 7B", + "display_name": "DeepSeek R1 Distill Qwen 7B", + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "deepseek-v3-250324", + "name": "DeepSeek V3", + "display_name": "DeepSeek V3", "tool_call": true, "reasoning": { "supported": false @@ -33176,6 +34982,15 @@ "name": "Ollama", "display_name": "Ollama", "models": [ + { + "id": "minimax-m2:cloud", + "name": "MiniMax M2", + "display_name": "MiniMax M2", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "alfred:40b", "name": "Alfred 40b", @@ -62061,1585 +63876,436 @@ "id": "yi:6b-v1.5-q4_0", "name": "Yi 6b v1.5 q4_0", "display_name": "Yi 6b v1.5 q4_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:6b-v1.5-q4_1", - "name": "Yi 6b v1.5 q4_1", - "display_name": "Yi 6b v1.5 q4_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:6b-v1.5-q5_0", - "name": "Yi 6b v1.5 q5_0", - "display_name": "Yi 6b v1.5 q5_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:6b-v1.5-q5_1", - "name": "Yi 6b v1.5 q5_1", - "display_name": "Yi 6b v1.5 q5_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:6b-v1.5-q8_0", - "name": "Yi 6b v1.5 q8_0", - "display_name": "Yi 6b v1.5 q8_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b", - "name": "Yi 9b", - "display_name": "Yi 9b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat", - "name": "Yi 9b chat", - "display_name": "Yi 9b chat", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat-v1.5-fp16", - "name": "Yi 9b chat v1.5 fp16", - "display_name": "Yi 9b chat v1.5 fp16", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat-v1.5-q4_0", - "name": "Yi 9b chat v1.5 q4_0", - "display_name": "Yi 9b chat v1.5 q4_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat-v1.5-q4_1", - "name": "Yi 9b chat v1.5 q4_1", - "display_name": "Yi 9b chat v1.5 q4_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat-v1.5-q5_0", - "name": "Yi 9b chat v1.5 q5_0", - "display_name": "Yi 9b chat v1.5 q5_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat-v1.5-q5_1", - "name": "Yi 9b chat v1.5 q5_1", - "display_name": "Yi 9b chat v1.5 q5_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-chat-v1.5-q8_0", - "name": "Yi 9b chat v1.5 q8_0", - "display_name": "Yi 9b chat v1.5 q8_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5", - "name": "Yi 9b v1.5", - "display_name": "Yi 9b v1.5", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5-fp16", - "name": "Yi 9b v1.5 fp16", - "display_name": "Yi 9b v1.5 fp16", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5-q4_0", - "name": "Yi 9b v1.5 q4_0", - "display_name": "Yi 9b v1.5 q4_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5-q4_1", - "name": "Yi 9b v1.5 q4_1", - "display_name": "Yi 9b v1.5 q4_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5-q5_0", - "name": "Yi 9b v1.5 q5_0", - "display_name": "Yi 9b v1.5 q5_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5-q5_1", - "name": "Yi 9b v1.5 q5_1", - "display_name": "Yi 9b v1.5 q5_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:9b-v1.5-q8_0", - "name": "Yi 9b v1.5 q8_0", - "display_name": "Yi 9b v1.5 q8_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:latest", - "name": "Yi Latest", - "display_name": "Yi Latest", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "yi:v1.5", - "name": "Yi V1.5", - "display_name": "Yi V1.5", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:141b", - "name": "Zephyr 141b", - "display_name": "Zephyr 141b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:141b-v0.1", - "name": "Zephyr 141b v0.1", - "display_name": "Zephyr 141b v0.1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:141b-v0.1-fp16", - "name": "Zephyr 141b v0.1 fp16", - "display_name": "Zephyr 141b v0.1 fp16", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:141b-v0.1-q4_0", - "name": "Zephyr 141b v0.1 q4_0", - "display_name": "Zephyr 141b v0.1 q4_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:141b-v0.1-q8_0", - "name": "Zephyr 141b v0.1 q8_0", - "display_name": "Zephyr 141b v0.1 q8_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b", - "name": "Zephyr 7b", - "display_name": "Zephyr 7b", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha", - "name": "Zephyr 7b alpha", - "display_name": "Zephyr 7b alpha", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha-fp16", - "name": "Zephyr 7b alpha fp16", - "display_name": "Zephyr 7b alpha fp16", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha-q4_0", - "name": "Zephyr 7b alpha q4_0", - "display_name": "Zephyr 7b alpha q4_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha-q4_1", - "name": "Zephyr 7b alpha q4_1", - "display_name": "Zephyr 7b alpha q4_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha-q5_0", - "name": "Zephyr 7b alpha q5_0", - "display_name": "Zephyr 7b alpha q5_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha-q5_1", - "name": "Zephyr 7b alpha q5_1", - "display_name": "Zephyr 7b alpha q5_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-alpha-q8_0", - "name": "Zephyr 7b alpha q8_0", - "display_name": "Zephyr 7b alpha q8_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta", - "name": "Zephyr 7b beta", - "display_name": "Zephyr 7b beta", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta-fp16", - "name": "Zephyr 7b beta fp16", - "display_name": "Zephyr 7b beta fp16", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta-q4_0", - "name": "Zephyr 7b beta q4_0", - "display_name": "Zephyr 7b beta q4_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta-q4_1", - "name": "Zephyr 7b beta q4_1", - "display_name": "Zephyr 7b beta q4_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta-q5_0", - "name": "Zephyr 7b beta q5_0", - "display_name": "Zephyr 7b beta q5_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta-q5_1", - "name": "Zephyr 7b beta q5_1", - "display_name": "Zephyr 7b beta q5_1", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:7b-beta-q8_0", - "name": "Zephyr 7b beta q8_0", - "display_name": "Zephyr 7b beta q8_0", - "tool_call": false, - "reasoning": { - "supported": false - } - }, - { - "id": "zephyr:latest", - "name": "Zephyr Latest", - "display_name": "Zephyr Latest", - "tool_call": false, - "reasoning": { - "supported": false - } - } - ] - }, - "ppinfra": { - "id": "ppinfra", - "name": "ppinfra", - "display_name": "ppinfra", - "models": [ - { - "id": "deepseek/deepseek-r1-0528", - "name": "deepseek/deepseek-r1-0528", - "display_name": "deepseek/deepseek-r1-0528" - }, - { - "id": "deepseek/deepseek-v3-0324", - "name": "deepseek/deepseek-v3-0324", - "display_name": "deepseek/deepseek-v3-0324" - }, - { - "id": "deepseek/deepseek-prover-v2-671b", - "name": "deepseek/deepseek-prover-v2-671b", - "display_name": "deepseek/deepseek-prover-v2-671b", - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "meta-llama/llama-3.3-70b-instruct", - "name": "meta-llama/llama-3.3-70b-instruct", - "display_name": "meta-llama/llama-3.3-70b-instruct", - "tool_call": false, - "reasoning": { - "supported": true, - "default": true - } - } - ] - }, - "siliconflow": { - "id": "siliconflow", - "name": "SiliconFlow", - "display_name": "SiliconFlow", - "models": [ - { - "id": "stepfun-ai/step3", - "name": "stepfun-ai/step3", - "display_name": "stepfun-ai/step3", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 8192 - }, - "tool_call": true, - "reasoning": { - "supported": false - } - }, - { - "id": "ascend-tribe/pangu-pro-moe", - "name": "ascend-tribe/pangu-pro-moe", - "display_name": "ascend-tribe/pangu-pro-moe", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 8192 - }, - "tool_call": true, + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "netease-youdao/bce-embedding-base_v1", - "name": "netease-youdao/bce-embedding-base_v1", - "display_name": "netease-youdao/bce-embedding-base_v1", - "modalities": { - "input": [ - "text" - ], - "output": [ - "embedding" - ] - }, - "limit": { - "context": 8192, - "output": 2048 - }, + "id": "yi:6b-v1.5-q4_1", + "name": "Yi 6b v1.5 q4_1", + "display_name": "Yi 6b v1.5 q4_1", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "netease-youdao/bce-reranker-base_v1", - "name": "netease-youdao/bce-reranker-base_v1", - "display_name": "netease-youdao/bce-reranker-base_v1", - "modalities": { - "input": [ - "text" - ], - "output": [ - "score" - ] - }, - "limit": { - "context": 8192, - "output": 2048 - }, + "id": "yi:6b-v1.5-q5_0", + "name": "Yi 6b v1.5 q5_0", + "display_name": "Yi 6b v1.5 q5_0", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "deepseek-ai/deepseek-vl2", - "name": "deepseek-ai/deepseek-vl2", - "display_name": "deepseek-ai/deepseek-vl2", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 8192 - }, - "tool_call": true, + "id": "yi:6b-v1.5-q5_1", + "name": "Yi 6b v1.5 q5_1", + "display_name": "Yi 6b v1.5 q5_1", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "internlm/internlm2_5-7b-chat", - "name": "internlm/internlm2_5-7b-chat", - "display_name": "internlm/internlm2_5-7b-chat", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 8192 - }, - "tool_call": true, + "id": "yi:6b-v1.5-q8_0", + "name": "Yi 6b v1.5 q8_0", + "display_name": "Yi 6b v1.5 q8_0", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "stabilityai/stable-diffusion-xl-base-1.0", - "name": "stabilityai/stable-diffusion-xl-base-1.0", - "display_name": "stabilityai/stable-diffusion-xl-base-1.0", - "modalities": { - "input": [ - "text" - ], - "output": [ - "image" - ] - }, + "id": "yi:9b", + "name": "Yi 9b", + "display_name": "Yi 9b", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "stabilityai/stable-diffusion-3-5-large", - "name": "stabilityai/stable-diffusion-3-5-large", - "display_name": "stabilityai/stable-diffusion-3-5-large", - "modalities": { - "input": [ - "text" - ], - "output": [ - "image" - ] - }, + "id": "yi:9b-chat", + "name": "Yi 9b chat", + "display_name": "Yi 9b chat", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "fishaudio/fish-speech-1.4", - "name": "fishaudio/fish-speech-1.4", - "display_name": "fishaudio/fish-speech-1.4", - "modalities": { - "input": [ - "text" - ], - "output": [ - "audio" - ] - }, + "id": "yi:9b-chat-v1.5-fp16", + "name": "Yi 9b chat v1.5 fp16", + "display_name": "Yi 9b chat v1.5 fp16", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "fishaudio/fish-speech-1.5", - "name": "fishaudio/fish-speech-1.5", - "display_name": "fishaudio/fish-speech-1.5", - "modalities": { - "input": [ - "text" - ], - "output": [ - "audio" - ] - }, + "id": "yi:9b-chat-v1.5-q4_0", + "name": "Yi 9b chat v1.5 q4_0", + "display_name": "Yi 9b chat v1.5 q4_0", "tool_call": false, "reasoning": { "supported": false } - } - ] - }, - "zenmux": { - "id": "zenmux", - "name": "zenmux", - "display_name": "zenmux", - "models": [ + }, { - "id": "anthropic/claude-3.5-haiku", - "name": "Claude Haiku 3.5", - "display_name": "Claude Haiku 3.5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-chat-v1.5-q4_1", + "name": "Yi 9b chat v1.5 q4_1", + "display_name": "Yi 9b chat v1.5 q4_1", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-07-31", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "cost": { - "input": 0.8, - "output": 4, - "cache_read": 0.08, - "cache_write": 1 } }, { - "id": "anthropic/claude-3.5-sonnet", - "name": "Claude Sonnet 3.5 v2", - "display_name": "Claude Sonnet 3.5 v2", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 8192 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-chat-v1.5-q5_0", + "name": "Yi 9b chat v1.5 q5_0", + "display_name": "Yi 9b chat v1.5 q5_0", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04-30", - "release_date": "2024-10-22", - "last_updated": "2024-10-22", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 } }, { - "id": "anthropic/claude-3.7-sonnet", - "name": "Claude Sonnet 3.7", - "display_name": "Claude Sonnet 3.7", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-chat-v1.5-q5_1", + "name": "Yi 9b chat v1.5 q5_1", + "display_name": "Yi 9b chat v1.5 q5_1", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-10-31", - "release_date": "2025-02-19", - "last_updated": "2025-02-19", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "supported": false } }, { - "id": "anthropic/claude-opus-4", - "name": "Claude Opus 4", - "display_name": "Claude Opus 4", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 32000 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-chat-v1.5-q8_0", + "name": "Yi 9b chat v1.5 q8_0", + "display_name": "Yi 9b chat v1.5 q8_0", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "supported": false } }, { - "id": "anthropic/claude-opus-4.1", - "name": "Claude Opus 4.1", - "display_name": "Claude Opus 4.1", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 32000 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-v1.5", + "name": "Yi 9b v1.5", + "display_name": "Yi 9b v1.5", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", - "cost": { - "input": 15, - "output": 75, - "cache_read": 1.5, - "cache_write": 18.75 + "supported": false } }, { - "id": "anthropic/claude-sonnet-4", - "name": "Claude Sonnet 4", - "display_name": "Claude Sonnet 4", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-v1.5-fp16", + "name": "Yi 9b v1.5 fp16", + "display_name": "Yi 9b v1.5 fp16", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-03-31", - "release_date": "2025-05-22", - "last_updated": "2025-05-22", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "supported": false } }, { - "id": "anthropic/claude-sonnet-4.5", - "name": "Claude Sonnet 4.5", - "display_name": "Claude Sonnet 4.5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 64000 - }, - "temperature": true, - "tool_call": true, + "id": "yi:9b-v1.5-q4_0", + "name": "Yi 9b v1.5 q4_0", + "display_name": "Yi 9b v1.5 q4_0", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07-31", - "release_date": "2025-09-29", - "last_updated": "2025-09-29", - "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "supported": false } }, { - "id": "deepseek/deepseek-chat", - "name": "DeepSeek: DeepSeek V3", - "display_name": "DeepSeek: DeepSeek V3", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 163840, - "output": 163840 - }, - "tool_call": true, + "id": "yi:9b-v1.5-q4_1", + "name": "Yi 9b v1.5 q4_1", + "display_name": "Yi 9b v1.5 q4_1", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "deepseek/deepseek-chat-v3.1", - "name": "DeepSeek: DeepSeek V3.1", - "display_name": "DeepSeek: DeepSeek V3.1", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 32768 - }, - "tool_call": true, + "id": "yi:9b-v1.5-q5_0", + "name": "Yi 9b v1.5 q5_0", + "display_name": "Yi 9b v1.5 q5_0", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "deepseek/deepseek-r1-0528", - "name": "DeepSeek: R1 0528", - "display_name": "DeepSeek: R1 0528", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 163840, - "output": 163840 - }, - "tool_call": true, + "id": "yi:9b-v1.5-q5_1", + "name": "Yi 9b v1.5 q5_1", + "display_name": "Yi 9b v1.5 q5_1", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "google/gemini-2.0-flash", - "name": "Google: Gemini 2.0 Flash", - "display_name": "Google: Gemini 2.0 Flash", - "modalities": { - "input": [ - "text", - "image", - "audio" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1048576, - "output": 8192 - }, - "tool_call": true, + "id": "yi:9b-v1.5-q8_0", + "name": "Yi 9b v1.5 q8_0", + "display_name": "Yi 9b v1.5 q8_0", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true + } }, { - "id": "google/gemini-2.0-flash-lite-001", - "name": "Google: Gemini 2.0 Flash Lite", - "display_name": "Google: Gemini 2.0 Flash Lite", - "modalities": { - "input": [ - "text", - "image", - "audio" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1048576, - "output": 8192 - }, - "tool_call": true, + "id": "yi:latest", + "name": "Yi Latest", + "display_name": "Yi Latest", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true + } + }, + { + "id": "yi:v1.5", + "name": "Yi V1.5", + "display_name": "Yi V1.5", + "tool_call": false, + "reasoning": { + "supported": false + } }, { - "id": "google/gemini-2.5-flash", - "name": "Google: Gemini 2.5 Flash", - "display_name": "Google: Gemini 2.5 Flash", - "modalities": { - "input": [ - "image", - "text", - "audio" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1048576, - "output": 65535 - }, - "tool_call": true, + "id": "zephyr:141b", + "name": "Zephyr 141b", + "display_name": "Zephyr 141b", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true + "supported": false + } }, { - "id": "google/gemini-2.5-flash-lite", - "name": "Google: Gemini 2.5 Flash Lite", - "display_name": "Google: Gemini 2.5 Flash Lite", - "modalities": { - "input": [ - "image", - "text", - "audio" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1048576, - "output": 65535 - }, - "tool_call": true, + "id": "zephyr:141b-v0.1", + "name": "Zephyr 141b v0.1", + "display_name": "Zephyr 141b v0.1", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true + "supported": false + } }, { - "id": "google/gemini-2.5-pro", - "name": "Google: Gemini 2.5 Pro", - "display_name": "Google: Gemini 2.5 Pro", - "modalities": { - "input": [ - "image", - "text", - "audio" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1048576, - "output": 65536 - }, - "tool_call": true, + "id": "zephyr:141b-v0.1-fp16", + "name": "Zephyr 141b v0.1 fp16", + "display_name": "Zephyr 141b v0.1 fp16", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true + "supported": false + } }, { - "id": "inclusionai/ling-1t", - "name": "inclusionAI: Ling-1T", - "display_name": "inclusionAI: Ling-1T", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "tool_call": true, + "id": "zephyr:141b-v0.1-q4_0", + "name": "Zephyr 141b v0.1 q4_0", + "display_name": "Zephyr 141b v0.1 q4_0", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "inclusionai/ling-flash-2.0", - "name": "inclusionAI: Ling Flash 2.0", - "display_name": "inclusionAI: Ling Flash 2.0", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "tool_call": true, + "id": "zephyr:141b-v0.1-q8_0", + "name": "Zephyr 141b v0.1 q8_0", + "display_name": "Zephyr 141b v0.1 q8_0", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "inclusionai/ling-mini-2.0", - "name": "inclusionAI: Ling Mini 2.0", - "display_name": "inclusionAI: Ling Mini 2.0", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "tool_call": true, + "id": "zephyr:7b", + "name": "Zephyr 7b", + "display_name": "Zephyr 7b", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "inclusionai/ring-1t", - "name": "inclusionAI: Ring 1T", - "display_name": "inclusionAI: Ring 1T", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "tool_call": true, + "id": "zephyr:7b-alpha", + "name": "Zephyr 7b alpha", + "display_name": "Zephyr 7b alpha", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "inclusionai/ring-flash-2.0", - "name": "inclusionAI: Ring Flash 2.0", - "display_name": "inclusionAI: Ring Flash 2.0", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "tool_call": true, + "id": "zephyr:7b-alpha-fp16", + "name": "Zephyr 7b alpha fp16", + "display_name": "Zephyr 7b alpha fp16", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "inclusionai/ring-mini-2.0", - "name": "inclusionAI: Ring Mini 2.0", - "display_name": "inclusionAI: Ring Mini 2.0", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 131072, - "output": 131072 - }, - "tool_call": true, + "id": "zephyr:7b-alpha-q4_0", + "name": "Zephyr 7b alpha q4_0", + "display_name": "Zephyr 7b alpha q4_0", + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "moonshotai/kimi-k2-0711", - "name": "kimi-k2-0711", - "display_name": "kimi-k2-0711", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "tool_call": true, + "id": "zephyr:7b-alpha-q4_1", + "name": "Zephyr 7b alpha q4_1", + "display_name": "Zephyr 7b alpha q4_1", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "moonshotai/kimi-k2-0905", - "name": "MoonshotAI: Kimi K2 0905", - "display_name": "MoonshotAI: Kimi K2 0905", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 262144, - "output": 262144 - }, - "tool_call": true, + "id": "zephyr:7b-alpha-q5_0", + "name": "Zephyr 7b alpha q5_0", + "display_name": "Zephyr 7b alpha q5_0", + "tool_call": false, "reasoning": { "supported": false } }, { - "id": "openai/gpt-4.1", - "name": "GPT-4.1", - "display_name": "GPT-4.1", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1047576, - "output": 32768 - }, - "temperature": true, - "tool_call": true, + "id": "zephyr:7b-alpha-q5_1", + "name": "Zephyr 7b alpha q5_1", + "display_name": "Zephyr 7b alpha q5_1", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "cost": { - "input": 2, - "output": 8, - "cache_read": 0.5 } }, { - "id": "openai/gpt-4.1-mini", - "name": "GPT-4.1 mini", - "display_name": "GPT-4.1 mini", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1047576, - "output": 32768 - }, - "temperature": true, - "tool_call": true, + "id": "zephyr:7b-alpha-q8_0", + "name": "Zephyr 7b alpha q8_0", + "display_name": "Zephyr 7b alpha q8_0", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "cost": { - "input": 0.4, - "output": 1.6, - "cache_read": 0.1 } }, { - "id": "openai/gpt-4.1-nano", - "name": "GPT-4.1 nano", - "display_name": "GPT-4.1 nano", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1047576, - "output": 32768 - }, - "temperature": true, - "tool_call": true, + "id": "zephyr:7b-beta", + "name": "Zephyr 7b beta", + "display_name": "Zephyr 7b beta", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-04", - "release_date": "2025-04-14", - "last_updated": "2025-04-14", - "cost": { - "input": 0.1, - "output": 0.4, - "cache_read": 0.03 } }, { - "id": "openai/gpt-4o", - "name": "GPT-4o", - "display_name": "GPT-4o", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 128000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "id": "zephyr:7b-beta-fp16", + "name": "Zephyr 7b beta fp16", + "display_name": "Zephyr 7b beta fp16", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2023-09", - "release_date": "2024-05-13", - "last_updated": "2024-08-06", - "cost": { - "input": 2.5, - "output": 10, - "cache_read": 1.25 } }, { - "id": "openai/gpt-4o-mini", - "name": "GPT-4o mini", - "display_name": "GPT-4o mini", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 128000, - "output": 16384 - }, - "temperature": true, - "tool_call": true, + "id": "zephyr:7b-beta-q4_0", + "name": "Zephyr 7b beta q4_0", + "display_name": "Zephyr 7b beta q4_0", + "tool_call": false, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2023-09", - "release_date": "2024-07-18", - "last_updated": "2024-07-18", - "cost": { - "input": 0.15, - "output": 0.6, - "cache_read": 0.08 } }, { - "id": "openai/gpt-5", - "name": "gpt-5", - "display_name": "gpt-5", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 400000, - "output": 128000 - }, - "temperature": false, - "tool_call": true, + "id": "zephyr:7b-beta-q4_1", + "name": "Zephyr 7b beta q4_1", + "display_name": "Zephyr 7b beta q4_1", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-09-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "cost": { - "input": 1.25, - "output": 10, - "cache_read": 0.13 + "supported": false } }, { - "id": "openai/gpt-5-chat", - "name": "gpt-5-chat", - "display_name": "gpt-5-chat" + "id": "zephyr:7b-beta-q5_0", + "name": "Zephyr 7b beta q5_0", + "display_name": "Zephyr 7b beta q5_0", + "tool_call": false, + "reasoning": { + "supported": false + } }, { - "id": "openai/gpt-5-mini", - "name": "gpt-5-mini", - "display_name": "gpt-5-mini", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 400000, - "output": 128000 - }, - "temperature": false, - "tool_call": true, + "id": "zephyr:7b-beta-q5_1", + "name": "Zephyr 7b beta q5_1", + "display_name": "Zephyr 7b beta q5_1", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "cost": { - "input": 0.25, - "output": 2, - "cache_read": 0.03 + "supported": false } }, { - "id": "openai/gpt-5-nano", - "name": "gpt-5-nano", - "display_name": "gpt-5-nano", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 400000, - "output": 128000 - }, - "temperature": false, - "tool_call": true, + "id": "zephyr:7b-beta-q8_0", + "name": "Zephyr 7b beta q8_0", + "display_name": "Zephyr 7b beta q8_0", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "cost": { - "input": 0.05, - "output": 0.4, - "cache_read": 0.01 + "supported": false } }, { - "id": "openai/o4-mini", - "name": "o4-mini", - "display_name": "o4-mini", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 100000 - }, - "temperature": false, - "tool_call": true, + "id": "zephyr:latest", + "name": "Zephyr Latest", + "display_name": "Zephyr Latest", + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2024-05", - "release_date": "2025-04-16", - "last_updated": "2025-04-16", - "cost": { - "input": 1.1, - "output": 4.4, - "cache_read": 0.28 + "supported": false } + } + ] + }, + "ppinfra": { + "id": "ppinfra", + "name": "ppinfra", + "display_name": "ppinfra", + "models": [ + { + "id": "deepseek/deepseek-r1-0528", + "name": "deepseek/deepseek-r1-0528", + "display_name": "deepseek/deepseek-r1-0528" }, { - "id": "qwen/qwen3-235b-a22b-2507", - "name": "Qwen: Qwen3 235B A22B Instruct 2507", - "display_name": "Qwen: Qwen3 235B A22B Instruct 2507", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 262144, - "output": 262144 - }, - "tool_call": true, + "id": "deepseek/deepseek-v3-0324", + "name": "deepseek/deepseek-v3-0324", + "display_name": "deepseek/deepseek-v3-0324" + }, + { + "id": "deepseek/deepseek-prover-v2-671b", + "name": "deepseek/deepseek-prover-v2-671b", + "display_name": "deepseek/deepseek-prover-v2-671b", "reasoning": { "supported": true, "default": true } }, { - "id": "qwen/qwen3-235b-a22b-thinking-2507", - "name": "Qwen: Qwen3 235B A22B Thinking 2507", - "display_name": "Qwen: Qwen3 235B A22B Thinking 2507", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 262144, - "output": 262144 - }, - "tool_call": true, + "id": "meta-llama/llama-3.3-70b-instruct", + "name": "meta-llama/llama-3.3-70b-instruct", + "display_name": "meta-llama/llama-3.3-70b-instruct", + "tool_call": false, "reasoning": { "supported": true, "default": true } - }, + } + ] + }, + "siliconflow": { + "id": "siliconflow", + "name": "SiliconFlow", + "display_name": "SiliconFlow", + "models": [ { - "id": "qwen/qwen3-coder", - "name": "Qwen: Qwen3 Coder 480B A35B", - "display_name": "Qwen: Qwen3 Coder 480B A35B", + "id": "stepfun-ai/step3", + "name": "stepfun-ai/step3", + "display_name": "stepfun-ai/step3", "modalities": { "input": [ "text" @@ -63649,28 +64315,18 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 131072, + "output": 8192 }, "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "qwen/qwen3-coder-plus", - "name": "Qwen: Qwen3 Coder 480B A35B Instruct", - "display_name": "Qwen: Qwen3 Coder 480B A35B Instruct", - "tool_call": false, "reasoning": { "supported": false } }, { - "id": "qwen/qwen3-max", - "name": "Qwen: Qwen3 Max", - "display_name": "Qwen: Qwen3 Max", + "id": "ascend-tribe/pangu-pro-moe", + "name": "ascend-tribe/pangu-pro-moe", + "display_name": "ascend-tribe/pangu-pro-moe", "modalities": { "input": [ "text" @@ -63680,8 +64336,8 @@ ] }, "limit": { - "context": 256000, - "output": 32768 + "context": 131072, + "output": 8192 }, "tool_call": true, "reasoning": { @@ -63689,263 +64345,155 @@ } }, { - "id": "qwen/qwen3-vl-plus", - "name": "Qwen: Qwen3 VL 235B A22B Instruct", - "display_name": "Qwen: Qwen3 VL 235B A22B Instruct", + "id": "netease-youdao/bce-embedding-base_v1", + "name": "netease-youdao/bce-embedding-base_v1", + "display_name": "netease-youdao/bce-embedding-base_v1", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ - "text" + "embedding" ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 8192, + "output": 2048 }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true + "supported": false } }, { - "id": "x-ai/grok-4", - "name": "Grok 4", - "display_name": "Grok 4", + "id": "netease-youdao/bce-reranker-base_v1", + "name": "netease-youdao/bce-reranker-base_v1", + "display_name": "netease-youdao/bce-reranker-base_v1", "modalities": { "input": [ "text" ], "output": [ - "text" + "score" ] }, "limit": { - "context": 256000, - "output": 64000 + "context": 8192, + "output": 2048 }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": false, - "knowledge": "2025-07", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "cost": { - "input": 3, - "output": 15, - "reasoning": 15, - "cache_read": 0.75 + "supported": false } }, { - "id": "x-ai/grok-4-fast", - "name": "Grok 4 Fast", - "display_name": "Grok 4 Fast", + "id": "deepseek-ai/deepseek-vl2", + "name": "deepseek-ai/deepseek-vl2", + "display_name": "deepseek-ai/deepseek-vl2", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 2000000, - "output": 30000 + "context": 131072, + "output": 8192 }, - "temperature": true, "tool_call": true, "reasoning": { - "supported": true, - "default": true - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "cost": { - "input": 0.2, - "output": 0.5, - "cache_read": 0.05 + "supported": false } }, { - "id": "x-ai/grok-4-fast-non-reasoning", - "name": "Grok 4 Fast (Non-Reasoning)", - "display_name": "Grok 4 Fast (Non-Reasoning)", + "id": "internlm/internlm2_5-7b-chat", + "name": "internlm/internlm2_5-7b-chat", + "display_name": "internlm/internlm2_5-7b-chat", "modalities": { "input": [ - "text", - "image" + "text" ], "output": [ "text" ] }, "limit": { - "context": 2000000, - "output": 30000 + "context": 131072, + "output": 8192 }, - "temperature": true, "tool_call": true, "reasoning": { "supported": false - }, - "attachment": true, - "open_weights": false, - "knowledge": "2025-07", - "release_date": "2025-09-19", - "last_updated": "2025-09-19", - "cost": { - "input": 0.2, - "output": 0.5, - "cache_read": 0.05 } }, { - "id": "x-ai/grok-code-fast-1", - "name": "Grok Code Fast 1", - "display_name": "Grok Code Fast 1", + "id": "stabilityai/stable-diffusion-xl-base-1.0", + "name": "stabilityai/stable-diffusion-xl-base-1.0", + "display_name": "stabilityai/stable-diffusion-xl-base-1.0", "modalities": { "input": [ "text" ], "output": [ - "text" + "image" ] }, - "limit": { - "context": 256000, - "output": 10000 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": false, - "knowledge": "2023-10", - "release_date": "2025-08-28", - "last_updated": "2025-08-28", - "cost": { - "input": 0.2, - "output": 1.5, - "cache_read": 0.02 + "supported": false } }, { - "id": "z-ai/glm-4.5", - "name": "GLM-4.5", - "display_name": "GLM-4.5", + "id": "stabilityai/stable-diffusion-3-5-large", + "name": "stabilityai/stable-diffusion-3-5-large", + "display_name": "stabilityai/stable-diffusion-3-5-large", "modalities": { "input": [ "text" ], "output": [ - "text" + "image" ] }, - "limit": { - "context": 131072, - "output": 98304 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.11, - "cache_write": 0 + "supported": false } }, { - "id": "z-ai/glm-4.5-air", - "name": "GLM-4.5-Air", - "display_name": "GLM-4.5-Air", + "id": "fishaudio/fish-speech-1.4", + "name": "fishaudio/fish-speech-1.4", + "display_name": "fishaudio/fish-speech-1.4", "modalities": { "input": [ "text" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 131072, - "output": 98304 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-07-28", - "last_updated": "2025-07-28", - "cost": { - "input": 0.2, - "output": 1.1, - "cache_read": 0.03, - "cache_write": 0 + "supported": false } }, { - "id": "z-ai/glm-4.6", - "name": "GLM-4.6", - "display_name": "GLM-4.6", + "id": "fishaudio/fish-speech-1.5", + "name": "fishaudio/fish-speech-1.5", + "display_name": "fishaudio/fish-speech-1.5", "modalities": { "input": [ "text" ], "output": [ - "text" + "audio" ] }, - "limit": { - "context": 204800, - "output": 131072 - }, - "temperature": true, - "tool_call": true, + "tool_call": false, "reasoning": { - "supported": true, - "default": true - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-04", - "release_date": "2025-09-30", - "last_updated": "2025-09-30", - "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.11, - "cache_write": 0 + "supported": false } } ] @@ -64018,15 +64566,6 @@ "supported": false } }, - { - "id": "allenai/molmo-7b-d", - "name": "AllenAI: Molmo 7B D", - "display_name": "AllenAI: Molmo 7B D", - "tool_call": false, - "reasoning": { - "supported": false - } - }, { "id": "allenai/olmo-2-0325-32b-instruct", "name": "AllenAI: Olmo 2 32B Instruct", @@ -64063,6 +64602,15 @@ "supported": false } }, + { + "id": "openrouter/andromeda-alpha", + "name": "Andromeda Alpha", + "display_name": "Andromeda Alpha", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "anthropic/claude-3-haiku", "name": "Anthropic: Claude 3 Haiku", @@ -64135,6 +64683,15 @@ "supported": false } }, + { + "id": "anthropic/claude-haiku-4.5", + "name": "Anthropic: Claude Haiku 4.5", + "display_name": "Anthropic: Claude Haiku 4.5", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "anthropic/claude-opus-4", "name": "Anthropic: Claude Opus 4", @@ -64405,6 +64962,24 @@ "supported": false } }, + { + "id": "deepcogito/cogito-v2-preview-llama-405b", + "name": "Deep Cogito: Cogito V2 Preview Llama 405B", + "display_name": "Deep Cogito: Cogito V2 Preview Llama 405B", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "deepcogito/cogito-v2-preview-llama-70b", + "name": "Deep Cogito: Cogito V2 Preview Llama 70B", + "display_name": "Deep Cogito: Cogito V2 Preview Llama 70B", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "deepseek/deepseek-prover-v2", "name": "DeepSeek: DeepSeek Prover V2", @@ -64416,8 +64991,8 @@ }, { "id": "deepseek/deepseek-r1-0528-qwen3-8b", - "name": "DeepSeek: Deepseek R1 0528 Qwen3 8B", - "display_name": "DeepSeek: Deepseek R1 0528 Qwen3 8B", + "name": "DeepSeek: DeepSeek R1 0528 Qwen3 8B", + "display_name": "DeepSeek: DeepSeek R1 0528 Qwen3 8B", "tool_call": false, "reasoning": { "supported": false @@ -64459,6 +65034,15 @@ "supported": false } }, + { + "id": "deepseek/deepseek-v3.1-terminus:exacto", + "name": "DeepSeek: DeepSeek V3.1 Terminus (exacto)", + "display_name": "DeepSeek: DeepSeek V3.1 Terminus (exacto)", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "deepseek/deepseek-v3.2-exp", "name": "DeepSeek: DeepSeek V3.2 Exp", @@ -64702,6 +65286,15 @@ "supported": false } }, + { + "id": "ibm-granite/granite-4.0-h-micro", + "name": "IBM: Granite 4.0 Micro", + "display_name": "IBM: Granite 4.0 Micro", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "inception/mercury", "name": "Inception: Mercury", @@ -64729,6 +65322,15 @@ "supported": false } }, + { + "id": "inclusionai/ring-1t", + "name": "inclusionAI: Ring 1T", + "display_name": "inclusionAI: Ring 1T", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "inflection/inflection-3-pi", "name": "Inflection: Inflection 3 Pi", @@ -64748,18 +65350,18 @@ } }, { - "id": "liquid/lfm-3b", - "name": "Liquid: LFM 3B", - "display_name": "Liquid: LFM 3B", + "id": "liquid/lfm-2.2-6b", + "name": "LiquidAI/LFM2-2.6B", + "display_name": "LiquidAI/LFM2-2.6B", "tool_call": false, "reasoning": { "supported": false } }, { - "id": "liquid/lfm-7b", - "name": "Liquid: LFM 7B", - "display_name": "Liquid: LFM 7B", + "id": "liquid/lfm2-8b-a1b", + "name": "LiquidAI/LFM2-8B-A1B", + "display_name": "LiquidAI/LFM2-8B-A1B", "tool_call": false, "reasoning": { "supported": false @@ -65323,6 +65925,15 @@ "supported": false } }, + { + "id": "moonshotai/kimi-k2-0905:exacto", + "name": "MoonshotAI: Kimi K2 0905 (exacto)", + "display_name": "MoonshotAI: Kimi K2 0905 (exacto)", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "morph/morph-v3-fast", "name": "Morph: Morph V3 Fast", @@ -65710,6 +66321,24 @@ "supported": false } }, + { + "id": "openai/gpt-5-image", + "name": "OpenAI: GPT-5 Image", + "display_name": "OpenAI: GPT-5 Image", + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "openai/gpt-5-image-mini", + "name": "OpenAI: GPT-5 Image Mini", + "display_name": "OpenAI: GPT-5 Image Mini", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "openai/gpt-5-mini", "name": "OpenAI: GPT-5 Mini", @@ -65746,6 +66375,15 @@ "supported": false } }, + { + "id": "openai/gpt-oss-120b:exacto", + "name": "OpenAI: gpt-oss-120b (exacto)", + "display_name": "OpenAI: gpt-oss-120b (exacto)", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "openai/gpt-oss-20b", "name": "OpenAI: gpt-oss-20b", @@ -65773,15 +66411,6 @@ "supported": false } }, - { - "id": "openai/o1-mini-2024-09-12", - "name": "OpenAI: o1-mini (2024-09-12)", - "display_name": "OpenAI: o1-mini (2024-09-12)", - "tool_call": false, - "reasoning": { - "supported": false - } - }, { "id": "openai/o1-pro", "name": "OpenAI: o1-pro", @@ -66124,6 +66753,15 @@ "supported": false } }, + { + "id": "qwen/qwen3-coder:exacto", + "name": "Qwen: Qwen3 Coder 480B A35B (exacto)", + "display_name": "Qwen: Qwen3 Coder 480B A35B (exacto)", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "qwen/qwen3-coder-480b-a35b-instruct", "name": "Qwen: Qwen3 Coder 480B A35B Instruct", @@ -66214,6 +66852,15 @@ "supported": false } }, + { + "id": "qwen/qwen3-vl-32b-instruct", + "name": "Qwen: Qwen3 VL 32B Instruct", + "display_name": "Qwen: Qwen3 VL 32B Instruct", + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "qwen/qwen3-vl-8b-instruct", "name": "Qwen: Qwen3 VL 8B Instruct", @@ -66591,6 +67238,15 @@ "reasoning": { "supported": false } + }, + { + "id": "z-ai/glm-4.6:exacto", + "name": "Z.AI: GLM 4.6 (exacto)", + "display_name": "Z.AI: GLM 4.6 (exacto)", + "tool_call": false, + "reasoning": { + "supported": false + } } ] }, @@ -67851,8 +68507,8 @@ ] }, "limit": { - "context": 131072, - "output": 32768 + "context": 163840, + "output": 163840 }, "tool_call": true, "reasoning": { @@ -67914,8 +68570,7 @@ ] }, "limit": { - "context": 163840, - "output": 163840 + "context": 163840 }, "tool_call": true, "reasoning": { @@ -68088,8 +68743,7 @@ ] }, "limit": { - "context": 131072, - "output": 16384 + "context": 131072 }, "tool_call": false, "reasoning": { @@ -68428,7 +69082,8 @@ "modalities": { "input": [ "image", - "text" + "text", + "audio" ], "output": [ "text" @@ -68662,8 +69317,7 @@ ] }, "limit": { - "context": 96000, - "output": 8192 + "context": 131072 }, "tool_call": false, "reasoning": { @@ -68945,6 +69599,46 @@ "supported": false } }, + { + "id": "liquid/lfm-2.2-6b", + "name": "LiquidAI/LFM2-2.6B", + "display_name": "LiquidAI/LFM2-2.6B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768 + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, + { + "id": "liquid/lfm2-8b-a1b", + "name": "LiquidAI/LFM2-8B-A1B", + "display_name": "LiquidAI/LFM2-8B-A1B", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 32768 + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "mancer/weaver", "name": "Mancer: Weaver (alpha)", @@ -69273,8 +69967,7 @@ ] }, "limit": { - "context": 131072, - "output": 2048 + "context": 131072 }, "tool_call": true, "reasoning": { @@ -69681,6 +70374,52 @@ "default": true } }, + { + "id": "minimax/minimax-m2", + "name": "MiniMax: MiniMax M2", + "display_name": "MiniMax: MiniMax M2", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 196608, + "output": 196608 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "minimax/minimax-m2:free", + "name": "MiniMax: MiniMax M2 (free)", + "display_name": "MiniMax: MiniMax M2 (free)", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 204800, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, { "id": "mistralai/codestral-2501", "name": "Mistral: Codestral 2501", @@ -69911,7 +70650,7 @@ ] }, "limit": { - "context": 128000 + "context": 131072 }, "temperature": true, "tool_call": true, @@ -70299,7 +71038,8 @@ ] }, "limit": { - "context": 128000 + "context": 96000, + "output": 96000 }, "temperature": true, "tool_call": true, @@ -70619,8 +71359,8 @@ ] }, "limit": { - "context": 81920, - "output": 38000 + "context": 262144, + "output": 131072 }, "tool_call": false, "reasoning": { @@ -70918,6 +71658,51 @@ "default": true } }, + { + "id": "nvidia/nemotron-nano-12b-v2-vl", + "name": "NVIDIA: Nemotron Nano 12B 2 VL", + "display_name": "NVIDIA: Nemotron Nano 12B 2 VL", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 131072 + }, + "tool_call": false, + "reasoning": { + "supported": true, + "default": true + } + }, + { + "id": "nvidia/nemotron-nano-12b-v2-vl:free", + "name": "NVIDIA: Nemotron Nano 12B 2 VL (free)", + "display_name": "NVIDIA: Nemotron Nano 12B 2 VL (free)", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 128000, + "output": 128000 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + } + }, { "id": "nvidia/nemotron-nano-9b-v2", "name": "NVIDIA: Nemotron Nano 9B V2", @@ -71778,32 +72563,9 @@ } }, { - "id": "openai/o1", - "name": "OpenAI: o1", - "display_name": "OpenAI: o1", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 200000, - "output": 100000 - }, - "tool_call": true, - "reasoning": { - "supported": false - }, - "attachment": true - }, - { - "id": "openai/o1-mini", - "name": "OpenAI: o1-mini", - "display_name": "OpenAI: o1-mini", + "id": "openai/gpt-oss-safeguard-20b", + "name": "OpenAI: gpt-oss-safeguard-20b", + "display_name": "OpenAI: gpt-oss-safeguard-20b", "modalities": { "input": [ "text" @@ -71813,34 +72575,37 @@ ] }, "limit": { - "context": 128000, + "context": 131072, "output": 65536 }, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true } }, { - "id": "openai/o1-mini-2024-09-12", - "name": "OpenAI: o1-mini (2024-09-12)", - "display_name": "OpenAI: o1-mini (2024-09-12)", + "id": "openai/o1", + "name": "OpenAI: o1", + "display_name": "OpenAI: o1", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" ] }, "limit": { - "context": 128000, - "output": 65536 + "context": 200000, + "output": 100000 }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": false - } + }, + "attachment": true }, { "id": "openai/o1-pro", @@ -72080,29 +72845,6 @@ "supported": false } }, - { - "id": "openrouter/andromeda-alpha", - "name": "Andromeda Alpha", - "display_name": "Andromeda Alpha", - "modalities": { - "input": [ - "image", - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 128000, - "output": 128000 - }, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - } - }, { "id": "openrouter/auto", "name": "Auto Router", @@ -72281,8 +73023,7 @@ ] }, "limit": { - "context": 32768, - "output": 16384 + "context": 32768 }, "tool_call": true, "reasoning": { @@ -72861,7 +73602,7 @@ "context": 128000, "output": 20000 }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": true, "default": true @@ -73072,7 +73813,8 @@ ] }, "limit": { - "context": 262144 + "context": 262144, + "output": 262144 }, "tool_call": true, "reasoning": { @@ -73094,8 +73836,8 @@ ] }, "limit": { - "context": 262144, - "output": 262144 + "context": 131072, + "output": 4000 }, "temperature": true, "tool_call": true, @@ -73141,8 +73883,8 @@ ] }, "limit": { - "context": 131072, - "output": 32768 + "context": 0, + "output": 4000 }, "temperature": true, "tool_call": true, @@ -73174,6 +73916,28 @@ "default": true } }, + { + "id": "qwen/qwen3-vl-32b-instruct", + "name": "Qwen: Qwen3 VL 32B Instruct", + "display_name": "Qwen: Qwen3 VL 32B Instruct", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 262144 + }, + "tool_call": false, + "reasoning": { + "supported": false + } + }, { "id": "qwen/qwen3-vl-8b-instruct", "name": "Qwen: Qwen3 VL 8B Instruct", @@ -73483,28 +74247,8 @@ ] }, "limit": { - "context": 32768 - }, - "tool_call": false, - "reasoning": { - "supported": true, - "default": true - } - }, - { - "id": "tencent/hunyuan-a13b-instruct:free", - "name": "Tencent: Hunyuan A13B Instruct (free)", - "display_name": "Tencent: Hunyuan A13B Instruct (free)", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 32768 + "context": 131072, + "output": 131072 }, "tool_call": false, "reasoning": { @@ -74307,6 +75051,23 @@ "default": true } }, + { + "id": "doubao-1.5-pro-32k", + "name": "doubao-1.5-pro-32k", + "display_name": "doubao-1.5-pro-32k", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "tool_call": true, + "reasoning": { + "supported": false + } + }, { "id": "baidu/ernie-4.5-300b-a47b-paddle", "name": "ERNIE 4.5 300B A47B", diff --git a/src/main/events.ts b/src/main/events.ts index 7e072d1bd..f58649c1e 100644 --- a/src/main/events.ts +++ b/src/main/events.ts @@ -34,7 +34,9 @@ export const CONFIG_EVENTS = { OAUTH_LOGIN_SUCCESS: 'config:oauth-login-success', // OAuth登录成功 OAUTH_LOGIN_ERROR: 'config:oauth-login-error', // OAuth登录失败 THEME_CHANGED: 'config:theme-changed', // 主题变更事件 - FONT_SIZE_CHANGED: 'config:font-size-changed' // 字体大小变更事件 + FONT_SIZE_CHANGED: 'config:font-size-changed', // 字体大小变更事件 + DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed', // Default system prompt changed event + CUSTOM_PROMPTS_CHANGED: 'config:custom-prompts-changed' // 自定义提示词变更事件 } // Provider DB(聚合 JSON)相关事件 @@ -110,7 +112,10 @@ export const MCP_EVENTS = { TOOL_CALL_RESULT: 'mcp:tool-call-result', SERVER_STATUS_CHANGED: 'mcp:server-status-changed', CLIENT_LIST_UPDATED: 'mcp:client-list-updated', - INITIALIZED: 'mcp:initialized' // 新增:MCP初始化完成事件 + INITIALIZED: 'mcp:initialized', // 新增:MCP初始化完成事件 + SAMPLING_REQUEST: 'mcp:sampling-request', + SAMPLING_DECISION: 'mcp:sampling-decision', + SAMPLING_CANCELLED: 'mcp:sampling-cancelled' } // 同步相关事件 @@ -118,6 +123,7 @@ export const SYNC_EVENTS = { BACKUP_STARTED: 'sync:backup-started', BACKUP_COMPLETED: 'sync:backup-completed', BACKUP_ERROR: 'sync:backup-error', + BACKUP_STATUS_CHANGED: 'sync:backup-status-changed', IMPORT_STARTED: 'sync:import-started', IMPORT_COMPLETED: 'sync:import-completed', IMPORT_ERROR: 'sync:import-error', diff --git a/src/main/lib/svgSanitizer.ts b/src/main/lib/svgSanitizer.ts index 3d63208fe..d013bec7e 100644 --- a/src/main/lib/svgSanitizer.ts +++ b/src/main/lib/svgSanitizer.ts @@ -304,7 +304,9 @@ export class SVGSanitizer { } // Export singleton instance for easy use -export const svgSanitizer = new SVGSanitizer() +export const svgSanitizer = new SVGSanitizer({ + maxSize: 1024 * 1024 +}) // Export factory function for custom options export const createSVGSanitizer = (options?: SanitizeOptions) => new SVGSanitizer(options) diff --git a/src/main/presenter/configPresenter/index.ts b/src/main/presenter/configPresenter/index.ts index 7623cefec..2657ee824 100644 --- a/src/main/presenter/configPresenter/index.ts +++ b/src/main/presenter/configPresenter/index.ts @@ -1302,6 +1302,10 @@ export class ConfigPresenter implements IConfigPresenter { await this.customPromptsStore.set('prompts', prompts) this.clearCustomPromptsCache() console.log(`[Config] Custom prompts cache updated: ${prompts.length} prompts`) + // Notify all windows about custom prompts change + eventBus.sendToRenderer(CONFIG_EVENTS.CUSTOM_PROMPTS_CHANGED, SendTarget.ALL_WINDOWS, { + count: prompts.length + }) } // 添加单个 prompt (optimized with cache) @@ -1309,7 +1313,6 @@ export class ConfigPresenter implements IConfigPresenter { const prompts = await this.getCustomPrompts() const updatedPrompts = [...prompts, prompt] // Create new array await this.setCustomPrompts(updatedPrompts) - this.clearCustomPromptsCache() console.log(`[Config] Added custom prompt: ${prompt.name}`) } @@ -1321,8 +1324,6 @@ export class ConfigPresenter implements IConfigPresenter { const updatedPrompts = [...prompts] // Create new array updatedPrompts[index] = { ...updatedPrompts[index], ...updates } await this.setCustomPrompts(updatedPrompts) - // remove cache - this.clearCustomPromptsCache() console.log(`[Config] Updated custom prompt: ${promptId}`) } else { console.warn(`[Config] Custom prompt not found for update: ${promptId}`) @@ -1341,7 +1342,6 @@ export class ConfigPresenter implements IConfigPresenter { } await this.setCustomPrompts(filteredPrompts) - this.clearCustomPromptsCache() console.log(`[Config] Deleted custom prompt: ${promptId}`) } @@ -1415,17 +1415,44 @@ export class ConfigPresenter implements IConfigPresenter { async setDefaultSystemPromptId(promptId: string): Promise { const prompts = await this.getSystemPrompts() const updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false })) + + if (promptId === 'empty') { + await this.setSystemPrompts(updatedPrompts) + await this.clearSystemPrompt() + eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_SYSTEM_PROMPT_CHANGED, SendTarget.ALL_WINDOWS, { + promptId: 'empty', + content: '' + }) + return + } + const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId) if (targetIndex !== -1) { updatedPrompts[targetIndex].isDefault = true await this.setSystemPrompts(updatedPrompts) + await this.setDefaultSystemPrompt(updatedPrompts[targetIndex].content) + eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_SYSTEM_PROMPT_CHANGED, SendTarget.ALL_WINDOWS, { + promptId, + content: updatedPrompts[targetIndex].content + }) + } else { + await this.setSystemPrompts(updatedPrompts) } } async getDefaultSystemPromptId(): Promise { const prompts = await this.getSystemPrompts() const defaultPrompt = prompts.find((p) => p.isDefault) - return defaultPrompt?.id || 'default' + if (defaultPrompt) { + return defaultPrompt.id + } + + const storedPrompt = this.getSetting('default_system_prompt') + if (!storedPrompt || storedPrompt.trim() === '') { + return 'empty' + } + + return prompts.find((p) => p.id === 'default')?.id || 'default' } // 获取更新渠道 diff --git a/src/main/presenter/configPresenter/mcpConfHelper.ts b/src/main/presenter/configPresenter/mcpConfHelper.ts index 78dd1483e..f0e1a6dfa 100644 --- a/src/main/presenter/configPresenter/mcpConfHelper.ts +++ b/src/main/presenter/configPresenter/mcpConfHelper.ts @@ -22,6 +22,7 @@ interface IMcpSettings { npmRegistryCache?: INpmRegistryCache // NPM registry cache customNpmRegistry?: string // User custom NPM registry autoDetectNpmRegistry?: boolean // Whether to enable auto detection + removedBuiltInServers?: string[] // Track built-in servers removed by user [key: string]: unknown // Allow arbitrary keys } export type MCPServerType = 'stdio' | 'sse' | 'inmemory' | 'http' @@ -294,15 +295,19 @@ const DEFAULT_MCP_SERVERS = { // First define built-in MCP servers ...DEFAULT_INMEMORY_SERVERS, // Then default third-party MCP servers - memory: { - command: 'npx', - args: ['-y', '@modelcontextprotocol/server-memory'], + 'nowledge-mem': { + command: '', + args: [], env: {}, - descriptions: '内存存储服务', + descriptions: 'Nowledge Mem MCP', icons: '🧠', autoApprove: ['all'], disable: true, - type: 'stdio' as MCPServerType + type: 'http' as MCPServerType, + baseUrl: 'http://localhost:14242/mcp', + customHeaders: { + APP: 'DeepChat' + } } }, defaultServers: [ @@ -312,6 +317,7 @@ const DEFAULT_MCP_SERVERS = { ], mcpEnabled: false // MCP functionality is disabled by default } +const BUILT_IN_SERVER_NAMES = new Set(Object.keys(DEFAULT_MCP_SERVERS.mcpServers)) // This part of MCP has system logic to determine whether to enable, not controlled by user configuration, but by software environment export const SYSTEM_INMEM_MCP_SERVERS: Record = { // custom-prompts-server has been removed, now provides prompt functionality through config data source @@ -330,26 +336,83 @@ export class McpConfHelper { mcpEnabled: DEFAULT_MCP_SERVERS.mcpEnabled, autoDetectNpmRegistry: true, npmRegistryCache: undefined, - customNpmRegistry: undefined + customNpmRegistry: undefined, + removedBuiltInServers: [] } }) } + private getRemovedBuiltInServers(): string[] { + return this.mcpStore.get('removedBuiltInServers') || [] + } + + private setRemovedBuiltInServers(servers: string[]): void { + this.mcpStore.set('removedBuiltInServers', Array.from(new Set(servers))) + } + + private clearRemovedBuiltInServers(): void { + this.mcpStore.set('removedBuiltInServers', []) + } + + private isBuiltInServer(name: string): boolean { + return BUILT_IN_SERVER_NAMES.has(name) + } + + private markBuiltInServerRemoved(name: string): void { + if (!this.isBuiltInServer(name)) return + const removed = new Set(this.getRemovedBuiltInServers()) + removed.add(name) + this.setRemovedBuiltInServers(Array.from(removed)) + } + + private unmarkBuiltInServerRemoved(name: string): void { + if (!this.isBuiltInServer(name)) return + const removed = this.getRemovedBuiltInServers().filter((server) => server !== name) + this.setRemovedBuiltInServers(removed) + } + + private cloneServerConfig(config: MCPServerConfig): MCPServerConfig { + const cloneFn = ( + globalThis as typeof globalThis & { + structuredClone?: (value: MCPServerConfig) => MCPServerConfig + } + ).structuredClone + + if (typeof cloneFn === 'function') { + return cloneFn(config) + } + + return JSON.parse(JSON.stringify(config)) as MCPServerConfig + } + // Get MCP server configuration async getMcpServers(): Promise> { const storedServers = this.mcpStore.get('mcpServers') || DEFAULT_MCP_SERVERS.mcpServers // 检查并补充缺少的inmemory服务 const updatedServers = { ...storedServers } + const removedBuiltInServers = new Set(this.getRemovedBuiltInServers()) - // 遍历所有默认的inmemory服务,确保它们都存在 - for (const [serverName, serverConfig] of Object.entries(DEFAULT_INMEMORY_SERVERS)) { + const ensureBuiltInServerExists = (serverName: string, serverConfig: MCPServerConfig): void => { + if (removedBuiltInServers.has(serverName)) { + return + } if (!updatedServers[serverName]) { - console.log(`Adding missing inmemory service: ${serverName}`) - updatedServers[serverName] = serverConfig + console.log(`Adding missing built-in MCP service: ${serverName}`) + updatedServers[serverName] = this.cloneServerConfig(serverConfig) } } + // 遍历所有默认的inmemory服务,确保它们都存在 + for (const [serverName, serverConfig] of Object.entries(DEFAULT_INMEMORY_SERVERS)) { + ensureBuiltInServerExists(serverName, serverConfig) + } + + // 确保 DEFAULT_MCP_SERVERS 中定义的服务存在 + for (const [serverName, serverConfig] of Object.entries(DEFAULT_MCP_SERVERS.mcpServers)) { + ensureBuiltInServerExists(serverName, serverConfig) + } + // 移除不支持当前平台的服务 const serversToRemove: string[] = [] for (const [serverName, serverConfig] of Object.entries(updatedServers)) { @@ -491,6 +554,9 @@ export class McpConfHelper { async addMcpServer(name: string, config: MCPServerConfig): Promise { const mcpServers = await this.getMcpServers() mcpServers[name] = config + if (this.isBuiltInServer(name)) { + this.unmarkBuiltInServerRemoved(name) + } await this.setMcpServers(mcpServers) return true } @@ -579,6 +645,9 @@ export class McpConfHelper { async removeMcpServer(name: string): Promise { const mcpServers = await this.getMcpServers() delete mcpServers[name] + if (this.isBuiltInServer(name)) { + this.markBuiltInServerRemoved(name) + } await this.setMcpServers(mcpServers) // 如果删除的服务器在默认服务器列表中,则从列表中移除 @@ -629,6 +698,7 @@ export class McpConfHelper { ] this.mcpStore.set('defaultServers', platformAwareDefaultServers) + this.clearRemovedBuiltInServers() eventBus.send(MCP_EVENTS.CONFIG_CHANGED, SendTarget.ALL_WINDOWS, { mcpServers: updatedServers, defaultServers: platformAwareDefaultServers, diff --git a/src/main/presenter/devicePresenter/index.ts b/src/main/presenter/devicePresenter/index.ts index 4868fcb64..ca9ce9d11 100644 --- a/src/main/presenter/devicePresenter/index.ts +++ b/src/main/presenter/devicePresenter/index.ts @@ -494,20 +494,6 @@ export class DevicePresenter implements IDevicePresenter { async sanitizeSvgContent(svgContent: string): Promise { try { console.log('Sanitizing SVG content, length:', svgContent.length) - - // 基本输入验证 - if (!svgContent || typeof svgContent !== 'string') { - console.warn('Invalid SVG content provided') - return null - } - - // 长度限制检查 - if (svgContent.length > 1024 * 1024) { - // 1MB limit - console.warn('SVG content exceeds size limit') - return null - } - // Debug: 显示SVG前100个字符 console.log('SVG preview:', svgContent.substring(0, 100) + '...') diff --git a/src/main/presenter/llmProviderPresenter/index.ts b/src/main/presenter/llmProviderPresenter/index.ts index 017bbabe1..497fd2c8e 100644 --- a/src/main/presenter/llmProviderPresenter/index.ts +++ b/src/main/presenter/llmProviderPresenter/index.ts @@ -355,7 +355,7 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { * 处理单个 provider 的原子更新 */ private handleProviderAtomicUpdate(change: ProviderChange): void { - console.log(`Handling atomic provider update:`, change) + // console.log(`Handling atomic provider update:`, change) switch (change.operation) { case 'add': diff --git a/src/main/presenter/mcpPresenter/index.ts b/src/main/presenter/mcpPresenter/index.ts index 733bc22f4..d56711713 100644 --- a/src/main/presenter/mcpPresenter/index.ts +++ b/src/main/presenter/mcpPresenter/index.ts @@ -8,7 +8,9 @@ import { Prompt, ResourceListEntry, Resource, - PromptListEntry + PromptListEntry, + McpSamplingRequestPayload, + McpSamplingDecision } from '@shared/presenter' import { ServerManager } from './serverManager' import { ToolManager } from './toolManager' @@ -85,6 +87,10 @@ export class McpPresenter implements IMCPPresenter { private isInitialized: boolean = false // McpRouter private mcprouter?: McpRouterManager + private pendingSamplingRequests = new Map< + string, + { resolve: (decision: McpSamplingDecision) => void; reject: (error: Error) => void } + >() constructor(configPresenter?: IConfigPresenter) { console.log('Initializing MCP Presenter') @@ -580,6 +586,60 @@ export class McpPresenter implements IMCPPresenter { return { content: formattedContent, rawData: toolCallResult } } + async handleSamplingRequest(request: McpSamplingRequestPayload): Promise { + if (!request || !request.requestId) { + throw new Error('Invalid sampling request: missing requestId') + } + + return new Promise((resolve, reject) => { + try { + this.pendingSamplingRequests.set(request.requestId, { resolve, reject }) + eventBus.sendToRenderer(MCP_EVENTS.SAMPLING_REQUEST, SendTarget.DEFAULT_TAB, request) + } catch (error) { + this.pendingSamplingRequests.delete(request.requestId) + reject(error instanceof Error ? error : new Error(String(error))) + } + }) + } + + async submitSamplingDecision(decision: McpSamplingDecision): Promise { + if (!decision || !decision.requestId) { + throw new Error('Invalid sampling decision: missing requestId') + } + + const pending = this.pendingSamplingRequests.get(decision.requestId) + if (!pending) { + console.warn( + `[MCP] Sampling request ${decision.requestId} not found when submitting decision` + ) + return + } + + this.pendingSamplingRequests.delete(decision.requestId) + pending.resolve(decision) + + eventBus.sendToRenderer(MCP_EVENTS.SAMPLING_DECISION, SendTarget.ALL_WINDOWS, decision) + } + + async cancelSamplingRequest(requestId: string, reason?: string): Promise { + if (!requestId) { + return + } + + const pending = this.pendingSamplingRequests.get(requestId) + if (!pending) { + return + } + + this.pendingSamplingRequests.delete(requestId) + pending.reject(new Error(reason ?? 'Sampling request cancelled')) + + eventBus.sendToRenderer(MCP_EVENTS.SAMPLING_CANCELLED, SendTarget.ALL_WINDOWS, { + requestId, + reason: reason ?? 'cancelled' + }) + } + // Convert MCPToolDefinition to MCPTool private mcpToolDefinitionToMcpTool( toolDefinition: MCPToolDefinition, diff --git a/src/main/presenter/mcpPresenter/mcpClient.ts b/src/main/presenter/mcpPresenter/mcpClient.ts index 647c4941c..1ceb05e6d 100644 --- a/src/main/presenter/mcpPresenter/mcpClient.ts +++ b/src/main/presenter/mcpPresenter/mcpClient.ts @@ -8,8 +8,12 @@ import { PromptListChangedNotificationSchema, ResourceListChangedNotificationSchema, ResourceUpdatedNotificationSchema, - LoggingMessageNotificationSchema + LoggingMessageNotificationSchema, + CreateMessageRequestSchema, + ErrorCode, + McpError } from '@modelcontextprotocol/sdk/types.js' +import type { CreateMessageRequest, CreateMessageResult } from '@modelcontextprotocol/sdk/types.js' import { eventBus, SendTarget } from '@/eventbus' import { MCP_EVENTS } from '@/events' import path from 'path' @@ -25,9 +29,19 @@ import { Tool, Prompt, ResourceListEntry, - Resource + Resource, + ChatMessage, + McpSamplingRequestPayload, + McpSamplingDecision } from '@shared/presenter' +const ALLOWED_SAMPLING_IMAGE_MIME_TYPES = new Set([ + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/webp' +]) + // TODO: resources 和 prompts 的类型,Notifactions 的类型 https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/examples/client/simpleStreamableHttp.ts // Simple OAuth provider for handling Bearer Token class SimpleOAuthProvider { @@ -58,6 +72,12 @@ interface SessionError extends Error { isSessionExpired?: boolean } +interface RequestHandlerContext { + signal?: AbortSignal + requestId?: string | number + [key: string]: unknown +} + // Helper function to check if error is session-related function isSessionError(error: unknown): error is SessionError { if (error instanceof Error) { @@ -590,7 +610,8 @@ export class McpClient { capabilities: { resources: {}, tools: {}, - prompts: {} + prompts: {}, + sampling: {} } } ) @@ -598,6 +619,11 @@ export class McpClient { // 设置通知处理器 this.registerNotificationHandlers() + // 注册采样请求处理器 + this.client.setRequestHandler(CreateMessageRequestSchema, async (request, extra) => { + return this.handleSamplingCreateMessage(request, extra) + }) + // 设置连接超时 const timeoutPromise = new Promise((_, reject) => { this.connectionTimeout = setTimeout( @@ -764,6 +790,296 @@ export class McpClient { }) } + private async handleSamplingCreateMessage( + request: CreateMessageRequest, + extra: RequestHandlerContext + ): Promise { + const params = request.params ?? {} + const requestId = this.resolveSamplingRequestId(extra) + const { payload, chatMessages } = this.prepareSamplingContext(requestId, params) + + const decisionPromise = presenter.mcpPresenter.handleSamplingRequest(payload) + const signal = extra?.signal as AbortSignal | undefined + + let decision: McpSamplingDecision + if (signal) { + decision = await new Promise((resolve, reject) => { + const onAbort = () => { + signal.removeEventListener('abort', onAbort) + void presenter.mcpPresenter + .cancelSamplingRequest(payload.requestId, 'cancelled by server') + .catch((error) => { + console.warn(`[MCP] Failed to cancel sampling request ${payload.requestId}:`, error) + }) + reject(new McpError(ErrorCode.RequestTimeout, 'Sampling request cancelled')) + } + + if (signal.aborted) { + onAbort() + return + } + + signal.addEventListener('abort', onAbort, { once: true }) + decisionPromise + .then((value) => { + signal.removeEventListener('abort', onAbort) + resolve(value) + }) + .catch((error) => { + signal.removeEventListener('abort', onAbort) + reject(error) + }) + }) + } else { + decision = await decisionPromise + } + + if (!decision.approved) { + throw new McpError(ErrorCode.InvalidRequest, 'User rejected sampling request') + } + + if (!decision.providerId || !decision.modelId) { + throw new McpError(ErrorCode.InvalidParams, 'No model selected for sampling request') + } + + let assistantText = '' + try { + assistantText = await presenter.llmproviderPresenter.generateCompletionStandalone( + decision.providerId, + chatMessages, + decision.modelId, + undefined, + params.maxTokens + ) + } catch (error) { + console.error(`[MCP] Sampling request failed for server ${this.serverName}:`, error) + throw new McpError( + ErrorCode.InternalError, + error instanceof Error ? error.message : 'Sampling request failed' + ) + } + + const modelName = + this.resolveModelDisplayName(decision.providerId, decision.modelId) ?? decision.modelId + + const result: CreateMessageResult = { + role: 'assistant', + model: modelName, + stopReason: 'endTurn', + content: { + type: 'text', + text: assistantText ?? '' + } + } + + return result + } + + private resolveSamplingRequestId(extra: RequestHandlerContext): string { + const rawId = extra?.requestId + if (typeof rawId === 'string' || typeof rawId === 'number') { + return String(rawId) + } + + return `${this.serverName}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}` + } + + private prepareSamplingContext( + requestId: string, + params: CreateMessageRequest['params'] + ): { payload: McpSamplingRequestPayload; chatMessages: ChatMessage[] } { + const payload: McpSamplingRequestPayload = { + requestId, + serverName: this.serverName, + serverLabel: this.getServerLabel(), + systemPrompt: typeof params?.systemPrompt === 'string' ? params.systemPrompt : undefined, + maxTokens: typeof params?.maxTokens === 'number' ? params.maxTokens : undefined, + modelPreferences: this.normalizeModelPreferences(params?.modelPreferences), + requiresVision: false, + messages: [] + } + + const chatMessages: ChatMessage[] = [] + + if (payload.systemPrompt) { + chatMessages.push({ role: 'system', content: payload.systemPrompt }) + } + + const messageList = Array.isArray(params?.messages) ? params.messages : [] + + for (const message of messageList) { + if (!message || (message.role !== 'user' && message.role !== 'assistant')) { + continue + } + + const rawContent = message.content + if (!rawContent || typeof rawContent !== 'object' || !('type' in rawContent)) { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling message content received') + } + + const content = rawContent as { type: string } & Record + + if (content.type === 'text') { + const text = typeof content.text === 'string' ? content.text : '' + payload.messages.push({ role: message.role, type: 'text', text }) + chatMessages.push({ role: message.role, content: text }) + } else if (content.type === 'image') { + const rawMimeType = typeof content.mimeType === 'string' ? content.mimeType : undefined + const normalizedMimeType = rawMimeType?.toLowerCase() + + if (normalizedMimeType && !ALLOWED_SAMPLING_IMAGE_MIME_TYPES.has(normalizedMimeType)) { + throw new McpError( + ErrorCode.InvalidParams, + `Unsupported sampling image mime type: ${rawMimeType}` + ) + } + + const mimeType = normalizedMimeType ?? 'image/png' + const data = this.sanitizeSamplingImageData(content.data) + const dataUrl = `data:${mimeType};base64,${data}` + payload.messages.push({ + role: message.role, + type: 'image', + dataUrl, + mimeType + }) + payload.requiresVision = true + chatMessages.push({ + role: message.role, + content: [ + { + type: 'image_url', + image_url: { url: dataUrl, detail: 'auto' as const } + } + ] + }) + } else if (content.type === 'audio') { + throw new McpError( + ErrorCode.InvalidParams, + 'Audio sampling content is not supported by this client' + ) + } else { + throw new McpError( + ErrorCode.InvalidParams, + `Unsupported sampling content type: ${String((content as { type?: unknown }).type)}` + ) + } + } + + return { payload, chatMessages } + } + + private sanitizeSamplingImageData(rawData: unknown): string { + if (typeof rawData !== 'string') { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling image payload received') + } + + const sanitized = rawData.replace(/\s+/g, '') + + if (!sanitized) { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling image payload received') + } + + if (sanitized.length % 4 !== 0 || /[^A-Za-z0-9+/=]/.test(sanitized)) { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling image payload received') + } + + let decoded: Buffer + + try { + decoded = Buffer.from(sanitized, 'base64') + } catch { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling image payload received') + } + + if (!decoded.length) { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling image payload received') + } + + const reencoded = decoded.toString('base64') + + if (reencoded.replace(/=+$/, '') !== sanitized.replace(/=+$/, '')) { + throw new McpError(ErrorCode.InvalidParams, 'Invalid sampling image payload received') + } + + return sanitized + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private normalizeModelPreferences( + preferences: any + ): McpSamplingRequestPayload['modelPreferences'] { + if (!preferences || typeof preferences !== 'object') { + return undefined + } + + const normalized: McpSamplingRequestPayload['modelPreferences'] = {} + + if (typeof preferences.costPriority === 'number') { + normalized.costPriority = preferences.costPriority + } + if (typeof preferences.speedPriority === 'number') { + normalized.speedPriority = preferences.speedPriority + } + if (typeof preferences.intelligencePriority === 'number') { + normalized.intelligencePriority = preferences.intelligencePriority + } + if (Array.isArray(preferences.hints)) { + normalized.hints = preferences.hints.map((hint: { name?: unknown }) => ({ + name: typeof hint?.name === 'string' ? hint.name : undefined + })) + } + + if ( + normalized.costPriority === undefined && + normalized.speedPriority === undefined && + normalized.intelligencePriority === undefined && + (!normalized.hints || normalized.hints.length === 0) + ) { + return undefined + } + + return normalized + } + + private getServerLabel(): string | undefined { + const config = this.serverConfig + if (!config) { + return undefined + } + + const candidates: Array = [ + typeof config['descriptions'] === 'string' ? (config['descriptions'] as string) : undefined, + typeof config['description'] === 'string' ? (config['description'] as string) : undefined, + typeof config['name'] === 'string' ? (config['name'] as string) : undefined + ] + + return candidates.find((label) => label && label.trim().length > 0) + } + + private resolveModelDisplayName(providerId: string, modelId: string): string | undefined { + try { + const models = presenter.configPresenter.getProviderModels(providerId) || [] + const match = models.find((model) => model.id === modelId) + if (match?.name) { + return match.name + } + + const customModels = presenter.configPresenter.getCustomModels?.(providerId) || [] + const customMatch = customModels.find((model) => model.id === modelId) + if (customMatch?.name) { + return customMatch.name + } + } catch (error) { + console.warn( + `[MCP] Failed to resolve model display name for ${providerId}/${modelId}:`, + error + ) + } + + return undefined + } + // 检查服务器是否正在运行 isServerRunning(): boolean { return this.isConnected && !!this.client diff --git a/src/main/presenter/shortcutPresenter.ts b/src/main/presenter/shortcutPresenter.ts index ce7ad5cea..3451e2116 100644 --- a/src/main/presenter/shortcutPresenter.ts +++ b/src/main/presenter/shortcutPresenter.ts @@ -119,11 +119,12 @@ export class ShortcutPresenter implements IShortcutPresenter { } }) } - + console.log('clean chat history shortcut', this.shortcutKeys.CleanChatHistory) // Command+L 或 Ctrl+L 清除聊天历史 if (this.shortcutKeys.CleanChatHistory) { globalShortcut.register(this.shortcutKeys.CleanChatHistory, () => { const focusedWindow = presenter.windowPresenter.getFocusedWindow() + console.log('clean chat history') if (focusedWindow?.isFocused()) { presenter.windowPresenter.sendToActiveTab( focusedWindow.id, @@ -137,6 +138,7 @@ export class ShortcutPresenter implements IShortcutPresenter { if (this.shortcutKeys.DeleteConversation) { globalShortcut.register(this.shortcutKeys.DeleteConversation, () => { const focusedWindow = presenter.windowPresenter.getFocusedWindow() + console.log('delete conversation') if (focusedWindow?.isFocused()) { presenter.windowPresenter.sendToActiveTab( focusedWindow.id, diff --git a/src/main/presenter/sqlitePresenter/importData.ts b/src/main/presenter/sqlitePresenter/importData.ts index 507786bc1..3e129699d 100644 --- a/src/main/presenter/sqlitePresenter/importData.ts +++ b/src/main/presenter/sqlitePresenter/importData.ts @@ -1,6 +1,13 @@ import Database from 'better-sqlite3-multiple-ciphers' -import { nanoid } from 'nanoid' -import path from 'path' + +export interface ImportSummary { + tableCounts: Record +} + +type ColumnInfo = { + name: string + pk: number +} /** * 数据导入类 @@ -9,438 +16,160 @@ import path from 'path' export class DataImporter { private sourceDb: Database.Database private targetDb: Database.Database - private idMappings: { - conversations: Map - messages: Map - attachments: Map - } - /** - * 构造函数 - * @param sourcePath 源数据库路径 - * @param targetDbOrPath 目标数据库实例或路径 - * @param sourcePassword 源数据库密码(如果有) - * @param targetPassword 目标数据库密码(如果有) - */ constructor( sourcePath: string, targetDbOrPath: Database.Database | string, sourcePassword?: string, targetPassword?: string ) { - // 初始化源数据库连接 this.sourceDb = new Database(sourcePath) this.sourceDb.pragma('journal_mode = WAL') - // 如果有密码,设置加密 if (sourcePassword) { - this.sourceDb.pragma(`cipher='sqlcipher'`) - this.sourceDb.pragma(`key='${sourcePassword}'`) + this.sourceDb.pragma("cipher='sqlcipher'") + const hex = Buffer.from(sourcePassword, 'utf8').toString('hex') + this.sourceDb.pragma(`key = "x'${hex}'"`) } - // 设置目标数据库 if (typeof targetDbOrPath === 'string') { - // 如果传入的是路径字符串,创建新的数据库连接 this.targetDb = new Database(targetDbOrPath) this.targetDb.pragma('journal_mode = WAL') - // 如果有目标数据库密码,设置加密 if (targetPassword) { - this.targetDb.pragma(`cipher='sqlcipher'`) - this.targetDb.pragma(`key='${targetPassword}'`) + this.targetDb.pragma("cipher='sqlcipher'") + const hex = Buffer.from(targetPassword, 'utf8').toString('hex') + this.targetDb.pragma(`key = "x'${hex}'"`) } } else { - // 如果传入的是数据库实例,直接使用 this.targetDb = targetDbOrPath } - - // 初始化ID映射 - this.idMappings = { - conversations: new Map(), - messages: new Map(), - attachments: new Map() - } } /** * 开始导入数据 - * @returns 导入的会话数量 */ - public async importData(): Promise { - // 获取所有会话 - 兼容不同版本的数据库schema - let conversations: any[] - - try { - // 尝试使用包含所有新字段的查询 - conversations = this.sourceDb - .prepare( - `SELECT - conv_id, title, created_at, updated_at, system_prompt, - temperature, context_length, max_tokens, provider_id, - model_id, - COALESCE(is_pinned, 0) as is_pinned, - COALESCE(is_new, 0) as is_new, - COALESCE(artifacts, 0) as artifacts, - enabled_mcp_tools, - thinking_budget, - reasoning_effort, - verbosity, - enable_search, - forced_search, - search_strategy - FROM conversations` - ) - .all() as any[] - } catch { - // 如果失败,使用基础字段查询(兼容旧版本数据库) - try { - conversations = this.sourceDb - .prepare( - `SELECT - conv_id, title, created_at, updated_at, system_prompt, - temperature, context_length, max_tokens, provider_id, - model_id, - COALESCE(is_pinned, 0) as is_pinned, - COALESCE(is_new, 0) as is_new, - COALESCE(artifacts, 0) as artifacts - FROM conversations` - ) - .all() as any[] - - // 为缺失的字段设置默认值 - conversations = conversations.map((conv) => ({ - ...conv, - enabled_mcp_tools: null, - thinking_budget: null, - reasoning_effort: null, - verbosity: null, - enable_search: null, - forced_search: null, - search_strategy: null - })) - } catch (fallbackError) { - throw new Error( - `Failed to query conversations: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}` - ) - } - } + public async importData(): Promise { + const tableCounts: Record = {} + const tables = this.getTablesInOrder() - // 使用better-sqlite3的transaction API来处理事务 const importTransaction = this.targetDb.transaction(() => { - let importedCount = 0 - for (const conv of conversations) { - // 如果是增量导入模式,检查会话是否已存在 - const existingConv = this.targetDb - .prepare('SELECT conv_id FROM conversations WHERE conv_id = ?') - .get(conv.conv_id) - if (existingConv) { - continue // 跳过已存在的会话 + for (const table of tables) { + try { + const inserted = this.importTable(table) + if (inserted > 0) { + tableCounts[table] = inserted + } + } catch (error) { + throw new Error( + `Failed to import table ${table}: ${error instanceof Error ? error.message : String(error)}` + ) } - - this.importConversation(conv) - importedCount++ } - return importedCount }) try { - // 执行事务并返回导入的会话数量 - return importTransaction() + importTransaction() + return { tableCounts } } catch (transactionError) { - // 事务会自动回滚,抛出详细错误 throw new Error( - `Failed to import data: ${transactionError instanceof Error ? transactionError.message : String(transactionError)}` + `Failed to import database: ${ + transactionError instanceof Error ? transactionError.message : String(transactionError) + }` ) } } - /** - * 导入单个会话及其相关数据 - * @param conv 会话数据 - */ - private importConversation(conv: any): void { - // 为会话生成新ID - // const newConvId = nanoid() - // this.idMappings.conversations.set(conv.conv_id, newConvId) + private getTablesInOrder(): string[] { + const tables = this.sourceDb + .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'") + .all() as { name: string }[] - try { - // 首先尝试使用包含所有新字段的INSERT语句 - this.targetDb - .prepare( - `INSERT INTO conversations ( - conv_id, title, created_at, updated_at, system_prompt, - temperature, context_length, max_tokens, provider_id, - model_id, is_pinned, is_new, artifacts, enabled_mcp_tools, - thinking_budget, reasoning_effort, verbosity, - enable_search, forced_search, search_strategy - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ) - .run( - conv.conv_id, - conv.title, - conv.created_at, - conv.updated_at, - conv.system_prompt, - conv.temperature, - conv.context_length, - conv.max_tokens, - conv.provider_id, - conv.model_id, - conv.is_pinned || 0, - conv.is_new || 0, - conv.artifacts || 0, - conv.enabled_mcp_tools || null, - conv.thinking_budget || null, - conv.reasoning_effort || null, - conv.verbosity || null, - conv.enable_search ?? null, - conv.forced_search ?? null, - conv.search_strategy ?? null - ) - } catch { - // 如果失败,使用基础字段的INSERT语句(兼容旧版本目标数据库) - try { - this.targetDb - .prepare( - `INSERT INTO conversations ( - conv_id, title, created_at, updated_at, system_prompt, - temperature, context_length, max_tokens, provider_id, - model_id, is_pinned, is_new, artifacts - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ) - .run( - conv.conv_id, - conv.title, - conv.created_at, - conv.updated_at, - conv.system_prompt, - conv.temperature, - conv.context_length, - conv.max_tokens, - conv.provider_id, - conv.model_id, - conv.is_pinned || 0, - conv.is_new || 0, - conv.artifacts || 0 - ) - } catch (fallbackError) { - throw new Error( - `Failed to insert conversation ${conv.conv_id}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}` - ) + const preferredOrder = ['conversations', 'messages', 'attachments', 'message_attachments'] + const preferredSet = new Set(preferredOrder) + + const preferredTables: string[] = [] + const remainingTables: string[] = [] + + for (const { name } of tables) { + if (preferredSet.has(name)) { + preferredTables.push(name) + } else { + remainingTables.push(name) } } - // 导入该会话的所有消息 - try { - this.importMessages(conv.conv_id) - } catch (messageError) { - throw new Error( - `Failed to import messages for conversation ${conv.conv_id}: ${messageError instanceof Error ? messageError.message : String(messageError)}` - ) - } - } + preferredTables.sort((a, b) => preferredOrder.indexOf(a) - preferredOrder.indexOf(b)) + remainingTables.sort() - /** - * 导入会话的所有消息 - * @param oldConvId 原会话ID - */ - private importMessages(oldConvId: string): void { - // 获取会话的所有消息 - const messages = this.sourceDb - .prepare( - `SELECT - msg_id, parent_id, role, content, created_at, - order_seq, token_count, status, metadata, - is_context_edge, is_variant - FROM messages - WHERE conversation_id = ? - ORDER BY order_seq` - ) - .all(oldConvId) as any[] + return [...preferredTables, ...remainingTables] + } - // 逐个导入消息 - for (const msg of messages) { - const newMsgId = nanoid() - this.idMappings.messages.set(msg.msg_id, newMsgId) + private importTable(tableName: string): number { + const sourceColumns = this.getTableColumns(this.sourceDb, tableName) + const targetColumns = this.getTableColumns(this.targetDb, tableName) - // 处理父消息ID映射 - let newParentId = '' - if (msg.parent_id && msg.parent_id !== '') { - newParentId = this.idMappings.messages.get(msg.parent_id) || '' - } + if (targetColumns.length === 0) { + return 0 + } - try { - // 插入消息 - this.targetDb - .prepare( - `INSERT INTO messages ( - msg_id, conversation_id, parent_id, role, content, - created_at, order_seq, token_count, status, metadata, - is_context_edge, is_variant - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ) - .run( - newMsgId, - oldConvId, - newParentId, - msg.role, - msg.content, - msg.created_at, - msg.order_seq, - msg.token_count || 0, - msg.status || 'sent', - msg.metadata || null, - msg.is_context_edge || 0, - msg.is_variant || 0 - ) + const targetColumnNames = new Set(targetColumns.map((column) => column.name)) + const commonColumns = sourceColumns.filter((column) => targetColumnNames.has(column.name)) - // 导入消息的附件 - this.importAttachments(msg.msg_id, newMsgId) - this.importMessageAttachments(msg.msg_id, newMsgId) - } catch (msgError) { - throw new Error( - `Failed to insert message ${msg.msg_id}: ${msgError instanceof Error ? msgError.message : String(msgError)}` - ) - } + if (commonColumns.length === 0) { + return 0 } - } - /** - * 导入消息的附件 - * @param oldMsgId 原消息ID - * @param newMsgId 新消息ID - */ - private importAttachments(oldMsgId: string, newMsgId: string): void { - // 获取消息的所有附件 - const attachments = this.sourceDb - .prepare( - `SELECT - attach_id, attachment_type, file_name, file_size, - storage_type, storage_path, thumbnail, vectorized, - data_summary, mime_type, created_at - FROM attachments - WHERE message_id = ?` - ) - .all(oldMsgId) as any[] + const pkColumns = targetColumns + .filter((column) => column.pk > 0 && commonColumns.some((col) => col.name === column.name)) + .sort((a, b) => a.pk - b.pk) - // 逐个导入附件 - for (const attachment of attachments) { - const newAttachId = nanoid() - this.idMappings.attachments.set(attachment.attach_id, newAttachId) + const wrappedTableName = this.wrapIdentifier(tableName) + const selectColumnsSql = commonColumns + .map((column) => this.wrapIdentifier(column.name)) + .join(', ') + const rows = this.sourceDb + .prepare(`SELECT ${selectColumnsSql} FROM ${wrappedTableName}`) + .all() as Record[] - // 处理存储路径 - let storagePath = attachment.storage_path - if (storagePath && attachment.storage_type === 'path') { - // 如果是文件路径,可能需要复制文件或调整路径 - // 这里简单处理,实际应用中可能需要更复杂的逻辑 - storagePath = path.basename(storagePath) - } + if (rows.length === 0) { + return 0 + } - // 插入附件 - this.targetDb - .prepare( - `INSERT INTO attachments ( - attach_id, message_id, attachment_type, file_name, - file_size, storage_type, storage_path, thumbnail, - vectorized, data_summary, mime_type, created_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ) - .run( - newAttachId, - newMsgId, - attachment.attachment_type, - attachment.file_name, - attachment.file_size || 0, - attachment.storage_type, - storagePath, - attachment.thumbnail, - attachment.vectorized || 0, - attachment.data_summary, - attachment.mime_type, - attachment.created_at - ) + const insertPlaceholders = Array.from({ length: commonColumns.length }, () => '?').join(', ') + const insertSql = + pkColumns.length > 0 + ? `INSERT OR IGNORE INTO ${wrappedTableName} (${selectColumnsSql}) VALUES (${insertPlaceholders})` + : `INSERT INTO ${wrappedTableName} (${selectColumnsSql}) VALUES (${insertPlaceholders})` + const insertStmt = this.targetDb.prepare(insertSql) + + let inserted = 0 + for (const row of rows) { + const values = commonColumns.map((column) => row[column.name]) + const info = insertStmt.run(...values) + if (pkColumns.length === 0 || info.changes > 0) { + inserted++ + } } - } - /** - * 导入消息的附件(message_attachments表) - * @param oldMsgId 原消息ID - * @param newMsgId 新消息ID - */ - private importMessageAttachments(oldMsgId: string, newMsgId: string): void { - // 获取消息的所有message_attachments - 兼容不同的schema版本 - let messageAttachments: any[] + return inserted + } + private getTableColumns(db: Database.Database, tableName: string): ColumnInfo[] { + const wrappedTableName = this.wrapIdentifier(tableName) try { - // 首先尝试包含metadata字段的查询 - messageAttachments = this.sourceDb - .prepare( - `SELECT - attachment_id, type, content, created_at, metadata - FROM message_attachments - WHERE message_id = ?` - ) - .all(oldMsgId) as any[] - } catch { - // 如果失败,使用不包含metadata的查询(兼容新版本schema) - messageAttachments = this.sourceDb - .prepare( - `SELECT - attachment_id, type, content, created_at - FROM message_attachments - WHERE message_id = ?` - ) - .all(oldMsgId) as any[] - - // 为缺失的字段设置默认值 - messageAttachments = messageAttachments.map((attachment) => ({ - ...attachment, - metadata: null - })) + const columns = db.prepare(`PRAGMA table_info(${wrappedTableName})`).all() as ColumnInfo[] + return columns + } catch (error) { + console.warn(`Failed to read table info for ${tableName}:`, error) + return [] } + } - // 逐个导入message_attachments - for (const attachment of messageAttachments) { - const newAttachmentId = nanoid() - - try { - // 首先尝试包含metadata字段的INSERT - this.targetDb - .prepare( - `INSERT INTO message_attachments ( - attachment_id, message_id, type, content, created_at, metadata - ) VALUES (?, ?, ?, ?, ?, ?)` - ) - .run( - newAttachmentId, - newMsgId, - attachment.type, - attachment.content, - attachment.created_at, - attachment.metadata - ) - } catch { - // 如果失败,使用不包含metadata的INSERT(兼容新版本schema) - this.targetDb - .prepare( - `INSERT INTO message_attachments ( - attachment_id, message_id, type, content, created_at - ) VALUES (?, ?, ?, ?, ?)` - ) - .run( - newAttachmentId, - newMsgId, - attachment.type, - attachment.content, - attachment.created_at - ) - } - } + private wrapIdentifier(identifier: string): string { + return `"${identifier.replace(/"/g, '""')}"` } - /** - * 关闭数据库连接 - */ public close(): void { if (this.sourceDb) { this.sourceDb.close() diff --git a/src/main/presenter/syncPresenter/index.ts b/src/main/presenter/syncPresenter/index.ts index 208fd9826..2227664f8 100644 --- a/src/main/presenter/syncPresenter/index.ts +++ b/src/main/presenter/syncPresenter/index.ts @@ -2,31 +2,56 @@ import { app, shell } from 'electron' import path from 'path' import fs from 'fs' import Database from 'better-sqlite3-multiple-ciphers' -import { ISyncPresenter, IConfigPresenter, ISQLitePresenter } from '@shared/presenter' +import { zipSync, unzipSync } from 'fflate' +import { + ISyncPresenter, + IConfigPresenter, + ISQLitePresenter, + SyncBackupInfo, + MCPServerConfig +} from '@shared/presenter' import { eventBus, SendTarget } from '@/eventbus' import { SYNC_EVENTS } from '@/events' import { DataImporter } from '../sqlitePresenter/importData' -import { ImportMode } from '../sqlitePresenter/index' +import { ImportMode } from '../sqlitePresenter' -// 为配置文件定义接口 -interface AppSettings { - syncEnabled?: boolean - syncFolderPath?: string - lastSyncTime?: number +interface PromptStore { + prompts: Array<{ id?: string; [key: string]: unknown }> +} + +interface McpSettings { + mcpServers?: Record + defaultServers?: string[] [key: string]: unknown } +type BackupStatus = 'idle' | 'preparing' | 'collecting' | 'compressing' | 'finalizing' | 'error' + +const BACKUP_PREFIX = 'backup-' +const BACKUP_EXTENSION = '.zip' +const BACKUP_FILE_NAME_REGEX = /^backup-\d+\.zip$/ + +const ZIP_PATHS = { + db: 'database/chat.db', + appSettings: 'configs/app-settings.json', + customPrompts: 'configs/custom_prompts.json', + systemPrompts: 'configs/system_prompts.json', + mcpSettings: 'configs/mcp-settings.json', + manifest: 'manifest.json' +} + export class SyncPresenter implements ISyncPresenter { private configPresenter: IConfigPresenter private sqlitePresenter: ISQLitePresenter - private isBackingUp: boolean = false + private isBackingUp = false + private currentBackupStatus: BackupStatus = 'idle' private backupTimer: NodeJS.Timeout | null = null - private readonly BACKUP_DELAY = 60 * 1000 // 60秒无变更后触发备份 + private readonly BACKUP_DELAY = 60 * 1000 private readonly APP_SETTINGS_PATH = path.join(app.getPath('userData'), 'app-settings.json') + private readonly CUSTOM_PROMPTS_PATH = path.join(app.getPath('userData'), 'custom_prompts.json') + private readonly SYSTEM_PROMPTS_PATH = path.join(app.getPath('userData'), 'system_prompts.json') private readonly MCP_SETTINGS_PATH = path.join(app.getPath('userData'), 'mcp-settings.json') - private readonly PROVIDER_MODELS_DIR_PATH = path.join(app.getPath('userData'), 'provider_models') private readonly DB_PATH = path.join(app.getPath('userData'), 'app_db', 'chat.db') - private readonly MODEL_CONFIG_PATH = path.join(app.getPath('userData'), 'model-config.json') constructor(configPresenter: IConfigPresenter, sqlitePresenter: ISQLitePresenter) { this.configPresenter = configPresenter @@ -35,68 +60,71 @@ export class SyncPresenter implements ISyncPresenter { } public init(): void { - // 监听数据变更事件,触发备份计划 this.listenForChanges() } public destroy(): void { - // 清理定时器 if (this.backupTimer) { clearTimeout(this.backupTimer) this.backupTimer = null } } - /** - * 检查同步文件夹状态 - */ public async checkSyncFolder(): Promise<{ exists: boolean; path: string }> { const syncFolderPath = this.configPresenter.getSyncFolderPath() const exists = fs.existsSync(syncFolderPath) - return { exists, path: syncFolderPath } } - /** - * 打开同步文件夹 - */ public async openSyncFolder(): Promise { const { exists, path: syncFolderPath } = await this.checkSyncFolder() - - // 如果文件夹不存在,先创建它 if (!exists) { fs.mkdirSync(syncFolderPath, { recursive: true }) } - - // 打开文件夹 shell.openPath(syncFolderPath) } - /** - * 获取备份状态 - */ public async getBackupStatus(): Promise<{ isBackingUp: boolean; lastBackupTime: number }> { const lastBackupTime = this.configPresenter.getLastSyncTime() return { isBackingUp: this.isBackingUp, lastBackupTime } } - /** - * 手动触发备份 - */ - public async startBackup(): Promise { + public async listBackups(): Promise { + const { path: syncFolderPath } = await this.checkSyncFolder() + const backupsDir = this.getBackupsDirectory(syncFolderPath) + if (!fs.existsSync(backupsDir)) { + return [] + } + + const entries = fs + .readdirSync(backupsDir) + .filter((file) => file.endsWith(BACKUP_EXTENSION)) + .map((fileName) => { + const match = fileName.match(/backup-(\d+)\.zip$/) + const createdAt = match + ? Number(match[1]) + : fs.statSync(path.join(backupsDir, fileName)).mtimeMs + const stats = fs.statSync(path.join(backupsDir, fileName)) + return { fileName, createdAt, size: stats.size } + }) + .sort((a, b) => b.createdAt - a.createdAt) + + return entries + } + + public async startBackup(): Promise { if (this.isBackingUp) { - return + return null } - // 检查同步功能是否启用 if (!this.configPresenter.getSyncEnabled()) { throw new Error('sync.error.notEnabled') } try { - await this.performBackup() - } catch (error: unknown) { - console.error('备份失败:', error) + return await this.performBackup() + } catch (error) { + console.error('Backup failed:', error) eventBus.send( SYNC_EVENTS.BACKUP_ERROR, SendTarget.ALL_WINDOWS, @@ -106,9 +134,6 @@ export class SyncPresenter implements ISyncPresenter { } } - /** - * 取消备份操作 - */ public async cancelBackup(): Promise { if (this.backupTimer) { clearTimeout(this.backupTimer) @@ -117,443 +142,567 @@ export class SyncPresenter implements ISyncPresenter { this.isBackingUp = false } - /** - * 从同步文件夹导入数据 - */ public async importFromSync( + backupFileName: string, importMode: ImportMode = ImportMode.INCREMENT ): Promise<{ success: boolean; message: string; count?: number }> { - // Cancel any pending backup to prevent overwriting the backup files during import if (this.backupTimer) { clearTimeout(this.backupTimer) this.backupTimer = null } - // 检查同步文件夹是否存在 const { exists, path: syncFolderPath } = await this.checkSyncFolder() if (!exists) { return { success: false, message: 'sync.error.folderNotExists' } } - // 检查是否有备份文件 - const dbBackupPath = path.join(syncFolderPath, 'chat.db') - const appSettingsBackupPath = path.join(syncFolderPath, 'app-settings.json') - const providerModelsBackupPath = path.join(syncFolderPath, 'provider_models') - const modelConfigBackupPath = path.join(syncFolderPath, 'model-config.json') - - if (!fs.existsSync(dbBackupPath) || !fs.existsSync(appSettingsBackupPath)) { + const backupsDir = this.getBackupsDirectory(syncFolderPath) + let backupZipPath: string + try { + const safeFileName = this.ensureSafeBackupFileName(backupFileName) + backupZipPath = path.join(backupsDir, safeFileName) + } catch (error) { + console.warn('Failed to validate backup file name', error) + return { success: false, message: 'sync.error.noValidBackup' } + } + if (!fs.existsSync(backupZipPath)) { return { success: false, message: 'sync.error.noValidBackup' } } - // 发出导入开始事件 eventBus.send(SYNC_EVENTS.IMPORT_STARTED, SendTarget.ALL_WINDOWS) - try { - // 关闭数据库连接 - this.sqlitePresenter.close() + const extractionDir = path.join(app.getPath('temp'), `deepchat-backup-${Date.now()}`) + fs.mkdirSync(extractionDir, { recursive: true }) - // 备份当前文件 - const tempDbPath = path.join(app.getPath('temp'), `chat_${Date.now()}.db`) - const tempAppSettingsPath = path.join(app.getPath('temp'), `app_settings_${Date.now()}.json`) - const tempProviderModelsPath = path.join(app.getPath('temp'), `provider_models_${Date.now()}`) - const tempMcpSettingsPath = path.join(app.getPath('temp'), `mcp_settings_${Date.now()}.json`) - const tempModelConfigPath = path.join(app.getPath('temp'), `model_config_${Date.now()}.json`) - // 创建临时备份 - if (fs.existsSync(this.DB_PATH)) { - fs.copyFileSync(this.DB_PATH, tempDbPath) - } - - if (fs.existsSync(this.APP_SETTINGS_PATH)) { - fs.copyFileSync(this.APP_SETTINGS_PATH, tempAppSettingsPath) - } + const tempCurrentFiles: Record = { + db: null, + appSettings: null, + customPrompts: null, + systemPrompts: null, + mcpSettings: null + } - if (fs.existsSync(this.MCP_SETTINGS_PATH)) { - fs.copyFileSync(this.MCP_SETTINGS_PATH, tempMcpSettingsPath) - } + try { + this.extractBackupArchive(backupZipPath, extractionDir) - // 备份模型配置文件 - if (fs.existsSync(this.MODEL_CONFIG_PATH)) { - fs.copyFileSync(this.MODEL_CONFIG_PATH, tempModelConfigPath) - } + const backupDbPath = path.join(extractionDir, ZIP_PATHS.db) + const backupAppSettingsPath = path.join(extractionDir, ZIP_PATHS.appSettings) + const backupCustomPromptsPath = path.join(extractionDir, ZIP_PATHS.customPrompts) + const backupSystemPromptsPath = path.join(extractionDir, ZIP_PATHS.systemPrompts) + const backupMcpSettingsPath = path.join(extractionDir, ZIP_PATHS.mcpSettings) - // 如果 provider_models 目录存在,备份整个目录 - if (fs.existsSync(this.PROVIDER_MODELS_DIR_PATH)) { - this.copyDirectory(this.PROVIDER_MODELS_DIR_PATH, tempProviderModelsPath) + if (!fs.existsSync(backupDbPath) || !fs.existsSync(backupAppSettingsPath)) { + throw new Error('sync.error.noValidBackup') } - let importedCount = 0 - try { - if (importMode === ImportMode.OVERWRITE) { - // For overwrite mode, count conversations from backup db in read-only mode - const backupDb = new Database(dbBackupPath, { readonly: true }) - const result = backupDb.prepare('SELECT COUNT(*) as count FROM conversations').get() as { - count: number - } - importedCount = result.count - backupDb.close() - - fs.copyFileSync(dbBackupPath, this.DB_PATH) - } else { - // For incremental mode, DataImporter returns the actual imported count - const importer = new DataImporter(dbBackupPath, this.DB_PATH) - importedCount = await importer.importData() - console.log(`成功导入 ${importedCount} 个会话`) - importer.close() - } - // 合并 app-settings.json 文件 (排除同步相关的设置) - if (fs.existsSync(appSettingsBackupPath)) { - // 读取当前的 app-settings - let currentSettings: AppSettings = {} - if (fs.existsSync(this.APP_SETTINGS_PATH)) { - const currentContent = fs.readFileSync(this.APP_SETTINGS_PATH, 'utf-8') - currentSettings = JSON.parse(currentContent) - } - - // 读取备份的 app-settings - const backupContent = fs.readFileSync(appSettingsBackupPath, 'utf-8') - const backupSettings = JSON.parse(backupContent) - - // 保留当前的同步相关设置 - const syncSettings: AppSettings = { - syncEnabled: currentSettings.syncEnabled, - syncFolderPath: currentSettings.syncFolderPath, - lastSyncTime: currentSettings.lastSyncTime - } - - // 合并设置: 使用备份的设置,但保留同步相关设置 - const mergedSettings = { ...backupSettings, ...syncSettings } - - // 保存合并后的设置 - fs.writeFileSync(this.APP_SETTINGS_PATH, JSON.stringify(mergedSettings, null, 2), 'utf-8') - } - - // 如果存在 provider_models 备份,复制整个目录(直接覆盖) - if (fs.existsSync(providerModelsBackupPath)) { - // 清空当前 provider_models 目录 - if (fs.existsSync(this.PROVIDER_MODELS_DIR_PATH)) { - this.removeDirectory(this.PROVIDER_MODELS_DIR_PATH) - } - // 确保目标目录存在 - fs.mkdirSync(this.PROVIDER_MODELS_DIR_PATH, { recursive: true }) - // 复制备份目录到应用目录 - this.copyDirectory(providerModelsBackupPath, this.PROVIDER_MODELS_DIR_PATH) - } - - // 导入模型配置文件 - if (fs.existsSync(modelConfigBackupPath)) { - fs.copyFileSync(modelConfigBackupPath, this.MODEL_CONFIG_PATH) - } + this.sqlitePresenter.close() - eventBus.send(SYNC_EVENTS.IMPORT_COMPLETED, SendTarget.ALL_WINDOWS) - return { success: true, message: 'sync.success.importComplete', count: importedCount } - } catch (error: unknown) { - console.error('导入文件失败,恢复备份:', error) + tempCurrentFiles.db = this.createTempBackup(this.DB_PATH, 'chat.db') + tempCurrentFiles.appSettings = this.createTempBackup( + this.APP_SETTINGS_PATH, + 'app-settings.json' + ) + tempCurrentFiles.customPrompts = this.createTempBackup( + this.CUSTOM_PROMPTS_PATH, + 'custom_prompts.json' + ) + tempCurrentFiles.systemPrompts = this.createTempBackup( + this.SYSTEM_PROMPTS_PATH, + 'system_prompts.json' + ) + tempCurrentFiles.mcpSettings = this.createTempBackup( + this.MCP_SETTINGS_PATH, + 'mcp-settings.json' + ) - // 恢复备份 - if (fs.existsSync(tempDbPath)) { - fs.copyFileSync(tempDbPath, this.DB_PATH) - } + let importedConversationCount = 0 - if (fs.existsSync(tempAppSettingsPath)) { - fs.copyFileSync(tempAppSettingsPath, this.APP_SETTINGS_PATH) + if (importMode === ImportMode.OVERWRITE) { + const backupDb = new Database(backupDbPath, { readonly: true }) + const result = backupDb.prepare('SELECT COUNT(*) as count FROM conversations').get() as { + count: number } + importedConversationCount = result?.count || 0 + backupDb.close() - if (fs.existsSync(tempMcpSettingsPath)) { - fs.copyFileSync(tempMcpSettingsPath, this.MCP_SETTINGS_PATH) - } + this.copyFile(backupDbPath, this.DB_PATH) + this.mergeAppSettingsPreservingSync(backupAppSettingsPath, this.APP_SETTINGS_PATH) - if (fs.existsSync(tempProviderModelsPath)) { - if (fs.existsSync(this.PROVIDER_MODELS_DIR_PATH)) { - this.removeDirectory(this.PROVIDER_MODELS_DIR_PATH) - } - this.copyDirectory(tempProviderModelsPath, this.PROVIDER_MODELS_DIR_PATH) + if (fs.existsSync(backupCustomPromptsPath)) { + this.copyFile(backupCustomPromptsPath, this.CUSTOM_PROMPTS_PATH) } - // 恢复模型配置文件 - if (fs.existsSync(tempModelConfigPath)) { - fs.copyFileSync(tempModelConfigPath, this.MODEL_CONFIG_PATH) + if (fs.existsSync(backupSystemPromptsPath)) { + this.copyFile(backupSystemPromptsPath, this.SYSTEM_PROMPTS_PATH) } - eventBus.send( - SYNC_EVENTS.IMPORT_ERROR, - SendTarget.ALL_WINDOWS, - (error as Error).message || 'sync.error.unknown' - ) - return { success: false, message: 'sync.error.importFailed' } - } finally { - // 清理临时文件 - if (fs.existsSync(tempDbPath)) { - fs.unlinkSync(tempDbPath) + if (fs.existsSync(backupMcpSettingsPath)) { + this.copyFile(backupMcpSettingsPath, this.MCP_SETTINGS_PATH) } - - if (fs.existsSync(tempAppSettingsPath)) { - fs.unlinkSync(tempAppSettingsPath) + } else { + const importer = new DataImporter(backupDbPath, this.DB_PATH) + const summary = await importer.importData() + importer.close() + importedConversationCount = summary.tableCounts.conversations || 0 + + this.mergeAppSettingsPreservingSync(backupAppSettingsPath, this.APP_SETTINGS_PATH) + if (fs.existsSync(backupCustomPromptsPath)) { + this.mergePromptStore(backupCustomPromptsPath, this.CUSTOM_PROMPTS_PATH) } - - if (fs.existsSync(tempMcpSettingsPath)) { - fs.unlinkSync(tempMcpSettingsPath) + if (fs.existsSync(backupSystemPromptsPath)) { + this.mergePromptStore(backupSystemPromptsPath, this.SYSTEM_PROMPTS_PATH) } - - if (fs.existsSync(tempProviderModelsPath)) { - this.removeDirectory(tempProviderModelsPath) + if (fs.existsSync(backupMcpSettingsPath)) { + this.mergeMcpSettings(backupMcpSettingsPath, this.MCP_SETTINGS_PATH) } + } - // 清理模型配置临时文件 - if (fs.existsSync(tempModelConfigPath)) { - fs.unlinkSync(tempModelConfigPath) - } + eventBus.send(SYNC_EVENTS.IMPORT_COMPLETED, SendTarget.ALL_WINDOWS) + return { + success: true, + message: 'sync.success.importComplete', + count: importedConversationCount } - } catch (error: unknown) { - console.error('导入过程出错:', error) + } catch (error) { + console.error('import failed,reverting:', error) + this.restoreFromTempBackup(tempCurrentFiles) eventBus.send( SYNC_EVENTS.IMPORT_ERROR, SendTarget.ALL_WINDOWS, (error as Error).message || 'sync.error.unknown' ) - return { success: false, message: 'sync.error.importProcess' } + return { success: false, message: 'sync.error.importFailed' } + } finally { + this.cleanupTempFiles(Object.values(tempCurrentFiles)) + this.removeDirectory(extractionDir) } } - /** - * 执行实际的备份操作 - */ - private async performBackup(): Promise { - // 标记备份开始 + private async performBackup(): Promise { this.isBackingUp = true + this.emitBackupStatus('preparing') eventBus.send(SYNC_EVENTS.BACKUP_STARTED, SendTarget.ALL_WINDOWS) - try { - const syncFolderPath = this.configPresenter.getSyncFolderPath() - - // 确保同步文件夹存在 - if (!fs.existsSync(syncFolderPath)) { - fs.mkdirSync(syncFolderPath, { recursive: true }) - } + const syncFolderPath = this.configPresenter.getSyncFolderPath() + if (!fs.existsSync(syncFolderPath)) { + fs.mkdirSync(syncFolderPath, { recursive: true }) + } + const backupsDir = this.getBackupsDirectory(syncFolderPath) + fs.mkdirSync(backupsDir, { recursive: true }) - // 生成临时备份文件路径(防止导入过程中的文件冲突) - const tempDbBackupPath = path.join(syncFolderPath, `chat_${Date.now()}.db.tmp`) - const tempAppSettingsBackupPath = path.join( - syncFolderPath, - `app_settings_${Date.now()}.json.tmp` - ) - const tempProviderModelsBackupPath = path.join( - syncFolderPath, - `provider_models_${Date.now()}.tmp` - ) - const tempMcpSettingsBackupPath = path.join( - syncFolderPath, - `mcp_settings_${Date.now()}.json.tmp` - ) - const tempModelConfigBackupPath = path.join( - syncFolderPath, - `model_config_${Date.now()}.json.tmp` - ) + const timestamp = Date.now() + const backupFileName = `${BACKUP_PREFIX}${timestamp}${BACKUP_EXTENSION}` + const tempZipPath = path.join(backupsDir, `${backupFileName}.tmp`) + const finalZipPath = path.join(backupsDir, backupFileName) - const finalDbBackupPath = path.join(syncFolderPath, 'chat.db') - const finalAppSettingsBackupPath = path.join(syncFolderPath, 'app-settings.json') - const finalProviderModelsBackupPath = path.join(syncFolderPath, 'provider_models') - const finalMcpSettingsBackupPath = path.join(syncFolderPath, 'mcp-settings.json') - const finalModelConfigBackupPath = path.join(syncFolderPath, 'model-config.json') + let completedTimestamp: number | null = null + let encounteredError = false - // 确保数据库文件存在 + try { if (!fs.existsSync(this.DB_PATH)) { - console.warn('数据库文件不存在:', this.DB_PATH) throw new Error('sync.error.dbNotExists') } - // 确保配置文件存在 if (!fs.existsSync(this.APP_SETTINGS_PATH)) { - console.warn('配置文件不存在:', this.APP_SETTINGS_PATH) throw new Error('sync.error.configNotExists') } - // 备份数据库 - fs.copyFileSync(this.DB_PATH, tempDbBackupPath) - - // 备份配置文件(过滤掉同步相关的设置) - if (fs.existsSync(this.APP_SETTINGS_PATH)) { - const appSettingsContent = fs.readFileSync(this.APP_SETTINGS_PATH, 'utf-8') - const appSettings = JSON.parse(appSettingsContent) - - // 创建配置副本,不包含同步相关的设置 - const filteredSettings = { ...appSettings } - // 删除同步相关的设置 - delete filteredSettings.syncEnabled - delete filteredSettings.syncFolderPath - delete filteredSettings.lastSyncTime - - fs.writeFileSync( - tempAppSettingsBackupPath, - JSON.stringify(filteredSettings, null, 2), - 'utf-8' - ) + this.emitBackupStatus('collecting') + const files: Record = {} + files[ZIP_PATHS.db] = new Uint8Array(fs.readFileSync(this.DB_PATH)) + files[ZIP_PATHS.appSettings] = new Uint8Array(fs.readFileSync(this.APP_SETTINGS_PATH)) + this.addOptionalFile(files, ZIP_PATHS.customPrompts, this.CUSTOM_PROMPTS_PATH) + this.addOptionalFile(files, ZIP_PATHS.systemPrompts, this.SYSTEM_PROMPTS_PATH) + this.addOptionalFile(files, ZIP_PATHS.mcpSettings, this.MCP_SETTINGS_PATH) + + const manifest = { + version: 1, + createdAt: timestamp, + files: Object.keys(files) } + files[ZIP_PATHS.manifest] = new Uint8Array( + Buffer.from(JSON.stringify(manifest, null, 2), 'utf-8') + ) - // 备份 MCP 设置 - if (fs.existsSync(this.MCP_SETTINGS_PATH)) { - fs.copyFileSync(this.MCP_SETTINGS_PATH, tempMcpSettingsBackupPath) - } + this.emitBackupStatus('compressing') + const zipData = zipSync(files, { level: 6 }) + fs.writeFileSync(tempZipPath, Buffer.from(zipData)) - // 备份模型配置文件 - if (fs.existsSync(this.MODEL_CONFIG_PATH)) { - fs.copyFileSync(this.MODEL_CONFIG_PATH, tempModelConfigBackupPath) + if (fs.existsSync(finalZipPath)) { + fs.unlinkSync(finalZipPath) } - - // 备份 provider_models 目录 - if (fs.existsSync(this.PROVIDER_MODELS_DIR_PATH)) { - // 确保临时目录存在 - fs.mkdirSync(tempProviderModelsBackupPath, { recursive: true }) - // 复制整个 provider_models 目录 - this.copyDirectory(this.PROVIDER_MODELS_DIR_PATH, tempProviderModelsBackupPath) + this.emitBackupStatus('finalizing') + fs.renameSync(tempZipPath, finalZipPath) + + const backupStats = fs.statSync(finalZipPath) + this.configPresenter.setLastSyncTime(timestamp) + eventBus.send(SYNC_EVENTS.BACKUP_COMPLETED, SendTarget.ALL_WINDOWS, timestamp) + completedTimestamp = timestamp + + return { fileName: backupFileName, createdAt: timestamp, size: backupStats.size } + } catch (error) { + if (fs.existsSync(tempZipPath)) { + fs.unlinkSync(tempZipPath) } + encounteredError = true + this.emitBackupStatus('error', { + message: (error as Error)?.message || 'sync.error.unknown' + }) + throw error + } finally { + this.isBackingUp = false + const extra: Record = {} + if (completedTimestamp) { + extra.lastSuccessfulBackupTime = completedTimestamp + } + if (encounteredError) { + extra.failed = true + } + this.emitBackupStatus('idle', extra) + } + } - // 检查临时文件是否成功创建 - if (!fs.existsSync(tempDbBackupPath)) { - throw new Error('sync.error.tempDbFailed') + private listenForChanges(): void { + const scheduleBackup = () => { + if (!this.configPresenter.getSyncEnabled()) { + return } + if (this.backupTimer) { + clearTimeout(this.backupTimer) + } + this.backupTimer = setTimeout(async () => { + if (!this.isBackingUp) { + try { + await this.performBackup() + } catch (error) { + console.error('auto backup failed:', error) + } + } + }, this.BACKUP_DELAY) + } + + eventBus.on(SYNC_EVENTS.DATA_CHANGED, scheduleBackup) + } + + private getBackupsDirectory(syncFolderPath: string): string { + return syncFolderPath + } + + private emitBackupStatus(status: BackupStatus, extra: Record = {}): void { + eventBus.send(SYNC_EVENTS.BACKUP_STATUS_CHANGED, SendTarget.ALL_WINDOWS, { + status, + previousStatus: this.currentBackupStatus, + ...extra + }) + this.currentBackupStatus = status + } + + private ensureSafeBackupFileName(fileName: string): string { + const normalized = fileName.replace(/\\/g, '/').trim() + if (!normalized) { + throw new Error('sync.error.noValidBackup') + } - if (!fs.existsSync(tempAppSettingsBackupPath)) { - throw new Error('sync.error.tempConfigFailed') + const baseName = path.posix.basename(normalized) + if (baseName !== normalized) { + throw new Error('sync.error.noValidBackup') + } + + if (!BACKUP_FILE_NAME_REGEX.test(baseName)) { + throw new Error('sync.error.noValidBackup') + } + + return baseName + } + + private addOptionalFile( + files: Record, + zipPath: string, + filePath: string + ): void { + if (fs.existsSync(filePath)) { + files[zipPath] = new Uint8Array(fs.readFileSync(filePath)) + } + } + + private extractBackupArchive(zipPath: string, targetDir: string): void { + const zipContent = new Uint8Array(fs.readFileSync(zipPath)) + const extracted = unzipSync(zipContent) + const resolvedTargetDir = path.resolve(targetDir) + + for (const entryName of Object.keys(extracted)) { + const fileContent = extracted[entryName] + if (!fileContent) { + continue } - if (!fs.existsSync(tempMcpSettingsBackupPath)) { - throw new Error('sync.error.tempMcpSettingsFailed') + const normalizedEntry = entryName.replace(/\\/g, '/') + if (!normalizedEntry) { + continue } - // 重命名临时文件为最终文件 - if (fs.existsSync(finalDbBackupPath)) { - fs.unlinkSync(finalDbBackupPath) + if (/^[A-Za-z]:/.test(normalizedEntry) || normalizedEntry.startsWith('/')) { + throw new Error('sync.error.noValidBackup') } - if (fs.existsSync(finalAppSettingsBackupPath)) { - fs.unlinkSync(finalAppSettingsBackupPath) + const segments = normalizedEntry.split('/') + const safeSegments: string[] = [] + for (const segment of segments) { + if (!segment || segment === '.') { + continue + } + if (segment === '..') { + throw new Error('sync.error.noValidBackup') + } + safeSegments.push(segment) } - // 如果存在之前的 provider_models 备份目录,删除它 - if (fs.existsSync(finalProviderModelsBackupPath)) { - this.removeDirectory(finalProviderModelsBackupPath) + if (safeSegments.length === 0) { + continue } - if (fs.existsSync(finalMcpSettingsBackupPath)) { - fs.unlinkSync(finalMcpSettingsBackupPath) + const isDirectoryEntry = normalizedEntry.endsWith('/') + const destination = path.resolve(resolvedTargetDir, ...safeSegments) + const relativeToTarget = path.relative(resolvedTargetDir, destination) + if (relativeToTarget.startsWith('..') || path.isAbsolute(relativeToTarget)) { + throw new Error('sync.error.noValidBackup') } - // 清理之前的模型配置文件备份 - if (fs.existsSync(finalModelConfigBackupPath)) { - fs.unlinkSync(finalModelConfigBackupPath) + if (isDirectoryEntry) { + fs.mkdirSync(destination, { recursive: true }) + continue } - // 确保临时文件存在后再执行重命名 - fs.renameSync(tempDbBackupPath, finalDbBackupPath) - fs.renameSync(tempAppSettingsBackupPath, finalAppSettingsBackupPath) - fs.renameSync(tempMcpSettingsBackupPath, finalMcpSettingsBackupPath) + fs.mkdirSync(path.dirname(destination), { recursive: true }) + fs.writeFileSync(destination, Buffer.from(fileContent)) + } + } + + private mergeAppSettingsPreservingSync(backupPath: string, targetPath: string): void { + if (!fs.existsSync(backupPath)) { + return + } - // 重命名模型配置文件 - if (fs.existsSync(tempModelConfigBackupPath)) { - fs.renameSync(tempModelConfigBackupPath, finalModelConfigBackupPath) - } + let backupSettingsRaw: string + try { + backupSettingsRaw = fs.readFileSync(backupPath, 'utf-8') + } catch (error) { + console.error('Failed to read backup app settings file:', error) + throw new Error('sync.error.noValidBackup') + } - // 重命名 provider_models 临时目录 - if (fs.existsSync(tempProviderModelsBackupPath)) { - fs.renameSync(tempProviderModelsBackupPath, finalProviderModelsBackupPath) + let backupSettings: Record + try { + const parsed = JSON.parse(backupSettingsRaw) + if (!parsed || typeof parsed !== 'object') { + throw new Error('sync.error.noValidBackup') } + backupSettings = parsed as Record + } catch (error) { + console.error('Failed to parse backup app settings JSON:', error) + throw new Error('sync.error.noValidBackup') + } - // 更新最后备份时间 - const now = Date.now() - this.configPresenter.setLastSyncTime(now) + const preservedSettings: Record = {} + preservedSettings.syncEnabled = this.configPresenter.getSyncEnabled() + preservedSettings.syncFolderPath = this.configPresenter.getSyncFolderPath() + preservedSettings.lastSyncTime = this.configPresenter.getLastSyncTime() - // 发送备份完成事件 - eventBus.send(SYNC_EVENTS.BACKUP_COMPLETED, SendTarget.ALL_WINDOWS, now) - } catch (error: unknown) { - console.error('备份过程出错:', error) - eventBus.send( - SYNC_EVENTS.BACKUP_ERROR, - SendTarget.ALL_WINDOWS, - (error as Error).message || 'sync.error.unknown' + const mergedSettings = { + ...backupSettings, + ...Object.fromEntries( + Object.entries(preservedSettings).filter( + ([, value]) => value !== undefined && value !== null + ) ) - throw error - } finally { - // 标记备份结束 - this.isBackingUp = false } + + fs.mkdirSync(path.dirname(targetPath), { recursive: true }) + fs.writeFileSync(targetPath, JSON.stringify(mergedSettings, null, 2), 'utf-8') } - /** - * 监听数据变更事件,触发备份计划 - */ - private listenForChanges(): void { - // 监听多种数据变更事件,使用防抖逻辑触发备份 - const scheduleBackup = () => { - // 如果同步功能未启用,不执行备份 - if (!this.configPresenter.getSyncEnabled()) { - return - } + private createTempBackup(originalPath: string, name: string): string | null { + if (!fs.existsSync(originalPath)) { + return null + } + const tempPath = path.join(app.getPath('temp'), `${name}.${Date.now()}.bak`) + this.copyFile(originalPath, tempPath) + return tempPath + } - // 清除现有定时器 - if (this.backupTimer) { - clearTimeout(this.backupTimer) - } + private copyFile(source: string, target: string): void { + fs.mkdirSync(path.dirname(target), { recursive: true }) + fs.copyFileSync(source, target) + } - // 设置新的定时器,延迟执行备份 - this.backupTimer = setTimeout(async () => { - if (!this.isBackingUp) { - try { - await this.performBackup() - } catch (error) { - console.error('自动备份失败:', error) - } + private restoreFromTempBackup(tempFiles: Record): void { + if (tempFiles.db) { + this.copyFile(tempFiles.db, this.DB_PATH) + } + if (tempFiles.appSettings) { + this.copyFile(tempFiles.appSettings, this.APP_SETTINGS_PATH) + } + if (tempFiles.customPrompts) { + this.copyFile(tempFiles.customPrompts, this.CUSTOM_PROMPTS_PATH) + } + if (tempFiles.systemPrompts) { + this.copyFile(tempFiles.systemPrompts, this.SYSTEM_PROMPTS_PATH) + } + if (tempFiles.mcpSettings) { + this.copyFile(tempFiles.mcpSettings, this.MCP_SETTINGS_PATH) + } + } + + private cleanupTempFiles(paths: Array): void { + for (const filePath of paths) { + if (filePath && fs.existsSync(filePath)) { + try { + fs.unlinkSync(filePath) + } catch (error) { + console.warn('Failed to remove temp file:', filePath, error) } - }, this.BACKUP_DELAY) + } } + } - // 监听消息相关变更 - eventBus.on(SYNC_EVENTS.DATA_CHANGED, scheduleBackup) + private removeDirectory(dirPath: string): void { + if (!fs.existsSync(dirPath)) { + return + } + const entries = fs.readdirSync(dirPath, { withFileTypes: true }) + for (const entry of entries) { + const entryPath = path.join(dirPath, entry.name) + if (entry.isDirectory()) { + this.removeDirectory(entryPath) + } else { + fs.unlinkSync(entryPath) + } + } + fs.rmdirSync(dirPath) } - /** - * 辅助方法:复制目录 - */ - private copyDirectory(source: string, target: string): void { - // 确保目标目录存在 - if (!fs.existsSync(target)) { - fs.mkdirSync(target, { recursive: true }) + private mergePromptStore(backupPath: string, targetPath: string): number { + const backupData = this.readPromptStore(backupPath) + if (!backupData) { + return 0 } + const targetData = this.readPromptStore(targetPath) || { prompts: [] } - // 读取源目录 - const entries = fs.readdirSync(source, { withFileTypes: true }) + const existingIds = new Set(targetData.prompts.map((prompt) => prompt.id).filter(Boolean)) + let added = 0 - // 复制每个文件和子目录 - for (const entry of entries) { - const srcPath = path.join(source, entry.name) - const destPath = path.join(target, entry.name) + for (const prompt of backupData.prompts) { + const id = prompt.id + if (!id || existingIds.has(id)) { + continue + } + targetData.prompts.push(prompt) + existingIds.add(id) + added++ + } - if (entry.isDirectory()) { - // 递归复制子目录 - this.copyDirectory(srcPath, destPath) - } else { - // 复制文件 - fs.copyFileSync(srcPath, destPath) + if (added > 0) { + fs.writeFileSync(targetPath, JSON.stringify(targetData, null, 2), 'utf-8') + } + return added + } + + private readPromptStore(filePath: string): PromptStore | null { + if (!fs.existsSync(filePath)) { + return null + } + try { + const content = fs.readFileSync(filePath, 'utf-8') + const parsed = JSON.parse(content) + if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.prompts)) { + return { prompts: [] } } + return parsed as PromptStore + } catch (error) { + console.warn('Failed to read prompt store:', filePath, error) + return { prompts: [] } } } - /** - * 辅助方法:删除目录及其内容 - */ - private removeDirectory(dirPath: string): void { - if (fs.existsSync(dirPath)) { - const entries = fs.readdirSync(dirPath, { withFileTypes: true }) - - for (const entry of entries) { - const fullPath = path.join(dirPath, entry.name) - if (entry.isDirectory()) { - this.removeDirectory(fullPath) - } else { - fs.unlinkSync(fullPath) + private mergeMcpSettings(backupPath: string, targetPath: string): void { + const backupSettings = this.readMcpSettings(backupPath) + if (!backupSettings) { + return + } + + const currentSettings = this.readMcpSettings(targetPath) || {} + const mergedServers: Record = currentSettings.mcpServers + ? { ...currentSettings.mcpServers } + : {} + + let addedServers = false + for (const [name, config] of Object.entries(backupSettings.mcpServers || {})) { + if (this.isKnowledgeMcp(name, config)) { + continue + } + if (!mergedServers[name]) { + mergedServers[name] = config + addedServers = true + } + } + + const currentDefaults = new Set(currentSettings.defaultServers || []) + let defaultsChanged = false + for (const serverName of backupSettings.defaultServers || []) { + const serverConfig = backupSettings.mcpServers?.[serverName] + if (serverConfig && !this.isKnowledgeMcp(serverName, serverConfig)) { + const beforeSize = currentDefaults.size + currentDefaults.add(serverName) + if (currentDefaults.size !== beforeSize) { + defaultsChanged = true } } + } + + const mergedSettings: McpSettings = { ...currentSettings } + mergedSettings.mcpServers = mergedServers + mergedSettings.defaultServers = Array.from(currentDefaults) + + let settingsChanged = false + for (const [key, value] of Object.entries(backupSettings)) { + if (key === 'mcpServers' || key === 'defaultServers') { + continue + } + if (mergedSettings[key] === undefined) { + mergedSettings[key] = value + settingsChanged = true + } + } + + if (addedServers || defaultsChanged || settingsChanged) { + fs.writeFileSync(targetPath, JSON.stringify(mergedSettings, null, 2), 'utf-8') + return + } + + if (!fs.existsSync(targetPath)) { + fs.writeFileSync(targetPath, JSON.stringify(mergedSettings, null, 2), 'utf-8') + } + } + + private readMcpSettings(filePath: string): McpSettings | null { + if (!fs.existsSync(filePath)) { + return null + } + try { + const content = fs.readFileSync(filePath, 'utf-8') + return JSON.parse(content) as McpSettings + } catch (error) { + console.warn('Failed to read MCP settings:', filePath, error) + return null + } + } - fs.rmdirSync(dirPath) + private isKnowledgeMcp(name: string, config: MCPServerConfig | undefined): boolean { + const normalizedName = name.toLowerCase() + if (normalizedName.includes('knowledge')) { + return true } + const command = typeof config?.command === 'string' ? config.command.toLowerCase() : '' + return command.includes('knowledge') } } diff --git a/src/main/presenter/tabPresenter.ts b/src/main/presenter/tabPresenter.ts index ebfcd57e9..af9bc15ef 100644 --- a/src/main/presenter/tabPresenter.ts +++ b/src/main/presenter/tabPresenter.ts @@ -484,6 +484,10 @@ export class TabPresenter implements ITabPresenter { return tabId ? this.tabWindowMap.get(tabId) : undefined } + getTabWindowId(tabId: number): number | undefined { + return this.tabWindowMap.get(tabId) + } + /** * 通知渲染进程更新标签列表 */ diff --git a/src/main/presenter/threadPresenter/conversationExporter.ts b/src/main/presenter/threadPresenter/conversationExporter.ts new file mode 100644 index 000000000..be4d89bd2 --- /dev/null +++ b/src/main/presenter/threadPresenter/conversationExporter.ts @@ -0,0 +1,572 @@ +import { AssistantMessageBlock, Message, UserMessageContent } from '@shared/chat' +import { CONVERSATION } from '../../../shared/presenter' +import { getNormalizedUserMessageText } from './messageContent' +import { conversationExportTemplates } from './templates/conversationExportTemplates' + +export type ConversationExportFormat = 'markdown' | 'html' | 'txt' + +export function generateExportFilename( + format: ConversationExportFormat, + timestamp: Date = new Date() +): string { + const extension = format === 'markdown' ? 'md' : format + const formattedTimestamp = timestamp + .toISOString() + .replace(/[:.]/g, '-') + .replace('T', '_') + .substring(0, 19) + + return `export_deepchat_${formattedTimestamp}.${extension}` +} + +export function buildConversationExportContent( + conversation: CONVERSATION, + messages: Message[], + format: ConversationExportFormat +): string { + switch (format) { + case 'markdown': + return exportToMarkdown(conversation, messages) + case 'html': + return exportToHtml(conversation, messages) + case 'txt': + return exportToText(conversation, messages) + default: + throw new Error(`Unsupported export format: ${format}`) + } +} + +function exportToMarkdown(conversation: CONVERSATION, messages: Message[]): string { + const lines: string[] = [] + + lines.push(`# ${conversation.title}`) + lines.push('') + lines.push(`**Export Time:** ${new Date().toLocaleString()}`) + lines.push(`**Conversation ID:** ${conversation.id}`) + lines.push(`**Message Count:** ${messages.length}`) + if (conversation.settings.modelId) { + lines.push(`**Model:** ${conversation.settings.modelId}`) + } + if (conversation.settings.providerId) { + lines.push(`**Provider:** ${conversation.settings.providerId}`) + } + lines.push('') + lines.push('---') + lines.push('') + + for (const message of messages) { + const messageTime = new Date(message.timestamp).toLocaleString() + + if (message.role === 'user') { + lines.push(`## 👤 用户 (${messageTime})`) + lines.push('') + + const userContent = message.content as UserMessageContent + const messageText = getNormalizedUserMessageText(userContent) + + lines.push(messageText) + lines.push('') + + if (userContent.files && userContent.files.length > 0) { + lines.push('**附件:**') + for (const file of userContent.files) { + lines.push(`- ${file.name ?? ''} (${file.mimeType ?? 'unknown'})`) + } + lines.push('') + } + + if (userContent.links && userContent.links.length > 0) { + lines.push('**链接:**') + for (const link of userContent.links) { + lines.push(`- ${link}`) + } + lines.push('') + } + } else if (message.role === 'assistant') { + lines.push(`## 🤖 助手 (${messageTime})`) + lines.push('') + + const assistantBlocks = message.content as AssistantMessageBlock[] + + for (const block of assistantBlocks) { + switch (block.type) { + case 'content': + if (block.content) { + lines.push(block.content) + lines.push('') + } + break + case 'reasoning_content': + if (block.content) { + lines.push('### 🤔 思考过程') + lines.push('') + lines.push('```') + lines.push(block.content) + lines.push('```') + lines.push('') + } + break + case 'tool_call': + if (block.tool_call) { + lines.push(`### 🔧 工具调用: ${block.tool_call.name ?? ''}`) + lines.push('') + if (block.tool_call.params) { + lines.push('**参数:**') + lines.push('```json') + try { + const params = JSON.parse(block.tool_call.params) + lines.push(JSON.stringify(params, null, 2)) + } catch { + lines.push(block.tool_call.params) + } + lines.push('```') + lines.push('') + } + if (block.tool_call.response) { + lines.push('**响应:**') + lines.push('```') + lines.push(block.tool_call.response) + lines.push('```') + lines.push('') + } + } + break + case 'search': + lines.push('### 🔍 网络搜索') + if (block.extra?.total !== undefined) { + lines.push(`找到 ${block.extra.total} 个搜索结果`) + } + lines.push('') + break + case 'image': + lines.push('### 🖼️ 图片') + lines.push('*[图片内容]*') + lines.push('') + break + case 'error': + if (block.content) { + lines.push('### ❌ 错误') + lines.push('') + lines.push(`\`${block.content}\``) + lines.push('') + } + break + case 'artifact-thinking': + if (block.content) { + lines.push('### 💭 创作思考') + lines.push('') + lines.push('```') + lines.push(block.content) + lines.push('```') + lines.push('') + } + break + } + } + } + + lines.push('---') + lines.push('') + } + + return lines.join('\n') +} + +function exportToHtml(conversation: CONVERSATION, messages: Message[]): string { + const lines: string[] = [] + const { html, styles, templates } = conversationExportTemplates + + const styleLines = styles.map((styleRule) => ` ${styleRule}`).join('\n') + lines.push( + ...renderTemplate(html.documentStart, { + title: escapeHtml(conversation.title), + styleLines + }) + ) + + const metaRows: string[] = [] + metaRows.push( + ...renderTemplate(templates.metaRow, { + label: '导出时间', + value: escapeHtml(new Date().toLocaleString()) + }) + ) + metaRows.push( + ...renderTemplate(templates.metaRow, { + label: '会话 ID', + value: escapeHtml(conversation.id) + }) + ) + metaRows.push( + ...renderTemplate(templates.metaRow, { + label: '消息数量', + value: escapeHtml(String(messages.length)) + }) + ) + if (conversation.settings.modelId) { + metaRows.push( + ...renderTemplate(templates.metaRow, { + label: '模型', + value: escapeHtml(conversation.settings.modelId) + }) + ) + } + if (conversation.settings.providerId) { + metaRows.push( + ...renderTemplate(templates.metaRow, { + label: '服务商', + value: escapeHtml(conversation.settings.providerId) + }) + ) + } + + lines.push( + ...renderTemplate(templates.header, { + title: escapeHtml(conversation.title), + metaRows: metaRows.join('\n') + }) + ) + + for (let index = 0; index < messages.length; index++) { + const message = messages[index] + const messageTime = escapeHtml(new Date(message.timestamp).toLocaleString()) + + if (message.role === 'user') { + const userContent = message.content as UserMessageContent + const messageText = getNormalizedUserMessageText(userContent) + + const attachmentItems = + userContent.files?.map((file) => + renderTemplate(templates.attachmentItem, { + name: escapeHtml(file.name ?? ''), + mime: escapeHtml(file.mimeType ?? 'unknown') + }) + ) ?? [] + + const linkItems = + userContent.links?.map((link) => { + const safeHref = sanitizeHref(link) + return renderTemplate(templates.linkItem, { + href: escapeHtml(safeHref), + label: escapeHtml(link) + }) + }) ?? [] + + const attachmentsSection = + attachmentItems.length > 0 + ? renderTemplate(templates.attachmentsSection, { + items: attachmentItems.flat().join('\n') + }).join('\n') + : '' + + const linksSection = + linkItems.length > 0 + ? renderTemplate(templates.linksSection, { + items: linkItems.flat().join('\n') + }).join('\n') + : '' + + lines.push( + ...renderTemplate(templates.userMessage, { + timestamp: messageTime, + content: formatInlineHtml(messageText), + attachmentsSection, + linksSection + }) + ) + } else if (message.role === 'assistant') { + const assistantBlocks = message.content as AssistantMessageBlock[] + const blockLines: string[] = [] + + for (const block of assistantBlocks) { + switch (block.type) { + case 'content': + if (block.content) { + blockLines.push( + ...renderTemplate(templates.assistantContent, { + content: formatInlineHtml(block.content) + }) + ) + } + break + case 'reasoning_content': + if (block.content) { + blockLines.push( + ...renderTemplate(templates.assistantReasoning, { + content: escapeHtml(block.content) + }) + ) + } + break + case 'artifact-thinking': + if (block.content) { + blockLines.push( + ...renderTemplate(templates.assistantArtifact, { + content: escapeHtml(block.content) + }) + ) + } + break + case 'tool_call': + if (block.tool_call) { + const toolName = + block.tool_call.name && block.tool_call.name.length > 0 + ? renderTemplate(templates.assistantToolName, { + value: escapeHtml(block.tool_call.name) + }).join('\n') + : '' + + let toolParams = '' + if (block.tool_call.params) { + let paramsContent = block.tool_call.params + try { + const parsed = JSON.parse(block.tool_call.params) + paramsContent = JSON.stringify(parsed, null, 2) + } catch { + // keep original params text if JSON.parse fails + } + toolParams = renderTemplate(templates.assistantToolParams, { + value: escapeHtml(paramsContent) + }).join('\n') + } + + const toolResponse = + block.tool_call.response && block.tool_call.response.length > 0 + ? renderTemplate(templates.assistantToolResponse, { + value: escapeHtml(block.tool_call.response) + }).join('\n') + : '' + + blockLines.push( + ...renderTemplate(templates.assistantToolCall, { + name: toolName, + params: toolParams, + response: toolResponse + }) + ) + } + break + case 'search': + blockLines.push( + ...renderTemplate(templates.assistantSearch, { + caption: + block.extra?.total !== undefined + ? renderTemplate(templates.assistantSearchCaption, { + total: escapeHtml(String(block.extra.total)) + }).join('\n') + : '' + }) + ) + break + case 'image': + blockLines.push(...renderTemplate(templates.assistantImage)) + break + case 'error': + if (block.content) { + blockLines.push( + ...renderTemplate(templates.assistantError, { + content: escapeHtml(block.content) + }) + ) + } + break + } + } + + lines.push( + ...renderTemplate(templates.assistantMessage, { + timestamp: messageTime, + assistantBlocks: blockLines.join('\n') + }) + ) + } + + if (index < messages.length - 1) { + lines.push(...renderTemplate(templates.divider)) + } + } + + lines.push(...renderTemplate(html.documentEnd)) + + return lines.join('\n') +} + +type TemplateInput = string | string[] + +function renderTemplate( + template: TemplateInput, + replacements: Record = {} +): string[] { + const source = Array.isArray(template) ? template : [template] + const output: string[] = [] + + for (const line of source) { + let rendered = line + + for (const [key, value] of Object.entries(replacements)) { + const token = `{{${key}}}` + if (rendered.includes(token)) { + rendered = rendered.split(token).join(value) + } + } + + rendered = rendered.replace(/{{\w+}}/g, '') + output.push(...rendered.split('\n')) + } + + return output +} + +function formatInlineHtml(content: string): string { + return escapeHtml(content).replace(/\n/g, '
') +} + +function sanitizeHref(link: string): string { + const trimmed = link?.trim() + if (!trimmed) { + return '#' + } + + const lower = trimmed.toLowerCase() + if ( + lower.startsWith('http://') || + lower.startsWith('https://') || + lower.startsWith('mailto:') || + lower.startsWith('#') + ) { + return trimmed + } + + // Allow relative URLs (no scheme) + if (!/^[a-z][\w+.-]*:/i.test(trimmed)) { + return trimmed + } + + return '#' +} + +function exportToText(conversation: CONVERSATION, messages: Message[]): string { + const lines: string[] = [] + + lines.push(`${conversation.title}`) + lines.push(''.padEnd(conversation.title.length, '=')) + lines.push('') + lines.push(`导出时间: ${new Date().toLocaleString()}`) + lines.push(`会话ID: ${conversation.id}`) + lines.push(`消息数量: ${messages.length}`) + if (conversation.settings.modelId) { + lines.push(`模型: ${conversation.settings.modelId}`) + } + if (conversation.settings.providerId) { + lines.push(`提供商: ${conversation.settings.providerId}`) + } + lines.push('') + lines.push(''.padEnd(80, '-')) + lines.push('') + + for (const message of messages) { + const messageTime = new Date(message.timestamp).toLocaleString() + + if (message.role === 'user') { + lines.push(`[用户] ${messageTime}`) + lines.push('') + + const userContent = message.content as UserMessageContent + const messageText = getNormalizedUserMessageText(userContent) + + lines.push(messageText) + lines.push('') + + if (userContent.files && userContent.files.length > 0) { + lines.push('附件:') + for (const file of userContent.files) { + lines.push(`- ${file.name} (${file.mimeType})`) + } + lines.push('') + } + + if (userContent.links && userContent.links.length > 0) { + lines.push('链接:') + for (const link of userContent.links) { + lines.push(`- ${link}`) + } + lines.push('') + } + } else if (message.role === 'assistant') { + lines.push(`[助手] ${messageTime}`) + lines.push('') + + const assistantBlocks = message.content as AssistantMessageBlock[] + + for (const block of assistantBlocks) { + switch (block.type) { + case 'content': + if (block.content) { + lines.push(block.content) + lines.push('') + } + break + case 'reasoning_content': + if (block.content) { + lines.push('[思考过程]') + lines.push(block.content) + lines.push('') + } + break + case 'tool_call': + if (block.tool_call) { + lines.push(`[工具调用] ${block.tool_call.name ?? ''}`) + if (block.tool_call.params) { + lines.push('参数:') + lines.push(block.tool_call.params) + } + if (block.tool_call.response) { + lines.push('响应:') + lines.push(block.tool_call.response) + } + lines.push('') + } + break + case 'search': + lines.push('[网络搜索]') + if (block.extra?.total !== undefined) { + lines.push(`找到 ${block.extra.total} 个搜索结果`) + } + lines.push('') + break + case 'image': + lines.push('[图片内容]') + lines.push('') + break + case 'error': + if (block.content) { + lines.push(`[错误] ${block.content}`) + lines.push('') + } + break + case 'artifact-thinking': + if (block.content) { + lines.push('[创作思考]') + lines.push(block.content) + lines.push('') + } + break + } + } + } + + lines.push(''.padEnd(80, '-')) + lines.push('') + } + + return lines.join('\n') +} + +function escapeHtml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} diff --git a/src/main/presenter/threadPresenter/fileContext.ts b/src/main/presenter/threadPresenter/fileContext.ts deleted file mode 100644 index 5983c3d06..000000000 --- a/src/main/presenter/threadPresenter/fileContext.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MessageFile } from '@shared/chat' - -export const getFileContext = (files: MessageFile[]) => { - return files.length > 0 - ? ` - - - ${files - .map( - (file) => ` - ${file.name} - ${file.mimeType} - ${file.metadata.fileSize} - ${file.path} - ${!file.mimeType.startsWith('image') ? file.content : ''} - ` - ) - .join('\n')} - - ` - : '' -} diff --git a/src/main/presenter/threadPresenter/index.ts b/src/main/presenter/threadPresenter/index.ts index 428c69d97..dbb03c5f1 100644 --- a/src/main/presenter/threadPresenter/index.ts +++ b/src/main/presenter/threadPresenter/index.ts @@ -11,10 +11,10 @@ import { IConfigPresenter, ILlmProviderPresenter, MCPToolResponse, + MCPToolDefinition, ChatMessage, - ChatMessageContent, LLMAgentEventData -} from '../../../shared/presenter' +} from '@shared/presenter' import { presenter } from '@/presenter' import { MessageManager } from './messageManager' import { eventBus, SendTarget } from '@/eventbus' @@ -25,53 +25,34 @@ import { SearchEngineTemplate, UserMessage, MessageFile, - UserMessageContent, - UserMessageTextBlock, - UserMessageMentionBlock, - UserMessageCodeBlock + UserMessageContent } from '@shared/chat' -import { ModelType } from '@shared/model' -import { approximateTokenSize } from 'tokenx' -import { generateSearchPrompt, SearchManager } from './searchManager' -import { getFileContext } from './fileContext' +import { SearchManager } from './searchManager' import { ContentEnricher } from './contentEnricher' import { CONVERSATION_EVENTS, STREAM_EVENTS, TAB_EVENTS } from '@/events' -import { DEFAULT_SETTINGS } from './const' import { nanoid } from 'nanoid' +import { + buildUserMessageContext, + formatUserMessageContent, + getNormalizedUserMessageText +} from './messageContent' +import { + preparePromptContent, + buildContinueToolCallContext, + buildPostToolExecutionContext +} from './promptBuilder' +import { + buildConversationExportContent, + generateExportFilename, + ConversationExportFormat +} from './conversationExporter' +import type { GeneratingMessageState } from './types' +import { finalizeAssistantMessageBlocks } from '@shared/chat/messageBlocks' +import { approximateTokenSize } from 'tokenx' +import { DEFAULT_SETTINGS } from './const' -interface GeneratingMessageState { - message: AssistantMessage - conversationId: string - startTime: number - firstTokenTime: number | null - promptTokens: number - reasoningStartTime: number | null - reasoningEndTime: number | null - lastReasoningTime: number | null - isSearching?: boolean - isCancelled?: boolean - totalUsage?: { - prompt_tokens: number - completion_tokens: number - total_tokens: number - context_length: number - } - // 统一的自适应内容处理 - adaptiveBuffer?: { - content: string - lastUpdateTime: number - updateCount: number - totalSize: number - isLargeContent: boolean - chunks?: string[] - currentChunkIndex?: number - // 精确追踪已发送内容的位置 - sentPosition: number // 已发送到渲染器的内容位置 - isProcessing?: boolean - } - flushTimeout?: NodeJS.Timeout - throttleTimeout?: NodeJS.Timeout - lastRendererUpdateTime?: number +export interface CreateConversationOptions { + forceNewAndActivate?: boolean } export class ThreadPresenter implements IThreadPresenter { @@ -81,11 +62,11 @@ export class ThreadPresenter implements IThreadPresenter { private configPresenter: IConfigPresenter private searchManager: SearchManager private generatingMessages: Map = new Map() + private activeConversationIds: Map = new Map() + private fetchThreadLength = 300 public searchAssistantModel: MODEL_META | null = null public searchAssistantProviderId: string | null = null private searchingMessages: Set = new Set() - private activeConversationIds: Map = new Map() - private fetchThreadLength: number = 300 constructor( sqlitePresenter: ISQLitePresenter, @@ -100,8 +81,9 @@ export class ThreadPresenter implements IThreadPresenter { // 监听Tab关闭事件,清理绑定关系 eventBus.on(TAB_EVENTS.CLOSED, (tabId: number) => { - if (this.activeConversationIds.has(tabId)) { - this.activeConversationIds.delete(tabId) + const activeConversationId = this.getActiveConversationIdSync(tabId) + if (activeConversationId) { + this.clearActiveConversation(tabId, { notify: true }) console.log(`ThreadPresenter: Cleaned up conversation binding for closed tab ${tabId}.`) } }) @@ -113,6 +95,11 @@ export class ThreadPresenter implements IThreadPresenter { this.messageManager.initializeUnfinishedMessages() } + setSearchAssistantModel(model: MODEL_META, providerId: string): void { + this.searchAssistantModel = model + this.searchAssistantProviderId = providerId + } + /** * 新增:查找指定会话ID所在的Tab ID * @param conversationId 会话ID @@ -121,45 +108,33 @@ export class ThreadPresenter implements IThreadPresenter { async findTabForConversation(conversationId: string): Promise { for (const [tabId, activeId] of this.activeConversationIds.entries()) { if (activeId === conversationId) { - // 验证该tab是否还真实存在 - const tabView = await presenter.tabPresenter.getTab(tabId) - if (tabView && !tabView.webContents.isDestroyed()) { - return tabId + try { + const tabView = await presenter.tabPresenter.getTab(tabId) + if (tabView && !tabView.webContents.isDestroyed()) { + return tabId + } + } catch (error) { + console.error('Error finding tab for conversation:', error) } } } return null } - private async getTabWindowType(tabId: number): Promise<'floating' | 'main' | 'unknown'> { - try { - const tabView = await presenter.tabPresenter.getTab(tabId) - if (!tabView) { - return 'unknown' - } - const windowId = presenter.tabPresenter['tabWindowMap'].get(tabId) - return windowId ? 'main' : 'floating' - } catch (error) { - console.error('Error determining tab window type:', error) - return 'unknown' - } - } - async handleLLMAgentError(msg: LLMAgentEventData) { const { eventId, error } = msg const state = this.generatingMessages.get(eventId) if (state) { - // 刷新剩余缓冲内容 if (state.adaptiveBuffer) { await this.flushAdaptiveBuffer(eventId) } - // 清理缓冲相关资源 this.cleanupContentBuffer(state) await this.messageManager.handleMessageError(eventId, String(error)) this.generatingMessages.delete(eventId) } + this.searchingMessages.delete(eventId) eventBus.sendToRenderer(STREAM_EVENTS.ERROR, SendTarget.ALL_WINDOWS, msg) } @@ -167,11 +142,12 @@ export class ThreadPresenter implements IThreadPresenter { const { eventId, userStop } = msg const state = this.generatingMessages.get(eventId) if (state) { - console.log( - `[ThreadPresenter] Handling LLM agent end for message: ${eventId}, userStop: ${userStop}` - ) + if (state.adaptiveBuffer) { + await this.flushAdaptiveBuffer(eventId) + } + + this.cleanupContentBuffer(state) - // 检查是否有未处理的权限请求 const hasPendingPermissions = state.message.content.some( (block) => block.type === 'action' && @@ -180,11 +156,6 @@ export class ThreadPresenter implements IThreadPresenter { ) if (hasPendingPermissions) { - console.log( - `[ThreadPresenter] Message ${eventId} has pending permissions, keeping in generating state` - ) - // 保持消息在generating状态,等待权限响应 - // 但是要更新非权限块为success状态 state.message.content.forEach((block) => { if ( !(block.type === 'action' && block.action_type === 'tool_call_permission') && @@ -194,48 +165,473 @@ export class ThreadPresenter implements IThreadPresenter { } }) await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) + this.searchingMessages.delete(eventId) return } - console.log(`[ThreadPresenter] Finalizing message ${eventId} - no pending permissions`) - - // 正常完成流程 - await this.finalizeMessage(state, eventId, userStop || false) + await this.finalizeMessage(state, eventId, Boolean(userStop)) } + this.searchingMessages.delete(eventId) eventBus.sendToRenderer(STREAM_EVENTS.END, SendTarget.ALL_WINDOWS, msg) } - // 清理所有缓冲相关资源 - private cleanupContentBuffer(state: GeneratingMessageState): void { - if (state.flushTimeout) { - clearTimeout(state.flushTimeout) - state.flushTimeout = undefined + async handleLLMAgentResponse(msg: LLMAgentEventData) { + const currentTime = Date.now() + const { + eventId, + content, + reasoning_content, + tool_call_id, + tool_call_name, + tool_call_params, + tool_call_response, + maximum_tool_calls_reached, + tool_call_server_name, + tool_call_server_icons, + tool_call_server_description, + tool_call_response_raw, + tool_call, + permission_request, + totalUsage, + image_data + } = msg + const state = this.generatingMessages.get(eventId) + if (!state) { + return } - if (state.throttleTimeout) { - clearTimeout(state.throttleTimeout) - state.throttleTimeout = undefined + + if (state.firstTokenTime === null && (content || reasoning_content)) { + state.firstTokenTime = currentTime + await this.messageManager.updateMessageMetadata(eventId, { + firstTokenTime: currentTime - state.startTime + }) } - state.adaptiveBuffer = undefined - state.lastRendererUpdateTime = undefined + if (totalUsage) { + state.totalUsage = totalUsage + state.promptTokens = totalUsage.prompt_tokens + } + + if (maximum_tool_calls_reached) { + this.finalizeLastBlock(state) + state.message.content.push({ + type: 'action', + content: 'common.error.maximumToolCallsReached', + status: 'success', + timestamp: currentTime, + action_type: 'maximum_tool_calls_reached', + tool_call: { + id: tool_call_id, + name: tool_call_name, + params: tool_call_params, + server_name: tool_call_server_name, + server_icons: tool_call_server_icons, + server_description: tool_call_server_description + }, + extra: { + needContinue: true + } + }) + await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) + return + } + + if (reasoning_content) { + if (state.reasoningStartTime === null) { + state.reasoningStartTime = currentTime + await this.messageManager.updateMessageMetadata(eventId, { + reasoningStartTime: currentTime - state.startTime + }) + } + state.lastReasoningTime = currentTime + } + + const lastBlock = state.message.content[state.message.content.length - 1] + + if (tool_call_response_raw && tool_call === 'end') { + try { + const hasSearchResults = + Array.isArray(tool_call_response_raw.content) && + tool_call_response_raw.content.some( + (item: { type: string; resource?: { mimeType: string } }) => + item?.type === 'resource' && + item?.resource?.mimeType === 'application/deepchat-webpage' + ) + + if (hasSearchResults && Array.isArray(tool_call_response_raw.content)) { + const searchResults = tool_call_response_raw.content + .filter( + (item: { + type: string + resource?: { mimeType: string; text: string; uri?: string } + }) => + item.type === 'resource' && + item.resource?.mimeType === 'application/deepchat-webpage' + ) + .map((item: { resource: { text: string; uri?: string } }) => { + try { + const blobContent = JSON.parse(item.resource.text) as { + title?: string + url?: string + content?: string + icon?: string + } + return { + title: blobContent.title || '', + url: blobContent.url || item.resource.uri || '', + content: blobContent.content || '', + description: blobContent.content || '', + icon: blobContent.icon || '' + } + } catch (e) { + console.error('解析搜索结果失败:', e) + return null + } + }) + .filter(Boolean) + + if (searchResults.length > 0) { + const searchId = nanoid() + const pages = searchResults + .filter((item) => item && (item.icon || item.favicon)) + .slice(0, 6) + .map((item) => ({ + url: item?.url ?? '', + icon: item?.icon || item?.favicon || '' + })) + + const searchBlock: AssistantMessageBlock = { + id: searchId, + type: 'search', + content: '', + status: 'success', + timestamp: currentTime, + extra: { + total: searchResults.length, + searchId, + pages, + label: tool_call_name || 'web_search', + name: tool_call_name || 'web_search', + engine: tool_call_server_name || undefined, + provider: tool_call_server_name || undefined + } + } + + this.finalizeLastBlock(state) + state.message.content.push(searchBlock) + + for (const result of searchResults) { + await this.sqlitePresenter.addMessageAttachment( + eventId, + 'search_result', + JSON.stringify({ + title: result?.title || '', + url: result?.url || '', + content: result?.content || '', + description: result?.description || '', + icon: result?.icon || result?.favicon || '', + rank: typeof result?.rank === 'number' ? result.rank : undefined, + searchId + }) + ) + } + + await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) + } + } + } catch (error) { + console.error('处理搜索结果时出错:', error) + } + } + + if (tool_call) { + if (tool_call === 'start') { + this.finalizeLastBlock(state) + state.message.content.push({ + type: 'tool_call', + content: '', + status: 'loading', + timestamp: currentTime, + tool_call: { + id: tool_call_id, + name: tool_call_name, + params: tool_call_params || '', + server_name: tool_call_server_name, + server_icons: tool_call_server_icons, + server_description: tool_call_server_description + } + }) + } else if (tool_call === 'update') { + const toolCallBlock = state.message.content.find( + (block) => + block.type === 'tool_call' && + block.tool_call?.id === tool_call_id && + block.status === 'loading' + ) + + if (toolCallBlock && toolCallBlock.type === 'tool_call' && toolCallBlock.tool_call) { + toolCallBlock.tool_call.params = tool_call_params || '' + } + } else if (tool_call === 'running') { + const toolCallBlock = state.message.content.find( + (block) => + block.type === 'tool_call' && + block.tool_call?.id === tool_call_id && + block.status === 'loading' + ) + + if (toolCallBlock && toolCallBlock.type === 'tool_call') { + if (toolCallBlock.tool_call) { + toolCallBlock.tool_call.params = tool_call_params || '' + toolCallBlock.tool_call.server_name = tool_call_server_name + toolCallBlock.tool_call.server_icons = tool_call_server_icons + toolCallBlock.tool_call.server_description = tool_call_server_description + } + } + } else if (tool_call === 'permission-required') { + // Define allowed permission types + const ALLOWED_PERMISSION_TYPES = ['read', 'write', 'all'] as const + type PermissionType = (typeof ALLOWED_PERMISSION_TYPES)[number] + + // Validate and sanitize permission type + let permissionType: PermissionType = 'read' // Default to 'read' for safety + const requestedType = permission_request?.permissionType + + if (typeof requestedType === 'string') { + const normalizedType = requestedType.toLowerCase() + if (ALLOWED_PERMISSION_TYPES.includes(normalizedType as PermissionType)) { + permissionType = normalizedType as PermissionType + } else { + console.warn( + `[ThreadPresenter] Invalid permission type received: "${requestedType}". Defaulting to "read". Allowed types: ${ALLOWED_PERMISSION_TYPES.join(', ')}` + ) + } + } else if (requestedType !== undefined) { + console.warn( + `[ThreadPresenter] Permission type is not a string: ${typeof requestedType}. Defaulting to "read".` + ) + } + + const extra: Record = { + needsUserAction: true, + permissionType + } + + const serverName = permission_request?.serverName || tool_call_server_name + if (serverName) { + extra.serverName = serverName + } + + const toolName = permission_request?.toolName || tool_call_name + if (toolName) { + extra.toolName = toolName + } + + if (permission_request) { + extra.permissionRequest = JSON.stringify(permission_request) + } + + if (lastBlock && lastBlock.type === 'tool_call' && lastBlock.tool_call) { + lastBlock.status = 'success' + } + + this.finalizeLastBlock(state) + const permissionExtra: Record = { + needsUserAction: true + } + + if (permission_request?.permissionType) { + permissionExtra.permissionType = permission_request.permissionType + } + if (permission_request) { + permissionExtra.permissionRequest = JSON.stringify(permission_request) + if (permission_request.toolName) { + permissionExtra.toolName = permission_request.toolName + } + if (permission_request.serverName) { + permissionExtra.serverName = permission_request.serverName + } + } else { + if (tool_call_name) { + permissionExtra.toolName = tool_call_name + } + if (tool_call_server_name) { + permissionExtra.serverName = tool_call_server_name + } + } + + state.message.content.push({ + type: 'action', + content: tool_call_response || '', + status: 'pending', + timestamp: currentTime, + action_type: 'tool_call_permission', + tool_call: { + id: tool_call_id, + name: tool_call_name, + params: tool_call_params || '', + server_name: tool_call_server_name, + server_icons: tool_call_server_icons, + server_description: tool_call_server_description + }, + extra: permissionExtra + }) + + if (state) { + state.pendingToolCall = { + id: tool_call_id || '', + name: tool_call_name || '', + params: tool_call_params || '', + serverName: tool_call_server_name, + serverIcons: tool_call_server_icons, + serverDescription: tool_call_server_description + } + } + + this.searchingMessages.add(eventId) + state.isSearching = true + } else if (tool_call === 'permission-granted') { + if ( + lastBlock && + lastBlock.type === 'action' && + lastBlock.action_type === 'tool_call_permission' + ) { + lastBlock.status = 'granted' + lastBlock.content = tool_call_response || '' + if (lastBlock.extra) { + lastBlock.extra.needsUserAction = false + if ( + !lastBlock.extra.grantedPermissions && + typeof lastBlock.extra.permissionType === 'string' + ) { + lastBlock.extra.grantedPermissions = lastBlock.extra.permissionType + } + } + } + this.searchingMessages.delete(eventId) + state.isSearching = false + if (state) { + state.pendingToolCall = { + id: tool_call_id || '', + name: tool_call_name || '', + params: tool_call_params || '', + serverName: tool_call_server_name, + serverIcons: tool_call_server_icons, + serverDescription: tool_call_server_description + } + } + } else if (tool_call === 'permission-denied') { + if ( + lastBlock && + lastBlock.type === 'action' && + lastBlock.action_type === 'tool_call_permission' + ) { + lastBlock.status = 'denied' + lastBlock.content = tool_call_response || '' + if (lastBlock.extra) { + lastBlock.extra.needsUserAction = false + } + } + this.searchingMessages.delete(eventId) + state.isSearching = false + if (state) { + state.pendingToolCall = undefined + } + } else if (tool_call === 'continue') { + if ( + lastBlock && + lastBlock.type === 'action' && + lastBlock.action_type === 'tool_call_permission' + ) { + lastBlock.status = 'success' + } + } else if (tool_call === 'end') { + const toolCallBlock = state.message.content.find( + (block) => + block.type === 'tool_call' && + block.tool_call?.id === tool_call_id && + block.status === 'loading' + ) + + if (toolCallBlock && toolCallBlock.type === 'tool_call') { + toolCallBlock.status = 'success' + if (toolCallBlock.tool_call) { + toolCallBlock.tool_call.response = tool_call_response || '' + } + } + + if ( + lastBlock && + lastBlock.type === 'action' && + lastBlock.action_type === 'tool_call_permission' + ) { + lastBlock.status = 'success' + } + this.searchingMessages.delete(eventId) + state.isSearching = false + if (state) { + state.pendingToolCall = undefined + } + } + } + + if (image_data) { + const imageBlock: AssistantMessageBlock = { + type: 'image', + status: 'success', + timestamp: currentTime, + content: image_data + } + state.message.content.push(imageBlock) + } + + if (content) { + if (!lastBlock || lastBlock.type !== 'content' || lastBlock.status !== 'loading') { + this.finalizeLastBlock(state) + state.message.content.push({ + type: 'content', + content: content || '', + status: 'loading', + timestamp: currentTime + }) + } else if (lastBlock.type === 'content') { + lastBlock.content += content + } + } + + if (reasoning_content) { + if (!lastBlock || lastBlock.type !== 'reasoning_content') { + this.finalizeLastBlock(state) + state.message.content.push({ + type: 'reasoning_content', + content: reasoning_content || '', + status: 'loading', + timestamp: currentTime + }) + } else if (lastBlock.type === 'reasoning_content') { + lastBlock.content += reasoning_content + } + } + + await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) + } + + private finalizeLastBlock(state: GeneratingMessageState): void { + finalizeAssistantMessageBlocks(state.message.content) } - // 完成消息的通用方法 private async finalizeMessage( state: GeneratingMessageState, eventId: string, userStop: boolean ): Promise { - // 将所有块设为success状态,但保留权限块的状态 state.message.content.forEach((block) => { if (block.type === 'action' && block.action_type === 'tool_call_permission') { - // 权限块保持其当前状态(granted/denied/error) return } block.status = 'success' }) - // 计算completion tokens let completionTokens = 0 if (state.totalUsage) { completionTokens = state.totalUsage.completion_tokens @@ -251,7 +647,6 @@ export class ThreadPresenter implements IThreadPresenter { } } - // 检查是否有内容块 const hasContentBlock = state.message.content.some( (block) => block.type === 'content' || @@ -260,7 +655,6 @@ export class ThreadPresenter implements IThreadPresenter { block.type === 'image' ) - // 如果没有内容块,添加错误信息 if (!hasContentBlock && !userStop) { state.message.content.push({ type: 'error', @@ -272,12 +666,12 @@ export class ThreadPresenter implements IThreadPresenter { const totalTokens = state.promptTokens + completionTokens const generationTime = Date.now() - (state.firstTokenTime ?? state.startTime) - const tokensPerSecond = completionTokens / (generationTime / 1000) + const safeMs = Math.max(1, generationTime) + const tokensPerSecond = completionTokens / (safeMs / 1000) const contextUsage = state?.totalUsage?.context_length ? (totalTokens / state.totalUsage.context_length) * 100 : 0 - // 如果有reasoning_content,记录结束时间 const metadata: Partial = { totalTokens, inputTokens: state.promptTokens, @@ -293,24 +687,14 @@ export class ThreadPresenter implements IThreadPresenter { metadata.reasoningEndTime = state.lastReasoningTime - state.startTime } - // 刷新剩余缓冲内容 - if (state.adaptiveBuffer) { - await this.flushAdaptiveBuffer(eventId) - } - - // 清理缓冲相关资源 - this.cleanupContentBuffer(state) - - // 更新消息的usage信息 await this.messageManager.updateMessageMetadata(eventId, metadata) await this.messageManager.updateMessageStatus(eventId, 'sent') await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) this.generatingMessages.delete(eventId) + this.searchingMessages.delete(eventId) - // 处理标题更新和会话更新 await this.handleConversationUpdates(state) - // 广播消息生成完成事件 const finalMessage = await this.messageManager.getMessage(eventId) if (finalMessage) { eventBus.sendToMain(CONVERSATION_EVENTS.MESSAGE_GENERATED, { @@ -320,141 +704,102 @@ export class ThreadPresenter implements IThreadPresenter { } } - // 处理会话更新和标题生成 private async handleConversationUpdates(state: GeneratingMessageState): Promise { - const conversation = await this.sqlitePresenter.getConversation(state.conversationId) - let titleUpdated = false + const conversation = await this.getConversation(state.conversationId) if (conversation.is_new === 1) { try { - this.summaryTitles(undefined, state.conversationId).then((title) => { - if (title) { - this.renameConversation(state.conversationId, title).then(() => { - titleUpdated = true - }) - } + const title = await this.summaryTitles(undefined, state.conversationId) + if (title) { + await this.renameConversation(state.conversationId, title) + return + } + } catch (error) { + console.error('[ThreadPresenter] Failed to summarize title', { + conversationId: state.conversationId, + err: error }) - } catch (e) { - console.error('Failed to summarize title in main process:', e) } } - if (!titleUpdated) { - this.sqlitePresenter - .updateConversation(state.conversationId, { - updatedAt: Date.now() - }) - .then(() => { - console.log('updated conv time', state.conversationId) - }) - await this.broadcastThreadListUpdate() - } + await this.sqlitePresenter.updateConversation(state.conversationId, { + updatedAt: Date.now() + }) + await this.broadcastThreadListUpdate() } - // 释放缓冲的内容 - - // 统一的自适应内容刷新 - private async flushAdaptiveBuffer(eventId: string): Promise { - const state = this.generatingMessages.get(eventId) - if (!state?.adaptiveBuffer) return - - const buffer = state.adaptiveBuffer - const now = Date.now() - - // 清理超时 + private cleanupContentBuffer(state: GeneratingMessageState): void { if (state.flushTimeout) { clearTimeout(state.flushTimeout) state.flushTimeout = undefined } - - // 处理缓冲的内容 - 只发送从 sentPosition 开始的新内容 - if (buffer.content && buffer.sentPosition < buffer.content.length) { - const newContent = buffer.content.slice(buffer.sentPosition) - if (newContent) { - await this.processBufferedContent(eventId, newContent, now) - // 更新已发送位置 - buffer.sentPosition = buffer.content.length - } + if (state.throttleTimeout) { + clearTimeout(state.throttleTimeout) + state.throttleTimeout = undefined } - - // 清理缓冲 state.adaptiveBuffer = undefined + state.lastRendererUpdateTime = undefined } - // 优化的自适应内容处理 - 核心逻辑 (当前未使用) - // private async addToAdaptiveBuffer(eventId: string, content: string): Promise { - // // 方法保留以备将来使用 - // } - - // 分块大内容 - 使用更小的分块避免UI阻塞 - private splitLargeContent(content: string): string[] { - const chunks: string[] = [] - let maxChunkSize = 4096 // 默认4KB + private async flushAdaptiveBuffer(eventId: string): Promise { + const state = this.generatingMessages.get(eventId) + if (!state?.adaptiveBuffer) return - // 对于图片base64内容,使用非常小的分块 - if (content.includes('data:image/')) { - maxChunkSize = 512 // 图片内容使用512字节分块 - } + const buffer = state.adaptiveBuffer + const now = Date.now() - // 对于超长内容,进一步减小分块 - if (content.length > 50000) { - maxChunkSize = Math.min(maxChunkSize, 256) + if (state.flushTimeout) { + clearTimeout(state.flushTimeout) + state.flushTimeout = undefined } - for (let i = 0; i < content.length; i += maxChunkSize) { - chunks.push(content.slice(i, i + maxChunkSize)) + try { + if (buffer.content && buffer.sentPosition < buffer.content.length) { + const newContent = buffer.content.slice(buffer.sentPosition) + if (newContent) { + await this.processBufferedContent(state, eventId, newContent, now) + buffer.sentPosition = buffer.content.length + } + } + } catch (error) { + console.error('[ContentBuffer] ERROR flushing adaptive buffer', { + eventId, + err: error + }) + throw error + } finally { + state.adaptiveBuffer = undefined } - - return chunks } - // 智能判断是否需要分块处理 - 优化阈值判断 - private shouldSplitContent(content: string): boolean { - const sizeThreshold = 8192 // 8KB - 适中的阈值 - const hasBase64Image = content.includes('data:image/') && content.includes('base64,') - const hasLargeBase64 = hasBase64Image && content.length > 5120 // 图片内容超过5KB才分块 - - return content.length > sizeThreshold || hasLargeBase64 - } - - // 处理缓冲的内容 - 优化异步处理 private async processBufferedContent( + state: GeneratingMessageState, eventId: string, content: string, currentTime: number ): Promise { - const state = this.generatingMessages.get(eventId) - if (!state) return - const buffer = state.adaptiveBuffer - // 如果是大内容,使用分块处理 if (buffer?.isLargeContent) { - await this.processLargeContentAsynchronously(eventId, content, currentTime) + await this.processLargeContentAsynchronously(state, eventId, content, currentTime) return } - // 正常内容处理 - await this.processNormalContent(eventId, content, currentTime) + await this.processNormalContent(state, eventId, content, currentTime) } - // 异步处理大内容 - 避免阻塞主进程 private async processLargeContentAsynchronously( + state: GeneratingMessageState, eventId: string, content: string, currentTime: number ): Promise { - const state = this.generatingMessages.get(eventId) - if (!state) return - const buffer = state.adaptiveBuffer if (!buffer) return - // 设置处理状态 buffer.isProcessing = true try { - // 动态分块 - 只处理传入的新增内容 const chunks = this.splitLargeContent(content) const totalChunks = chunks.length @@ -462,7 +807,6 @@ export class ThreadPresenter implements IThreadPresenter { `[ThreadPresenter] Processing ${totalChunks} chunks asynchronously for ${content.length} bytes` ) - // 初始化或获取内容块 const lastBlock = state.message.content[state.message.content.length - 1] let contentBlock: any @@ -479,20 +823,16 @@ export class ThreadPresenter implements IThreadPresenter { state.message.content.push(contentBlock) } - // 批量处理分块,每次处琅5个 const batchSize = 5 for (let batchStart = 0; batchStart < chunks.length; batchStart += batchSize) { const batchEnd = Math.min(batchStart + batchSize, chunks.length) const batch = chunks.slice(batchStart, batchEnd) - // 合并当前批次的内容 const batchContent = batch.join('') contentBlock.content += batchContent - // 更新数据库 await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) - // 发送渲染器事件 const eventData: any = { eventId, content: batchContent, @@ -506,7 +846,6 @@ export class ThreadPresenter implements IThreadPresenter { eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, eventData) - // 每批次之间的延迟,让出event loop if (batchEnd < chunks.length) { await new Promise((resolve) => setImmediate(resolve)) } @@ -516,20 +855,16 @@ export class ThreadPresenter implements IThreadPresenter { } catch (error) { console.error('[ThreadPresenter] Error in processLargeContentAsynchronously:', error) } finally { - // 清理处理状态 buffer.isProcessing = false } } - // 处理普通内容 private async processNormalContent( + state: GeneratingMessageState, eventId: string, content: string, currentTime: number ): Promise { - const state = this.generatingMessages.get(eventId) - if (!state) return - const lastBlock = state.message.content[state.message.content.length - 1] if (lastBlock && lastBlock.type === 'content') { @@ -544,397 +879,28 @@ export class ThreadPresenter implements IThreadPresenter { }) } - // 只更新数据库,不额外发送到渲染器(避免重复发送) await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) } - // 完成最后一个块的状态 - private finalizeLastBlock(state: GeneratingMessageState): void { - const lastBlock = - state.message.content.length > 0 - ? state.message.content[state.message.content.length - 1] - : undefined - - if (lastBlock) { - if ( - lastBlock.type === 'action' && - lastBlock.action_type === 'tool_call_permission' && - lastBlock.status === 'pending' - ) { - lastBlock.status = 'granted' - return - } - if (!(lastBlock.type === 'tool_call' && lastBlock.status === 'loading')) { - lastBlock.status = 'success' - } - } - } - - // 统一的数据库和渲染器更新 (当前未使用) - // private async updateMessageAndRenderer(eventId: string, content: string, currentTime: number, chunkInfo?: any): Promise { - // // 方法保留以备将来使用 - // } - - async handleLLMAgentResponse(msg: LLMAgentEventData) { - const currentTime = Date.now() - const { - eventId, - content, - reasoning_content, - tool_call_id, - tool_call_name, - tool_call_params, - tool_call_response, - maximum_tool_calls_reached, - tool_call_server_name, - tool_call_server_icons, - tool_call_server_description, - tool_call_response_raw, - tool_call, - totalUsage, - image_data - } = msg - const state = this.generatingMessages.get(eventId) - if (state) { - // 使用保护逻辑 - const finalizeLastBlock = () => { - const lastBlock = - state.message.content.length > 0 - ? state.message.content[state.message.content.length - 1] - : undefined - if (lastBlock) { - if ( - lastBlock.type === 'action' && - lastBlock.action_type === 'tool_call_permission' && - lastBlock.status === 'pending' - ) { - lastBlock.status = 'granted' - return - } - // 只有当上一个块不是一个正在等待结果的工具调用时,才将其标记为成功 - if (!(lastBlock.type === 'tool_call' && lastBlock.status === 'loading')) { - lastBlock.status = 'success' - } - } - } - - // 记录第一个token的时间 - if (state.firstTokenTime === null && (content || reasoning_content)) { - state.firstTokenTime = currentTime - await this.messageManager.updateMessageMetadata(eventId, { - firstTokenTime: currentTime - state.startTime - }) - } - if (totalUsage) { - state.totalUsage = totalUsage - state.promptTokens = totalUsage.prompt_tokens - } - - // 处理工具调用达到最大次数的情况 - if (maximum_tool_calls_reached) { - finalizeLastBlock() // 使用保护逻辑 - state.message.content.push({ - type: 'action', - content: 'common.error.maximumToolCallsReached', - status: 'success', - timestamp: currentTime, - action_type: 'maximum_tool_calls_reached', - tool_call: { - id: tool_call_id, - name: tool_call_name, - params: tool_call_params, - server_name: tool_call_server_name, - server_icons: tool_call_server_icons, - server_description: tool_call_server_description - }, - extra: { - needContinue: true - } - }) - await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) - return - } - - // 处理reasoning_content的时间戳 - if (reasoning_content) { - if (state.reasoningStartTime === null) { - state.reasoningStartTime = currentTime - await this.messageManager.updateMessageMetadata(eventId, { - reasoningStartTime: currentTime - state.startTime - }) - } - state.lastReasoningTime = currentTime - } - - const lastBlock = state.message.content[state.message.content.length - 1] - - // 检查tool_call_response_raw中是否包含搜索结果 - if (tool_call_response_raw && tool_call === 'end') { - try { - // 检查返回的内容中是否有deepchat-webpage类型的资源 - // 确保content是数组才调用some方法 - const hasSearchResults = - Array.isArray(tool_call_response_raw.content) && - tool_call_response_raw.content.some( - (item: { type: string; resource?: { mimeType: string } }) => - item?.type === 'resource' && - item?.resource?.mimeType === 'application/deepchat-webpage' - ) - - if (hasSearchResults && Array.isArray(tool_call_response_raw.content)) { - // 解析搜索结果 - const searchResults = tool_call_response_raw.content - .filter( - (item: { - type: string - resource?: { mimeType: string; text: string; uri?: string } - }) => - item.type === 'resource' && - item.resource?.mimeType === 'application/deepchat-webpage' - ) - .map((item: { resource: { text: string; uri?: string } }) => { - try { - const blobContent = JSON.parse(item.resource.text) as { - title?: string - url?: string - content?: string - icon?: string - } - return { - title: blobContent.title || '', - url: blobContent.url || item.resource.uri || '', - content: blobContent.content || '', - description: blobContent.content || '', - icon: blobContent.icon || '' - } - } catch (e) { - console.error('解析搜索结果失败:', e) - return null - } - }) - .filter(Boolean) - - if (searchResults.length > 0) { - const searchId = nanoid() - const pages = searchResults - .filter((item) => item && (item.icon || item.favicon)) - .slice(0, 6) - .map((item) => ({ - url: item?.url ?? '', - icon: item?.icon || item?.favicon || '' - })) - - const searchBlock: AssistantMessageBlock = { - id: searchId, - type: 'search', - content: '', - status: 'success', - timestamp: currentTime, - extra: { - total: searchResults.length, - searchId, - pages, - label: tool_call_name || 'web_search', - name: tool_call_name || 'web_search', - engine: tool_call_server_name || undefined, - provider: tool_call_server_name || undefined - } - } - - finalizeLastBlock() - state.message.content.push(searchBlock) - - for (const result of searchResults) { - await this.sqlitePresenter.addMessageAttachment( - eventId, - 'search_result', - JSON.stringify({ - title: result?.title || '', - url: result?.url || '', - content: result?.content || '', - description: result?.description || '', - icon: result?.icon || result?.favicon || '', - rank: typeof result?.rank === 'number' ? result.rank : undefined, - searchId - }) - ) - } - - await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) - } - } - } catch (error) { - console.error('处理搜索结果时出错:', error) - } - } - - // 处理工具调用 - if (tool_call) { - if (tool_call === 'start') { - // 创建新的工具调用块 - finalizeLastBlock() // 使用保护逻辑 - state.message.content.push({ - type: 'tool_call', - content: '', - status: 'loading', - timestamp: currentTime, - tool_call: { - id: tool_call_id, - name: tool_call_name, - params: tool_call_params || '', - server_name: tool_call_server_name, - server_icons: tool_call_server_icons, - server_description: tool_call_server_description - } - }) - } else if (tool_call === 'update') { - // 更新工具调用参数 - const toolCallBlock = state.message.content.find( - (block) => - block.type === 'tool_call' && - block.tool_call?.id === tool_call_id && - block.status === 'loading' - ) - - if (toolCallBlock && toolCallBlock.type === 'tool_call' && toolCallBlock.tool_call) { - toolCallBlock.tool_call.params = tool_call_params || '' - } - } else if (tool_call === 'running') { - // 工具调用正在执行 - const toolCallBlock = state.message.content.find( - (block) => - block.type === 'tool_call' && - block.tool_call?.id === tool_call_id && - block.status === 'loading' - ) - - if (toolCallBlock && toolCallBlock.type === 'tool_call') { - // 保持 loading 状态,但更新工具信息 - if (toolCallBlock.tool_call) { - toolCallBlock.tool_call.params = tool_call_params || '' - toolCallBlock.tool_call.server_name = tool_call_server_name - toolCallBlock.tool_call.server_icons = tool_call_server_icons - toolCallBlock.tool_call.server_description = tool_call_server_description - } - } - } else if (tool_call === 'permission-required') { - // 处理权限请求:创建权限请求块 - // 注意:不调用finalizeLastBlock,因为工具调用还没有完成,在等待权限 - - // 从 msg 中获取权限请求信息 - const { permission_request } = msg - - state.message.content.push({ - type: 'action', - action_type: 'tool_call_permission', - content: - typeof tool_call_response === 'string' - ? tool_call_response - : 'Permission required for this operation', - status: 'pending', - timestamp: currentTime, - tool_call: { - id: tool_call_id, - name: tool_call_name, - params: tool_call_params || '', - server_name: tool_call_server_name, - server_icons: tool_call_server_icons, - server_description: tool_call_server_description - }, - extra: { - permissionType: permission_request?.permissionType || 'write', - serverName: permission_request?.serverName || tool_call_server_name || '', - toolName: permission_request?.toolName || tool_call_name || '', - needsUserAction: true, - permissionRequest: JSON.stringify( - permission_request || { - toolName: tool_call_name || '', - serverName: tool_call_server_name || '', - permissionType: 'write' as const, - description: 'Permission required for this operation' - } - ) - } - }) - } else if (tool_call === 'end' || tool_call === 'error') { - // 查找对应的工具调用块 - const toolCallBlock = state.message.content.find( - (block) => - block.type === 'tool_call' && - ((tool_call_id && block.tool_call?.id === tool_call_id) || - block.tool_call?.name === tool_call_name) && - block.status === 'loading' - ) + private splitLargeContent(content: string): string[] { + const chunks: string[] = [] + let maxChunkSize = 4096 - if (toolCallBlock && toolCallBlock.type === 'tool_call') { - if (tool_call === 'error') { - toolCallBlock.status = 'error' - if (toolCallBlock.tool_call) { - if (typeof tool_call_response === 'string') { - toolCallBlock.tool_call.response = tool_call_response || '执行失败' - } else { - toolCallBlock.tool_call.response = JSON.stringify(tool_call_response) - } - } - } else { - toolCallBlock.status = 'success' - if (toolCallBlock.tool_call) { - if (typeof tool_call_response === 'string') { - toolCallBlock.tool_call.response = tool_call_response - } else { - toolCallBlock.tool_call.response = JSON.stringify(tool_call_response) - } - } - } - } - } - } else if (image_data) { - // 处理图像数据 - finalizeLastBlock() // 使用保护逻辑 - state.message.content.push({ - type: 'image', - content: 'image', - status: 'success', - timestamp: currentTime, - image_data: image_data - }) - } else if (content) { - // 简化的直接内容处理 - await this.processContentDirectly(state.message.id, content, currentTime) - } + if (content.includes('data:image/')) { + maxChunkSize = 512 + } - // 处理推理内容 - if (reasoning_content) { - if (lastBlock && lastBlock.type === 'reasoning_content') { - lastBlock.content += reasoning_content - if (lastBlock.reasoning_time) { - lastBlock.reasoning_time.end = currentTime - } - } else { - finalizeLastBlock() // 使用保护逻辑 - state.message.content.push({ - type: 'reasoning_content', - content: reasoning_content, - status: 'loading', - reasoning_time: { - start: currentTime, - end: currentTime - }, - timestamp: currentTime - }) - } - } + if (content.length > 50000) { + maxChunkSize = Math.min(maxChunkSize, 256) + } - // 更新消息内容 - await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) + for (let i = 0; i < content.length; i += maxChunkSize) { + chunks.push(content.slice(i, i + maxChunkSize)) } - eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, msg) - } - setSearchAssistantModel(model: MODEL_META, providerId: string) { - this.searchAssistantModel = model - this.searchAssistantProviderId = providerId + return chunks } + async getSearchEngines(): Promise { return this.searchManager.getEngines() } @@ -968,141 +934,246 @@ export class ThreadPresenter implements IThreadPresenter { } } - async renameConversation(conversationId: string, title: string): Promise { - await this.sqlitePresenter.renameConversation(conversationId, title) - await this.broadcastThreadListUpdate() // 必须广播 + getActiveConversationIdSync(tabId: number): string | null { + return this.activeConversationIds.get(tabId) || null + } - const conversation = await this.getConversation(conversationId) + getTabsByConversation(conversationId: string): number[] { + return Array.from(this.activeConversationIds.entries()) + .filter(([, id]) => id === conversationId) + .map(([tabId]) => tabId) + } - // 新增:找到与此 conversationId 关联的 tabId - let tabId: number | undefined - for (const [key, value] of this.activeConversationIds.entries()) { - if (value === conversationId) { - tabId = key - break + clearActiveConversation(tabId: number, options: { notify?: boolean } = {}): void { + if (!this.activeConversationIds.has(tabId)) { + return + } + this.activeConversationIds.delete(tabId) + if (options.notify) { + eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, { tabId }) + } + } + + clearConversationBindings(conversationId: string): void { + for (const [tabId, activeId] of this.activeConversationIds.entries()) { + if (activeId === conversationId) { + this.activeConversationIds.delete(tabId) + eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, { + tabId + }) } } + } - // 新增:发出事件通知UI更新标题 - if (tabId !== undefined) { - const windowId = presenter.tabPresenter['tabWindowMap'].get(tabId) - eventBus.sendToRenderer(TAB_EVENTS.TITLE_UPDATED, SendTarget.ALL_WINDOWS, { - tabId, - conversationId, - title: conversation.title, - windowId // 附带 windowId - }) + private async getTabWindowType(tabId: number): Promise<'floating' | 'main' | 'unknown'> { + try { + const tabView = await presenter.tabPresenter.getTab(tabId) + if (!tabView) { + return 'unknown' + } + const windowId = presenter.tabPresenter.getTabWindowId(tabId) + return windowId ? 'main' : 'floating' + } catch (error) { + console.error('Error determining tab window type:', error) + return 'unknown' } + } - return conversation + async setActiveConversation(conversationId: string, tabId: number): Promise { + const existingTabId = await this.findTabForConversation(conversationId) + + if (existingTabId !== null && existingTabId !== tabId) { + console.log( + `Conversation ${conversationId} is already open in tab ${existingTabId}. Switching to it.` + ) + const currentTabType = await this.getTabWindowType(tabId) + const existingTabType = await this.getTabWindowType(existingTabId) + + if (currentTabType !== existingTabType) { + this.activeConversationIds.delete(existingTabId) + eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, { + tabId: existingTabId + }) + this.activeConversationIds.set(tabId, conversationId) + eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, { + conversationId, + tabId + }) + return + } + + await presenter.tabPresenter.switchTab(existingTabId) + return + } + + const conversation = await this.getConversation(conversationId) + if (!conversation) { + throw new Error(`Conversation ${conversationId} not found`) + } + + if (this.activeConversationIds.get(tabId) === conversationId) { + return + } + + this.activeConversationIds.set(tabId, conversationId) + eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, { + conversationId, + tabId + }) + } + + async getActiveConversation(tabId: number): Promise { + const conversationId = this.activeConversationIds.get(tabId) + if (!conversationId) { + return null + } + return this.getConversation(conversationId) } + + async getConversation(conversationId: string): Promise { + return await this.sqlitePresenter.getConversation(conversationId) + } + async createConversation( title: string, settings: Partial = {}, tabId: number, - options: { forceNewAndActivate?: boolean } = {} // 新增参数,允许强制创建新会话 + options: CreateConversationOptions = {} ): Promise { - console.log('createConversation', title, settings) + let latestConversation: CONVERSATION | null = null - const latestConversation = await this.getLatestConversation() + try { + latestConversation = await this.getLatestConversation() - // 只有在非强制模式下,才执行空会话的单例检查 - if (!options.forceNewAndActivate) { - if (latestConversation) { - const { list: messages } = await this.getMessages(latestConversation.id, 1, 1) + if (!options.forceNewAndActivate && latestConversation) { + const { list: messages } = await this.messageManager.getMessageThread( + latestConversation.id, + 1, + 1 + ) if (messages.length === 0) { await this.setActiveConversation(latestConversation.id, tabId) return latestConversation.id } } - } - let defaultSettings = DEFAULT_SETTINGS - if (latestConversation?.settings) { - defaultSettings = { ...latestConversation.settings } - defaultSettings.systemPrompt = '' - defaultSettings.reasoningEffort = undefined - defaultSettings.enableSearch = undefined - defaultSettings.forcedSearch = undefined - defaultSettings.searchStrategy = undefined - } - Object.keys(settings).forEach((key) => { - if (settings[key] === undefined || settings[key] === null || settings[key] === '') { - delete settings[key] + let defaultSettings = DEFAULT_SETTINGS + if (latestConversation?.settings) { + defaultSettings = { ...latestConversation.settings } + defaultSettings.systemPrompt = '' + defaultSettings.reasoningEffort = undefined + defaultSettings.enableSearch = undefined + defaultSettings.forcedSearch = undefined + defaultSettings.searchStrategy = undefined } - }) - const mergedSettings = { ...defaultSettings, ...settings } - const defaultModelsSettings = this.configPresenter.getModelConfig( - mergedSettings.modelId, - mergedSettings.providerId - ) - if (defaultModelsSettings) { - mergedSettings.maxTokens = defaultModelsSettings.maxTokens - mergedSettings.contextLength = defaultModelsSettings.contextLength - mergedSettings.temperature = defaultModelsSettings.temperature ?? 0.7 - if (settings.thinkingBudget === undefined) { - mergedSettings.thinkingBudget = defaultModelsSettings.thinkingBudget + + const sanitizedSettings: Partial = { ...settings } + Object.keys(sanitizedSettings).forEach((key) => { + const typedKey = key as keyof CONVERSATION_SETTINGS + const value = sanitizedSettings[typedKey] + if (value === undefined || value === null || value === '') { + delete sanitizedSettings[typedKey] + } + }) + + const mergedSettings = { ...defaultSettings } + const previewSettings = { ...mergedSettings, ...sanitizedSettings } + + const defaultModelsSettings = this.configPresenter.getModelConfig( + previewSettings.modelId, + previewSettings.providerId + ) + + if (defaultModelsSettings) { + if (defaultModelsSettings.maxTokens !== undefined) { + mergedSettings.maxTokens = defaultModelsSettings.maxTokens + } + if (defaultModelsSettings.contextLength !== undefined) { + mergedSettings.contextLength = defaultModelsSettings.contextLength + } + mergedSettings.temperature = defaultModelsSettings.temperature ?? 0.7 + if ( + sanitizedSettings.thinkingBudget === undefined && + defaultModelsSettings.thinkingBudget !== undefined + ) { + mergedSettings.thinkingBudget = defaultModelsSettings.thinkingBudget + } } - } - if (settings.artifacts) { - mergedSettings.artifacts = settings.artifacts - } - if (settings.maxTokens) { - mergedSettings.maxTokens = settings.maxTokens - } - if (settings.temperature !== undefined && settings.temperature !== null) { - mergedSettings.temperature = settings.temperature - } - if (settings.contextLength) { - mergedSettings.contextLength = settings.contextLength - } - if (settings.systemPrompt) { - mergedSettings.systemPrompt = settings.systemPrompt - } - const conversationId = await this.sqlitePresenter.createConversation(title, mergedSettings) - // 根据 forceNewAndActivate 标志决定激活行为 - if (options.forceNewAndActivate) { - // 强制模式:直接为当前 tabId 激活新会话,不进行任何检查 - this.activeConversationIds.set(tabId, conversationId) - eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, { - conversationId, - tabId + Object.assign(mergedSettings, sanitizedSettings) + + if (mergedSettings.temperature === undefined || mergedSettings.temperature === null) { + mergedSettings.temperature = defaultModelsSettings?.temperature ?? 0.7 + } + + const conversationId = await this.sqlitePresenter.createConversation(title, mergedSettings) + + if (options.forceNewAndActivate) { + this.activeConversationIds.set(tabId, conversationId) + eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, { + conversationId, + tabId + }) + } else { + await this.setActiveConversation(conversationId, tabId) + } + + await this.broadcastThreadListUpdate() + return conversationId + } catch (error) { + console.error('ThreadPresenter: Failed to create conversation', { + title, + tabId, + options, + latestConversationId: latestConversation?.id, + errorMessage: error instanceof Error ? error.message : String(error), + errorStack: error instanceof Error ? error.stack : undefined }) - } else { - // 默认模式:保持原有的、防止重复打开的激活逻辑 - await this.setActiveConversation(conversationId, tabId) + throw error } - - await this.broadcastThreadListUpdate() // 必须广播 - return conversationId } - async deleteConversation(conversationId: string): Promise { - await this.sqlitePresenter.deleteConversation(conversationId) + async renameConversation(conversationId: string, title: string): Promise { + await this.sqlitePresenter.renameConversation(conversationId, title) + await this.broadcastThreadListUpdate() - // 作为兜底,确保所有与此会话相关的绑定都被移除 - for (const [tabId, activeId] of this.activeConversationIds.entries()) { - if (activeId === conversationId) { - this.activeConversationIds.delete(tabId) + const conversation = await this.getConversation(conversationId) + + let tabId: number | undefined + for (const [key, value] of this.activeConversationIds.entries()) { + if (value === conversationId) { + tabId = key + break } } - await this.broadcastThreadListUpdate() // 必须广播 + if (tabId !== undefined) { + const windowId = presenter.tabPresenter.getTabWindowId(tabId) + eventBus.sendToRenderer(TAB_EVENTS.TITLE_UPDATED, SendTarget.ALL_WINDOWS, { + tabId, + conversationId, + title: conversation.title, + windowId + }) + } + + return conversation } - async getConversation(conversationId: string): Promise { - return await this.sqlitePresenter.getConversation(conversationId) + async deleteConversation(conversationId: string): Promise { + await this.sqlitePresenter.deleteConversation(conversationId) + this.clearConversationBindings(conversationId) + await this.broadcastThreadListUpdate() } async toggleConversationPinned(conversationId: string, pinned: boolean): Promise { await this.sqlitePresenter.updateConversation(conversationId, { is_pinned: pinned ? 1 : 0 }) - await this.broadcastThreadListUpdate() // 必须广播 + await this.broadcastThreadListUpdate() } async updateConversationTitle(conversationId: string, title: string): Promise { await this.sqlitePresenter.updateConversation(conversationId, { title }) - await this.broadcastThreadListUpdate() // 必须广播 + await this.broadcastThreadListUpdate() } async updateConversationSettings( @@ -1111,29 +1182,30 @@ export class ThreadPresenter implements IThreadPresenter { ): Promise { const conversation = await this.getConversation(conversationId) const mergedSettings = { ...conversation.settings } - for (const key in settings) { - if (settings[key] !== undefined) { - mergedSettings[key] = settings[key] - } - } - console.log('updateConversationSettings', mergedSettings) - // 检查是否有 modelId 的变化 - if (settings.modelId && settings.modelId !== conversation.settings.modelId) { - // 获取模型配置 + + const sanitizedOverrides = Object.fromEntries( + Object.entries(settings).filter(([, value]) => value !== undefined) + ) as Partial + Object.assign(mergedSettings, sanitizedOverrides) + + const modelChanged = + (settings.modelId !== undefined && settings.modelId !== conversation.settings.modelId) || + (settings.providerId !== undefined && + settings.providerId !== conversation.settings.providerId) + + if (modelChanged) { const modelConfig = this.configPresenter.getModelConfig( mergedSettings.modelId, mergedSettings.providerId ) - console.log('check model default config', modelConfig) if (modelConfig) { - // 如果当前设置小于推荐值,则使用推荐值 mergedSettings.maxTokens = modelConfig.maxTokens mergedSettings.contextLength = modelConfig.contextLength } } await this.sqlitePresenter.updateConversation(conversationId, { settings: mergedSettings }) - await this.broadcastThreadListUpdate() // 必须广播 + await this.broadcastThreadListUpdate() } async getConversationList( @@ -1144,80 +1216,63 @@ export class ThreadPresenter implements IThreadPresenter { } async loadMoreThreads(): Promise<{ hasMore: boolean; total: number }> { - // 获取会话总数 const total = await this.sqlitePresenter.getConversationCount() - - // 检查是否还有更多会话可以加载 const hasMore = this.fetchThreadLength < total if (hasMore) { - // 增加 fetchThreadLength,每次增加 500 this.fetchThreadLength = Math.min(this.fetchThreadLength + 300, total) - - // 广播更新的会话列表 await this.broadcastThreadListUpdate() } return { hasMore: this.fetchThreadLength < total, total } } - async setActiveConversation(conversationId: string, tabId: number): Promise { - // 【核心修正】由主进程负责全部决策(防重和自动切换逻辑) - const existingTabId = await this.findTabForConversation(conversationId) + async broadcastThreadListUpdate(): Promise { + const result = await this.sqlitePresenter.getConversationList(1, this.fetchThreadLength) - // 如果会话已在其他Tab打开,并且不是当前Tab,则切换到那个Tab - if (existingTabId !== null && existingTabId !== tabId) { - console.log( - `Conversation ${conversationId} is already open in tab ${existingTabId}. Switching to it.` - ) - // 命令TabPresenter切换到已存在的Tab - const currentTabType = await this.getTabWindowType(tabId) - const existingTabType = await this.getTabWindowType(existingTabId) - if (currentTabType !== existingTabType) { - this.activeConversationIds.delete(existingTabId) - eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, { - tabId: existingTabId - }) - this.activeConversationIds.set(tabId, conversationId) - eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, { - conversationId, - tabId - }) - return + const pinnedConversations: CONVERSATION[] = [] + const normalConversations: CONVERSATION[] = [] + + result.list.forEach((conv) => { + if (conv.is_pinned === 1) { + pinnedConversations.push(conv) } else { - await presenter.tabPresenter.switchTab(existingTabId) - // 注意:这里不应该再为 requesting tab (即 tabId) 设置 activeConversationId - // 也不需要发送ACTIVATED事件,因为tab-session的绑定关系没有改变。 - // switchTab 自身会处理UI的激活。 - return + normalConversations.push(conv) } + }) + + pinnedConversations.sort((a, b) => b.updatedAt - a.updatedAt) + normalConversations.sort((a, b) => b.updatedAt - a.updatedAt) + + const groupedThreads: Map = new Map() + + if (pinnedConversations.length > 0) { + groupedThreads.set('Pinned', pinnedConversations) } - // 如果会话未在其他Tab打开,或者是请求激活当前Tab已绑定的会话,则正常执行绑定 - const conversation = await this.getConversation(conversationId) - if (conversation) { - // 检查当前Tab是否已经绑定了这个会话,避免不必要的事件广播 - if (this.activeConversationIds.get(tabId) === conversationId) { - return // 状态未改变,无需操作 + normalConversations.forEach((conv) => { + const date = new Date(conv.updatedAt).toISOString().split('T')[0] + if (!groupedThreads.has(date)) { + groupedThreads.set(date, []) } + groupedThreads.get(date)!.push(conv) + }) - this.activeConversationIds.set(tabId, conversationId) - // 广播事件,通知所有渲染进程UI更新 - eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, { - conversationId, - tabId - }) - } else { - throw new Error(`Conversation ${conversationId} not found`) - } + const finalGroupedList = Array.from(groupedThreads.entries()).map(([dt, dtThreads]) => ({ + dt, + dtThreads + })) + + eventBus.sendToRenderer( + CONVERSATION_EVENTS.LIST_UPDATED, + SendTarget.ALL_WINDOWS, + finalGroupedList + ) } - async getActiveConversation(tabId: number): Promise { - const conversationId = this.activeConversationIds.get(tabId) - if (!conversationId) { - return null - } - return this.getConversation(conversationId) + private async getLatestConversation(): Promise { + const result = await this.getConversationList(1, 1) + return result.list[0] || null } async getMessages( @@ -1230,83 +1285,11 @@ export class ThreadPresenter implements IThreadPresenter { async getContextMessages(conversationId: string): Promise { const conversation = await this.getConversation(conversationId) - // 计算需要获取的消息数量(假设每条消息平均300字) let messageCount = Math.ceil(conversation.settings.contextLength / 300) if (messageCount < 2) { messageCount = 2 } - const messages = await this.messageManager.getContextMessages(conversationId, messageCount) - - // 确保消息列表以用户消息开始 - while (messages.length > 0 && messages[0].role !== 'user') { - messages.shift() - } - - return messages.map((msg) => { - if (msg.role === 'user') { - const newMsg = { ...msg } - const msgContent = newMsg.content as UserMessageContent - if (msgContent.content) { - ;(newMsg.content as UserMessageContent).text = this.formatUserMessageContent( - msgContent.content - ) - } - return newMsg - } else { - return msg - } - }) - } - - private formatUserMessageContent( - msgContentBlock: (UserMessageTextBlock | UserMessageMentionBlock | UserMessageCodeBlock)[] - ) { - return msgContentBlock - .map((block) => { - if (block.type === 'mention') { - if (block.category === 'resources') { - return `@${block.content}` - } else if (block.category === 'tools') { - return `@${block.id}` - } else if (block.category === 'files') { - return `@${block.id}` - } else if (block.category === 'prompts') { - try { - // 尝试解析prompt内容 - const promptData = JSON.parse(block.content) - // 如果包含messages数组,尝试提取其中的文本内容 - if (promptData && Array.isArray(promptData.messages)) { - const messageTexts = promptData.messages - .map((msg) => { - if (typeof msg.content === 'string') { - return msg.content - } else if (msg.content && msg.content.type === 'text') { - return msg.content.text - } else { - // 对于其他类型的内容(如图片等),返回空字符串或特定标记 - return `[${msg.content?.type || 'content'}]` - } - }) - .filter(Boolean) - .join('\n') - return `@${block.id} ${messageTexts || block.content}` - } - } catch (e) { - // 如果解析失败,直接返回原始内容 - console.log('解析prompt内容失败:', e) - } - // 默认返回原内容 - return `@${block.id} ${block.content}` - } - return `@${block.id}` - } else if (block.type === 'text') { - return block.content - } else if (block.type === 'code') { - return `\`\`\`${block.content}\`\`\`` - } - return '' - }) - .join('') + return this.messageManager.getContextMessages(conversationId, messageCount) } async clearContext(conversationId: string): Promise { @@ -1434,25 +1417,7 @@ export class ThreadPresenter implements IThreadPresenter { * @returns 历史消息列表,按时间正序排列 */ private async getMessageHistory(messageId: string, limit: number = 100): Promise { - const message = await this.messageManager.getMessage(messageId) - if (!message) { - throw new Error('找不到指定的消息') - } - - const { list: messages } = await this.messageManager.getMessageThread( - message.conversationId, - 1, - limit * 2 - ) - - // 找到目标消息在列表中的位置 - const targetIndex = messages.findIndex((msg) => msg.id === messageId) - if (targetIndex === -1) { - return [message] - } - - // 返回目标消息之前的消息(包括目标消息) - return messages.slice(Math.max(0, targetIndex - limit + 1), targetIndex + 1) + return this.messageManager.getMessageHistory(messageId, limit) } private async rewriteUserSearchQuery( @@ -1581,7 +1546,8 @@ export class ThreadPresenter implements IThreadPresenter { .map((msg) => { if (msg.role === 'user') { const content = msg.content as UserMessageContent - return `user: ${content.text}${getFileContext(content.files)}` + const userContext = buildUserMessageContext(content) + return `user: ${userContext}` } else if (msg.role === 'assistant') { let finalContent = 'assistant: ' const content = msg.content as AssistantMessageBlock[] @@ -1808,17 +1774,18 @@ export class ThreadPresenter implements IThreadPresenter { this.throwIfCancelled(state.message.id) // 4. 准备提示内容 - const { finalContent, promptTokens } = await this.preparePromptContent( + const { finalContent, promptTokens } = await preparePromptContent({ conversation, userContent, contextMessages, searchResults, urlResults, userMessage, - vision, - vision ? imageFiles : [], - modelConfig.functionCall - ) + vision: Boolean(vision), + imageFiles: vision ? imageFiles : [], + supportsFunctionCall: modelConfig.functionCall, + modelType: modelConfig.type + }) // 检查是否已被取消 this.throwIfCancelled(state.message.id) @@ -1976,17 +1943,18 @@ export class ThreadPresenter implements IThreadPresenter { } = conversation.settings const modelConfig = this.configPresenter.getModelConfig(modelId, providerId) - const { finalContent, promptTokens } = await this.preparePromptContent( + const { finalContent, promptTokens } = await preparePromptContent({ conversation, - 'continue', + userContent: 'continue', contextMessages, - null, // 不进行搜索 - [], // 没有 URL 结果 + searchResults: null, // 不进行搜索 + urlResults: [], // 没有 URL 结果 userMessage, - false, - [], // 没有图片文件 - modelConfig.functionCall - ) + vision: false, + imageFiles: [], // 没有图片文件 + supportsFunctionCall: modelConfig.functionCall, + modelType: modelConfig.type + }) // 8. 更新生成状态 await this.updateGenerationState(state, promptTokens) @@ -2162,7 +2130,7 @@ export class ThreadPresenter implements IThreadPresenter { if (userMessage.role === 'user') { const msgContent = userMessage.content as UserMessageContent if (msgContent.content && !msgContent.text) { - msgContent.text = this.formatUserMessageContent(msgContent.content) + msgContent.text = formatUserMessageContent(msgContent.content) } } @@ -2181,17 +2149,11 @@ export class ThreadPresenter implements IThreadPresenter { imageFiles: MessageFile[] // 图片文件列表 }> { // 处理文本内容 - const userContent = ` - ${ - userMessage.content.content - ? this.formatUserMessageContent(userMessage.content.content) - : userMessage.content.text - } - ${getFileContext(userMessage.content.files)} - ` + const userContent = buildUserMessageContext(userMessage.content) // 从用户消息中提取并丰富URL内容 - const urlResults = await ContentEnricher.extractAndEnrichUrls(userMessage.content.text) + const normalizedText = getNormalizedUserMessageText(userMessage.content) + const urlResults = await ContentEnricher.extractAndEnrichUrls(normalizedText) // 提取图片文件 @@ -2207,496 +2169,6 @@ export class ThreadPresenter implements IThreadPresenter { return { userContent, urlResults, imageFiles } } - // 准备提示内容 - private async preparePromptContent( - conversation: CONVERSATION, - userContent: string, - contextMessages: Message[], - searchResults: SearchResult[] | null, - urlResults: SearchResult[], - userMessage: Message, - vision: boolean, - imageFiles: MessageFile[], - supportsFunctionCall: boolean, - modelType?: ModelType - ): Promise<{ - finalContent: ChatMessage[] - promptTokens: number - }> { - const { systemPrompt, contextLength, artifacts, enabledMcpTools } = conversation.settings - - // 判断是否为图片生成模型 - const isImageGeneration = modelType === ModelType.ImageGeneration - - // 图片生成模型不使用搜索、系统提示词和MCP工具 - const searchPrompt = - !isImageGeneration && searchResults ? generateSearchPrompt(userContent, searchResults) : '' - const enrichedUserMessage = - !isImageGeneration && urlResults.length > 0 - ? '\n\n' + ContentEnricher.enrichUserMessageWithUrlContent(userContent, urlResults) - : '' - - // 处理系统提示词,添加当前时间信息 - const finalSystemPrompt = this.enhanceSystemPromptWithDateTime(systemPrompt, isImageGeneration) - - // 计算token数量(使用处理后的系统提示词) - const searchPromptTokens = searchPrompt ? approximateTokenSize(searchPrompt ?? '') : 0 - const systemPromptTokens = - !isImageGeneration && finalSystemPrompt ? approximateTokenSize(finalSystemPrompt ?? '') : 0 - const userMessageTokens = approximateTokenSize(userContent + enrichedUserMessage) - // 图片生成模型不使用MCP工具 - const mcpTools = !isImageGeneration - ? await presenter.mcpPresenter.getAllToolDefinitions(enabledMcpTools) - : [] - const mcpToolsTokens = mcpTools.reduce( - (acc, tool) => acc + approximateTokenSize(JSON.stringify(tool)), - 0 - ) - // 计算剩余可用的上下文长度 - const reservedTokens = - searchPromptTokens + systemPromptTokens + userMessageTokens + mcpToolsTokens - const remainingContextLength = contextLength - reservedTokens - - // 选择合适的上下文消息 - const selectedContextMessages = this.selectContextMessages( - contextMessages, - userMessage, - remainingContextLength - ) - - // 格式化消息 - const formattedMessages = this.formatMessagesForCompletion( - selectedContextMessages, - isImageGeneration ? '' : finalSystemPrompt, // 图片生成模型不使用系统提示词 - artifacts, - searchPrompt, - userContent, - enrichedUserMessage, - imageFiles, - vision, - supportsFunctionCall - ) - - // 合并连续的相同角色消息 - const mergedMessages = this.mergeConsecutiveMessages(formattedMessages) - - // 计算prompt tokens - let promptTokens = 0 - for (const msg of mergedMessages) { - if (typeof msg.content === 'string') { - promptTokens += approximateTokenSize(msg.content) - } else { - promptTokens += - approximateTokenSize(msg.content?.map((item) => item.text).join('') || '') + - imageFiles.reduce((acc, file) => acc + file.token, 0) - } - } - // console.log('preparePromptContent', mergedMessages, promptTokens) - - return { finalContent: mergedMessages, promptTokens } - } - - // 选择上下文消息 - private selectContextMessages( - contextMessages: Message[], - userMessage: Message, - remainingContextLength: number - ): Message[] { - if (remainingContextLength <= 0) { - return [] - } - - const messages = contextMessages.filter((msg) => msg.id !== userMessage?.id).reverse() - - let currentLength = 0 - const selectedMessages: Message[] = [] - - for (const msg of messages) { - if (msg.status !== 'sent') { - continue - } - const msgContent = msg.role === 'user' ? (msg.content as UserMessageContent) : null - const msgText = msgContent - ? msgContent.text || - (msgContent.content ? this.formatUserMessageContent(msgContent.content) : '') - : '' - - const msgTokens = approximateTokenSize( - msg.role === 'user' - ? `${msgText}${getFileContext(msgContent?.files || [])}` - : JSON.stringify(msg.content) - ) - - if (currentLength + msgTokens <= remainingContextLength) { - // 如果是用户消息且有 content 但没有 text,添加 text - if (msg.role === 'user') { - const userMsgContent = msg.content as UserMessageContent - if (userMsgContent.content && !userMsgContent.text) { - userMsgContent.text = this.formatUserMessageContent(userMsgContent.content) - } - } - - selectedMessages.unshift(msg) - currentLength += msgTokens - } else { - break - } - } - while (selectedMessages.length > 0 && selectedMessages[0].role !== 'user') { - selectedMessages.shift() - } - return selectedMessages - } - - // 格式化消息用于完成 - private formatMessagesForCompletion( - contextMessages: Message[], - systemPrompt: string, - artifacts: number, - searchPrompt: string, - userContent: string, - enrichedUserMessage: string, - imageFiles: MessageFile[], - vision: boolean, - supportsFunctionCall: boolean - ): ChatMessage[] { - const formattedMessages: ChatMessage[] = [] - - // 添加上下文消息 - formattedMessages.push( - ...this.addContextMessages(contextMessages, vision, supportsFunctionCall) - ) - - // 添加系统提示 - if (systemPrompt) { - // formattedMessages.push(...this.addSystemPrompt(formattedMessages, systemPrompt, artifacts)) - formattedMessages.unshift({ - role: 'system', - content: systemPrompt - }) - // console.log('-------------> system prompt \n', systemPrompt, artifacts, formattedMessages) - } - - // 添加当前用户消息 - let finalContent = searchPrompt || userContent - - if (enrichedUserMessage) { - finalContent += enrichedUserMessage - } - - if (artifacts === 1) { - // formattedMessages.push({ - // role: 'user', - // content: ARTIFACTS_PROMPT - // }) - console.log('artifacts目前由mcp提供,此处为兼容性保留') - } - // 没有 vision 就不用塞进去了 - if (vision && imageFiles.length > 0) { - formattedMessages.push(this.addImageFiles(finalContent, imageFiles)) - } else { - formattedMessages.push({ - role: 'user', - content: finalContent.trim() - }) - } - - return formattedMessages - } - - private addImageFiles(finalContent: string, imageFiles: MessageFile[]): ChatMessage { - return { - role: 'user', - content: [ - ...imageFiles.map((file) => ({ - type: 'image_url' as const, - image_url: { url: file.content, detail: 'auto' as const } - })), - { type: 'text' as const, text: finalContent.trim() } - ] - } - } - - // 添加上下文消息 - private addContextMessages( - contextMessages: Message[], - vision: boolean, - supportsFunctionCall: boolean - ): ChatMessage[] { - const resultMessages = [] as ChatMessage[] - - // 对于原生fc模型,支持正确的tool_call response history插入 - if (supportsFunctionCall) { - contextMessages.forEach((msg) => { - if (msg.role === 'user') { - // 处理用户消息 - const msgContent = msg.content as UserMessageContent - const msgText = msgContent.content - ? this.formatUserMessageContent(msgContent.content) - : msgContent.text - const userContent = `${msgText}${getFileContext(msgContent.files)}` - resultMessages.push({ - role: 'user', - content: userContent - }) - } else if (msg.role === 'assistant') { - // 处理助手消息 - let afterSearch = false - const assistantBlocks = msg.content as AssistantMessageBlock[] - for (const subMsg of assistantBlocks) { - if ( - subMsg.type === 'tool_call' && - subMsg?.tool_call?.id?.trim() && - subMsg?.tool_call?.name?.trim() && - subMsg?.tool_call?.params?.trim() && - subMsg?.tool_call?.response?.trim() - ) { - resultMessages.push({ - role: 'assistant', - tool_calls: [ - { - id: subMsg.tool_call.id, - type: 'function', - function: { - name: subMsg.tool_call.name, - arguments: subMsg.tool_call.params - } - } - ] - }) - resultMessages.push({ - role: 'tool', - tool_call_id: subMsg.tool_call.id, - content: subMsg.tool_call.response - }) - } else if (subMsg.type === 'search') { - // 删除强制搜索结果中遗留的[x]引文标记 - afterSearch = true - } else if (subMsg.type === 'content') { - // 删除强制搜索结果中遗留的[x]引文标记 - let content = subMsg.content ?? '' - if (afterSearch) content = content.replace(/\[\d+\]/g, '') - resultMessages.push({ - role: 'assistant', - content: content - }) - afterSearch = false - } - } - } - }) - return resultMessages - } else { - // 对于非原生fc模型,支持规范化prompt实现 - contextMessages.forEach((msg) => { - if (msg.role === 'user') { - // 处理用户消息 - const msgContent = msg.content as UserMessageContent - const msgText = msgContent.content - ? this.formatUserMessageContent(msgContent.content) - : msgContent.text - const userContent = `${msgText}${getFileContext(msgContent.files)}` - resultMessages.push({ - role: 'user', - content: userContent - }) - } else if (msg.role === 'assistant') { - // 处理助手消息 - const assistantBlocks = msg.content as AssistantMessageBlock[] - // 提取文本内容块,同时将工具调用的响应内容提取出来 - let afterSearch = false - const textContent = assistantBlocks - .filter( - (block) => - block.type === 'content' || block.type === 'search' || block.type === 'tool_call' - ) - .map((block) => { - if (block.type === 'search') { - // 删除强制搜索结果中遗留的[x]引文标记 - afterSearch = true - return '' - } else if (block.type === 'content') { - // 删除强制搜索结果中遗留的[x]引文标记 - let content = block.content ?? '' - if (afterSearch) content = content.replace(/\[\d+\]/g, '') - afterSearch = false - return content - } else if ( - block.type === 'tool_call' && - block.tool_call?.response && - block.tool_call?.params - ) { - let parsedParams - let parsedResponse - - try { - parsedParams = JSON.parse(block.tool_call.params) - } catch { - parsedParams = block.tool_call.params // 保留原字符串 - } - - try { - parsedResponse = JSON.parse(block.tool_call.response) - } catch { - parsedResponse = block.tool_call.response // 保留原字符串 - } - - return ( - '' + - JSON.stringify({ - function_call_record: { - name: block.tool_call.name, - arguments: parsedParams, - response: parsedResponse - } - }) + - '' - ) - } else { - return '' // 若 tool_call 或 response、params 是 undefined 返回。只是便于调试而已,可以为空。 - } - }) - .join('\n') - - // 查找图像块 - const imageBlocks = assistantBlocks.filter( - (block) => block.type === 'image' && block.image_data - ) - - // 如果没有任何内容,则跳过此消息 - if (!textContent && imageBlocks.length === 0) { - return - } - - // 如果有图像,则使用复合内容格式 - if (vision && imageBlocks.length > 0) { - const content: ChatMessageContent[] = [] - - // 添加图像内容 - imageBlocks.forEach((block) => { - if (block.image_data) { - content.push({ - type: 'image_url', - image_url: { - url: block.image_data.data, - detail: 'auto' - } - }) - } - }) - - // 添加文本内容 - if (textContent) { - content.push({ - type: 'text', - text: textContent - }) - } - - resultMessages.push({ - role: 'assistant', - content: content - }) - } else { - // 仅有文本内容 - resultMessages.push({ - role: 'assistant', - content: textContent - }) - } - } - }) - - return resultMessages - } - } - - // 合并连续的相同角色的content,但注意assistant下content不能跟tool_calls合并 - private mergeConsecutiveMessages(messages: ChatMessage[]): ChatMessage[] { - if (!messages || messages.length === 0) { - return [] - } - - const mergedResult: ChatMessage[] = [] - // 为第一条消息创建一个深拷贝并添加到结果数组 - mergedResult.push(JSON.parse(JSON.stringify(messages[0]))) - - for (let i = 1; i < messages.length; i++) { - // 为当前消息创建一个深拷贝 - const currentMessage = JSON.parse(JSON.stringify(messages[i])) as ChatMessage - const lastPushedMessage = mergedResult[mergedResult.length - 1] - - let allowMessagePropertiesMerge = false // 标志是否允许消息属性(如content)合并 - - // 步骤 1: 判断消息本身是否允许合并(基于role和tool_calls) - if (lastPushedMessage.role === currentMessage.role) { - if (currentMessage.role === 'assistant') { - // Assistant消息: 仅当两条消息都【不】包含tool_calls时,才允许合并 - if (!lastPushedMessage.tool_calls && !currentMessage.tool_calls) { - allowMessagePropertiesMerge = true - } - } else { - // 其他角色 (user, system): 如果role相同,则允许合并 - allowMessagePropertiesMerge = true - } - } - - if (allowMessagePropertiesMerge) { - // 步骤 2: 如果消息允许合并,尝试合并其 content 字段 - const LMC = lastPushedMessage.content // 上一条已推送消息的内容 - const CMC = currentMessage.content // 当前待处理消息的内容 - - let newCombinedContent: string | ChatMessageContent[] | undefined = undefined - let contentTypesCompatibleForMerging = false - - if (LMC === undefined && CMC === undefined) { - newCombinedContent = undefined - contentTypesCompatibleForMerging = true - } else if (typeof LMC === 'string' && (typeof CMC === 'string' || CMC === undefined)) { - // LMC是string, CMC是string或undefined - const sLMC = LMC || '' - const sCMC = CMC || '' - if (sLMC && sCMC) newCombinedContent = `${sLMC}\n${sCMC}` - else newCombinedContent = sLMC || sCMC // 保留有内容的一方 - if (newCombinedContent === '') newCombinedContent = undefined // 空字符串视为undefined - contentTypesCompatibleForMerging = true - } else if (Array.isArray(LMC) && (Array.isArray(CMC) || CMC === undefined)) { - // LMC是数组, CMC是数组或undefined - const arrLMC = LMC - const arrCMC = CMC || [] // 如果CMC是undefined, 视为空数组进行合并 - newCombinedContent = [...arrLMC, ...arrCMC] - if (newCombinedContent.length === 0) newCombinedContent = undefined // 空数组视为undefined - contentTypesCompatibleForMerging = true - } else if (LMC === undefined && CMC !== undefined) { - // LMC是undefined, CMC有值 (string或array) - newCombinedContent = CMC - contentTypesCompatibleForMerging = true - } else if (LMC !== undefined && CMC === undefined) { - // LMC有值, CMC是undefined -> content保持LMC的值,无需改变 - newCombinedContent = LMC - contentTypesCompatibleForMerging = true // 视为成功合并(当前消息内容被"吸收") - } - // 如果LMC和CMC的类型不兼容 (例如一个是string, 另一个是array), - // contentTypesCompatibleForMerging 将保持 false - - if (contentTypesCompatibleForMerging) { - lastPushedMessage.content = newCombinedContent - // currentMessage 被成功合并,不需单独push - } else { - // 角色和tool_calls条件允许合并,但内容类型不兼容 - // 因此,不合并消息,将 currentMessage 作为新消息加入 - mergedResult.push(currentMessage) - } - } else { - // 角色不同,或者 assistant 消息因 tool_calls 而不允许合并 - // 将 currentMessage 作为新消息加入 - mergedResult.push(currentMessage) - } - } - - return mergedResult - } - // 更新生成状态 private async updateGenerationState( state: GeneratingMessageState, @@ -2836,12 +2308,7 @@ export class ThreadPresenter implements IThreadPresenter { } async getActiveConversationId(tabId: number): Promise { - return this.activeConversationIds.get(tabId) || null - } - - private async getLatestConversation(): Promise { - const result = await this.getConversationList(1, 1) - return result.list[0] || null + return this.getActiveConversationIdSync(tabId) } getGeneratingMessageState(messageId: string): GeneratingMessageState | null { @@ -2914,8 +2381,8 @@ export class ThreadPresenter implements IThreadPresenter { } async summaryTitles(tabId?: number, conversationId?: string): Promise { - const targetConversationId = - conversationId ?? (tabId !== undefined ? this.activeConversationIds.get(tabId) : undefined) + const activeId = tabId !== undefined ? this.getActiveConversationIdSync(tabId) : null + const targetConversationId = conversationId ?? activeId ?? undefined if (!targetConversationId) { throw new Error('找不到当前对话') } @@ -2948,16 +2415,14 @@ export class ThreadPresenter implements IThreadPresenter { const messagesWithLength = variantAwareMessages .map((msg) => { if (msg.role === 'user') { + const userContent = msg.content as UserMessageContent + const serializedContent = buildUserMessageContext(userContent) return { message: msg, - length: `${(msg.content as UserMessageContent).text}${getFileContext( - (msg.content as UserMessageContent).files - )}`.length, + length: serializedContent.length, formattedMessage: { role: 'user' as const, - content: `${(msg.content as UserMessageContent).text}${getFileContext( - (msg.content as UserMessageContent).files - )}` + content: serializedContent } } } else { @@ -2989,18 +2454,15 @@ export class ThreadPresenter implements IThreadPresenter { } async clearActiveThread(tabId: number): Promise { - this.activeConversationIds.delete(tabId) - eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, { tabId }) + this.clearActiveConversation(tabId, { notify: true }) } async clearAllMessages(conversationId: string): Promise { await this.messageManager.clearAllMessages(conversationId) // 检查所有 tab 中的活跃会话 - for (const [, activeId] of this.activeConversationIds.entries()) { - if (activeId === conversationId) { - // 停止所有正在生成的消息 - await this.stopConversationGeneration(conversationId) - } + const tabs = this.getTabsByConversation(conversationId) + if (tabs.length > 0) { + await this.stopConversationGeneration(conversationId) } } @@ -3271,84 +2733,32 @@ export class ThreadPresenter implements IThreadPresenter { ] let aiAnswer = '' - const stream = this.llmProviderPresenter.startStreamCompletion( - providerId, - messages, - modelId, - 'ask-ai-' + Date.now(), - 0.7, - 1000 - ) - - for await (const event of stream) { - if (event.type === 'response') { - const msg = event.data as LLMAgentEventData - if (msg.content) { - aiAnswer += msg.content - } - } else if (event.type === 'error') { - const msg = event.data as { eventId: string; error: string } - throw new Error(msg.error || 'AI回答失败') - } - } - - return aiAnswer.trim() - } catch (error) { - console.error('AI询问失败:', error) - throw error - } - } - - private async broadcastThreadListUpdate(): Promise { - // 1. 获取所有会话 (假设9999足够大) - const result = await this.sqlitePresenter.getConversationList(1, this.fetchThreadLength) - - // 2. 分离置顶和非置顶会话 - const pinnedConversations: CONVERSATION[] = [] - const normalConversations: CONVERSATION[] = [] - - result.list.forEach((conv) => { - if (conv.is_pinned === 1) { - pinnedConversations.push(conv) - } else { - normalConversations.push(conv) - } - }) - - // 3. 对置顶会话按更新时间排序 - pinnedConversations.sort((a, b) => b.updatedAt - a.updatedAt) - - // 4. 对普通会话按更新时间排序 - normalConversations.sort((a, b) => b.updatedAt - a.updatedAt) - - // 5. 按日期分组 - const groupedThreads: Map = new Map() - - // 先添加置顶分组(如果有置顶会话) - if (pinnedConversations.length > 0) { - groupedThreads.set('Pinned', pinnedConversations) - } + const stream = this.llmProviderPresenter.startStreamCompletion( + providerId, + messages, + modelId, + 'ask-ai-' + Date.now(), + 0.7, + 1000 + ) - // 再添加普通会话的日期分组 - normalConversations.forEach((conv) => { - const date = new Date(conv.updatedAt).toISOString().split('T')[0] - if (!groupedThreads.has(date)) { - groupedThreads.set(date, []) + for await (const event of stream) { + if (event.type === 'response') { + const msg = event.data as LLMAgentEventData + if (msg.content) { + aiAnswer += msg.content + } + } else if (event.type === 'error') { + const msg = event.data as { eventId: string; error: string } + throw new Error(msg.error || 'AI回答失败') + } } - groupedThreads.get(date)!.push(conv) - }) - const finalGroupedList = Array.from(groupedThreads.entries()).map(([dt, dtThreads]) => ({ - dt, - dtThreads - })) - - // 6. 广播这个格式化好的完整列表 - eventBus.sendToRenderer( - CONVERSATION_EVENTS.LIST_UPDATED, - SendTarget.ALL_WINDOWS, - finalGroupedList - ) + return aiAnswer.trim() + } catch (error) { + console.error('AI询问失败:', error) + throw error + } } /** @@ -3359,7 +2769,7 @@ export class ThreadPresenter implements IThreadPresenter { */ async exportConversation( conversationId: string, - format: 'markdown' | 'html' | 'txt' = 'markdown' + format: ConversationExportFormat = 'markdown' ): Promise<{ filename: string content: string @@ -3397,30 +2807,9 @@ export class ThreadPresenter implements IThreadPresenter { return msg }) - // 生成文件名 - 使用简化的时间戳格式 - const timestamp = new Date() - .toISOString() - .replace(/[:.]/g, '-') - .replace('T', '_') - .substring(0, 19) - const extension = format === 'markdown' ? 'md' : format - const filename = `export_deepchat_${timestamp}.${extension}` - - // 生成内容(在主进程中直接处理,避免Worker的复杂性) - let content: string - switch (format) { - case 'markdown': - content = this.exportToMarkdown(conversation, variantAwareMessages) - break - case 'html': - content = this.exportToHtml(conversation, variantAwareMessages) - break - case 'txt': - content = this.exportToText(conversation, variantAwareMessages) - break - default: - throw new Error(`不支持的导出格式: ${format}`) - } + // 生成文件名 + const filename = generateExportFilename(format) + const content = buildConversationExportContent(conversation, variantAwareMessages, format) return { filename, content } } catch (error) { @@ -3429,531 +2818,6 @@ export class ThreadPresenter implements IThreadPresenter { } } - /** - * 导出为 Markdown 格式 - */ - private exportToMarkdown(conversation: CONVERSATION, messages: Message[]): string { - const lines: string[] = [] - - // 标题和元信息 - lines.push(`# ${conversation.title}`) - lines.push('') - lines.push(`**Export Time:** ${new Date().toLocaleString()}`) - lines.push(`**Conversation ID:** ${conversation.id}`) - lines.push(`**Message Count:** ${messages.length}`) - if (conversation.settings.modelId) { - lines.push(`**Model:** ${conversation.settings.modelId}`) - } - if (conversation.settings.providerId) { - lines.push(`**Provider:** ${conversation.settings.providerId}`) - } - lines.push('') - lines.push('---') - lines.push('') - - // 处理每条消息 - for (const message of messages) { - const messageTime = new Date(message.timestamp).toLocaleString() - - if (message.role === 'user') { - lines.push(`## 👤 用户 (${messageTime})`) - lines.push('') - - const userContent = message.content as UserMessageContent - const messageText = userContent.content - ? this.formatUserMessageContent(userContent.content) - : userContent.text - - lines.push(messageText) - - // 处理文件附件 - if (userContent.files && userContent.files.length > 0) { - lines.push('') - lines.push('**附件:**') - for (const file of userContent.files) { - lines.push(`- ${file.name} (${file.mimeType})`) - } - } - - // 处理链接 - if (userContent.links && userContent.links.length > 0) { - lines.push('') - lines.push('**链接:**') - for (const link of userContent.links) { - lines.push(`- ${link}`) - } - } - } else if (message.role === 'assistant') { - lines.push(`## 🤖 助手 (${messageTime})`) - lines.push('') - - const assistantBlocks = message.content as AssistantMessageBlock[] - - for (const block of assistantBlocks) { - switch (block.type) { - case 'content': - if (block.content) { - lines.push(block.content) - lines.push('') - } - break - - case 'reasoning_content': - if (block.content) { - lines.push('### 🤔 思考过程') - lines.push('') - lines.push('```') - lines.push(block.content) - lines.push('```') - lines.push('') - } - break - - case 'tool_call': - if (block.tool_call) { - lines.push(`### 🔧 工具调用: ${block.tool_call.name}`) - lines.push('') - if (block.tool_call.params) { - lines.push('**参数:**') - lines.push('```json') - try { - const params = JSON.parse(block.tool_call.params) - lines.push(JSON.stringify(params, null, 2)) - } catch { - lines.push(block.tool_call.params) - } - lines.push('```') - lines.push('') - } - if (block.tool_call.response) { - lines.push('**响应:**') - lines.push('```') - lines.push(block.tool_call.response) - lines.push('```') - lines.push('') - } - } - break - - case 'search': - lines.push('### 🔍 网络搜索') - if (block.extra?.total) { - lines.push(`找到 ${block.extra.total} 个搜索结果`) - } - lines.push('') - break - - case 'image': - lines.push('### 🖼️ 图片') - lines.push('*[图片内容]*') - lines.push('') - break - - case 'error': - if (block.content) { - lines.push(`### ❌ 错误`) - lines.push('') - lines.push(`\`${block.content}\``) - lines.push('') - } - break - - case 'artifact-thinking': - if (block.content) { - lines.push('### 💭 创作思考') - lines.push('') - lines.push('```') - lines.push(block.content) - lines.push('```') - lines.push('') - } - break - } - } - } - - lines.push('---') - lines.push('') - } - - return lines.join('\n') - } - - /** - * 导出为 HTML 格式 - */ - private exportToHtml(conversation: CONVERSATION, messages: Message[]): string { - const lines: string[] = [] - - // HTML 头部 - lines.push('') - lines.push('') - lines.push('') - lines.push(' ') - lines.push(' ') - lines.push(` ${this.escapeHtml(conversation.title)}`) - lines.push(' ') - lines.push('') - lines.push('') - - // 标题和元信息 - lines.push('
') - lines.push(`

${this.escapeHtml(conversation.title)}

`) - lines.push(`

导出时间: ${new Date().toLocaleString()}

`) - lines.push(`

会话ID: ${conversation.id}

`) - lines.push(`

消息数量: ${messages.length}

`) - if (conversation.settings.modelId) { - lines.push( - `

模型: ${this.escapeHtml(conversation.settings.modelId)}

` - ) - } - if (conversation.settings.providerId) { - lines.push( - `

提供商: ${this.escapeHtml(conversation.settings.providerId)}

` - ) - } - lines.push('
') - - // 处理每条消息 - for (const message of messages) { - const messageTime = new Date(message.timestamp).toLocaleString() - - if (message.role === 'user') { - lines.push(`
`) - lines.push( - `
👤 用户 (${messageTime})
` - ) - - const userContent = message.content as UserMessageContent - const messageText = userContent.content - ? this.formatUserMessageContent(userContent.content) - : userContent.text - - lines.push(`
${this.escapeHtml(messageText).replace(/\n/g, '
')}
`) - - // 处理文件附件 - if (userContent.files && userContent.files.length > 0) { - lines.push('
') - lines.push(' 附件:') - lines.push('
    ') - for (const file of userContent.files) { - lines.push( - `
  • ${this.escapeHtml(file.name)} (${this.escapeHtml(file.mimeType)})
  • ` - ) - } - lines.push('
') - lines.push('
') - } - - // 处理链接 - if (userContent.links && userContent.links.length > 0) { - lines.push('
') - lines.push(' 链接:') - lines.push(' ') - lines.push('
') - } - - lines.push('
') - } else if (message.role === 'assistant') { - lines.push(`
`) - lines.push( - `
🤖 助手 (${messageTime})
` - ) - - const assistantBlocks = message.content as AssistantMessageBlock[] - - for (const block of assistantBlocks) { - switch (block.type) { - case 'content': - if (block.content) { - lines.push( - `
${this.escapeHtml(block.content).replace(/\n/g, '
')}
` - ) - } - break - - case 'reasoning_content': - if (block.content) { - lines.push('
') - lines.push(' 🤔 思考过程:') - lines.push(`
${this.escapeHtml(block.content)}
`) - lines.push('
') - } - break - - case 'tool_call': - if (block.tool_call) { - lines.push('
') - lines.push( - ` 🔧 工具调用: ${this.escapeHtml(block.tool_call.name || '')}` - ) - if (block.tool_call.params) { - lines.push('
参数:
') - lines.push( - `
${this.escapeHtml(block.tool_call.params)}
` - ) - } - if (block.tool_call.response) { - lines.push('
响应:
') - lines.push( - `
${this.escapeHtml(block.tool_call.response)}
` - ) - } - lines.push('
') - } - break - - case 'search': - lines.push('
') - lines.push(' 🔍 网络搜索') - if (block.extra?.total) { - lines.push(`

找到 ${block.extra.total} 个搜索结果

`) - } - lines.push('
') - break - - case 'image': - lines.push('
') - lines.push(' 🖼️ 图片') - lines.push('

[图片内容]

') - lines.push('
') - break - - case 'error': - if (block.content) { - lines.push('
') - lines.push(' ❌ 错误') - lines.push(`

${this.escapeHtml(block.content)}

`) - lines.push('
') - } - break - - case 'artifact-thinking': - if (block.content) { - lines.push('
') - lines.push(' 💭 创作思考:') - lines.push(`
${this.escapeHtml(block.content)}
`) - lines.push('
') - } - break - } - } - - lines.push('
') - } - } - - // HTML 尾部 - lines.push('') - lines.push('') - - return lines.join('\n') - } - - /** - * 导出为纯文本格式 - */ - private exportToText(conversation: CONVERSATION, messages: Message[]): string { - const lines: string[] = [] - - // 标题和元信息 - lines.push(`${conversation.title}`) - lines.push(''.padEnd(conversation.title.length, '=')) - lines.push('') - lines.push(`导出时间: ${new Date().toLocaleString()}`) - lines.push(`会话ID: ${conversation.id}`) - lines.push(`消息数量: ${messages.length}`) - if (conversation.settings.modelId) { - lines.push(`模型: ${conversation.settings.modelId}`) - } - if (conversation.settings.providerId) { - lines.push(`提供商: ${conversation.settings.providerId}`) - } - lines.push('') - lines.push(''.padEnd(80, '-')) - lines.push('') - - // 处理每条消息 - for (const message of messages) { - const messageTime = new Date(message.timestamp).toLocaleString() - - if (message.role === 'user') { - lines.push(`[用户] ${messageTime}`) - lines.push('') - - const userContent = message.content as UserMessageContent - const messageText = userContent.content - ? this.formatUserMessageContent(userContent.content) - : userContent.text - - lines.push(messageText) - - // 处理文件附件 - if (userContent.files && userContent.files.length > 0) { - lines.push('') - lines.push('附件:') - for (const file of userContent.files) { - lines.push(`- ${file.name} (${file.mimeType})`) - } - } - - // 处理链接 - if (userContent.links && userContent.links.length > 0) { - lines.push('') - lines.push('链接:') - for (const link of userContent.links) { - lines.push(`- ${link}`) - } - } - } else if (message.role === 'assistant') { - lines.push(`[助手] ${messageTime}`) - lines.push('') - - const assistantBlocks = message.content as AssistantMessageBlock[] - - for (const block of assistantBlocks) { - switch (block.type) { - case 'content': - if (block.content) { - lines.push(block.content) - lines.push('') - } - break - - case 'reasoning_content': - if (block.content) { - lines.push('[思考过程]') - lines.push(block.content) - lines.push('') - } - break - - case 'tool_call': - if (block.tool_call) { - lines.push(`[工具调用] ${block.tool_call.name}`) - if (block.tool_call.params) { - lines.push('参数:') - lines.push(block.tool_call.params) - } - if (block.tool_call.response) { - lines.push('响应:') - lines.push(block.tool_call.response) - } - lines.push('') - } - break - - case 'search': - lines.push('[网络搜索]') - if (block.extra?.total) { - lines.push(`找到 ${block.extra.total} 个搜索结果`) - } - lines.push('') - break - - case 'image': - lines.push('[图片内容]') - lines.push('') - break - - case 'error': - if (block.content) { - lines.push(`[错误] ${block.content}`) - lines.push('') - } - break - - case 'artifact-thinking': - if (block.content) { - lines.push('[创作思考]') - lines.push(block.content) - lines.push('') - } - break - } - } - } - - lines.push(''.padEnd(80, '-')) - lines.push('') - } - - return lines.join('\n') - } - - /** - * HTML 转义辅助函数 - */ - private escapeHtml(text: string): string { - return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, ''') - } - // 权限响应处理方法 - 重新设计为基于消息数据的流程 async handlePermissionResponse( messageId: string, @@ -4013,6 +2877,52 @@ export class ThreadPresenter implements IThreadPresenter { } } + // 2.1 同步内存中的生成状态,避免后续覆盖数据库中的授权结果 + const generatingState = this.generatingMessages.get(messageId) + if (generatingState) { + const statePermissionBlockIndex = generatingState.message.content.findIndex( + (block) => + block.type === 'action' && + block.action_type === 'tool_call_permission' && + block.tool_call?.id === toolCallId + ) + + if (statePermissionBlockIndex !== -1) { + const statePermissionBlock = generatingState.message.content[statePermissionBlockIndex] + generatingState.message.content[statePermissionBlockIndex] = { + ...statePermissionBlock, + ...permissionBlock, + extra: permissionBlock.extra + ? { + ...permissionBlock.extra + } + : undefined, + tool_call: permissionBlock.tool_call + ? { + ...permissionBlock.tool_call + } + : undefined + } + } else { + console.warn( + `[ThreadPresenter] Permission block not found in generating state, synchronizing snapshot` + ) + generatingState.message.content = content.map((block) => ({ + ...block, + extra: block.extra + ? { + ...block.extra + } + : undefined, + tool_call: block.tool_call + ? { + ...block.tool_call + } + : undefined + })) + } + } + // 3. 保存消息更新 await this.messageManager.editMessage(messageId, JSON.stringify(content)) console.log(`[ThreadPresenter] Updated permission block status to: ${permissionBlock.status}`) @@ -4059,10 +2969,10 @@ export class ThreadPresenter implements IThreadPresenter { await this.restartAgentLoopAfterPermission(messageId) } else { console.log( - `[ThreadPresenter] Permission denied, ending generation for message: ${messageId}` + `[ThreadPresenter] Permission denied, continuing generation with error context for message: ${messageId}` ) - // 6. 权限被拒绝 - 正常结束消息 - await this.finalizeMessageAfterPermissionDenied(messageId) + // 6. 权限被拒绝 - 继续agent loop,将拒绝信息作为工具调用失败结果 + await this.continueAfterPermissionDenied(messageId) } } catch (error) { console.error(`[ThreadPresenter] Failed to handle permission response:`, error) @@ -4142,35 +3052,208 @@ export class ThreadPresenter implements IThreadPresenter { const state = this.generatingMessages.get(messageId) if (state) { console.log(`[ThreadPresenter] Message still in generating state, resuming from memory`) - await this.resumeStreamCompletion(conversationId, messageId) + if (state.pendingToolCall) { + console.log( + `[ThreadPresenter] Pending tool call detected after permission grant, executing tool before resuming` + ) + await this.resumeAfterPermissionWithPendingToolCall( + state, + message as AssistantMessage, + conversationId + ) + } else { + await this.resumeStreamCompletion(conversationId, messageId) + } return } // 否则重新启动完整的agent loop console.log(`[ThreadPresenter] Message not in generating state, starting fresh agent loop`) - // 重新创建生成状态 - const assistantMessage = message as AssistantMessage + // 重新创建生成状态 + const assistantMessage = message as AssistantMessage + + this.generatingMessages.set(messageId, { + message: assistantMessage, + conversationId, + startTime: Date.now(), + firstTokenTime: null, + promptTokens: 0, + reasoningStartTime: null, + reasoningEndTime: null, + lastReasoningTime: null, + pendingToolCall: this.findPendingToolCallAfterPermission(content) || undefined + }) + + console.log(`[ThreadPresenter] Created new generating state for message: ${messageId}`) + + // 启动新的流式完成 + await this.startStreamCompletion(conversationId, messageId) + } catch (error) { + console.error(`[ThreadPresenter] Failed to restart agent loop:`, error) + + // 确保清理生成状态 + this.generatingMessages.delete(messageId) + + try { + await this.messageManager.handleMessageError(messageId, String(error)) + } catch (updateError) { + console.error(`[ThreadPresenter] Failed to update message error status:`, updateError) + } + + throw error + } + } + + // 权限被拒绝后继续生成,将拒绝信息作为工具调用失败告知LLM + private async continueAfterPermissionDenied(messageId: string): Promise { + console.log(`[ThreadPresenter] Continuing generation after permission denied: ${messageId}`) + + try { + const message = await this.messageManager.getMessage(messageId) + if (!message || message.role !== 'assistant') { + const errorMsg = `Message not found or not an assistant message (messageId: ${messageId})` + console.error(`[ThreadPresenter] ${errorMsg}`) + throw new Error(errorMsg) + } + + const conversationId = message.conversationId + const content = message.content as AssistantMessageBlock[] + + // 查找被拒绝的权限块 + const deniedPermissionBlock = content.find( + (block) => + block.type === 'action' && + block.action_type === 'tool_call_permission' && + block.status === 'denied' + ) + + if (!deniedPermissionBlock?.tool_call) { + console.warn(`[ThreadPresenter] No denied permission block found for message: ${messageId}`) + return + } + + const toolCall = deniedPermissionBlock.tool_call + + // 构建工具调用失败的响应消息 + const errorMessage = `Tool execution failed: Permission denied by user for ${toolCall.name || 'this tool'}` + + console.log(`[ThreadPresenter] Notifying LLM about permission denial: ${errorMessage}`) + + // 发送工具调用失败事件给renderer + eventBus.sendToRenderer(STREAM_EVENTS.RESPONSE, SendTarget.ALL_WINDOWS, { + eventId: messageId, + tool_call: 'end', + tool_call_id: toolCall.id, + tool_call_name: toolCall.name, + tool_call_params: toolCall.params, + tool_call_response: errorMessage, + tool_call_server_name: toolCall.server_name, + tool_call_server_icons: toolCall.server_icons, + tool_call_server_description: toolCall.server_description + }) + + // 获取或创建生成状态 + let state = this.generatingMessages.get(messageId) + if (!state) { + // 重新创建生成状态 + const assistantMessage = message as AssistantMessage + state = { + message: assistantMessage, + conversationId, + startTime: Date.now(), + firstTokenTime: null, + promptTokens: 0, + reasoningStartTime: null, + reasoningEndTime: null, + lastReasoningTime: null + } + this.generatingMessages.set(messageId, state) + } + + // 清除pending tool call(如果有) + state.pendingToolCall = undefined + + // 获取会话和上下文 + const { conversation, contextMessages, userMessage } = await this.prepareConversationContext( + conversationId, + messageId + ) + + const { + providerId, + modelId, + temperature, + maxTokens, + enabledMcpTools, + thinkingBudget, + reasoningEffort, + verbosity, + enableSearch, + forcedSearch, + searchStrategy + } = conversation.settings + + const modelConfig = this.configPresenter.getModelConfig(modelId, providerId) + + // 将 snake_case 转换为 camelCase 并构建包含工具调用失败信息的上下文 + const completedToolCall = { + id: toolCall.id || '', + name: toolCall.name || '', + params: toolCall.params || '', + response: errorMessage, + // 注意:从 snake_case 转换为 camelCase + serverName: toolCall.server_name, + serverIcons: toolCall.server_icons, + serverDescription: toolCall.server_description + } + + const finalContent = await buildPostToolExecutionContext({ + conversation, + contextMessages, + userMessage, + currentAssistantMessage: state.message, + completedToolCall, + modelConfig + }) + + console.log( + `[ThreadPresenter] Restarting agent loop with tool failure context for message: ${messageId}` + ) - this.generatingMessages.set(messageId, { - message: assistantMessage, - conversationId, - startTime: Date.now(), - firstTokenTime: null, - promptTokens: 0, - reasoningStartTime: null, - reasoningEndTime: null, - lastReasoningTime: null - }) + // 继续agent loop + const stream = this.llmProviderPresenter.startStreamCompletion( + providerId, + finalContent, + modelId, + messageId, + temperature, + maxTokens, + enabledMcpTools, + thinkingBudget, + reasoningEffort, + verbosity, + enableSearch, + forcedSearch, + searchStrategy + ) - console.log(`[ThreadPresenter] Created new generating state for message: ${messageId}`) + for await (const event of stream) { + const msg = event.data + if (event.type === 'response') { + await this.handleLLMAgentResponse(msg) + } else if (event.type === 'error') { + await this.handleLLMAgentError(msg) + } else if (event.type === 'end') { + await this.handleLLMAgentEnd(msg) + } + } - // 启动新的流式完成 - await this.startStreamCompletion(conversationId, messageId) + console.log(`[ThreadPresenter] Successfully continued after permission denial: ${messageId}`) } catch (error) { - console.error(`[ThreadPresenter] Failed to restart agent loop:`, error) + console.error(`[ThreadPresenter] Failed to continue after permission denial:`, error) - // 确保清理生成状态 + // 清理生成状态 this.generatingMessages.delete(messageId) try { @@ -4183,53 +3266,6 @@ export class ThreadPresenter implements IThreadPresenter { } } - // 权限被拒绝后完成消息 - private async finalizeMessageAfterPermissionDenied(messageId: string): Promise { - console.log(`[ThreadPresenter] Finalizing message after permission denied: ${messageId}`) - - try { - const message = await this.messageManager.getMessage(messageId) - if (!message) return - - const content = message.content as AssistantMessageBlock[] - - // 将所有loading状态的块设为success,但保留权限块的状态 - content.forEach((block) => { - if (block.type === 'action' && block.action_type === 'tool_call_permission') { - // 权限块保持其当前状态(granted/denied/error) - return - } - if (block.status === 'loading') { - block.status = 'success' - } - }) - - // 添加权限被拒绝的提示 - content.push({ - type: 'error', - content: 'Permission denied by user', - status: 'error', - timestamp: Date.now() - }) - - await this.messageManager.editMessage(messageId, JSON.stringify(content)) - await this.messageManager.updateMessageStatus(messageId, 'sent') - - // 清理生成状态 - this.generatingMessages.delete(messageId) - - // 发送结束事件 - eventBus.sendToRenderer(STREAM_EVENTS.END, SendTarget.ALL_WINDOWS, { - eventId: messageId, - userStop: false - }) - - console.log(`[ThreadPresenter] Message finalized after permission denial: ${messageId}`) - } catch (error) { - console.error(`[ThreadPresenter] Failed to finalize message after permission denial:`, error) - } - } - // 恢复流式完成 (用于内存状态存在的情况) private async resumeStreamCompletion(conversationId: string, messageId: string): Promise { const state = this.generatingMessages.get(messageId) @@ -4300,13 +3336,13 @@ export class ThreadPresenter implements IThreadPresenter { ) // 构建专门的继续执行上下文 - const finalContent = await this.buildContinueToolCallContext( + const finalContent = await buildContinueToolCallContext({ conversation, contextMessages, userMessage, pendingToolCall, modelConfig - ) + }) console.log(`[ThreadPresenter] Built continue context for tool: ${pendingToolCall.name}`) @@ -4353,6 +3389,210 @@ export class ThreadPresenter implements IThreadPresenter { } } + private async resumeAfterPermissionWithPendingToolCall( + state: GeneratingMessageState, + message: AssistantMessage, + conversationId: string + ): Promise { + const pendingToolCall = state.pendingToolCall + if (!pendingToolCall || !pendingToolCall.id || !pendingToolCall.name) { + console.warn( + `[ThreadPresenter] Pending tool call data missing, falling back to standard resume` + ) + await this.resumeStreamCompletion(conversationId, message.id) + return + } + + try { + const { conversation, contextMessages, userMessage } = await this.prepareConversationContext( + conversationId, + message.id + ) + + const { + providerId, + modelId, + temperature, + maxTokens, + enabledMcpTools, + thinkingBudget, + reasoningEffort, + verbosity, + enableSearch, + forcedSearch, + searchStrategy + } = conversation.settings + + const modelConfig = this.configPresenter.getModelConfig(modelId, providerId) + if (!modelConfig) { + console.warn( + `[ThreadPresenter] Model config not found for ${modelId} (${providerId}), falling back to standard resume` + ) + await this.resumeStreamCompletion(conversationId, message.id) + return + } + + let toolDef: MCPToolDefinition | undefined + try { + const toolDefinitions = await presenter.mcpPresenter.getAllToolDefinitions(enabledMcpTools) + toolDef = toolDefinitions.find((definition) => { + if (definition.function.name !== pendingToolCall.name) { + return false + } + if (pendingToolCall.serverName) { + return definition.server.name === pendingToolCall.serverName + } + return true + }) + } catch (error) { + console.error('[ThreadPresenter] Failed to load tool definitions:', error) + } + + if (!toolDef) { + console.warn( + `[ThreadPresenter] Tool definition not found for ${pendingToolCall.name}, falling back to standard resume` + ) + await this.resumeStreamCompletion(conversationId, message.id) + return + } + + const resolvedToolDef = toolDef as MCPToolDefinition + + await this.handleLLMAgentResponse({ + eventId: message.id, + tool_call: 'running', + tool_call_id: pendingToolCall.id, + tool_call_name: pendingToolCall.name, + tool_call_params: pendingToolCall.params, + tool_call_server_name: resolvedToolDef.server.name, + tool_call_server_icons: resolvedToolDef.server.icons, + tool_call_server_description: resolvedToolDef.server.description + }) + + let toolContent = '' + let toolRawData: MCPToolResponse | null = null + try { + const toolCallResult = await presenter.mcpPresenter.callTool({ + id: pendingToolCall.id, + type: 'function', + function: { + name: pendingToolCall.name, + arguments: pendingToolCall.params + }, + server: resolvedToolDef.server + }) + toolContent = toolCallResult.content + toolRawData = toolCallResult.rawData + } catch (toolError) { + console.error('[ThreadPresenter] Failed to execute pending tool call:', toolError) + await this.handleLLMAgentResponse({ + eventId: message.id, + tool_call: 'error', + tool_call_id: pendingToolCall.id, + tool_call_name: pendingToolCall.name, + tool_call_params: pendingToolCall.params, + tool_call_response: toolError instanceof Error ? toolError.message : String(toolError), + tool_call_server_name: resolvedToolDef.server.name, + tool_call_server_icons: resolvedToolDef.server.icons, + tool_call_server_description: resolvedToolDef.server.description + }) + throw toolError + } + + if (toolRawData?.requiresPermission) { + console.warn( + `[ThreadPresenter] Tool ${pendingToolCall.name} still requires permission after grant` + ) + await this.handleLLMAgentResponse({ + eventId: message.id, + tool_call: 'permission-required', + tool_call_id: pendingToolCall.id, + tool_call_name: pendingToolCall.name, + tool_call_params: pendingToolCall.params, + tool_call_server_name: + toolRawData.permissionRequest?.serverName || resolvedToolDef.server.name, + tool_call_server_icons: resolvedToolDef.server.icons, + tool_call_server_description: resolvedToolDef.server.description, + tool_call_response: toolContent, + permission_request: toolRawData.permissionRequest + }) + // A new permission request will trigger a new handling flow + return + } + + const serializedResponse = toolContent + + await this.handleLLMAgentResponse({ + eventId: message.id, + tool_call: 'end', + tool_call_id: pendingToolCall.id, + tool_call_name: pendingToolCall.name, + tool_call_params: pendingToolCall.params, + tool_call_response: serializedResponse, + tool_call_server_name: resolvedToolDef.server.name, + tool_call_server_icons: resolvedToolDef.server.icons, + tool_call_server_description: resolvedToolDef.server.description, + tool_call_response_raw: toolRawData ?? undefined + }) + + state.pendingToolCall = undefined + + const finalContent = await buildPostToolExecutionContext({ + conversation, + contextMessages, + userMessage, + currentAssistantMessage: state.message, + completedToolCall: { + ...pendingToolCall, + response: serializedResponse + }, + modelConfig + }) + + const stream = this.llmProviderPresenter.startStreamCompletion( + providerId, + finalContent, + modelId, + message.id, + temperature, + maxTokens, + enabledMcpTools, + thinkingBudget, + reasoningEffort, + verbosity, + enableSearch, + forcedSearch, + searchStrategy + ) + + for await (const event of stream) { + const msg = event.data + if (event.type === 'response') { + await this.handleLLMAgentResponse(msg) + } else if (event.type === 'error') { + await this.handleLLMAgentError(msg) + } else if (event.type === 'end') { + await this.handleLLMAgentEnd(msg) + } + } + } catch (error) { + console.error( + '[ThreadPresenter] Failed to resume after permission with pending tool call:', + error + ) + + this.generatingMessages.delete(message.id) + + try { + await this.messageManager.handleMessageError(message.id, String(error)) + } catch (updateError) { + console.error(`[ThreadPresenter] Failed to update message error status:`, updateError) + } + + throw error + } + } + // 等待MCP服务重启完成并准备就绪 private async waitForMcpServiceReady( serverName: string, @@ -4426,168 +3666,4 @@ export class ThreadPresenter implements IThreadPresenter { return { id, name, params } } - - // 构建继续工具调用执行的上下文 - private async buildContinueToolCallContext( - conversation: any, - contextMessages: any[], - userMessage: any, - pendingToolCall: { id: string; name: string; params: string }, - modelConfig: any - ): Promise { - const { systemPrompt } = conversation.settings - const formattedMessages: ChatMessage[] = [] - - // 1. 添加系统提示(包含当前时间信息) - if (systemPrompt) { - const finalSystemPrompt = this.enhanceSystemPromptWithDateTime(systemPrompt) - formattedMessages.push({ - role: 'system', - content: finalSystemPrompt - }) - } - - // 2. 添加上下文消息 - const contextChatMessages = this.addContextMessages( - contextMessages, - false, - modelConfig.functionCall - ) - formattedMessages.push(...contextChatMessages) - - // 3. 添加当前用户消息 - const userContent = userMessage.content - const msgText = userContent.content - ? this.formatUserMessageContent(userContent.content) - : userContent.text - const finalUserContent = `${msgText}${getFileContext(userContent.files || [])}` - - formattedMessages.push({ - role: 'user', - content: finalUserContent - }) - - // 4. 添加助手消息,说明需要执行工具调用 - if (modelConfig.functionCall) { - // 对于原生支持函数调用的模型,添加tool_calls - formattedMessages.push({ - role: 'assistant', - tool_calls: [ - { - id: pendingToolCall.id, - type: 'function', - function: { - name: pendingToolCall.name, - arguments: pendingToolCall.params - } - } - ] - }) - - // 添加一个虚拟的工具响应,说明权限已经授予 - formattedMessages.push({ - role: 'tool', - tool_call_id: pendingToolCall.id, - content: `Permission granted. Please proceed with executing the ${pendingToolCall.name} function.` - }) - } else { - // 对于非原生支持的模型,使用文本提示 - formattedMessages.push({ - role: 'assistant', - content: `I need to call the ${pendingToolCall.name} function with the following parameters: ${pendingToolCall.params}` - }) - - formattedMessages.push({ - role: 'user', - content: `Permission has been granted for the ${pendingToolCall.name} function. Please proceed with the execution.` - }) - } - - return formattedMessages - } - - /** - * 为系统提示词添加当前时间信息 - * @param systemPrompt 原始系统提示词 - * @param isImageGeneration 是否为图片生成模型 - * @returns 处理后的系统提示词 - */ - private enhanceSystemPromptWithDateTime( - systemPrompt: string, - isImageGeneration: boolean = false - ): string { - // 如果是图片生成模型或者系统提示词为空,则直接返回原值 - if (isImageGeneration || !systemPrompt || !systemPrompt.trim()) { - return systemPrompt - } - - // 生成当前时间字符串,包含完整的时区信息 - const currentDateTime = new Date().toLocaleString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - timeZoneName: 'short', - hour12: false - }) - - return `${systemPrompt}\nToday's date and time is ${currentDateTime}` - } - - /** - * 直接处理内容的方法 - */ - private async processContentDirectly( - eventId: string, - content: string, - currentTime: number - ): Promise { - const state = this.generatingMessages.get(eventId) - if (!state) return - - // 检查是否需要分块处理 - if (this.shouldSplitContent(content)) { - await this.processLargeContentInChunks(eventId, content, currentTime) - } else { - await this.processNormalContent(eventId, content, currentTime) - } - } - - /** - * 分块处理大内容 - */ - private async processLargeContentInChunks( - eventId: string, - content: string, - currentTime: number - ): Promise { - const state = this.generatingMessages.get(eventId) - if (!state) return - - console.log(`[ThreadPresenter] Processing large content in chunks: ${content.length} bytes`) - - const lastBlock = state.message.content[state.message.content.length - 1] - let contentBlock: any - - if (lastBlock && lastBlock.type === 'content') { - contentBlock = lastBlock - } else { - this.finalizeLastBlock(state) - contentBlock = { - type: 'content', - content: '', - status: 'loading', - timestamp: currentTime - } - state.message.content.push(contentBlock) - } - - // 直接添加内容,不做复杂分块 - contentBlock.content += content - - // 只更新数据库,不额外发送到渲染器(避免重复发送) - await this.messageManager.editMessage(eventId, JSON.stringify(state.message.content)) - } } diff --git a/src/main/presenter/threadPresenter/messageContent.ts b/src/main/presenter/threadPresenter/messageContent.ts new file mode 100644 index 000000000..62ab741cb --- /dev/null +++ b/src/main/presenter/threadPresenter/messageContent.ts @@ -0,0 +1,164 @@ +import type { + MessageFile, + UserMessageContent, + UserMessageCodeBlock, + UserMessageMentionBlock, + UserMessageTextBlock +} from '@shared/chat' + +const FILE_CONTENT_MAX_CHARS = 8000 +const FILE_CONTENT_TRUNCATION_SUFFIX = '…(truncated)' + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null +} + +function isTextBlock(content: unknown): content is { type: 'text'; text: string } { + return isRecord(content) && content.type === 'text' && typeof content.text === 'string' +} + +function extractPromptMessageText(message: unknown): string { + if (!isRecord(message)) { + return '' + } + + const content = message.content + + if (typeof content === 'string') { + return content + } + + if (isTextBlock(content)) { + return content.text + } + + if (isRecord(content) && typeof content.type === 'string') { + return `[${content.type}]` + } + + return '[content]' +} + +function truncateFileContent(content: string): string { + if (content.length <= FILE_CONTENT_MAX_CHARS) { + return content + } + + return `${content.slice(0, FILE_CONTENT_MAX_CHARS)}${FILE_CONTENT_TRUNCATION_SUFFIX}` +} + +function escapeTagContent(value: string): string { + return String(value).replace(/[&<>\u0000-\u001F]/g, (ch) => { + switch (ch) { + case '&': + return '&' + case '<': + return '<' + case '>': + return '>' + case '\n': + return ' ' + case '\r': + return ' ' + case '\t': + return ' ' + default: + return '' + } + }) +} + +export type UserMessageRichBlock = + | UserMessageTextBlock + | UserMessageMentionBlock + | UserMessageCodeBlock + +export function formatUserMessageContent(msgContentBlock: UserMessageRichBlock[]): string { + if (!Array.isArray(msgContentBlock)) { + return '' + } + + return msgContentBlock + .map((block) => { + if (block.type === 'mention') { + if (block.category === 'resources') { + return `@${block.content}` + } else if (block.category === 'tools') { + return `@${block.id}` + } else if (block.category === 'files') { + return `@${block.id}` + } else if (block.category === 'prompts') { + try { + const promptData = JSON.parse(block.content) + if (isRecord(promptData) && Array.isArray(promptData.messages)) { + const messageTexts = promptData.messages + .map(extractPromptMessageText) + .filter((text) => text) + const escapedContent = messageTexts.length + ? messageTexts.map(escapeTagContent).join('\n') + : escapeTagContent(block.content ?? '') + return `@${block.id} ${escapedContent}` + } + } catch (e) { + console.warn('Failed to parse prompt content:', e) + } + return `@${block.id} ${escapeTagContent(block.content ?? '')}` + } + return `@${block.id}` + } else if (block.type === 'text') { + return block.content + } else if (block.type === 'code') { + return `\`\`\`${block.content}\`\`\`` + } + return '' + }) + .join('') +} + +export function getFileContext(files?: MessageFile[]): string { + if (!files || files.length === 0) { + return '' + } + + return ` + + ${files + .map( + (file) => ` + ${file.name ?? ''} + ${file.mimeType ?? ''} + ${file.metadata?.fileSize ?? 0} + ${ + file.mimeType && !file.mimeType.startsWith('image') + ? truncateFileContent(String(file.content ?? '')) + : '' + } + ` + ) + .join('\n')} + + ` +} + +export function getNormalizedUserMessageText(content: UserMessageContent | undefined): string { + if (!content) { + return '' + } + + if (content.content && Array.isArray(content.content) && content.content.length > 0) { + return formatUserMessageContent(content.content) + } + + return content.text || '' +} + +export function buildUserMessageContext(content: UserMessageContent | undefined): string { + if (!content) { + return '' + } + + const messageText = getNormalizedUserMessageText(content) + const fileContext = getFileContext(content.files) + + return `${messageText}${fileContext}` +} diff --git a/src/main/presenter/threadPresenter/messageManager.ts b/src/main/presenter/threadPresenter/messageManager.ts index b34aad858..74340c01e 100644 --- a/src/main/presenter/threadPresenter/messageManager.ts +++ b/src/main/presenter/threadPresenter/messageManager.ts @@ -14,6 +14,7 @@ import { UserMessageMentionBlock, UserMessageCodeBlock } from '@shared/chat' +import { formatUserMessageContent } from './messageContent' import { eventBus, SendTarget } from '@/eventbus' import { CONVERSATION_EVENTS } from '@/events' @@ -227,30 +228,77 @@ export class MessageManager implements IMessageManager { }) } - async getContextMessages(conversationId: string, messageCount: number): Promise { + async getContextMessages( + conversationId: string, + messageCount: number, + { ensureUserStart = true, normalizeUserText = true } = {} + ): Promise { const sqliteMessages = await this.sqlitePresenter.queryMessages(conversationId) - // 按创建时间和序号倒序排序 const messages = sqliteMessages .sort((a, b) => { - // 首先按创建时间倒序排序 const timeCompare = b.created_at - a.created_at if (timeCompare !== 0) return timeCompare - // 如果创建时间相同,按序号倒序排序 return b.order_seq - a.order_seq }) - .slice(0, messageCount) // 只取需要的消息数量 + .slice(0, messageCount) .sort((a, b) => { - // 再次按正序排序以保持对话顺序 const timeCompare = a.created_at - b.created_at if (timeCompare !== 0) return timeCompare return a.order_seq - b.order_seq }) .map((msg) => this.convertToMessage(msg)) + if (ensureUserStart) { + while (messages.length > 0 && messages[0].role !== 'user') { + messages.shift() + } + } + + if (normalizeUserText) { + return messages.map((msg) => { + if (msg.role !== 'user') { + return msg + } + const userContent = msg.content as UserMessageContent + if (userContent?.content) { + return { + ...msg, + content: { + ...userContent, + text: formatUserMessageContent(userContent.content) + } + } + } + return msg + }) + } + return messages } + async getMessageHistory(messageId: string, limit: number = 100): Promise { + if (limit <= 0) { + return [] + } + + const message = await this.getMessage(messageId) + const sqliteMessages = await this.sqlitePresenter.queryMessages(message.conversationId) + const orderedMessages = sqliteMessages + .sort((a, b) => { + const timeDiff = a.created_at - b.created_at + return timeDiff !== 0 ? timeDiff : a.order_seq - b.order_seq + }) + .map((sqliteMessage) => this.convertToMessage(sqliteMessage)) + + const targetIndex = orderedMessages.findIndex((msg) => msg.id === messageId) + if (targetIndex === -1) { + return [message] + } + + return orderedMessages.slice(Math.max(0, targetIndex - limit + 1), targetIndex + 1) + } + async getLastUserMessage(conversationId: string): Promise { const sqliteMessage = await this.sqlitePresenter.getLastUserMessage(conversationId) if (!sqliteMessage) { diff --git a/src/main/presenter/threadPresenter/promptBuilder.ts b/src/main/presenter/threadPresenter/promptBuilder.ts new file mode 100644 index 000000000..39d1661a5 --- /dev/null +++ b/src/main/presenter/threadPresenter/promptBuilder.ts @@ -0,0 +1,657 @@ +import { approximateTokenSize } from 'tokenx' +import { presenter } from '@/presenter' +import { + AssistantMessage, + AssistantMessageBlock, + Message, + MessageFile, + UserMessageContent +} from '@shared/chat' +import { ModelType } from '@shared/model' +import { + CONVERSATION, + ModelConfig, + SearchResult, + ChatMessage, + ChatMessageContent +} from '../../../shared/presenter' +import type { MCPToolDefinition } from '../../../shared/presenter' +import { ContentEnricher } from './contentEnricher' +import { buildUserMessageContext, getNormalizedUserMessageText } from './messageContent' +import { generateSearchPrompt } from './searchManager' + +export type PendingToolCall = { + id: string + name: string + params: string + serverName?: string + serverIcons?: string + serverDescription?: string +} +type VisionUserMessageContent = UserMessageContent & { images?: string[] } + +export interface PreparePromptContentParams { + conversation: CONVERSATION + userContent: string + contextMessages: Message[] + searchResults: SearchResult[] | null + urlResults: SearchResult[] + userMessage: Message + vision: boolean + imageFiles: MessageFile[] + supportsFunctionCall: boolean + modelType?: ModelType +} + +export interface ContinueToolCallContextParams { + conversation: CONVERSATION + contextMessages: Message[] + userMessage: Message + pendingToolCall: PendingToolCall + modelConfig: ModelConfig +} + +export interface PostToolExecutionContextParams { + conversation: CONVERSATION + contextMessages: Message[] + userMessage: Message + currentAssistantMessage: AssistantMessage + completedToolCall: PendingToolCall & { response: string } + modelConfig: ModelConfig +} + +export async function preparePromptContent({ + conversation, + userContent, + contextMessages, + searchResults, + urlResults, + userMessage, + vision, + imageFiles, + supportsFunctionCall, + modelType +}: PreparePromptContentParams): Promise<{ + finalContent: ChatMessage[] + promptTokens: number +}> { + const { systemPrompt, contextLength, artifacts, enabledMcpTools } = conversation.settings + + const isImageGeneration = modelType === ModelType.ImageGeneration + const searchPrompt = + !isImageGeneration && searchResults ? generateSearchPrompt(userContent, searchResults) : '' + + const enrichedUserMessage = + !isImageGeneration && urlResults.length > 0 + ? '\n\n' + ContentEnricher.enrichUserMessageWithUrlContent(userContent, urlResults) + : '' + + const finalSystemPrompt = enhanceSystemPromptWithDateTime(systemPrompt, isImageGeneration) + + const searchPromptTokens = searchPrompt ? approximateTokenSize(searchPrompt) : 0 + const systemPromptTokens = + !isImageGeneration && finalSystemPrompt ? approximateTokenSize(finalSystemPrompt) : 0 + const userMessageTokens = approximateTokenSize(userContent + enrichedUserMessage) + + let mcpTools: MCPToolDefinition[] = [] + if (!isImageGeneration) { + try { + const toolDefinitions = await presenter.mcpPresenter.getAllToolDefinitions(enabledMcpTools) + if (Array.isArray(toolDefinitions)) { + mcpTools = toolDefinitions + } + } catch (error) { + console.warn('ThreadPresenter: Failed to load MCP tool definitions', error) + mcpTools = [] + } + } + const mcpToolsTokens = mcpTools.reduce( + (acc, tool) => acc + approximateTokenSize(JSON.stringify(tool)), + 0 + ) + + const reservedTokens = + searchPromptTokens + systemPromptTokens + userMessageTokens + mcpToolsTokens + const remainingContextLength = contextLength - reservedTokens + + const selectedContextMessages = selectContextMessages( + contextMessages, + userMessage, + remainingContextLength + ) + + const formattedMessages = formatMessagesForCompletion( + selectedContextMessages, + isImageGeneration ? '' : finalSystemPrompt, + artifacts, + searchPrompt, + userContent, + enrichedUserMessage, + imageFiles, + vision, + supportsFunctionCall + ) + + const mergedMessages = mergeConsecutiveMessages(formattedMessages) + + let promptTokens = 0 + const imageTokenCost = imageFiles.reduce((acc, file) => acc + (file.token ?? 0), 0) + for (let i = 0; i < mergedMessages.length; i++) { + const msg = mergedMessages[i] + + if (typeof msg.content === 'string' || msg.content === undefined) { + promptTokens += approximateTokenSize(msg.content || '') + continue + } + + const textContent = + msg.content?.reduce((acc, item) => { + if (item.type === 'text' && typeof item.text === 'string') { + return acc + item.text + } + return acc + }, '') ?? '' + + promptTokens += approximateTokenSize(textContent) + + const isFinalUserWithImages = + i === mergedMessages.length - 1 && + msg.role === 'user' && + Array.isArray(msg.content) && + msg.content.some((block) => block.type === 'image_url') + + if (isFinalUserWithImages && imageTokenCost > 0) { + promptTokens += imageTokenCost + } + } + + return { finalContent: mergedMessages, promptTokens } +} + +export async function buildContinueToolCallContext({ + conversation, + contextMessages, + userMessage, + pendingToolCall, + modelConfig +}: ContinueToolCallContextParams): Promise { + const { systemPrompt } = conversation.settings + const formattedMessages: ChatMessage[] = [] + + if (systemPrompt) { + const finalSystemPrompt = enhanceSystemPromptWithDateTime(systemPrompt) + formattedMessages.push({ + role: 'system', + content: finalSystemPrompt + }) + } + + const contextChatMessages = addContextMessages(contextMessages, false, modelConfig.functionCall) + formattedMessages.push(...contextChatMessages) + + const userContent = userMessage.content as UserMessageContent + const finalUserContent = buildUserMessageContext(userContent) + + formattedMessages.push({ + role: 'user', + content: finalUserContent + }) + + if (modelConfig.functionCall) { + formattedMessages.push({ + role: 'assistant', + tool_calls: [ + { + id: pendingToolCall.id, + type: 'function', + function: { + name: pendingToolCall.name, + arguments: pendingToolCall.params + } + } + ] + }) + + formattedMessages.push({ + role: 'user', + content: `Permission granted to call ${pendingToolCall.name}. Proceed with execution.` + }) + } else { + formattedMessages.push({ + role: 'assistant', + content: `I need to call the ${pendingToolCall.name} function with the following parameters: ${pendingToolCall.params}` + }) + + formattedMessages.push({ + role: 'user', + content: `Permission has been granted for the ${pendingToolCall.name} function. Please proceed with the execution.` + }) + } + + return formattedMessages +} + +export async function buildPostToolExecutionContext({ + conversation, + contextMessages, + userMessage, + currentAssistantMessage, + completedToolCall, + modelConfig +}: PostToolExecutionContextParams): Promise { + const { systemPrompt } = conversation.settings + const formattedMessages: ChatMessage[] = [] + + if (systemPrompt) { + const finalSystemPrompt = enhanceSystemPromptWithDateTime(systemPrompt) + formattedMessages.push({ + role: 'system', + content: finalSystemPrompt + }) + } + + const contextChatMessages = addContextMessages(contextMessages, false, modelConfig.functionCall) + formattedMessages.push(...contextChatMessages) + + const userContent = userMessage.content as UserMessageContent + const finalUserContent = buildUserMessageContext(userContent) + + formattedMessages.push({ + role: 'user', + content: finalUserContent + }) + + const assistantPreface = collectAssistantTextBeforePermission(currentAssistantMessage?.content) + if (assistantPreface.trim().length > 0) { + formattedMessages.push({ + role: 'assistant', + content: assistantPreface + }) + } + + if (modelConfig.functionCall) { + formattedMessages.push({ + role: 'assistant', + tool_calls: [ + { + id: completedToolCall.id, + type: 'function', + function: { + name: completedToolCall.name, + arguments: completedToolCall.params + } + } + ] + }) + + formattedMessages.push({ + role: 'tool', + tool_call_id: completedToolCall.id, + content: completedToolCall.response + }) + } else { + const formattedRecord = `${JSON.stringify({ + function_call_record: { + name: completedToolCall.name, + arguments: completedToolCall.params, + response: completedToolCall.response + } + })}` + + formattedMessages.push({ + role: 'assistant', + content: formattedRecord + '\n' + }) + + const userPromptText = + '以上是你刚执行的工具调用及其响应信息,已帮你插入,请仔细阅读工具响应,并继续你的回答。' + formattedMessages.push({ + role: 'user', + content: [{ type: 'text', text: userPromptText }] + }) + } + + return formattedMessages +} + +function collectAssistantTextBeforePermission(blocks: AssistantMessageBlock[] | undefined): string { + if (!blocks?.length) { + return '' + } + + const parts: string[] = [] + + for (const block of blocks) { + if (block.type === 'action' && block.action_type === 'tool_call_permission') { + break + } + + if (block.type === 'content' && typeof block.content === 'string') { + parts.push(block.content) + } + + if (block.type === 'reasoning_content' && typeof block.content === 'string') { + parts.push(block.content) + } + } + + return parts.join('') +} + +function selectContextMessages( + contextMessages: Message[], + userMessage: Message, + remainingContextLength: number +): Message[] { + if (remainingContextLength <= 0) { + return [] + } + + const messages = contextMessages.filter((msg) => msg.id !== userMessage?.id).reverse() + + let currentLength = 0 + const selectedMessages: Message[] = [] + + for (const msg of messages) { + if (msg.status !== 'sent') { + continue + } + + const msgContent = msg.role === 'user' ? (msg.content as UserMessageContent) : null + const msgTokens = approximateTokenSize( + msgContent ? getNormalizedUserMessageText(msgContent) : JSON.stringify(msg.content) + ) + + if (currentLength + msgTokens <= remainingContextLength) { + selectedMessages.unshift(msg) + currentLength += msgTokens + } else { + break + } + } + + while (selectedMessages.length > 0 && selectedMessages[0].role !== 'user') { + selectedMessages.shift() + } + + return selectedMessages +} + +function formatMessagesForCompletion( + contextMessages: Message[], + systemPrompt: string, + artifacts: number, + searchPrompt: string, + userContent: string, + enrichedUserMessage: string, + imageFiles: MessageFile[], + vision: boolean, + supportsFunctionCall: boolean +): ChatMessage[] { + const formattedMessages: ChatMessage[] = [] + + formattedMessages.push(...addContextMessages(contextMessages, vision, supportsFunctionCall)) + + if (systemPrompt) { + formattedMessages.unshift({ + role: 'system', + content: systemPrompt + }) + } + + let finalContent = searchPrompt || userContent + if (enrichedUserMessage) { + finalContent += enrichedUserMessage + } + + if (artifacts === 1) { + console.debug('Artifacts are provided by MCP; this is a backward-compatibility placeholder') + } + + if (vision && imageFiles.length > 0) { + formattedMessages.push(addImageFiles(finalContent, imageFiles)) + } else { + formattedMessages.push({ + role: 'user', + content: finalContent.trim() + }) + } + + return formattedMessages +} + +function addImageFiles(finalContent: string, imageFiles: MessageFile[]): ChatMessage { + return { + role: 'user', + content: [ + ...imageFiles.map((file) => ({ + type: 'image_url' as const, + image_url: { url: file.content, detail: 'auto' as const } + })), + { type: 'text' as const, text: finalContent.trim() } + ] + } +} + +function addContextMessages( + contextMessages: Message[], + vision: boolean, + supportsFunctionCall: boolean +): ChatMessage[] { + const resultMessages: ChatMessage[] = [] + + if (supportsFunctionCall) { + contextMessages.forEach((msg) => { + if (msg.role === 'user') { + const msgContent = msg.content as VisionUserMessageContent + const normalizedText = getNormalizedUserMessageText(msgContent) + if (vision && msgContent.images && msgContent.images.length > 0) { + resultMessages.push({ + role: 'user', + content: [ + ...msgContent.images.map((image) => ({ + type: 'image_url' as const, + image_url: { url: image, detail: 'auto' as const } + })), + { type: 'text' as const, text: normalizedText } + ] + }) + } else { + resultMessages.push({ + role: 'user', + content: normalizedText + }) + } + } else if (msg.role === 'assistant') { + const content = msg.content as AssistantMessageBlock[] + const messageContent: ChatMessageContent[] = [] + const toolCalls: ChatMessage['tool_calls'] = [] + + content.forEach((block) => { + if (block.type === 'tool_call' && block.tool_call) { + toolCalls.push({ + id: block.tool_call.id, + type: 'function', + function: { + name: block.tool_call.name, + arguments: block.tool_call.params || '' + } + }) + if (block.tool_call.response) { + messageContent.push({ type: 'text', text: block.tool_call.response }) + } + } else if (block.type === 'content' && block.content) { + messageContent.push({ type: 'text', text: block.content }) + } + }) + + if (toolCalls.length > 0) { + resultMessages.push({ + role: 'assistant', + content: messageContent.length > 0 ? messageContent : undefined, + tool_calls: toolCalls + }) + } else if (messageContent.length > 0) { + resultMessages.push({ + role: 'assistant', + content: messageContent + }) + } + } else { + resultMessages.push({ + role: msg.role, + content: JSON.stringify(msg.content) + }) + } + }) + + return resultMessages + } + + contextMessages.forEach((msg) => { + if (msg.role === 'user') { + const msgContent = msg.content as VisionUserMessageContent + const normalizedText = getNormalizedUserMessageText(msgContent) + if (vision && msgContent.images && msgContent.images.length > 0) { + resultMessages.push({ + role: 'user', + content: [ + ...msgContent.images.map((image) => ({ + type: 'image_url' as const, + image_url: { url: image, detail: 'auto' as const } + })), + { type: 'text' as const, text: normalizedText } + ] + }) + } else { + resultMessages.push({ + role: 'user', + content: normalizedText + }) + } + } else if (msg.role === 'assistant') { + const content = msg.content as AssistantMessageBlock[] + const textContent = content + .filter((block) => block.type === 'content' && block.content) + .map((block) => block.content) + .join('\n') + + if (textContent) { + resultMessages.push({ + role: 'assistant', + content: textContent + }) + } + } else { + resultMessages.push({ + role: msg.role, + content: JSON.stringify(msg.content) + }) + } + }) + + return resultMessages +} + +function mergeConsecutiveMessages(messages: ChatMessage[]): ChatMessage[] { + if (!messages || messages.length === 0) { + return [] + } + + const mergedResult: ChatMessage[] = [] + mergedResult.push(JSON.parse(JSON.stringify(messages[0]))) + + for (let i = 1; i < messages.length; i++) { + const currentMessage = JSON.parse(JSON.stringify(messages[i])) as ChatMessage + const lastPushedMessage = mergedResult[mergedResult.length - 1] + + let allowMessagePropertiesMerge = false + + if (lastPushedMessage.role === currentMessage.role) { + if (currentMessage.role === 'assistant') { + if (!lastPushedMessage.tool_calls && !currentMessage.tool_calls) { + allowMessagePropertiesMerge = true + } + } else { + allowMessagePropertiesMerge = true + } + } + + if (allowMessagePropertiesMerge) { + const lastContent = lastPushedMessage.content + const currentContent = currentMessage.content + + let newCombinedContent: string | ChatMessageContent[] | undefined = undefined + let contentTypesCompatible = false + + if (lastContent === undefined && currentContent === undefined) { + newCombinedContent = undefined + contentTypesCompatible = true + } else if ( + typeof lastContent === 'string' && + (typeof currentContent === 'string' || currentContent === undefined) + ) { + const previous = lastContent || '' + const current = currentContent || '' + if (previous && current) { + newCombinedContent = `${previous}\n${current}` + } else { + newCombinedContent = previous || current + } + if (newCombinedContent === '') { + newCombinedContent = undefined + } + contentTypesCompatible = true + } else if ( + Array.isArray(lastContent) && + (Array.isArray(currentContent) || currentContent === undefined) + ) { + const prevArray = lastContent + const currArray = currentContent || [] + newCombinedContent = [...prevArray, ...currArray] + if (newCombinedContent.length === 0) { + newCombinedContent = undefined + } + contentTypesCompatible = true + } else if (lastContent === undefined && currentContent !== undefined) { + newCombinedContent = currentContent + contentTypesCompatible = true + } else if (lastContent !== undefined && currentContent === undefined) { + newCombinedContent = lastContent + contentTypesCompatible = true + } + + if (contentTypesCompatible) { + lastPushedMessage.content = newCombinedContent + } else { + mergedResult.push(currentMessage) + } + } else { + mergedResult.push(currentMessage) + } + } + + return mergedResult +} + +function enhanceSystemPromptWithDateTime( + systemPrompt: string, + isImageGeneration: boolean = false +): string { + if (isImageGeneration || !systemPrompt || !systemPrompt.trim()) { + return systemPrompt + } + + const currentDateTime = new Date().toLocaleString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZoneName: 'short', + hour12: false + }) + + return `${systemPrompt}\nToday's date and time is ${currentDateTime}` +} diff --git a/src/main/presenter/threadPresenter/templates/conversationExportTemplates.ts b/src/main/presenter/threadPresenter/templates/conversationExportTemplates.ts new file mode 100644 index 000000000..98b799ff3 --- /dev/null +++ b/src/main/presenter/threadPresenter/templates/conversationExportTemplates.ts @@ -0,0 +1,170 @@ +export type ExportTemplates = { + html: { + documentStart: string[] + documentEnd: string[] + } + styles: string[] + templates: Record +} + +export const conversationExportTemplates: ExportTemplates = { + html: { + documentStart: [ + '', + '', + '', + ' ', + ' ', + ' {{title}}', + ' ', + '', + '', + '
' + ], + documentEnd: ['
', '', ''] + }, + styles: [ + ':root { color-scheme: light; }', + '* { box-sizing: border-box; }', + 'body { margin: 0; padding: 40px 16px 48px; background: #f1f4f9; color: #1f2933; font-family: "Inter", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif; line-height: 1.7; }', + '.page { max-width: 840px; margin: 0 auto; display: flex; flex-direction: column; gap: 24px; }', + '.header { background: #ffffff; border-radius: 18px; padding: 28px 32px; border: 1px solid rgba(148, 163, 184, 0.24); box-shadow: 0 14px 35px rgba(15, 23, 42, 0.05); }', + '.header h1 { margin: 0 0 16px; font-size: 2.1rem; font-weight: 700; color: #0f172a; }', + '.meta { display: grid; gap: 6px; font-size: 0.92rem; color: #475569; }', + '.meta-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }', + '.meta-label { font-weight: 600; color: #1e293b; }', + '.message { background: #ffffff; border-radius: 16px; padding: 24px 28px; border: 1px solid rgba(203, 213, 225, 0.6); box-shadow: 0 6px 20px rgba(15, 23, 42, 0.04); }', + '.message.user-message { border-left: 4px solid #2563eb; }', + '.message.assistant-message { border-left: 4px solid #0ea5e9; }', + '.message-header { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; }', + '.message-avatar { font-size: 1.4rem; line-height: 1; }', + '.message-identity { font-weight: 600; font-size: 1.02rem; color: #0f172a; }', + '.message-meta { font-size: 0.85rem; color: #64748b; }', + '.message-content { font-size: 1rem; color: #1f2937; word-break: break-word; }', + '.section { margin-top: 18px; }', + '.section-title { font-weight: 600; font-size: 0.95rem; color: #0f172a; display: flex; gap: 6px; align-items: center; }', + '.section-label { font-size: 0.75rem; font-weight: 600; color: #475569; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px; }', + '.section-caption { font-size: 0.85rem; color: #475569; margin-bottom: 4px; }', + '.attachments, .search-block, .tool-call, .error-block, .reasoning-block { border-radius: 12px; padding: 18px 20px; border: 1px solid rgba(203, 213, 225, 0.7); background: #f8fafc; }', + '.links { border-radius: 12px; padding: 18px 20px; border: 1px solid rgba(203, 213, 225, 0.7); background: #f8fafc; }', + '.attachments { background: #fef3c7; border-color: #fcd34d; }', + '.attachments ul { margin: 8px 0 0; padding-left: 20px; }', + '.attachments li { margin: 4px 0; }', + '.tool-call { background: #ecfdf3; border-color: #c4f0d6; }', + '.reasoning-block { background: #e0e7ff; border-color: #c7d2fe; color: #1e293b; }', + '.error-block { background: #fee2e2; border-color: #fecaca; color: #b91c1c; }', + '.search-block { background: #eff6ff; border-color: #bfdbfe; color: #1e3a8a; }', + 'pre.code { background: #0f172a; color: #e2e8f0; border-radius: 10px; padding: 16px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.88rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; margin: 10px 0 0; }', + 'a { color: #1d4ed8; text-decoration: none; font-weight: 500; }', + 'a:hover { text-decoration: underline; }', + '.divider { height: 1px; background: linear-gradient(90deg, rgba(148, 163, 184, 0.4), rgba(148, 163, 184, 0)); margin: 12px auto 32px; max-width: 820px; }' + ], + templates: { + header: [ + '
', + '

{{title}}

', + '
', + '{{metaRows}}', + '
', + '
' + ], + metaRow: + '
{{label}}{{value}}
', + userMessage: [ + '
', + '
', + '
👤
', + '
', + '
用户
', + '
{{timestamp}}
', + '
', + '
', + '
{{content}}
', + '{{attachmentsSection}}', + '{{linksSection}}', + '
' + ], + assistantMessage: [ + '
', + '
', + '
🤖
', + '
', + '
助手
', + '
{{timestamp}}
', + '
', + '
', + '{{assistantBlocks}}', + '
' + ], + attachmentsSection: [ + '
', + '
📎 附件
', + '
    ', + '{{items}}', + '
', + '
' + ], + attachmentItem: '
  • {{name}} ({{mime}})
  • ', + linksSection: [ + ' ' + ], + linkItem: + '
  • {{label}}
  • ', + assistantContent: ['
    {{content}}
    '], + assistantReasoning: [ + '
    ', + '
    🤔 思考过程
    ', + '
    {{content}}
    ', + '
    ' + ], + assistantArtifact: [ + '
    ', + '
    💭 创作思考
    ', + '
    {{content}}
    ', + '
    ' + ], + assistantToolCall: [ + '
    ', + '
    🔧 工具调用
    ', + '{{name}}', + '{{params}}', + '{{response}}', + '
    ' + ], + assistantToolName: '
    {{value}}
    ', + assistantToolParams: [ + ' ', + '
    {{value}}
    ' + ], + assistantToolResponse: [ + ' ', + '
    {{value}}
    ' + ], + assistantSearch: [ + '
    ', + '
    🔍 网络搜索
    ', + '{{caption}}', + '
    ' + ], + assistantSearchCaption: '
    找到 {{total}} 个搜索结果
    ', + assistantImage: [ + '
    ', + '
    🖼️ 图片
    ', + '
    *[图片内容]*
    ', + '
    ' + ], + assistantError: [ + '
    ', + ' ❌ {{content}}', + '
    ' + ], + divider: '
    ' + } +} diff --git a/src/main/presenter/threadPresenter/types.ts b/src/main/presenter/threadPresenter/types.ts new file mode 100644 index 000000000..ae537fbb8 --- /dev/null +++ b/src/main/presenter/threadPresenter/types.ts @@ -0,0 +1,36 @@ +import type { AssistantMessage } from '@shared/chat' +import type { PendingToolCall } from './promptBuilder' + +export interface GeneratingMessageState { + message: AssistantMessage + conversationId: string + startTime: number + firstTokenTime: number | null + promptTokens: number + reasoningStartTime: number | null + reasoningEndTime: number | null + lastReasoningTime: number | null + isSearching?: boolean + isCancelled?: boolean + pendingToolCall?: PendingToolCall + totalUsage?: { + prompt_tokens: number + completion_tokens: number + total_tokens: number + context_length: number + } + adaptiveBuffer?: { + content: string + lastUpdateTime: number + updateCount: number + totalSize: number + isLargeContent: boolean + chunks?: string[] + currentChunkIndex?: number + sentPosition: number + isProcessing?: boolean + } + flushTimeout?: NodeJS.Timeout + throttleTimeout?: NodeJS.Timeout + lastRendererUpdateTime?: number +} diff --git a/src/main/presenter/windowPresenter/index.ts b/src/main/presenter/windowPresenter/index.ts index 9dee0cc10..79f577e82 100644 --- a/src/main/presenter/windowPresenter/index.ts +++ b/src/main/presenter/windowPresenter/index.ts @@ -533,6 +533,14 @@ export class WindowPresenter implements IWindowPresenter { } } + if (this.settingsWindow && !this.settingsWindow.isDestroyed()) { + try { + this.settingsWindow.webContents.send(channel, ...args) + } catch (error) { + console.error(`Error sending message "${channel}" to settings window:`, error) + } + } + if (this.floatingChatWindow && this.floatingChatWindow.isShowing()) { const floatingWindow = this.floatingChatWindow.getWindow() if (floatingWindow && !floatingWindow.isDestroyed()) { @@ -554,6 +562,21 @@ export class WindowPresenter implements IWindowPresenter { */ sendToWindow(windowId: number, channel: string, ...args: unknown[]): boolean { console.log(`Sending message "${channel}" to window ${windowId}.`) + + if ( + this.settingsWindow && + !this.settingsWindow.isDestroyed() && + this.settingsWindow.id === windowId + ) { + try { + this.settingsWindow.webContents.send(channel, ...args) + return true + } catch (error) { + console.error(`Error sending message "${channel}" to settings window ${windowId}:`, error) + return false + } + } + const window = this.windows.get(windowId) if (window && !window.isDestroyed()) { // 向窗口主 WebContents 发送 @@ -1253,10 +1276,19 @@ export class WindowPresenter implements IWindowPresenter { // Choose icon based on platform const iconFile = nativeImage.createFromPath(process.platform === 'win32' ? iconWin : icon) - // Create Settings Window with fixed size (no state persistence) + // Initialize window state manager to remember position and size + const settingsWindowState = windowStateManager({ + file: 'settings-window-state.json', + defaultWidth: 900, + defaultHeight: 600 + }) + + // Create Settings Window with state persistence const settingsWindow = new BrowserWindow({ - width: 900, - height: 600, + x: settingsWindowState.x, + y: settingsWindowState.y, + width: settingsWindowState.width, + height: settingsWindowState.height, show: false, autoHideMenuBar: true, fullscreenable: false, @@ -1289,6 +1321,9 @@ export class WindowPresenter implements IWindowPresenter { this.settingsWindow = settingsWindow const windowId = settingsWindow.id + // Manage window state to track position and size changes + settingsWindowState.manage(settingsWindow) + // Ensure links with target="_blank" open in the user's default browser settingsWindow.webContents.setWindowOpenHandler(({ url }) => { try { @@ -1322,6 +1357,8 @@ export class WindowPresenter implements IWindowPresenter { settingsWindow.on('closed', () => { console.log(`Settings window ${windowId} closed.`) + // Unmanage window state when window is closed + settingsWindowState.unmanage() this.settingsWindow = null }) diff --git a/src/renderer/floating/index.html b/src/renderer/floating/index.html index 0df14ab22..d9eb359f2 100644 --- a/src/renderer/floating/index.html +++ b/src/renderer/floating/index.html @@ -5,6 +5,10 @@ + Floating Button diff --git a/src/renderer/src/components/message/MessageBlockContent.vue b/src/renderer/src/components/message/MessageBlockContent.vue index 47204d93a..5de3e13ce 100644 --- a/src/renderer/src/components/message/MessageBlockContent.vue +++ b/src/renderer/src/components/message/MessageBlockContent.vue @@ -54,40 +54,43 @@ watch( () => { nextTick(() => { for (const part of processedContent.value) { - if (part.type === 'artifact' && part.artifact) { - if (props.block.status === 'loading') { - if (artifactStore.currentArtifact?.id === part.artifact.identifier) { - // Use updateArtifactContent to trigger reactivity - artifactStore.updateArtifactContent({ - content: part.content, - title: part.artifact.title, - type: part.artifact.type, - status: part.loading ? 'loading' : 'loaded' - }) - } else { - artifactStore.showArtifact( - { - id: part.artifact.identifier, - type: part.artifact.type, - title: part.artifact.title, - language: part.artifact.language, - content: part.content, - status: part.loading ? 'loading' : 'loaded' - }, - props.messageId, - props.threadId - ) - } + const artifact = part.type === 'artifact' && part.artifact + if (!artifact) continue + const { title, type } = artifact + const { content, loading } = part + if (props.block.status === 'loading') { + const status = loading ? 'loading' : 'loaded' + if (artifactStore.currentArtifact?.id === artifact.identifier) { + // Use updateArtifactContent to trigger reactivity + artifactStore.updateArtifactContent({ + content, + title, + type, + status + }) } else { - if (artifactStore.currentArtifact?.id === part.artifact.identifier) { - // Use updateArtifactContent to trigger reactivity - artifactStore.updateArtifactContent({ - content: part.content, - title: part.artifact.title, - type: part.artifact.type, - status: 'loaded' - }) - } + artifactStore.showArtifact( + { + id: artifact.identifier, + type, + title, + language: artifact.language, + content, + status + }, + props.messageId, + props.threadId + ) + } + } else { + if (artifactStore.currentArtifact?.id === artifact.identifier) { + // Use updateArtifactContent to trigger reactivity + artifactStore.updateArtifactContent({ + content, + title: artifact.title, + type, + status: 'loaded' + }) } } } diff --git a/src/renderer/src/components/message/MessageBlockImage.vue b/src/renderer/src/components/message/MessageBlockImage.vue index b46b7349c..ba402261a 100644 --- a/src/renderer/src/components/message/MessageBlockImage.vue +++ b/src/renderer/src/components/message/MessageBlockImage.vue @@ -66,6 +66,13 @@ import { AssistantMessageBlock } from '@shared/chat' import { useI18n } from 'vue-i18n' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@shadcn/components/ui/dialog' +const keyMap = { + 'image.title': '生成的图片', + 'image.generatedImage': 'AI生成的图片', + 'image.loadError': '图片加载失败', + 'image.viewFull': '查看原图', + 'image.close': '关闭' +} // 创建一个安全的翻译函数 const t = (() => { try { @@ -73,14 +80,7 @@ const t = (() => { return t } catch (e) { // 如果 i18n 未初始化,提供默认翻译 - return (key: string) => { - if (key === 'image.title') return '生成的图片' - if (key === 'image.generatedImage') return 'AI生成的图片' - if (key === 'image.loadError') return '图片加载失败' - if (key === 'image.viewFull') return '查看原图' - if (key === 'image.close') return '关闭' - return key - } + return (key: string) => keyMap[key] || key } })() diff --git a/src/renderer/src/components/message/MessageBlockPermissionRequest.vue b/src/renderer/src/components/message/MessageBlockPermissionRequest.vue index 6a0d43a37..8f291ba9d 100644 --- a/src/renderer/src/components/message/MessageBlockPermissionRequest.vue +++ b/src/renderer/src/components/message/MessageBlockPermissionRequest.vue @@ -208,6 +208,7 @@ const getStatusIconClass = () => { const getStatusText = () => { switch (props.block.status) { case 'granted': + case 'success': return t('components.messageBlockPermissionRequest.granted') case 'denied': return t('components.messageBlockPermissionRequest.denied') diff --git a/src/renderer/src/components/message/MessageBlockToolCall.vue b/src/renderer/src/components/message/MessageBlockToolCall.vue index 85b9fef78..b354a6a23 100644 --- a/src/renderer/src/components/message/MessageBlockToolCall.vue +++ b/src/renderer/src/components/message/MessageBlockToolCall.vue @@ -62,6 +62,17 @@ import { AssistantMessageBlock } from '@shared/chat' import { computed, ref } from 'vue' import { JsonObject } from '@/components/json-viewer' +const keyMap = { + 'toolCall.calling': '工具调用中', + 'toolCall.response': '工具响应', + 'toolCall.end': '工具调用完成', + 'toolCall.error': '工具调用错误', + 'toolCall.title': '工具调用', + 'toolCall.clickToView': '点击查看详情', + 'toolCall.functionName': '函数名称', + 'toolCall.params': '参数', + 'toolCall.responseData': '响应数据' +} // 创建一个安全的翻译函数 const t = (() => { try { @@ -69,18 +80,7 @@ const t = (() => { return t } catch (e) { // 如果 i18n 未初始化,提供默认翻译 - return (key: string) => { - if (key === 'toolCall.calling') return '工具调用中' - if (key === 'toolCall.response') return '工具响应' - if (key === 'toolCall.end') return '工具调用完成' - if (key === 'toolCall.error') return '工具调用错误' - if (key === 'toolCall.title') return '工具调用' - if (key === 'toolCall.clickToView') return '点击查看详情' - if (key === 'toolCall.functionName') return '函数名称' - if (key === 'toolCall.params') return '参数' - if (key === 'toolCall.responseData') return '响应数据' - return key - } + return (key: string) => keyMap[key] || key } })() diff --git a/src/renderer/src/components/message/MessageItemAssistant.vue b/src/renderer/src/components/message/MessageItemAssistant.vue index f39aa1018..7f66f68f9 100644 --- a/src/renderer/src/components/message/MessageItemAssistant.vue +++ b/src/renderer/src/components/message/MessageItemAssistant.vue @@ -25,7 +25,7 @@ :is-search-result="isSearchResult" /> diff --git a/src/renderer/src/components/message/MessageList.vue b/src/renderer/src/components/message/MessageList.vue index afcdcada6..62562e6ed 100644 --- a/src/renderer/src/components/message/MessageList.vue +++ b/src/renderer/src/components/message/MessageList.vue @@ -57,22 +57,6 @@ @bar-click="minimap.handleClick" /> - - - - {{ t('dialog.cleanMessages.title') }} - - {{ t('dialog.cleanMessages.description') }} - - - - - - - -