mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-28 05:11:24 +08:00
feat: add web search plugin for enhanced AI provider capabilities
- Introduced a new `webSearchPlugin` to provide unified web search functionality across multiple AI providers. - Added helper functions for adapting web search parameters for OpenAI, Gemini, and Anthropic providers. - Updated the built-in plugin index to export the new web search plugin and its configuration type. - Created a new `helper.ts` file to encapsulate web search adaptation logic and support checks for provider compatibility.
This commit is contained in:
parent
13162edcb2
commit
547e5785c0
@ -7,3 +7,4 @@ export const BUILT_IN_PLUGIN_PREFIX = 'built-in:'
|
||||
export { createLoggingPlugin } from './logging'
|
||||
export type { MCPPromptConfig, ToolUseResult } from './mcpPromptPlugin'
|
||||
export { createMCPPromptPlugin } from './mcpPromptPlugin'
|
||||
export { type WebSearchConfig, webSearchPlugin } from './webSearchPlugin'
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 网络搜索助手函数
|
||||
* 提取各个 ApiClient 中的网络搜索逻辑,提供统一的适配器
|
||||
*/
|
||||
import type { OpenAIProvider } from '@ai-sdk/openai'
|
||||
|
||||
// 派生自 OpenAI SDK 的标准工具入参类型
|
||||
type WebSearchPreviewParams = Parameters<OpenAIProvider['tools']['webSearchPreview']>[0]
|
||||
|
||||
// 使用交叉类型合并,并为 extra 添加注释
|
||||
export type WebSearchConfig = WebSearchPreviewParams & {
|
||||
/**
|
||||
* 扩展字段,用于提供给开发者自定义参数的能力
|
||||
* 这些参数将被合并到对应 provider 的 providerOptions 中
|
||||
*/
|
||||
extra?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* 适配 OpenAI 网络搜索
|
||||
* 基于 Vercel AI SDK 的 web_search_preview 工具
|
||||
*/
|
||||
export function adaptOpenAIWebSearch(params: any, webSearchConfig: WebSearchConfig | boolean): any {
|
||||
const config = typeof webSearchConfig === 'boolean' ? {} : webSearchConfig
|
||||
const { extra, ...stdParams } = config
|
||||
|
||||
const webSearchTool = {
|
||||
type: 'web_search_preview',
|
||||
...stdParams
|
||||
}
|
||||
|
||||
// 假设 params.tools 是一个数组或 undefined
|
||||
const existingTools = Array.isArray(params.tools) ? params.tools : []
|
||||
|
||||
// 将 extra 参数添加到 providerOptions 中
|
||||
const providerOptions = {
|
||||
...params.providerOptions,
|
||||
openai: {
|
||||
...params.providerOptions?.openai,
|
||||
...(extra || {})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...params,
|
||||
tools: [...existingTools, webSearchTool],
|
||||
providerOptions
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 适配 Gemini 网络搜索
|
||||
* 将 googleSearch 工具放入 providerOptions.google.tools
|
||||
*/
|
||||
export function adaptGeminiWebSearch(params: any, webSearchConfig: WebSearchConfig | boolean): any {
|
||||
const config = typeof webSearchConfig === 'boolean' ? {} : webSearchConfig
|
||||
const googleSearchTool = { googleSearch: {} }
|
||||
|
||||
const existingTools = Array.isArray(params.providerOptions?.google?.tools) ? params.providerOptions.google.tools : []
|
||||
|
||||
return {
|
||||
...params,
|
||||
providerOptions: {
|
||||
...params.providerOptions,
|
||||
google: {
|
||||
...params.providerOptions?.google,
|
||||
tools: [...existingTools, googleSearchTool],
|
||||
...(config.extra || {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 适配 Anthropic 网络搜索
|
||||
* 将 web_search_20250305 工具放入 providerOptions.anthropic.tools
|
||||
*/
|
||||
export function adaptAnthropicWebSearch(params: any, webSearchConfig: WebSearchConfig | boolean): any {
|
||||
const config = typeof webSearchConfig === 'boolean' ? {} : webSearchConfig
|
||||
const webSearchTool = {
|
||||
type: 'web_search_20250305',
|
||||
name: 'web_search',
|
||||
max_uses: 5 // 默认值,可以通过 extra 覆盖
|
||||
}
|
||||
|
||||
const existingTools = Array.isArray(params.providerOptions?.anthropic?.tools)
|
||||
? params.providerOptions.anthropic.tools
|
||||
: []
|
||||
|
||||
return {
|
||||
...params,
|
||||
providerOptions: {
|
||||
...params.providerOptions,
|
||||
anthropic: {
|
||||
...params.providerOptions?.anthropic,
|
||||
tools: [...existingTools, webSearchTool],
|
||||
...(config.extra || {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用网络搜索适配器
|
||||
* 根据 providerId 选择对应的适配函数
|
||||
*/
|
||||
export function adaptWebSearchForProvider(
|
||||
params: any,
|
||||
providerId: string,
|
||||
webSearchConfig: WebSearchConfig | boolean
|
||||
): any {
|
||||
switch (providerId) {
|
||||
case 'openai':
|
||||
return adaptOpenAIWebSearch(params, webSearchConfig)
|
||||
|
||||
case 'google':
|
||||
case 'google-vertex':
|
||||
return adaptGeminiWebSearch(params, webSearchConfig)
|
||||
|
||||
case 'anthropic':
|
||||
return adaptAnthropicWebSearch(params, webSearchConfig)
|
||||
|
||||
default:
|
||||
// 不支持的 provider,保持原样
|
||||
return params
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 provider 是否支持网络搜索
|
||||
*/
|
||||
export function isWebSearchSupported(providerId: string): boolean {
|
||||
const supportedProviders = ['openai', 'google', 'google-vertex', 'anthropic']
|
||||
|
||||
return supportedProviders.includes(providerId)
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Web Search Plugin
|
||||
* 提供统一的网络搜索能力,支持多个 AI Provider
|
||||
*/
|
||||
|
||||
import { definePlugin } from '../../'
|
||||
import type { AiRequestContext } from '../../types'
|
||||
import { adaptWebSearchForProvider, isWebSearchSupported, type WebSearchConfig } from './helper'
|
||||
|
||||
/**
|
||||
* 网络搜索插件
|
||||
*
|
||||
* 此插件会检查 params.providerOptions.[providerId].webSearch 来激活。
|
||||
* options.ts 文件负责将高层级的设置(如 assistant.enableWebSearch)
|
||||
* 转换为 providerOptions 中的 webSearch: { enabled: true } 配置。
|
||||
*/
|
||||
export const webSearchPlugin = definePlugin({
|
||||
name: 'webSearch',
|
||||
|
||||
transformParams: async (params: any, context: AiRequestContext) => {
|
||||
const { providerId } = context
|
||||
|
||||
// 从 providerOptions 中提取 webSearch 配置
|
||||
const webSearchConfig = params.providerOptions?.[providerId]?.webSearch
|
||||
|
||||
// 检查是否启用了网络搜索 (enabled: false 可用于显式禁用)
|
||||
if (!webSearchConfig || (typeof webSearchConfig === 'object' && webSearchConfig.enabled === false)) {
|
||||
return params
|
||||
}
|
||||
|
||||
// 检查当前 provider 是否支持网络搜索
|
||||
if (!isWebSearchSupported(providerId)) {
|
||||
// 对于不支持的 provider,只记录警告,不修改参数
|
||||
console.warn(
|
||||
`[webSearchPlugin] Provider '${providerId}' does not support web search. Ignoring webSearch parameter.`
|
||||
)
|
||||
return params
|
||||
}
|
||||
|
||||
// 使用适配器函数处理网络搜索
|
||||
const adaptedParams = adaptWebSearchForProvider(params, providerId, webSearchConfig as WebSearchConfig | boolean)
|
||||
|
||||
// 清理原始的 webSearch 配置
|
||||
if (adaptedParams.providerOptions?.[providerId]) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { webSearch, ...rest } = adaptedParams.providerOptions[providerId]
|
||||
adaptedParams.providerOptions[providerId] = rest
|
||||
}
|
||||
|
||||
return adaptedParams
|
||||
}
|
||||
})
|
||||
|
||||
// 导出类型定义供开发者使用
|
||||
export type { WebSearchConfig } from './helper'
|
||||
|
||||
// 默认导出
|
||||
export default webSearchPlugin
|
||||
@ -126,6 +126,7 @@ export default class ModernAiProvider {
|
||||
const plugins: AiPlugin[] = []
|
||||
// 1. 总是添加通用插件
|
||||
// plugins.push(textPlugin)
|
||||
// plugins.push(webSearchPlugin)
|
||||
|
||||
// 2. 推理模型时添加推理插件
|
||||
if (middlewareConfig.enableReasoning) {
|
||||
@ -138,7 +139,6 @@ export default class ModernAiProvider {
|
||||
createMCPPromptPlugin({
|
||||
enabled: true,
|
||||
createSystemMessage: (systemPrompt, params, context) => {
|
||||
console.log('createSystemMessage_context', context.isRecursiveCall)
|
||||
if (context.modelId.includes('o1-mini') || context.modelId.includes('o1-preview')) {
|
||||
if (context.isRecursiveCall) {
|
||||
return null
|
||||
|
||||
Loading…
Reference in New Issue
Block a user