mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-06 21:35:52 +08:00
feat: enhance web search plugin and tool handling
- Refactored `helper.ts` to export new types `AnthropicSearchInput` and `AnthropicSearchOutput` for better integration with the web search plugin. - Updated `index.ts` to include the new types in the exports for improved type safety. - Modified `AiSdkToChunkAdapter.ts` to handle tool calls more flexibly by introducing a `GenericProviderTool` type, allowing for better differentiation between MCP tools and provider-executed tools. - Adjusted `handleTooCallChunk.ts` to accommodate the new tool type structure, enhancing the handling of tool call responses. - Updated type definitions in `index.ts` to reflect changes in tool handling logic.
This commit is contained in:
parent
42c7ebd193
commit
3ab904e789
@ -1,5 +1,5 @@
|
|||||||
import type { anthropic } from '@ai-sdk/anthropic'
|
import { anthropic } from '@ai-sdk/anthropic'
|
||||||
import type { openai } from '@ai-sdk/openai'
|
import { openai } from '@ai-sdk/openai'
|
||||||
|
|
||||||
import { ProviderOptionsMap } from '../../../options/types'
|
import { ProviderOptionsMap } from '../../../options/types'
|
||||||
|
|
||||||
@ -8,7 +8,6 @@ import { ProviderOptionsMap } from '../../../options/types'
|
|||||||
*/
|
*/
|
||||||
type OpenAISearchConfig = Parameters<typeof openai.tools.webSearchPreview>[0]
|
type OpenAISearchConfig = Parameters<typeof openai.tools.webSearchPreview>[0]
|
||||||
type AnthropicSearchConfig = Parameters<typeof anthropic.tools.webSearch_20250305>[0]
|
type AnthropicSearchConfig = Parameters<typeof anthropic.tools.webSearch_20250305>[0]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XAI 特有的搜索参数
|
* XAI 特有的搜索参数
|
||||||
* @internal
|
* @internal
|
||||||
@ -77,3 +76,14 @@ export const getXaiProviderOptions = (providerOptions: any, config?: XaiProvider
|
|||||||
}
|
}
|
||||||
return providerOptions
|
return providerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AnthropicSearchInput = {
|
||||||
|
query: string
|
||||||
|
}
|
||||||
|
export type AnthropicSearchOutput = {
|
||||||
|
url: string
|
||||||
|
title: string
|
||||||
|
pageAge: string | null
|
||||||
|
encryptedContent: string
|
||||||
|
type: string
|
||||||
|
}[]
|
||||||
|
|||||||
@ -78,7 +78,7 @@ export const webSearchPlugin = (config: WebSearchPluginConfig = DEFAULT_WEB_SEAR
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 导出类型定义供开发者使用
|
// 导出类型定义供开发者使用
|
||||||
export type { WebSearchPluginConfig } from './helper'
|
export type { AnthropicSearchInput, AnthropicSearchOutput, WebSearchPluginConfig } from './helper'
|
||||||
|
|
||||||
// 默认导出
|
// 默认导出
|
||||||
export default webSearchPlugin
|
export default webSearchPlugin
|
||||||
|
|||||||
@ -81,6 +81,12 @@ export class AiSdkToChunkAdapter {
|
|||||||
console.log('AI SDK chunk type:', chunk.type, chunk)
|
console.log('AI SDK chunk type:', chunk.type, chunk)
|
||||||
switch (chunk.type) {
|
switch (chunk.type) {
|
||||||
// === 文本相关事件 ===
|
// === 文本相关事件 ===
|
||||||
|
// case 'text-start':
|
||||||
|
// this.onChunk({
|
||||||
|
// type: ChunkType.blo,
|
||||||
|
// text: chunk.text || ''
|
||||||
|
// })
|
||||||
|
// break
|
||||||
case 'text':
|
case 'text':
|
||||||
final.text += chunk.text || ''
|
final.text += chunk.text || ''
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
@ -93,6 +99,7 @@ export class AiSdkToChunkAdapter {
|
|||||||
type: ChunkType.TEXT_COMPLETE,
|
type: ChunkType.TEXT_COMPLETE,
|
||||||
text: final.text || ''
|
text: final.text || ''
|
||||||
})
|
})
|
||||||
|
final.text = ''
|
||||||
break
|
break
|
||||||
case 'reasoning':
|
case 'reasoning':
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
@ -206,9 +213,7 @@ export class AiSdkToChunkAdapter {
|
|||||||
case 'error':
|
case 'error':
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.ERROR,
|
type: ChunkType.ERROR,
|
||||||
error: {
|
error: chunk.error as Record<string, any>
|
||||||
message: chunk.error || 'Unknown error'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 工具调用 Chunk 处理模块
|
* 工具调用 Chunk 处理模块
|
||||||
*
|
* TODO: Tool包含了providerTool和普通的Tool还有MCPTool,后面需要重构
|
||||||
* 提供工具调用相关的处理API,每个交互使用一个新的实例
|
* 提供工具调用相关的处理API,每个交互使用一个新的实例
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -8,7 +8,18 @@ import { ToolCallUnion, ToolResultUnion, ToolSet } from '@cherrystudio/ai-core/i
|
|||||||
import Logger from '@renderer/config/logger'
|
import Logger from '@renderer/config/logger'
|
||||||
import { MCPTool, MCPToolResponse } from '@renderer/types'
|
import { MCPTool, MCPToolResponse } from '@renderer/types'
|
||||||
import { Chunk, ChunkType } from '@renderer/types/chunk'
|
import { Chunk, ChunkType } from '@renderer/types/chunk'
|
||||||
|
// import type {
|
||||||
|
// AnthropicSearchOutput,
|
||||||
|
// WebSearchPluginConfig
|
||||||
|
// } from '@cherrystudio/ai-core/core/plugins/built-in/webSearchPlugin'
|
||||||
|
|
||||||
|
// 为 Provider 执行的工具创建一个通用类型
|
||||||
|
// 这避免了污染 MCPTool 的定义,同时提供了 UI 显示所需的基本信息
|
||||||
|
type GenericProviderTool = {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
type: 'provider'
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 工具调用处理器类
|
* 工具调用处理器类
|
||||||
*/
|
*/
|
||||||
@ -20,7 +31,8 @@ export class ToolCallChunkHandler {
|
|||||||
toolCallId: string
|
toolCallId: string
|
||||||
toolName: string
|
toolName: string
|
||||||
args: any
|
args: any
|
||||||
mcpTool: MCPTool
|
// mcpTool 现在可以是 MCPTool 或我们为 Provider 工具创建的通用类型
|
||||||
|
mcpTool: MCPTool | GenericProviderTool
|
||||||
}
|
}
|
||||||
>()
|
>()
|
||||||
constructor(
|
constructor(
|
||||||
@ -43,30 +55,47 @@ export class ToolCallChunkHandler {
|
|||||||
type: 'tool-call'
|
type: 'tool-call'
|
||||||
} & ToolCallUnion<ToolSet>
|
} & ToolCallUnion<ToolSet>
|
||||||
): void {
|
): void {
|
||||||
const toolCallId = chunk.toolCallId
|
const { toolCallId, toolName, input: args, providerExecuted } = chunk
|
||||||
const toolName = chunk.toolName
|
|
||||||
const args = chunk.input || {}
|
|
||||||
|
|
||||||
if (!toolCallId || !toolName) {
|
if (!toolCallId || !toolName) {
|
||||||
Logger.warn(`🔧 [ToolCallChunkHandler] Invalid tool call chunk: missing toolCallId or toolName`)
|
Logger.warn(`🔧 [ToolCallChunkHandler] Invalid tool call chunk: missing toolCallId or toolName`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 chunk 信息构造 MCPTool
|
let tool: MCPTool | GenericProviderTool
|
||||||
// const mcpTool = this.createMcpToolFromChunk(chunk)
|
|
||||||
|
// 根据 providerExecuted 标志区分处理逻辑
|
||||||
|
if (providerExecuted) {
|
||||||
|
// 如果是 Provider 执行的工具(如 web_search)
|
||||||
|
Logger.info(`[ToolCallChunkHandler] Handling provider-executed tool: ${toolName}`)
|
||||||
|
tool = {
|
||||||
|
name: toolName,
|
||||||
|
description: toolName,
|
||||||
|
type: 'provider'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果是客户端执行的 MCP 工具,沿用现有逻辑
|
||||||
|
Logger.info(`[ToolCallChunkHandler] Handling client-side MCP tool: ${toolName}`)
|
||||||
|
const mcpTool = this.mcpTools.find((t) => t.name === toolName)
|
||||||
|
if (!mcpTool) {
|
||||||
|
Logger.warn(`[ToolCallChunkHandler] MCP tool not found: ${toolName}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tool = mcpTool
|
||||||
|
}
|
||||||
|
|
||||||
// 记录活跃的工具调用
|
// 记录活跃的工具调用
|
||||||
this.activeToolCalls.set(toolCallId, {
|
this.activeToolCalls.set(toolCallId, {
|
||||||
toolCallId,
|
toolCallId,
|
||||||
toolName,
|
toolName,
|
||||||
args,
|
args,
|
||||||
mcpTool: this.mcpTools.find((tool) => tool.name === toolName)!
|
mcpTool: tool
|
||||||
})
|
})
|
||||||
|
|
||||||
// 创建 MCPToolResponse 格式
|
// 创建 MCPToolResponse 格式
|
||||||
const toolResponse: MCPToolResponse = {
|
const toolResponse: MCPToolResponse = {
|
||||||
id: toolCallId,
|
id: toolCallId,
|
||||||
tool: this.activeToolCalls.get(toolCallId)!.mcpTool,
|
tool: tool,
|
||||||
arguments: args,
|
arguments: args,
|
||||||
status: 'invoking',
|
status: 'invoking',
|
||||||
toolCallId: toolCallId
|
toolCallId: toolCallId
|
||||||
@ -121,7 +150,6 @@ export class ToolCallChunkHandler {
|
|||||||
},
|
},
|
||||||
toolCallId: toolCallId
|
toolCallId: toolCallId
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从活跃调用中移除(交互结束后整个实例会被丢弃)
|
// 从活跃调用中移除(交互结束后整个实例会被丢弃)
|
||||||
this.activeToolCalls.delete(toolCallId)
|
this.activeToolCalls.delete(toolCallId)
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,12 @@ import type { CSSProperties } from 'react'
|
|||||||
|
|
||||||
import type { Message } from './newMessage'
|
import type { Message } from './newMessage'
|
||||||
|
|
||||||
|
export type GenericProviderTool = {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
type: 'provider'
|
||||||
|
}
|
||||||
|
|
||||||
export type Assistant = {
|
export type Assistant = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@ -655,7 +661,7 @@ export interface MCPConfig {
|
|||||||
|
|
||||||
interface BaseToolResponse {
|
interface BaseToolResponse {
|
||||||
id: string // unique id
|
id: string // unique id
|
||||||
tool: MCPTool
|
tool: MCPTool | GenericProviderTool
|
||||||
arguments: Record<string, unknown> | undefined
|
arguments: Record<string, unknown> | undefined
|
||||||
status: string // 'invoking' | 'done'
|
status: string // 'invoking' | 'done'
|
||||||
response?: any
|
response?: any
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user