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:
MyPrototypeWhat 2025-07-04 19:35:37 +08:00
parent 13162edcb2
commit 547e5785c0
4 changed files with 196 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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