From e401685449c60e735472b358c081f68146560205 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 28 Sep 2025 14:57:01 +0800 Subject: [PATCH 1/7] lint: fix code format --- .../src/pages/home/Inputbar/MentionModelsButton.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx index 23c8fd13f..64d421269 100644 --- a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx @@ -251,11 +251,7 @@ const MentionModelsButton: FC = ({ if (action === 'esc') { // 只有在输入触发且有模型选择动作时才删除@字符和搜索文本 const triggerInfo = ctx?.triggerInfo ?? triggerInfoRef.current - if ( - hasModelActionRef.current && - triggerInfo?.type === 'input' && - triggerInfo?.position !== undefined - ) { + if (hasModelActionRef.current && triggerInfo?.type === 'input' && triggerInfo?.position !== undefined) { // 基于当前光标 + 搜索词精确定位并删除,position 仅作兜底 setText((currentText) => { const textArea = document.querySelector('.inputbar textarea') as HTMLTextAreaElement | null From 5365fddec945d126e6c96d8b05055b0a44406ba7 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 28 Sep 2025 15:07:21 +0800 Subject: [PATCH 2/7] chore: bump version to 1.6.2 - Updated release notes to reflect recent optimizations and bug fixes, including improvements to the note-taking feature and resolution of issues with CherryAI and VertexAI. - Bumped version number from 1.6.1 to 1.6.2 in package.json. --- electron-builder.yml | 60 +++----------------------------------------- package.json | 2 +- 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index 05fdc8b2f..56dba2795 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -125,59 +125,7 @@ afterSign: scripts/notarize.js artifactBuildCompleted: scripts/artifact-build-completed.js releaseInfo: releaseNotes: | - - 🚀 New Features: - - Refactored AI core engine for more efficient and stable content generation - - Added support for multiple AI model providers: CherryIN, AiOnly - - Added API server functionality for external application integration - - Added PaddleOCR document recognition for enhanced document processing - - Added Anthropic OAuth authentication support - - Added data storage space limit notifications - - Added font settings for global and code fonts customization - - Added auto-copy feature after translation completion - - Added keyboard shortcuts: rename topic, edit last message, etc. - - Added text attachment preview for viewing file contents in messages - - Added custom window control buttons (minimize, maximize, close) - - Support for Qwen long-text (qwen-long) and document analysis (qwen-doc) models with native file uploads - - Support for Qwen image recognition models (Qwen-Image) - - Added iFlow CLI support - - Converted knowledge base and web search to tool-calling approach for better flexibility - - 🎨 UI Improvements & Bug Fixes: - - Integrated HeroUI and Tailwind CSS framework - - Optimized message notification styles with unified toast component - - Moved free models to bottom with fixed position for easier access - - Refactored quick panel and input bar tools for smoother operation - - Optimized responsive design for navbar and sidebar - - Improved scrollbar component with horizontal scrolling support - - Fixed multiple translation issues: paste handling, file processing, state management - - Various UI optimizations and bug fixes - - 🚀 新功能: - - 重构 AI 核心引擎,提供更高效稳定的内容生成 - - 新增多个 AI 模型提供商支持:CherryIN、AiOnly - - 新增 API 服务器功能,支持外部应用集成 - - 新增 PaddleOCR 文档识别,增强文档处理能力 - - 新增 Anthropic OAuth 认证支持 - - 新增数据存储空间限制提醒 - - 新增字体设置,支持全局字体和代码字体自定义 - - 新增翻译完成后自动复制功能 - - 新增键盘快捷键:重命名主题、编辑最后一条消息等 - - 新增文本附件预览,可查看消息中的文件内容 - - 新增自定义窗口控制按钮(最小化、最大化、关闭) - - 支持通义千问长文本(qwen-long)和文档分析(qwen-doc)模型,原生文件上传 - - 支持通义千问图像识别模型(Qwen-Image) - - 新增 iFlow CLI 支持 - - 知识库和网页搜索转换为工具调用方式,提升灵活性 - - 🎨 界面改进与问题修复: - - 集成 HeroUI 和 Tailwind CSS 框架 - - 优化消息通知样式,统一 toast 组件 - - 免费模型移至底部固定位置,便于访问 - - 重构快捷面板和输入栏工具,操作更流畅 - - 优化导航栏和侧边栏响应式设计 - - 改进滚动条组件,支持水平滚动 - - 修复多个翻译问题:粘贴处理、文件处理、状态管理 - - 各种界面优化和问题修复 - - + Optimized note-taking feature, now able to quickly rename by modifying the title + Fixed issue where CherryAI free model could not be used + Fixed issue where VertexAI proxy address could not be called normally + Fixed issue where built-in tools from service providers could not be called normally diff --git a/package.json b/package.json index 6e5ab73a8..2e6b6251e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CherryStudio", - "version": "1.6.1", + "version": "1.6.2", "private": true, "description": "A powerful AI assistant for producer.", "main": "./out/main/index.js", From 4975c2d9e853dbab8fa9b1ce0fbb3108ab24f252 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 28 Sep 2025 16:07:09 +0800 Subject: [PATCH 3/7] chore: update build configurations to use secrets for sensitive environment variables - Modified GitHub Actions workflows to replace environment variable references with secrets for MAIN_VITE_MINERU_API_KEY, RENDERER_VITE_AIHUBMIX_SECRET, and RENDERER_VITE_PPIO_APP_SECRET. - Added onwarn handler in electron.vite.config.ts to suppress specific warnings related to CommonJS variables in ESM. --- .github/workflows/auto-i18n.yml | 4 ++-- .github/workflows/nightly-build.yml | 24 ++++++++++++------------ .github/workflows/release.yml | 24 ++++++++++++------------ electron.vite.config.ts | 8 ++++++++ 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/.github/workflows/auto-i18n.yml b/.github/workflows/auto-i18n.yml index 4cdd1481c..140d6208f 100644 --- a/.github/workflows/auto-i18n.yml +++ b/.github/workflows/auto-i18n.yml @@ -2,8 +2,8 @@ name: Auto I18N env: API_KEY: ${{ secrets.TRANSLATE_API_KEY }} - MODEL: ${{ vars.MODEL || 'deepseek/deepseek-v3.1'}} - BASE_URL: ${{ vars.BASE_URL || 'https://api.ppinfra.com/openai'}} + MODEL: ${{ vars.AUTO_I18N_MODEL || 'deepseek/deepseek-v3.1'}} + BASE_URL: ${{ vars.AUTO_I18N_BASE_URL || 'https://api.ppinfra.com/openai'}} on: pull_request: diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 7f7100dc5..42d0d6615 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -99,9 +99,9 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} - MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} - RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} - RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} + MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }} + RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }} + RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }} - name: Build Mac if: matrix.os == 'macos-latest' @@ -110,15 +110,15 @@ jobs: env: CSC_LINK: ${{ secrets.CSC_LINK }} CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} - APPLE_ID: ${{ vars.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ vars.APPLE_APP_SPECIFIC_PASSWORD }} - APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} - MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} - RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} - RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} + MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }} + RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }} + RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }} - name: Build Windows if: matrix.os == 'windows-latest' @@ -128,9 +128,9 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} - MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} - RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} - RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} + MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }} + RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }} + RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }} - name: Rename artifacts with nightly format shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4a772ad6..0ca1eb014 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,9 +86,9 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} - MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} - RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} - RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} + MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }} + RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }} + RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }} - name: Build Mac if: matrix.os == 'macos-latest' @@ -98,15 +98,15 @@ jobs: env: CSC_LINK: ${{ secrets.CSC_LINK }} CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} - APPLE_ID: ${{ vars.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ vars.APPLE_APP_SPECIFIC_PASSWORD }} - APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} - MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} - RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} - RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} + MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }} + RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }} + RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }} - name: Build Windows if: matrix.os == 'windows-latest' @@ -116,9 +116,9 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} - MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} - RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} - RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} + MAIN_VITE_MINERU_API_KEY: ${{ secrets.MAIN_VITE_MINERU_API_KEY }} + RENDERER_VITE_AIHUBMIX_SECRET: ${{ secrets.RENDERER_VITE_AIHUBMIX_SECRET }} + RENDERER_VITE_PPIO_APP_SECRET: ${{ secrets.RENDERER_VITE_PPIO_APP_SECRET }} - name: Release uses: ncipollo/release-action@v1 diff --git a/electron.vite.config.ts b/electron.vite.config.ts index b7c55793d..aa2bc13de 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -34,6 +34,10 @@ export default defineConfig({ output: { manualChunks: undefined, // 彻底禁用代码分割 - 返回 null 强制单文件打包 inlineDynamicImports: true // 内联所有动态导入,这是关键配置 + }, + onwarn(warning, warn) { + if (warning.code === 'COMMONJS_VARIABLE_IN_ESM') return + warn(warning) } }, sourcemap: isDev @@ -111,6 +115,10 @@ export default defineConfig({ selectionToolbar: resolve(__dirname, 'src/renderer/selectionToolbar.html'), selectionAction: resolve(__dirname, 'src/renderer/selectionAction.html'), traceWindow: resolve(__dirname, 'src/renderer/traceWindow.html') + }, + onwarn(warning, warn) { + if (warning.code === 'COMMONJS_VARIABLE_IN_ESM') return + warn(warning) } } }, From 483b4e090e767801971b1da1dab943f5bd0ba088 Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat Date: Sun, 28 Sep 2025 16:27:26 +0800 Subject: [PATCH 4/7] feat(toolUsePlugin): separate provider-defined tools from prompt tool (#10428) * feat(toolUsePlugin): separate provider-defined tools from prompt tools in context - Enhanced the `createPromptToolUsePlugin` function to distinguish between provider-defined tools and other tools, ensuring only non-provider-defined tools are saved in the context. - Updated the handling of tools in the transformed parameters to retain provider-defined tools while removing others. - Improved error handling in `ToolExecutor` by logging tool and tool use details for better debugging. - Refactored various components to use `NormalToolResponse` instead of `MCPToolResponse`, aligning with the new response structure across multiple message components. * refactor(toolUsePlugin): streamline tool handling in createPromptToolUsePlugin - Updated the `createPromptToolUsePlugin` function to improve type handling for tools, ensuring proper type inference and reducing the use of type assertions. - Enhanced clarity in the separation of provider-defined tools and prompt tools, maintaining functionality while improving code readability. * refactor(ToolExecutor): remove debug logging for tool and tool use - Removed console logging for tool and tool use details in the ToolExecutor class to clean up the code and improve performance. This change enhances the clarity of the code without affecting functionality. --- .../toolUsePlugin/promptToolUsePlugin.ts | 30 +++++++++++++++---- .../built-in/webSearchPlugin/helper.ts | 28 ++++++++++------- .../Messages/Tools/MessageKnowledgeSearch.tsx | 6 ++-- .../home/Messages/Tools/MessageMcpTool.tsx | 3 +- .../Messages/Tools/MessageMemorySearch.tsx | 4 +-- .../pages/home/Messages/Tools/MessageTool.tsx | 17 ++++++----- .../home/Messages/Tools/MessageWebSearch.tsx | 4 +-- src/renderer/src/types/newMessage.ts | 3 +- 8 files changed, 63 insertions(+), 32 deletions(-) diff --git a/packages/aiCore/src/core/plugins/built-in/toolUsePlugin/promptToolUsePlugin.ts b/packages/aiCore/src/core/plugins/built-in/toolUsePlugin/promptToolUsePlugin.ts index fce028f5c..a2cc7d9af 100644 --- a/packages/aiCore/src/core/plugins/built-in/toolUsePlugin/promptToolUsePlugin.ts +++ b/packages/aiCore/src/core/plugins/built-in/toolUsePlugin/promptToolUsePlugin.ts @@ -261,22 +261,39 @@ export const createPromptToolUsePlugin = (config: PromptToolUseConfig = {}) => { return params } - context.mcpTools = params.tools + // 分离 provider-defined 和其他类型的工具 + const providerDefinedTools: ToolSet = {} + const promptTools: ToolSet = {} - // 构建系统提示符 + for (const [toolName, tool] of Object.entries(params.tools as ToolSet)) { + if (tool.type === 'provider-defined') { + // provider-defined 类型的工具保留在 tools 参数中 + providerDefinedTools[toolName] = tool + } else { + // 其他工具转换为 prompt 模式 + promptTools[toolName] = tool + } + } + + // 只有当有非 provider-defined 工具时才保存到 context + if (Object.keys(promptTools).length > 0) { + context.mcpTools = promptTools + } + + // 构建系统提示符(只包含非 provider-defined 工具) const userSystemPrompt = typeof params.system === 'string' ? params.system : '' - const systemPrompt = buildSystemPrompt(userSystemPrompt, params.tools) + const systemPrompt = buildSystemPrompt(userSystemPrompt, promptTools) let systemMessage: string | null = systemPrompt if (config.createSystemMessage) { // 🎯 如果用户提供了自定义处理函数,使用它 systemMessage = config.createSystemMessage(systemPrompt, params, context) } - // 移除 tools,改为 prompt 模式 + // 保留 provider-defined tools,移除其他 tools const transformedParams = { ...params, ...(systemMessage ? { system: systemMessage } : {}), - tools: undefined + tools: Object.keys(providerDefinedTools).length > 0 ? providerDefinedTools : undefined } context.originalParams = transformedParams return transformedParams @@ -285,8 +302,9 @@ export const createPromptToolUsePlugin = (config: PromptToolUseConfig = {}) => { let textBuffer = '' // let stepId = '' + // 如果没有需要 prompt 模式处理的工具,直接返回原始流 if (!context.mcpTools) { - throw new Error('No tools available') + return new TransformStream() } // 从 context 中获取或初始化 usage 累加器 diff --git a/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/helper.ts b/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/helper.ts index 4845ce4ac..95c2cdda2 100644 --- a/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/helper.ts +++ b/packages/aiCore/src/core/plugins/built-in/webSearchPlugin/helper.ts @@ -1,6 +1,7 @@ import { anthropic } from '@ai-sdk/anthropic' import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' +import { InferToolInput, InferToolOutput } from 'ai' import { ProviderOptionsMap } from '../../../options/types' import { OpenRouterSearchConfig } from './openrouter' @@ -58,24 +59,31 @@ export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchPluginConfig = { export type WebSearchToolOutputSchema = { // Anthropic 工具 - 手动定义 - anthropicWebSearch: Array<{ - url: string - title: string - pageAge: string | null - encryptedContent: string - type: string - }> + anthropic: InferToolOutput> // OpenAI 工具 - 基于实际输出 - openaiWebSearch: { + // TODO: 上游定义不规范,是unknown + // openai: InferToolOutput> + openai: { + status: 'completed' | 'failed' + } + 'openai-chat': { status: 'completed' | 'failed' } - // Google 工具 - googleSearch: { + // TODO: 上游定义不规范,是unknown + // google: InferToolOutput> + google: { webSearchQueries?: string[] groundingChunks?: Array<{ web?: { uri: string; title: string } }> } } + +export type WebSearchToolInputSchema = { + anthropic: InferToolInput> + openai: InferToolInput> + google: InferToolInput> + 'openai-chat': InferToolInput> +} diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageKnowledgeSearch.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageKnowledgeSearch.tsx index 72a3f6e36..19c3a135d 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageKnowledgeSearch.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageKnowledgeSearch.tsx @@ -1,13 +1,13 @@ import { KnowledgeSearchToolInput, KnowledgeSearchToolOutput } from '@renderer/aiCore/tools/KnowledgeSearchTool' import Spinner from '@renderer/components/Spinner' import i18n from '@renderer/i18n' -import { MCPToolResponse } from '@renderer/types' +import { NormalToolResponse } from '@renderer/types' import { Typography } from 'antd' import { FileSearch } from 'lucide-react' import styled from 'styled-components' const { Text } = Typography -export function MessageKnowledgeSearchToolTitle({ toolResponse }: { toolResponse: MCPToolResponse }) { +export function MessageKnowledgeSearchToolTitle({ toolResponse }: { toolResponse: NormalToolResponse }) { const toolInput = toolResponse.arguments as KnowledgeSearchToolInput const toolOutput = toolResponse.response as KnowledgeSearchToolOutput @@ -28,7 +28,7 @@ export function MessageKnowledgeSearchToolTitle({ toolResponse }: { toolResponse ) } -export function MessageKnowledgeSearchToolBody({ toolResponse }: { toolResponse: MCPToolResponse }) { +export function MessageKnowledgeSearchToolBody({ toolResponse }: { toolResponse: NormalToolResponse }) { const toolOutput = toolResponse.response as KnowledgeSearchToolOutput return toolResponse.status === 'done' ? ( diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx index be5b21104..11f29221d 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageMcpTool.tsx @@ -4,6 +4,7 @@ import { useCodeStyle } from '@renderer/context/CodeStyleProvider' import { useMCPServers } from '@renderer/hooks/useMCPServers' import { useSettings } from '@renderer/hooks/useSettings' import { useTimer } from '@renderer/hooks/useTimer' +import { MCPToolResponse } from '@renderer/types' import type { ToolMessageBlock } from '@renderer/types/newMessage' import { isToolAutoApproved } from '@renderer/utils/mcp-tools' import { cancelToolAction, confirmToolAction } from '@renderer/utils/userConfirmation' @@ -57,7 +58,7 @@ const MessageMcpTool: FC = ({ block }) => { const [progress, setProgress] = useState(0) const { setTimeoutTimer } = useTimer() - const toolResponse = block.metadata?.rawMcpToolResponse + const toolResponse = block.metadata?.rawMcpToolResponse as MCPToolResponse const { id, tool, status, response } = toolResponse! const isPending = status === 'pending' diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageMemorySearch.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageMemorySearch.tsx index cb86d8a25..2d4914463 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageMemorySearch.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageMemorySearch.tsx @@ -1,6 +1,6 @@ import { MemorySearchToolInput, MemorySearchToolOutput } from '@renderer/aiCore/tools/MemorySearchTool' import Spinner from '@renderer/components/Spinner' -import { MCPToolResponse } from '@renderer/types' +import { NormalToolResponse } from '@renderer/types' import { Typography } from 'antd' import { ChevronRight } from 'lucide-react' import { useTranslation } from 'react-i18next' @@ -8,7 +8,7 @@ import styled from 'styled-components' const { Text } = Typography -export const MessageMemorySearchToolTitle = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { +export const MessageMemorySearchToolTitle = ({ toolResponse }: { toolResponse: NormalToolResponse }) => { const { t } = useTranslation() const toolInput = toolResponse.arguments as MemorySearchToolInput const toolOutput = toolResponse.response as MemorySearchToolOutput diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx index 38ae73e95..704fdafd0 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageTool.tsx @@ -1,4 +1,4 @@ -import { MCPToolResponse } from '@renderer/types' +import { NormalToolResponse } from '@renderer/types' import type { ToolMessageBlock } from '@renderer/types/newMessage' import { Collapse } from 'antd' @@ -11,8 +11,9 @@ interface Props { } const prefix = 'builtin_' -const ChooseTool = (toolResponse: MCPToolResponse): { label: React.ReactNode; body: React.ReactNode } | null => { +const ChooseTool = (toolResponse: NormalToolResponse): { label: React.ReactNode; body: React.ReactNode } | null => { let toolName = toolResponse.tool.name + const toolType = toolResponse.tool.type if (toolName.startsWith(prefix)) { toolName = toolName.slice(prefix.length) } @@ -20,10 +21,12 @@ const ChooseTool = (toolResponse: MCPToolResponse): { label: React.ReactNode; bo switch (toolName) { case 'web_search': case 'web_search_preview': - return { - label: , - body: null - } + return toolType === 'provider' + ? null + : { + label: , + body: null + } case 'knowledge_search': return { label: , @@ -41,7 +44,7 @@ const ChooseTool = (toolResponse: MCPToolResponse): { label: React.ReactNode; bo export default function MessageTool({ block }: Props) { // FIXME: 语义错误,这里已经不是 MCP tool 了,更改rawMcpToolResponse需要改用户数据, 所以暂时保留 - const toolResponse = block.metadata?.rawMcpToolResponse + const toolResponse = block.metadata?.rawMcpToolResponse as NormalToolResponse if (!toolResponse) return null diff --git a/src/renderer/src/pages/home/Messages/Tools/MessageWebSearch.tsx b/src/renderer/src/pages/home/Messages/Tools/MessageWebSearch.tsx index cd04de3a2..5fe71bbae 100644 --- a/src/renderer/src/pages/home/Messages/Tools/MessageWebSearch.tsx +++ b/src/renderer/src/pages/home/Messages/Tools/MessageWebSearch.tsx @@ -1,6 +1,6 @@ import { WebSearchToolInput, WebSearchToolOutput } from '@renderer/aiCore/tools/WebSearchTool' import Spinner from '@renderer/components/Spinner' -import { MCPToolResponse } from '@renderer/types' +import { NormalToolResponse } from '@renderer/types' import { Typography } from 'antd' import { Search } from 'lucide-react' import { useTranslation } from 'react-i18next' @@ -8,7 +8,7 @@ import styled from 'styled-components' const { Text } = Typography -export const MessageWebSearchToolTitle = ({ toolResponse }: { toolResponse: MCPToolResponse }) => { +export const MessageWebSearchToolTitle = ({ toolResponse }: { toolResponse: NormalToolResponse }) => { const { t } = useTranslation() const toolInput = toolResponse.arguments as WebSearchToolInput const toolOutput = toolResponse.response as WebSearchToolOutput diff --git a/src/renderer/src/types/newMessage.ts b/src/renderer/src/types/newMessage.ts index 74e2b8266..7ac6ab5bc 100644 --- a/src/renderer/src/types/newMessage.ts +++ b/src/renderer/src/types/newMessage.ts @@ -10,6 +10,7 @@ import type { MemoryItem, Metrics, Model, + NormalToolResponse, Topic, Usage, WebSearchResponse, @@ -113,7 +114,7 @@ export interface ToolMessageBlock extends BaseMessageBlock { arguments?: Record content?: string | object metadata?: BaseMessageBlock['metadata'] & { - rawMcpToolResponse?: MCPToolResponse + rawMcpToolResponse?: MCPToolResponse | NormalToolResponse } } From bb0ec0a3eca2f5ceef11cfbcb95d573ab8de7438 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 28 Sep 2025 16:32:42 +0800 Subject: [PATCH 5/7] chore: update @ai-sdk/google patch and refine getModelPath function - Updated the resolution and checksum for the @ai-sdk/google patch in yarn.lock. - Enhanced the getModelPath function to check for "models/" in the modelId before returning the path, improving its robustness. --- .yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch | 4 ++-- yarn.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch b/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch index 49bcec27d..f8868aa91 100644 --- a/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch +++ b/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch @@ -1,5 +1,5 @@ diff --git a/dist/index.mjs b/dist/index.mjs -index 110f37ec18c98b1d55ae2b73cc716194e6f9094d..3ea0fadd783f334db71266e45babdcce11076974 100644 +index 110f37ec18c98b1d55ae2b73cc716194e6f9094d..17e109b7778cbebb904f1919e768d21a2833d965 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -448,7 +448,7 @@ function convertToGoogleGenerativeAIMessages(prompt, options) { @@ -7,7 +7,7 @@ index 110f37ec18c98b1d55ae2b73cc716194e6f9094d..3ea0fadd783f334db71266e45babdcce // src/get-model-path.ts function getModelPath(modelId) { - return modelId.includes("/") ? modelId : `models/${modelId}`; -+ return `models/${modelId}`; ++ return modelId?.includes("models/") ? modelId : `models/${modelId}`; } // src/google-generative-ai-options.ts diff --git a/yarn.lock b/yarn.lock index 748d52512..9252c911d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -169,13 +169,13 @@ __metadata: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.14#~/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch": version: 2.0.14 - resolution: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.14#~/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch::version=2.0.14&hash=c6aff2" + resolution: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.14#~/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch::version=2.0.14&hash=351f1a" dependencies: "@ai-sdk/provider": "npm:2.0.0" "@ai-sdk/provider-utils": "npm:3.0.9" peerDependencies: zod: ^3.25.76 || ^4 - checksum: 10c0/2a0a09debab8de0603243503ff5044bd3fff87d6c5de2d76d43839fa459cc85d5412b59ec63d0dcf1a6d6cab02882eb3c69f0f155129d0fc153bcde4deecbd32 + checksum: 10c0/1ed5a0732a82b981d51f63c6241ed8ee94d5c29a842764db770305cfc2f49ab6e528cac438b5357fc7b02194104c7b76d4390a1dc1d019ace9c174b0849e0da6 languageName: node linkType: hard From 06ab2822be98258d2df6dbf6e2d37c179505024c Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat Date: Sun, 28 Sep 2025 19:38:44 +0800 Subject: [PATCH 6/7] Refactor/reasoning time (#10393) --- .../src/aiCore/chunk/AiSdkToChunkAdapter.ts | 5 +- .../src/aiCore/plugins/PluginBuilder.ts | 7 ++- .../home/Messages/Blocks/ThinkingBlock.tsx | 51 +++++++++++-------- .../Blocks/__tests__/ThinkingBlock.test.tsx | 3 +- .../callbacks/thinkingCallbacks.ts | 19 +++---- .../streamCallback.integration.test.ts | 5 +- 6 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts b/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts index 2e8ce3296..fb68cedb2 100644 --- a/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts +++ b/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts @@ -163,14 +163,13 @@ export class AiSdkToChunkAdapter { final.reasoningContent += chunk.text || '' this.onChunk({ type: ChunkType.THINKING_DELTA, - text: final.reasoningContent || '', - thinking_millsec: (chunk.providerMetadata?.metadata?.thinking_millsec as number) || 0 + text: final.reasoningContent || '' }) break case 'reasoning-end': this.onChunk({ type: ChunkType.THINKING_COMPLETE, - text: (chunk.providerMetadata?.metadata?.thinking_content as string) || final.reasoningContent + text: final.reasoningContent || '' }) final.reasoningContent = '' break diff --git a/src/renderer/src/aiCore/plugins/PluginBuilder.ts b/src/renderer/src/aiCore/plugins/PluginBuilder.ts index 7c5478eb7..7767564bd 100644 --- a/src/renderer/src/aiCore/plugins/PluginBuilder.ts +++ b/src/renderer/src/aiCore/plugins/PluginBuilder.ts @@ -5,7 +5,6 @@ import { getEnableDeveloperMode } from '@renderer/hooks/useSettings' import { Assistant } from '@renderer/types' import { AiSdkMiddlewareConfig } from '../middleware/AiSdkMiddlewareBuilder' -import reasoningTimePlugin from './reasoningTimePlugin' import { searchOrchestrationPlugin } from './searchOrchestrationPlugin' import { createTelemetryPlugin } from './telemetryPlugin' @@ -39,9 +38,9 @@ export function buildPlugins( } // 3. 推理模型时添加推理插件 - if (middlewareConfig.enableReasoning) { - plugins.push(reasoningTimePlugin) - } + // if (middlewareConfig.enableReasoning) { + // plugins.push(reasoningTimePlugin) + // } // 4. 启用Prompt工具调用时添加工具插件 if (middlewareConfig.isPromptToolUse) { diff --git a/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx index 98cad2a8c..109562f7d 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx @@ -5,7 +5,7 @@ import { useSettings } from '@renderer/hooks/useSettings' import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue' import { MessageBlockStatus, type ThinkingMessageBlock } from '@renderer/types/newMessage' import { Collapse, message as antdMessage, Tooltip } from 'antd' -import { memo, useCallback, useEffect, useMemo, useState } from 'react' +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -105,30 +105,37 @@ const ThinkingBlock: React.FC = ({ block }) => { const ThinkingTimeSeconds = memo( ({ blockThinkingTime, isThinking }: { blockThinkingTime: number; isThinking: boolean }) => { const { t } = useTranslation() - // const [thinkingTime, setThinkingTime] = useState(blockThinkingTime || 0) + const [displayTime, setDisplayTime] = useState(blockThinkingTime) - // FIXME: 这里统计的和请求处统计的有一定误差 - // useEffect(() => { - // let timer: NodeJS.Timeout | null = null - // if (isThinking) { - // timer = setInterval(() => { - // setThinkingTime((prev) => prev + 100) - // }, 100) - // } else if (timer) { - // // 立即清除计时器 - // clearInterval(timer) - // timer = null - // } + const timer = useRef(null) - // return () => { - // if (timer) { - // clearInterval(timer) - // timer = null - // } - // } - // }, [isThinking]) + useEffect(() => { + if (isThinking) { + if (!timer.current) { + timer.current = setInterval(() => { + setDisplayTime((prev) => prev + 100) + }, 100) + } + } else { + if (timer.current) { + clearInterval(timer.current) + timer.current = null + } + setDisplayTime(blockThinkingTime) + } - const thinkingTimeSeconds = useMemo(() => (blockThinkingTime / 1000).toFixed(1), [blockThinkingTime]) + return () => { + if (timer.current) { + clearInterval(timer.current) + timer.current = null + } + } + }, [isThinking, blockThinkingTime]) + + const thinkingTimeSeconds = useMemo( + () => ((displayTime < 1000 ? 100 : displayTime) / 1000).toFixed(1), + [displayTime] + ) return isThinking ? t('chat.thinking', { diff --git a/src/renderer/src/pages/home/Messages/Blocks/__tests__/ThinkingBlock.test.tsx b/src/renderer/src/pages/home/Messages/Blocks/__tests__/ThinkingBlock.test.tsx index 8db122d94..d57340822 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/__tests__/ThinkingBlock.test.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/__tests__/ThinkingBlock.test.tsx @@ -235,13 +235,12 @@ describe('ThinkingBlock', () => { renderThinkingBlock(thinkingBlock) const activeTimeText = getThinkingTimeText() - expect(activeTimeText).toHaveTextContent('1.0s') expect(activeTimeText).toHaveTextContent('Thinking...') }) it('should handle extreme thinking times correctly', () => { const testCases = [ - { thinking_millsec: 0, expectedTime: '0.0s' }, + { thinking_millsec: 0, expectedTime: '0.1s' }, // New logic: values < 1000ms display as 0.1s { thinking_millsec: 86400000, expectedTime: '86400.0s' }, // 1 day { thinking_millsec: 259200000, expectedTime: '259200.0s' } // 3 days ] diff --git a/src/renderer/src/services/messageStreaming/callbacks/thinkingCallbacks.ts b/src/renderer/src/services/messageStreaming/callbacks/thinkingCallbacks.ts index 4d717c6c6..605259b64 100644 --- a/src/renderer/src/services/messageStreaming/callbacks/thinkingCallbacks.ts +++ b/src/renderer/src/services/messageStreaming/callbacks/thinkingCallbacks.ts @@ -15,7 +15,7 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) => // 内部维护的状态 let thinkingBlockId: string | null = null - let _thinking_millsec = 0 + let thinking_millsec_now: number = 0 return { onThinkingStart: async () => { @@ -24,27 +24,27 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) => type: MessageBlockType.THINKING, content: '', status: MessageBlockStatus.STREAMING, - thinking_millsec: _thinking_millsec + thinking_millsec: 0 } thinkingBlockId = blockManager.initialPlaceholderBlockId! blockManager.smartBlockUpdate(thinkingBlockId, changes, MessageBlockType.THINKING, true) } else if (!thinkingBlockId) { const newBlock = createThinkingBlock(assistantMsgId, '', { status: MessageBlockStatus.STREAMING, - thinking_millsec: _thinking_millsec + thinking_millsec: 0 }) thinkingBlockId = newBlock.id await blockManager.handleBlockTransition(newBlock, MessageBlockType.THINKING) } + thinking_millsec_now = performance.now() }, - onThinkingChunk: async (text: string, thinking_millsec?: number) => { - _thinking_millsec = thinking_millsec || 0 + onThinkingChunk: async (text: string) => { if (thinkingBlockId) { const blockChanges: Partial = { content: text, - status: MessageBlockStatus.STREAMING, - thinking_millsec: _thinking_millsec + status: MessageBlockStatus.STREAMING + // thinking_millsec: performance.now() - thinking_millsec_now } blockManager.smartBlockUpdate(thinkingBlockId, blockChanges, MessageBlockType.THINKING) } @@ -52,14 +52,15 @@ export const createThinkingCallbacks = (deps: ThinkingCallbacksDependencies) => onThinkingComplete: (finalText: string) => { if (thinkingBlockId) { + const now = performance.now() const changes: Partial = { content: finalText, status: MessageBlockStatus.SUCCESS, - thinking_millsec: _thinking_millsec + thinking_millsec: now - thinking_millsec_now } blockManager.smartBlockUpdate(thinkingBlockId, changes, MessageBlockType.THINKING, true) thinkingBlockId = null - _thinking_millsec = 0 + thinking_millsec_now = 0 } else { logger.warn( `[onThinkingComplete] Received thinking.complete but last block was not THINKING (was ${blockManager.lastBlockType}) or lastBlockId is null.` diff --git a/src/renderer/src/store/thunk/__tests__/streamCallback.integration.test.ts b/src/renderer/src/store/thunk/__tests__/streamCallback.integration.test.ts index e8c113d62..49c71aea5 100644 --- a/src/renderer/src/store/thunk/__tests__/streamCallback.integration.test.ts +++ b/src/renderer/src/store/thunk/__tests__/streamCallback.integration.test.ts @@ -425,7 +425,10 @@ describe('streamCallback Integration Tests', () => { expect(thinkingBlock).toBeDefined() expect(thinkingBlock?.content).toBe('Final thoughts') expect(thinkingBlock?.status).toBe(MessageBlockStatus.SUCCESS) - expect((thinkingBlock as any)?.thinking_millsec).toBe(3000) + // thinking_millsec 现在是本地计算的,只验证它存在且是一个合理的数字 + expect((thinkingBlock as any)?.thinking_millsec).toBeDefined() + expect(typeof (thinkingBlock as any)?.thinking_millsec).toBe('number') + expect((thinkingBlock as any)?.thinking_millsec).toBeGreaterThanOrEqual(0) }) it('should handle tool call flow', async () => { From c7d2588f1a174f21eaa6a846dd60c092bec126ec Mon Sep 17 00:00:00 2001 From: LeaderOnePro Date: Sun, 28 Sep 2025 20:54:42 +0800 Subject: [PATCH 7/7] feat: add LongCat provider support (#10365) * feat: add LongCat provider support - Add LongCat to SystemProviderIds enum - Add LongCat provider logo and configuration - Configure API endpoints and URLs based on official docs - Add two models: LongCat-Flash-Chat and LongCat-Flash-Thinking - Update provider mappings for proper integration The LongCat provider uses OpenAI-compatible API format and supports up to 8K tokens output with daily free quota of 500K tokens. Signed-off-by: LeaderOnePro * feat: add migration for LongCat provider - Add migration version 158 for LongCat provider - Ensure existing users get LongCat provider on app update - Follow standard migration pattern for simple provider additions Signed-off-by: LeaderOnePro --------- Signed-off-by: LeaderOnePro --- .../src/assets/images/providers/longcat.png | Bin 0 -> 1658 bytes src/renderer/src/config/models/default.ts | 14 ++++++++++ src/renderer/src/config/providers.ts | 25 +++++++++++++++++- src/renderer/src/store/migrate.ts | 1 + src/renderer/src/types/index.ts | 3 ++- 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/renderer/src/assets/images/providers/longcat.png diff --git a/src/renderer/src/assets/images/providers/longcat.png b/src/renderer/src/assets/images/providers/longcat.png new file mode 100644 index 0000000000000000000000000000000000000000..146cf3ea7a9a1ae05038cf180f5e6d278fb11619 GIT binary patch literal 1658 zcmV-=28H>FP)Px#1am@3R0s$N2z&@+hyVZvBuPX;RCt{2o560|Mi7SoSt@|llXB?U)J^uK4tfm` z`UyfO%DM6rBz}TePY~w`K49wotRdCiy+9tMZp_R0wx(d#anOgE7 zQ0y|6bqKeTI3!g|gw0M^kKb=9tivR3)K%>*41`;RHTEx?{h`g64j!l?i$-rq1)o~U zi;n%c`AEDjhsbu^nr%Z0g6CkiX&I8D-tOm@&(^W!S@t@+fFi;(+wtu^kU6q$3xJtA zuHNYAb$1XV+l9{RD=Xf z*4({;9&7;(pw=OU*Vu$`i=*msQ@W^$J&+J=!29lv5|UEAx~hF4RZC_$?8dVhQjf%8 zEEh0X@BH_mAo+e!ETCcfHs^#j_Q7PGtXsnvLh}KEPtDuNw`QU_a8}tkGc>;*ZTBDF zw!LZ}Gcx6*O`&l#YW7d&f9_nhzrb(`kZxy09>_|tV<((1{&}(8KPF9208S-i%HWhW z_7}yQUS7VJ5+ccnvJjT+y>f2)qWR&R^<*<4%@}^wlP9^}L*@cX(>j@eqWO=~ysMYZ zK{^UQ91zeIja`Js(J1Wir_I52-p33!_dsD+yl9ZPnahd;-G-WlI(BKnevktv(a+spY~^2k>*|r;Mz9 zT{>vm99$EzQALPAg{yI4CSqEB))V^G#4d*)0sNJ@R#xQA+HV8+EFO8H-a~Q6z_Npa zv~HybPSZCE%lqfl_FxWx$K@uQ>EojL;T&dR+{k;(I;S9nRb+A_L{3@9I-az-^l@rp zRFu}O6wow%;|V^R*sY*44+I4HaY0(Q+5-)rP%%he8M&ic57cyJWJlOrRi(}qaF)Ut z-k4L~Td4;M?!B>vTJCC`E1;zM?3njfD&UQvxUfo-{1D#A;h|hA4PnzF?u;n!;ynSw zByLEazB!)2uhavD_ntcs-V;!gz5&3Nn-^8;fj{6;D6!8=>+T6CKRi?u-9&~;Jy3(C z;z!vF_XHFk9s(3^3vXhPrLzlq?{V z*4+x|07*qoM6N<$ Ef?2mPg#Z8m literal 0 HcmV?d00001 diff --git a/src/renderer/src/config/models/default.ts b/src/renderer/src/config/models/default.ts index 9fdced6a6..1858675ed 100644 --- a/src/renderer/src/config/models/default.ts +++ b/src/renderer/src/config/models/default.ts @@ -1804,5 +1804,19 @@ export const SYSTEM_MODELS: Record = provider: 'aionly', group: 'gemini' } + ], + longcat: [ + { + id: 'LongCat-Flash-Chat', + name: 'LongCat Flash Chat', + provider: 'longcat', + group: 'LongCat' + }, + { + id: 'LongCat-Flash-Thinking', + name: 'LongCat Flash Thinking', + provider: 'longcat', + group: 'LongCat' + } ] } diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index 543422d21..64e78e847 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -27,6 +27,7 @@ import InfiniProviderLogo from '@renderer/assets/images/providers/infini.png' import JinaProviderLogo from '@renderer/assets/images/providers/jina.png' import LanyunProviderLogo from '@renderer/assets/images/providers/lanyun.png' import LMStudioProviderLogo from '@renderer/assets/images/providers/lmstudio.png' +import LongCatProviderLogo from '@renderer/assets/images/providers/longcat.png' import MinimaxProviderLogo from '@renderer/assets/images/providers/minimax.png' import MistralProviderLogo from '@renderer/assets/images/providers/mistral.png' import ModelScopeProviderLogo from '@renderer/assets/images/providers/modelscope.png' @@ -622,6 +623,16 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = models: SYSTEM_MODELS['poe'], isSystem: true, enabled: false + }, + longcat: { + id: 'longcat', + name: 'LongCat', + type: 'openai', + apiKey: '', + apiHost: 'https://api.longcat.chat/openai', + models: SYSTEM_MODELS.longcat, + isSystem: true, + enabled: false } } as const @@ -684,7 +695,8 @@ export const PROVIDER_LOGO_MAP: AtLeast = { 'new-api': NewAPIProviderLogo, 'aws-bedrock': AwsProviderLogo, poe: 'poe', // use svg icon component - aionly: AiOnlyProviderLogo + aionly: AiOnlyProviderLogo, + longcat: LongCatProviderLogo } as const export function getProviderLogo(providerId: string) { @@ -1290,6 +1302,17 @@ export const PROVIDER_URLS: Record = { docs: 'https://www.aiionly.com/document', models: 'https://www.aiionly.com' } + }, + longcat: { + api: { + url: 'https://api.longcat.chat/openai' + }, + websites: { + official: 'https://longcat.chat', + apiKey: 'https://longcat.chat/platform/api_keys', + docs: 'https://longcat.chat/platform/docs/zh/', + models: 'https://longcat.chat/platform/docs/zh/APIDocs.html' + } } } diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index f10fc623d..1c954ba27 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2543,6 +2543,7 @@ const migrateConfig = { '158': (state: RootState) => { try { state.llm.providers = state.llm.providers.filter((provider) => provider.id !== 'cherryin') + addProvider(state, 'longcat') return state } catch (error) { logger.error('migrate 158 error', error as Error) diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 33abec085..2b9271d54 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -322,7 +322,8 @@ export const SystemProviderIds = { voyageai: 'voyageai', 'aws-bedrock': 'aws-bedrock', poe: 'poe', - aionly: 'aionly' + aionly: 'aionly', + longcat: 'longcat' } as const export type SystemProviderId = keyof typeof SystemProviderIds