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 fce028f5cd..a2cc7d9aff 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 4845ce4ace..95c2cdda2c 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 72a3f6e36c..19c3a135d7 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 be5b21104a..11f29221d6 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 cb86d8a259..2d49144633 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 38ae73e95e..704fdafd0d 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 cd04de3a24..5fe71bbae8 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 74e2b8266a..7ac6ab5bcb 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 } }