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:
MyPrototypeWhat 2025-07-11 19:15:21 +08:00
parent 42c7ebd193
commit 3ab904e789
5 changed files with 67 additions and 18 deletions

View File

@ -1,5 +1,5 @@
import type { anthropic } from '@ai-sdk/anthropic'
import type { openai } from '@ai-sdk/openai'
import { anthropic } from '@ai-sdk/anthropic'
import { openai } from '@ai-sdk/openai'
import { ProviderOptionsMap } from '../../../options/types'
@ -8,7 +8,6 @@ import { ProviderOptionsMap } from '../../../options/types'
*/
type OpenAISearchConfig = Parameters<typeof openai.tools.webSearchPreview>[0]
type AnthropicSearchConfig = Parameters<typeof anthropic.tools.webSearch_20250305>[0]
/**
* XAI
* @internal
@ -77,3 +76,14 @@ export const getXaiProviderOptions = (providerOptions: any, config?: XaiProvider
}
return providerOptions
}
export type AnthropicSearchInput = {
query: string
}
export type AnthropicSearchOutput = {
url: string
title: string
pageAge: string | null
encryptedContent: string
type: string
}[]

View File

@ -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

View File

@ -81,6 +81,12 @@ export class AiSdkToChunkAdapter {
console.log('AI SDK chunk type:', chunk.type, chunk)
switch (chunk.type) {
// === 文本相关事件 ===
// case 'text-start':
// this.onChunk({
// type: ChunkType.blo,
// text: chunk.text || ''
// })
// break
case 'text':
final.text += chunk.text || ''
this.onChunk({
@ -93,6 +99,7 @@ export class AiSdkToChunkAdapter {
type: ChunkType.TEXT_COMPLETE,
text: final.text || ''
})
final.text = ''
break
case 'reasoning':
this.onChunk({
@ -206,9 +213,7 @@ export class AiSdkToChunkAdapter {
case 'error':
this.onChunk({
type: ChunkType.ERROR,
error: {
message: chunk.error || 'Unknown error'
}
error: chunk.error as Record<string, any>
})
break

View File

@ -1,6 +1,6 @@
/**
* Chunk
*
* TODO: Tool包含了providerTool和普通的Tool还有MCPTool,
* API使
*/
@ -8,7 +8,18 @@ import { ToolCallUnion, ToolResultUnion, ToolSet } from '@cherrystudio/ai-core/i
import Logger from '@renderer/config/logger'
import { MCPTool, MCPToolResponse } from '@renderer/types'
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
toolName: string
args: any
mcpTool: MCPTool
// mcpTool 现在可以是 MCPTool 或我们为 Provider 工具创建的通用类型
mcpTool: MCPTool | GenericProviderTool
}
>()
constructor(
@ -43,30 +55,47 @@ export class ToolCallChunkHandler {
type: 'tool-call'
} & ToolCallUnion<ToolSet>
): void {
const toolCallId = chunk.toolCallId
const toolName = chunk.toolName
const args = chunk.input || {}
const { toolCallId, toolName, input: args, providerExecuted } = chunk
if (!toolCallId || !toolName) {
Logger.warn(`🔧 [ToolCallChunkHandler] Invalid tool call chunk: missing toolCallId or toolName`)
return
}
// 从 chunk 信息构造 MCPTool
// const mcpTool = this.createMcpToolFromChunk(chunk)
let tool: MCPTool | GenericProviderTool
// 根据 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, {
toolCallId,
toolName,
args,
mcpTool: this.mcpTools.find((tool) => tool.name === toolName)!
mcpTool: tool
})
// 创建 MCPToolResponse 格式
const toolResponse: MCPToolResponse = {
id: toolCallId,
tool: this.activeToolCalls.get(toolCallId)!.mcpTool,
tool: tool,
arguments: args,
status: 'invoking',
toolCallId: toolCallId
@ -121,7 +150,6 @@ export class ToolCallChunkHandler {
},
toolCallId: toolCallId
}
// 从活跃调用中移除(交互结束后整个实例会被丢弃)
this.activeToolCalls.delete(toolCallId)

View File

@ -5,6 +5,12 @@ import type { CSSProperties } from 'react'
import type { Message } from './newMessage'
export type GenericProviderTool = {
name: string
description: string
type: 'provider'
}
export type Assistant = {
id: string
name: string
@ -655,7 +661,7 @@ export interface MCPConfig {
interface BaseToolResponse {
id: string // unique id
tool: MCPTool
tool: MCPTool | GenericProviderTool
arguments: Record<string, unknown> | undefined
status: string // 'invoking' | 'done'
response?: any