refactor: enhance provider settings and update web search plugin configuration

- Updated providerSettings to allow optional 'mode' parameter for various providers, enhancing flexibility in model configuration.
- Refactored web search plugin to integrate Google search capabilities and streamline provider options handling.
- Removed deprecated code and improved type definitions for better clarity and maintainability.
- Added console logging for debugging purposes in the provider configuration process.
This commit is contained in:
MyPrototypeWhat 2025-07-17 18:12:26 +08:00
parent 650650a68f
commit e7d5626055
10 changed files with 49 additions and 99 deletions

View File

@ -33,7 +33,7 @@ export async function createBaseModel<T extends ProviderId>({
}: { }: {
providerId: T providerId: T
modelId: string modelId: string
providerSettings: ProviderSettingsMap[T] providerSettings: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' }
extraModelConfig?: any extraModelConfig?: any
// middlewares?: LanguageModelV1Middleware[] // middlewares?: LanguageModelV1Middleware[]
}): Promise<LanguageModelV2> }): Promise<LanguageModelV2>
@ -47,7 +47,7 @@ export async function createBaseModel({
}: { }: {
providerId: string providerId: string
modelId: string modelId: string
providerSettings: ProviderSettingsMap['openai-compatible'] providerSettings: ProviderSettingsMap['openai-compatible'] & { mode?: 'chat' | 'responses' }
extraModelConfig?: any extraModelConfig?: any
// middlewares?: LanguageModelV1Middleware[] // middlewares?: LanguageModelV1Middleware[]
}): Promise<LanguageModelV2> }): Promise<LanguageModelV2>
@ -61,7 +61,7 @@ export async function createBaseModel({
}: { }: {
providerId: string providerId: string
modelId: string modelId: string
providerSettings: ProviderSettingsMap[ProviderId] providerSettings: ProviderSettingsMap[ProviderId] & { mode?: 'chat' | 'responses' }
// middlewares?: LanguageModelV1Middleware[] // middlewares?: LanguageModelV1Middleware[]
extraModelConfig?: any extraModelConfig?: any
}): Promise<LanguageModelV2> { }): Promise<LanguageModelV2> {
@ -86,6 +86,7 @@ export async function createBaseModel({
`Creator function "${providerConfig.creatorFunctionName}" not found in the imported module for provider "${effectiveProviderId}"` `Creator function "${providerConfig.creatorFunctionName}" not found in the imported module for provider "${effectiveProviderId}"`
) )
} }
// TODO: 对openai 的 providerSettings.mode参数是否要删除,目前看没毛病
// 创建provider实例 // 创建provider实例
let provider = creatorFunction(providerSettings) let provider = creatorFunction(providerSettings)
@ -93,7 +94,7 @@ export async function createBaseModel({
if (providerConfig.id === 'openai') { if (providerConfig.id === 'openai') {
if ( if (
'mode' in providerSettings && 'mode' in providerSettings &&
providerSettings.mode === 'response' && providerSettings.mode === 'responses' &&
!isOpenAIChatCompletionOnlyModel(modelId) !isOpenAIChatCompletionOnlyModel(modelId)
) { ) {
provider = provider.responses provider = provider.responses

View File

@ -8,7 +8,7 @@ import type { ProviderId, ProviderSettingsMap } from '../../types'
export interface ModelConfig<T extends ProviderId = ProviderId> { export interface ModelConfig<T extends ProviderId = ProviderId> {
providerId: T providerId: T
modelId: string modelId: string
providerSettings: ProviderSettingsMap[T] & { mode: 'chat' | 'responses' } providerSettings: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' }
middlewares?: LanguageModelV2Middleware[] middlewares?: LanguageModelV2Middleware[]
// 额外模型参数 // 额外模型参数
extraModelConfig?: Record<string, any> extraModelConfig?: Record<string, any>

View File

@ -1,4 +1,5 @@
import { anthropic } from '@ai-sdk/anthropic' import { anthropic } from '@ai-sdk/anthropic'
import { google } from '@ai-sdk/google'
import { openai } from '@ai-sdk/openai' import { openai } from '@ai-sdk/openai'
import { ProviderOptionsMap } from '../../../options/types' import { ProviderOptionsMap } from '../../../options/types'
@ -8,16 +9,7 @@ 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]
/** type GoogleSearchConfig = Parameters<typeof google.tools.googleSearch>[0]
* XAI
* @internal
*/
interface XaiProviderOptions {
searchParameters?: {
sources?: any[]
safeSearch?: boolean
}
}
/** /**
* *
@ -28,20 +20,16 @@ export interface WebSearchPluginConfig {
openai?: OpenAISearchConfig openai?: OpenAISearchConfig
anthropic?: AnthropicSearchConfig anthropic?: AnthropicSearchConfig
xai?: ProviderOptionsMap['xai']['searchParameters'] xai?: ProviderOptionsMap['xai']['searchParameters']
google?: Pick<ProviderOptionsMap['google'], 'useSearchGrounding' | 'dynamicRetrievalConfig'> google?: GoogleSearchConfig
'google-vertex'?: Pick<ProviderOptionsMap['google'], 'useSearchGrounding' | 'dynamicRetrievalConfig'> 'google-vertex'?: GoogleSearchConfig
} }
/** /**
* *
*/ */
export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchPluginConfig = { export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchPluginConfig = {
google: { google: {},
useSearchGrounding: true 'google-vertex': {},
},
'google-vertex': {
useSearchGrounding: true
},
openai: {}, openai: {},
xai: { xai: {
mode: 'on', mode: 'on',
@ -53,37 +41,3 @@ export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchPluginConfig = {
maxUses: 5 maxUses: 5
} }
} }
/**
* Google providerOptions
*/
export const getGoogleProviderOptions = (providerOptions: any) => {
if (!providerOptions) providerOptions = {}
if (!providerOptions.google) providerOptions.google = {}
providerOptions.google.useSearchGrounding = true
return providerOptions
}
/**
* XAI providerOptions
*/
export const getXaiProviderOptions = (providerOptions: any, config?: XaiProviderOptions['searchParameters']) => {
if (!providerOptions) providerOptions = {}
if (!providerOptions.xai) providerOptions.xai = {}
providerOptions.xai.searchParameters = {
mode: 'on',
...(config ?? {})
}
return providerOptions
}
export type AnthropicSearchInput = {
query: string
}
export type AnthropicSearchOutput = {
url: string
title: string
pageAge: string | null
encryptedContent: string
type: string
}[]

View File

@ -3,9 +3,10 @@
* AI Provider * AI Provider
*/ */
import { anthropic } from '@ai-sdk/anthropic' import { anthropic } from '@ai-sdk/anthropic'
import { google } from '@ai-sdk/google'
import { openai } from '@ai-sdk/openai' import { openai } from '@ai-sdk/openai'
import { createGoogleOptions, createXaiOptions, mergeProviderOptions } from '../../../options' import { createXaiOptions, mergeProviderOptions } from '../../../options'
import { definePlugin } from '../../' import { definePlugin } from '../../'
import type { AiRequestContext } from '../../types' import type { AiRequestContext } from '../../types'
import { DEFAULT_WEB_SEARCH_CONFIG, WebSearchPluginConfig } from './helper' import { DEFAULT_WEB_SEARCH_CONFIG, WebSearchPluginConfig } from './helper'
@ -22,15 +23,12 @@ export const webSearchPlugin = (config: WebSearchPluginConfig = DEFAULT_WEB_SEAR
transformParams: async (params: any, context: AiRequestContext) => { transformParams: async (params: any, context: AiRequestContext) => {
const { providerId } = context const { providerId } = context
// console.log('providerId', providerId) console.log('providerId', providerId)
// const modelToProviderId = getModelToProviderId(modelId)
// console.log('modelToProviderId', modelToProviderId)
switch (providerId) { switch (providerId) {
case 'openai': { case 'openai': {
if (config.openai) { if (config.openai) {
if (!params.tools) params.tools = {} if (!params.tools) params.tools = {}
params.tools.web_search_preview = openai.tools.webSearchPreview(config.openai) params.tools.web_search_preview = openai.tools.webSearchPreview(config.openai)
// console.log('params.tools', params.tools)
} }
break break
} }
@ -45,11 +43,8 @@ export const webSearchPlugin = (config: WebSearchPluginConfig = DEFAULT_WEB_SEAR
case 'google': case 'google':
case 'google-vertex': { case 'google-vertex': {
// @ts-ignore - providerId is a string that can be used to index config if (!params.tools) params.tools = {}
if (config[providerId]) { params.tools.web_search = google.tools.googleSearch(config.google || {})
const searchOptions = createGoogleOptions({ useSearchGrounding: true })
params.providerOptions = mergeProviderOptions(params.providerOptions, searchOptions)
}
break break
} }
@ -62,23 +57,14 @@ export const webSearchPlugin = (config: WebSearchPluginConfig = DEFAULT_WEB_SEAR
} }
break break
} }
// default: {
// if (!params.providerOptions) params.providerOptions = {}
// params.providerOptions['aihubmix'] = {
// web_search: anthropic.tools.webSearch_20250305()
// }
// console.log('params.providerOptions', params.providerOptions)
// break
// }
} }
// console.log('params', params)
return params return params
} }
}) })
// 导出类型定义供开发者使用 // 导出类型定义供开发者使用
export type { AnthropicSearchInput, AnthropicSearchOutput, WebSearchPluginConfig } from './helper' export type { WebSearchPluginConfig } from './helper'
// 默认导出 // 默认导出
export default webSearchPlugin export default webSearchPlugin

View File

@ -27,7 +27,7 @@ import { RuntimeExecutor } from './executor'
*/ */
export function createExecutor<T extends ProviderId>( export function createExecutor<T extends ProviderId>(
providerId: T, providerId: T,
options: ProviderSettingsMap[T], options: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' },
plugins?: AiPlugin[] plugins?: AiPlugin[]
): RuntimeExecutor<T> { ): RuntimeExecutor<T> {
return RuntimeExecutor.create(providerId, options, plugins) return RuntimeExecutor.create(providerId, options, plugins)
@ -37,7 +37,7 @@ export function createExecutor<T extends ProviderId>(
* OpenAI Compatible执行器 * OpenAI Compatible执行器
*/ */
export function createOpenAICompatibleExecutor( export function createOpenAICompatibleExecutor(
options: ProviderSettingsMap['openai-compatible'], options: ProviderSettingsMap['openai-compatible'] & { mode?: 'chat' | 'responses' },
plugins: AiPlugin[] = [] plugins: AiPlugin[] = []
): RuntimeExecutor<'openai-compatible'> { ): RuntimeExecutor<'openai-compatible'> {
return RuntimeExecutor.createOpenAICompatible(options, plugins) return RuntimeExecutor.createOpenAICompatible(options, plugins)
@ -50,7 +50,7 @@ export function createOpenAICompatibleExecutor(
*/ */
export async function streamText<T extends ProviderId>( export async function streamText<T extends ProviderId>(
providerId: T, providerId: T,
options: ProviderSettingsMap[T], options: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' },
modelId: string, modelId: string,
params: Parameters<RuntimeExecutor<T>['streamText']>[1], params: Parameters<RuntimeExecutor<T>['streamText']>[1],
plugins?: AiPlugin[], plugins?: AiPlugin[],
@ -65,7 +65,7 @@ export async function streamText<T extends ProviderId>(
*/ */
export async function generateText<T extends ProviderId>( export async function generateText<T extends ProviderId>(
providerId: T, providerId: T,
options: ProviderSettingsMap[T], options: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' },
modelId: string, modelId: string,
params: Parameters<RuntimeExecutor<T>['generateText']>[1], params: Parameters<RuntimeExecutor<T>['generateText']>[1],
plugins?: AiPlugin[], plugins?: AiPlugin[],
@ -80,7 +80,7 @@ export async function generateText<T extends ProviderId>(
*/ */
export async function generateObject<T extends ProviderId>( export async function generateObject<T extends ProviderId>(
providerId: T, providerId: T,
options: ProviderSettingsMap[T], options: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' },
modelId: string, modelId: string,
params: Parameters<RuntimeExecutor<T>['generateObject']>[1], params: Parameters<RuntimeExecutor<T>['generateObject']>[1],
plugins?: AiPlugin[], plugins?: AiPlugin[],
@ -95,7 +95,7 @@ export async function generateObject<T extends ProviderId>(
*/ */
export async function streamObject<T extends ProviderId>( export async function streamObject<T extends ProviderId>(
providerId: T, providerId: T,
options: ProviderSettingsMap[T], options: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' },
modelId: string, modelId: string,
params: Parameters<RuntimeExecutor<T>['streamObject']>[1], params: Parameters<RuntimeExecutor<T>['streamObject']>[1],
plugins?: AiPlugin[], plugins?: AiPlugin[],

View File

@ -10,7 +10,7 @@ import { type AiPlugin } from '../plugins'
*/ */
export interface RuntimeConfig<T extends ProviderId = ProviderId> { export interface RuntimeConfig<T extends ProviderId = ProviderId> {
providerId: T providerId: T
providerSettings: ModelConfig<T>['providerSettings'] providerSettings: ModelConfig<T>['providerSettings'] & { mode?: 'chat' | 'responses' }
plugins?: AiPlugin[] plugins?: AiPlugin[]
} }

View File

@ -133,11 +133,11 @@ export class AiSdkToChunkAdapter {
// === 工具调用相关事件(原始 AI SDK 事件,如果没有被中间件处理) === // === 工具调用相关事件(原始 AI SDK 事件,如果没有被中间件处理) ===
case 'tool-input-start': // case 'tool-input-start':
case 'tool-input-delta': // case 'tool-input-delta':
case 'tool-input-end': // case 'tool-input-end':
this.toolCallHandler.handleToolCallCreated(chunk) // this.toolCallHandler.handleToolCallCreated(chunk)
break // break
// case 'tool-input-delta': // case 'tool-input-delta':
// this.toolCallHandler.handleToolCallCreated(chunk) // this.toolCallHandler.handleToolCallCreated(chunk)

View File

@ -46,7 +46,6 @@ function getActualProvider(model: Model): Provider {
if (provider.id === 'aihubmix') { if (provider.id === 'aihubmix') {
actualProvider = createAihubmixProvider(model, actualProvider) actualProvider = createAihubmixProvider(model, actualProvider)
console.log('actualProvider', actualProvider)
} }
if (actualProvider.type === 'gemini') { if (actualProvider.type === 'gemini') {
actualProvider.apiHost = formatApiHost(actualProvider.apiHost, 'v1beta') actualProvider.apiHost = formatApiHost(actualProvider.apiHost, 'v1beta')
@ -67,13 +66,14 @@ function providerToAiSdkConfig(actualProvider: Provider): {
const aiSdkProviderId = getAiSdkProviderId(actualProvider) const aiSdkProviderId = getAiSdkProviderId(actualProvider)
// console.log('aiSdkProviderId', aiSdkProviderId) // console.log('aiSdkProviderId', aiSdkProviderId)
// 如果provider是openai则使用strict模式并且默认responses api // 如果provider是openai则使用strict模式并且默认responses api
const actualProviderType = actualProvider.type const actualProviderId = actualProvider.id
const openaiResponseOptions = const openaiResponseOptions =
actualProviderType === 'openai-response' // 对于实际是openai的需要走responses,aiCore内部会判断model是否可用responses
actualProviderId === 'openai'
? { ? {
mode: 'response' mode: 'responses'
} }
: actualProviderType === 'openai' : aiSdkProviderId === 'openai'
? { ? {
mode: 'chat' mode: 'chat'
} }
@ -86,10 +86,9 @@ function providerToAiSdkConfig(actualProvider: Provider): {
aiSdkProviderId, aiSdkProviderId,
{ {
baseURL: actualProvider.apiHost, baseURL: actualProvider.apiHost,
apiKey: actualProvider.apiKey, apiKey: actualProvider.apiKey
headers: actualProvider.extra_headers
}, },
openaiResponseOptions { ...openaiResponseOptions, headers: actualProvider.extra_headers }
) )
return { return {
@ -224,7 +223,9 @@ export default class ModernAiProvider {
// try { // try {
// 根据条件构建插件数组 // 根据条件构建插件数组
const plugins = this.buildPlugins(middlewareConfig) const plugins = this.buildPlugins(middlewareConfig)
console.log('this.config.providerId', this.config.providerId)
console.log('this.config.options', this.config.options)
console.log('plugins', plugins)
// 用构建好的插件数组创建executor // 用构建好的插件数组创建executor
const executor = createExecutor(this.config.providerId, this.config.options, plugins) const executor = createExecutor(this.config.providerId, this.config.options, plugins)

View File

@ -26,6 +26,9 @@ export function getAiSdkProviderId(provider: Provider): ProviderId | 'openai-com
if (AiCore.isSupported(provider.id)) { if (AiCore.isSupported(provider.id)) {
return provider.id as ProviderId return provider.id as ProviderId
} }
if (AiCore.isSupported(provider.type)) {
return provider.type as ProviderId
}
return provider.id as ProviderId return provider.id as ProviderId
} }

View File

@ -35,7 +35,12 @@ export function buildProviderOptions(
switch (providerId) { switch (providerId) {
case 'openai': case 'openai':
case 'azure': case 'azure':
providerSpecificOptions = buildOpenAIProviderOptions(assistant, model, capabilities) providerSpecificOptions = {
...buildOpenAIProviderOptions(assistant, model, capabilities),
// 函数内有对于真实provider.id的判断,应该不会影响原生provider
...buildGenericProviderOptions(assistant, model, capabilities)
}
break break
case 'anthropic': case 'anthropic':