feat: enhance web search tool switching logic to support provider-specific context (#11769)

* feat: enhance web search tool switching logic to support provider-specific context

* Update packages/aiCore/src/core/plugins/built-in/webSearchPlugin/helper.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* refactor: consolidate control flow in switchWebSearchTool (#11771)

* Initial plan

* refactor: make control flow consistent in switchWebSearchTool

Replace early returns with break statements in all switch cases to ensure
consistent control flow. Move fallback logic into default case for clarity.

Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com>

* Update packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/aiCore/src/core/plugins/built-in/webSearchPlugin/index.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: format

* fix: ensure switchWebSearchTool is always called for cherryin providers

- Add missing else branch to prevent silent failure when provider extraction fails
- Add empty string check for extracted providerId from split operation
- Ensures web search functionality is preserved in all edge cases

Addresses PR review feedback from #11769

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* refactor: simplify repetitive switchWebSearchTool calls

- Extract providerId determination logic before calling switchWebSearchTool
- Call switchWebSearchTool only once at the end with updated providerId
- Reduce code duplication while maintaining all edge case handling

Addresses review feedback from @kangfenmao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* refactor: eliminate code duplication in switchWebSearchTool

- Extract helper functions: ensureToolsObject, applyToolBasedSearch, applyProviderOptionsSearch
- Replace switch statement and fallback if-else chain with providerHandlers map
- Use array-based priority order for fallback logic
- Reduce code from 73 lines to 80 lines but with much better maintainability
- Eliminates 12 instances of "if (!params.tools) params.tools = {}"
- Single source of truth for each provider's configuration logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
SuYao 2025-12-11 15:01:01 +08:00 committed by GitHub
parent c4fd48376d
commit 9ac7e2c78d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 95 additions and 24 deletions

View File

@ -6,6 +6,7 @@ import { type Tool } from 'ai'
import { createOpenRouterOptions, createXaiOptions, mergeProviderOptions } from '../../../options'
import type { ProviderOptionsMap } from '../../../options/types'
import type { AiRequestContext } from '../../'
import type { OpenRouterSearchConfig } from './openrouter'
/**
@ -95,28 +96,84 @@ export type WebSearchToolInputSchema = {
'openai-chat': InferToolInput<OpenAIChatWebSearchTool>
}
export const switchWebSearchTool = (config: WebSearchPluginConfig, params: any) => {
if (config.openai) {
if (!params.tools) params.tools = {}
params.tools.web_search = openai.tools.webSearch(config.openai)
} else if (config['openai-chat']) {
if (!params.tools) params.tools = {}
params.tools.web_search_preview = openai.tools.webSearchPreview(config['openai-chat'])
} else if (config.anthropic) {
if (!params.tools) params.tools = {}
params.tools.web_search = anthropic.tools.webSearch_20250305(config.anthropic)
} else if (config.google) {
// case 'google-vertex':
if (!params.tools) params.tools = {}
params.tools.web_search = google.tools.googleSearch(config.google || {})
} else if (config.xai) {
const searchOptions = createXaiOptions({
searchParameters: { ...config.xai, mode: 'on' }
})
params.providerOptions = mergeProviderOptions(params.providerOptions, searchOptions)
} else if (config.openrouter) {
const searchOptions = createOpenRouterOptions(config.openrouter)
params.providerOptions = mergeProviderOptions(params.providerOptions, searchOptions)
/**
* Helper function to ensure params.tools object exists
*/
const ensureToolsObject = (params: any) => {
if (!params.tools) params.tools = {}
}
/**
* Helper function to apply tool-based web search configuration
*/
const applyToolBasedSearch = (params: any, toolName: string, toolInstance: any) => {
ensureToolsObject(params)
params.tools[toolName] = toolInstance
}
/**
* Helper function to apply provider options-based web search configuration
*/
const applyProviderOptionsSearch = (params: any, searchOptions: any) => {
params.providerOptions = mergeProviderOptions(params.providerOptions, searchOptions)
}
export const switchWebSearchTool = (config: WebSearchPluginConfig, params: any, context?: AiRequestContext) => {
const providerId = context?.providerId
// Provider-specific configuration map
const providerHandlers: Record<string, () => void> = {
openai: () => {
const cfg = config.openai ?? DEFAULT_WEB_SEARCH_CONFIG.openai
applyToolBasedSearch(params, 'web_search', openai.tools.webSearch(cfg))
},
'openai-chat': () => {
const cfg = (config['openai-chat'] ?? DEFAULT_WEB_SEARCH_CONFIG['openai-chat']) as OpenAISearchPreviewConfig
applyToolBasedSearch(params, 'web_search_preview', openai.tools.webSearchPreview(cfg))
},
anthropic: () => {
const cfg = config.anthropic ?? DEFAULT_WEB_SEARCH_CONFIG.anthropic
applyToolBasedSearch(params, 'web_search', anthropic.tools.webSearch_20250305(cfg))
},
google: () => {
const cfg = (config.google ?? DEFAULT_WEB_SEARCH_CONFIG.google) as GoogleSearchConfig
applyToolBasedSearch(params, 'web_search', google.tools.googleSearch(cfg))
},
xai: () => {
const cfg = config.xai ?? DEFAULT_WEB_SEARCH_CONFIG.xai
const searchOptions = createXaiOptions({ searchParameters: { ...cfg, mode: 'on' } })
applyProviderOptionsSearch(params, searchOptions)
},
openrouter: () => {
const cfg = (config.openrouter ?? DEFAULT_WEB_SEARCH_CONFIG.openrouter) as OpenRouterSearchConfig
const searchOptions = createOpenRouterOptions(cfg)
applyProviderOptionsSearch(params, searchOptions)
}
}
// Try provider-specific handler first
const handler = providerId && providerHandlers[providerId]
if (handler) {
handler()
return params
}
// Fallback: apply based on available config keys (prioritized order)
const fallbackOrder: Array<keyof WebSearchPluginConfig> = [
'openai',
'openai-chat',
'anthropic',
'google',
'xai',
'openrouter'
]
for (const key of fallbackOrder) {
if (config[key]) {
providerHandlers[key]()
break
}
}
return params
}

View File

@ -17,8 +17,22 @@ export const webSearchPlugin = (config: WebSearchPluginConfig = DEFAULT_WEB_SEAR
name: 'webSearch',
enforce: 'pre',
transformParams: async (params: any) => {
switchWebSearchTool(config, params)
transformParams: async (params: any, context) => {
let { providerId } = context
// For cherryin providers, extract the actual provider from the model's provider string
// Expected format: "cherryin.{actualProvider}" (e.g., "cherryin.gemini")
if (providerId === 'cherryin' || providerId === 'cherryin-chat') {
const provider = params.model?.provider
if (provider && typeof provider === 'string' && provider.includes('.')) {
const extractedProviderId = provider.split('.')[1]
if (extractedProviderId) {
providerId = extractedProviderId
}
}
}
switchWebSearchTool(config, params, { ...context, providerId })
return params
}
})