From 53dcda6942e1cf4b0b07ca79210068d63f63d7cc Mon Sep 17 00:00:00 2001 From: lizhixuan Date: Tue, 26 Aug 2025 00:31:41 +0800 Subject: [PATCH] feat(aiCore): introduce Hub Provider and enhance provider management - Added a new example file demonstrating the usage of the Hub Provider for routing to multiple underlying providers. - Implemented the Hub Provider to support model ID parsing and routing based on a specified format. - Refactored provider management by introducing a Registry Management class for better organization and retrieval of provider instances. - Updated the Provider Initializer to streamline the initialization and registration of providers, enhancing overall flexibility and usability. - Removed outdated files related to provider creation and dynamic registration to simplify the codebase. --- .../aiCore/examples/hub-provider-usage.ts | 103 +++++ packages/aiCore/src/core/index.ts | 11 +- .../aiCore/src/core/models/ModelCreator.ts | 199 --------- .../aiCore/src/core/models/ModelResolver.ts | 76 ++-- .../core/providers/DynamicProviderRegistry.ts | 136 ------ .../aiCore/src/core/providers/HubProvider.ts | 148 ++++++ .../src/core/providers/RegistryManagement.ts | 150 +++++++ packages/aiCore/src/core/providers/creator.ts | 110 ----- packages/aiCore/src/core/providers/index.ts | 34 +- .../aiCore/src/core/providers/registry.ts | 421 +++++++++--------- packages/aiCore/src/core/runtime/executor.ts | 24 +- packages/aiCore/src/index.ts | 104 ++--- src/renderer/src/aiCore/index_new.ts | 17 +- src/renderer/src/aiCore/provider/factory.ts | 27 +- .../src/aiCore/provider/providerConfigs.ts | 31 +- 15 files changed, 788 insertions(+), 803 deletions(-) create mode 100644 packages/aiCore/examples/hub-provider-usage.ts delete mode 100644 packages/aiCore/src/core/models/ModelCreator.ts delete mode 100644 packages/aiCore/src/core/providers/DynamicProviderRegistry.ts create mode 100644 packages/aiCore/src/core/providers/HubProvider.ts create mode 100644 packages/aiCore/src/core/providers/RegistryManagement.ts delete mode 100644 packages/aiCore/src/core/providers/creator.ts diff --git a/packages/aiCore/examples/hub-provider-usage.ts b/packages/aiCore/examples/hub-provider-usage.ts new file mode 100644 index 0000000000..559e812bdb --- /dev/null +++ b/packages/aiCore/examples/hub-provider-usage.ts @@ -0,0 +1,103 @@ +/** + * Hub Provider 使用示例 + * + * 演示如何使用简化后的Hub Provider功能来路由到多个底层provider + */ + +import { createHubProvider, initializeProvider, providerRegistry } from '../src/index' + +async function demonstrateHubProvider() { + try { + // 1. 初始化底层providers + console.log('📦 初始化底层providers...') + + initializeProvider('openai', { + apiKey: process.env.OPENAI_API_KEY || 'sk-test-key' + }) + + initializeProvider('anthropic', { + apiKey: process.env.ANTHROPIC_API_KEY || 'sk-ant-test-key' + }) + + // 2. 创建Hub Provider(自动包含所有已初始化的providers) + console.log('🌐 创建Hub Provider...') + + const aihubmixProvider = createHubProvider({ + hubId: 'aihubmix', + debug: true + }) + + // 3. 注册Hub Provider + providerRegistry.registerProvider('aihubmix', aihubmixProvider) + + console.log('✅ Hub Provider "aihubmix" 注册成功') + + // 4. 使用Hub Provider访问不同的模型 + console.log('\n🚀 使用Hub模型...') + + // 通过Hub路由到OpenAI + const openaiModel = providerRegistry.languageModel('aihubmix:openai:gpt-4') + console.log('✓ OpenAI模型已获取:', openaiModel.modelId) + + // 通过Hub路由到Anthropic + const anthropicModel = providerRegistry.languageModel('aihubmix:anthropic:claude-3.5-sonnet') + console.log('✓ Anthropic模型已获取:', anthropicModel.modelId) + + // 5. 演示错误处理 + console.log('\n❌ 演示错误处理...') + + try { + // 尝试访问未初始化的provider + providerRegistry.languageModel('aihubmix:google:gemini-pro') + } catch (error) { + console.log('预期错误:', error.message) + } + + try { + // 尝试使用错误的模型ID格式 + providerRegistry.languageModel('aihubmix:invalid-format') + } catch (error) { + console.log('预期错误:', error.message) + } + + // 6. 多个Hub Provider示例 + console.log('\n🔄 创建多个Hub Provider...') + + const localHubProvider = createHubProvider({ + hubId: 'local-ai' + }) + + providerRegistry.registerProvider('local-ai', localHubProvider) + console.log('✅ Hub Provider "local-ai" 注册成功') + + console.log('\n🎉 Hub Provider演示完成!') + } catch (error) { + console.error('💥 演示过程中发生错误:', error) + } +} + +// 演示简化的使用方式 +function simplifiedUsageExample() { + console.log('\n📝 简化使用示例:') + console.log(` +// 1. 初始化providers +initializeProvider('openai', { apiKey: 'sk-xxx' }) +initializeProvider('anthropic', { apiKey: 'sk-ant-xxx' }) + +// 2. 创建并注册Hub Provider +const hubProvider = createHubProvider({ hubId: 'aihubmix' }) +providerRegistry.registerProvider('aihubmix', hubProvider) + +// 3. 直接使用 +const model1 = providerRegistry.languageModel('aihubmix:openai:gpt-4') +const model2 = providerRegistry.languageModel('aihubmix:anthropic:claude-3.5-sonnet') +`) +} + +// 运行演示 +if (require.main === module) { + demonstrateHubProvider() + simplifiedUsageExample() +} + +export { demonstrateHubProvider, simplifiedUsageExample } diff --git a/packages/aiCore/src/core/index.ts b/packages/aiCore/src/core/index.ts index 6ddd605a8f..2346ea8cd2 100644 --- a/packages/aiCore/src/core/index.ts +++ b/packages/aiCore/src/core/index.ts @@ -8,15 +8,8 @@ export type { NamedMiddleware } from './middleware' export { createMiddlewares, wrapModelWithMiddlewares } from './middleware' // 创建管理 -export { - createBaseModel, - createImageModel, - createModel, - getProviderInfo, - getSupportedProviders, - ModelCreationError -} from './models' -export type { ModelConfig } from './models/types' +export { globalModelResolver, ModelResolver } from './models' +export type { ModelConfig as ModelConfigType } from './models/types' // 执行管理 export type { ToolUseRequestContext } from './plugins/built-in/toolUsePlugin/type' diff --git a/packages/aiCore/src/core/models/ModelCreator.ts b/packages/aiCore/src/core/models/ModelCreator.ts deleted file mode 100644 index 22ae26f86f..0000000000 --- a/packages/aiCore/src/core/models/ModelCreator.ts +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Model Creator - * 负责基于 Provider 创建 AI SDK 的 Language Model 和 Image Model 实例 - */ -import { ImageModelV2, type LanguageModelV2 } from '@ai-sdk/provider' - -import { isOpenAIChatCompletionOnlyModel } from '../../utils/model' -import { createImageProvider, createProvider } from '../providers/creator' -import { aiProviderRegistry } from '../providers/registry' -import { type ProviderId, type ProviderSettingsMap } from '../providers/types' - -// 错误类型 -export class ModelCreationError extends Error { - constructor( - message: string, - public providerId?: string, - public cause?: Error - ) { - super(message) - this.name = 'ModelCreationError' - } -} - -/** - * 创建基础 AI SDK 模型实例 - * 对于已知的 Provider 使用严格类型检查,未知的 Provider 默认使用 openai-compatible - */ -export async function createBaseModel({ - providerId, - modelId, - providerSettings, - extraModelConfig -}: { - providerId: T - modelId: string - providerSettings: ProviderSettingsMap[T] & { mode?: 'chat' | 'responses' } - extraModelConfig?: any -}): Promise - -export async function createBaseModel({ - providerId, - modelId, - providerSettings, - extraModelConfig -}: { - providerId: string - modelId: string - providerSettings: ProviderSettingsMap['openai-compatible'] & { mode?: 'chat' | 'responses' } - extraModelConfig?: any -}): Promise - -export async function createBaseModel({ - providerId, - modelId, - providerSettings, - extraModelConfig -}: { - providerId: string - modelId: string - providerSettings: ProviderSettingsMap[ProviderId] & { mode?: 'chat' | 'responses' } - extraModelConfig?: any -}): Promise { - try { - // 获取Provider配置 - const providerConfig = aiProviderRegistry.getProvider(providerId) - if (!providerConfig) { - throw new ModelCreationError(`Provider "${providerId}" is not registered`, providerId) - } - - // 创建 provider 实例 - const provider = await createProvider(providerConfig, providerSettings) - - // 根据 provider 类型处理特殊逻辑 - const finalProvider = handleProviderSpecificLogic(provider, providerConfig.id, providerSettings, modelId) - - // 创建模型实例 - if (typeof finalProvider === 'function') { - const model: LanguageModelV2 = finalProvider(modelId, extraModelConfig) - return model - } else { - throw new ModelCreationError(`Unknown model access pattern for provider "${providerId}"`) - } - } catch (error) { - if (error instanceof ModelCreationError) { - throw error - } - throw new ModelCreationError( - `Failed to create base model for provider "${providerId}": ${error instanceof Error ? error.message : 'Unknown error'}`, - providerId, - error instanceof Error ? error : undefined - ) - } -} - -/** - * 处理特定 Provider 的逻辑 - */ -function handleProviderSpecificLogic(provider: any, providerId: string, providerSettings: any, modelId: string): any { - // OpenAI 特殊处理 - if (providerId === 'openai') { - if ( - 'mode' in providerSettings && - providerSettings.mode === 'responses' && - !isOpenAIChatCompletionOnlyModel(modelId) - ) { - return provider.responses - } else { - return provider.chat - } - } - - // 其他 provider 直接返回 - return provider -} - -/** - * 创建图像生成模型实例 - */ -export async function createImageModel( - providerId: T, - modelId: string, - options: ProviderSettingsMap[T] -): Promise -export async function createImageModel( - providerId: string, - modelId: string, - options: ProviderSettingsMap['openai-compatible'] -): Promise -export async function createImageModel( - providerId: string, - modelId: string = 'default', - options: any -): Promise { - try { - if (!aiProviderRegistry.isSupported(providerId)) { - throw new ModelCreationError(`Provider "${providerId}" is not supported`, providerId) - } - - const providerConfig = aiProviderRegistry.getProvider(providerId) - if (!providerConfig) { - throw new ModelCreationError(`Provider "${providerId}" is not registered`, providerId) - } - - if (!providerConfig.supportsImageGeneration) { - throw new ModelCreationError(`Provider "${providerId}" does not support image generation`, providerId) - } - - // 创建图像 provider 实例 - const provider = await createImageProvider(providerConfig, options) - - if (provider && typeof provider.image === 'function') { - return provider.image(modelId) - } else { - throw new ModelCreationError(`Image model function not found for provider "${providerId}"`) - } - } catch (error) { - if (error instanceof ModelCreationError) { - throw error - } - throw new ModelCreationError( - `Failed to create image model for provider "${providerId}": ${error instanceof Error ? error.message : 'Unknown error'}`, - providerId, - error instanceof Error ? error : undefined - ) - } -} - -/** - * 获取支持的 Providers 列表 - */ -export function getSupportedProviders(): Array<{ - id: string - name: string -}> { - return aiProviderRegistry.getAllProviders().map((provider) => ({ - id: provider.id, - name: provider.name - })) -} - -/** - * 获取 Provider 信息 - */ -export function getProviderInfo(providerId: string): { - id: string - name: string - isSupported: boolean - effectiveProvider: string -} { - const effectiveProviderId = aiProviderRegistry.isSupported(providerId) ? providerId : 'openai-compatible' - const provider = aiProviderRegistry.getProvider(effectiveProviderId) - - return { - id: providerId, - name: provider?.name || providerId, - isSupported: aiProviderRegistry.isSupported(providerId), - effectiveProvider: effectiveProviderId - } -} diff --git a/packages/aiCore/src/core/models/ModelResolver.ts b/packages/aiCore/src/core/models/ModelResolver.ts index f0f2974a88..7c20469351 100644 --- a/packages/aiCore/src/core/models/ModelResolver.ts +++ b/packages/aiCore/src/core/models/ModelResolver.ts @@ -2,11 +2,14 @@ * 模型解析器 - models模块的核心 * 负责将modelId解析为AI SDK的LanguageModel实例 * 支持传统格式和命名空间格式 + * 集成了来自 ModelCreator 的特殊处理逻辑 */ -import { EmbeddingModelV2, ImageModelV2, LanguageModelV2 } from '@ai-sdk/provider' +import { EmbeddingModelV2, ImageModelV2, LanguageModelV2, LanguageModelV2Middleware } from '@ai-sdk/provider' -import { globalDynamicRegistry } from '../providers/DynamicProviderRegistry' +import { isOpenAIChatCompletionOnlyModel } from '../../utils/model' +import { wrapModelWithMiddlewares } from '../middleware/wrapper' +import { globalRegistryManagement } from '../providers/RegistryManagement' export class ModelResolver { /** @@ -14,31 +17,48 @@ export class ModelResolver { * * @param modelId 模型ID,支持 'gpt-4' 和 'anthropic>claude-3' 两种格式 * @param fallbackProviderId 当modelId为传统格式时使用的providerId - * @param providerOptions provider配置选项(暂时保留,可能在未来使用) + * @param providerOptions provider配置选项(用于OpenAI模式选择等) + * @param middlewares 中间件数组,会应用到最终模型上 */ async resolveLanguageModel( modelId: string, fallbackProviderId: string, - _providerOptions?: any + providerOptions?: any, + middlewares?: LanguageModelV2Middleware[] ): Promise { - // 检查是否是命名空间格式 (aihubmix>anthropic>claude-3) - if (modelId.includes('>')) { - return this.resolveNamespacedModel(modelId) + let finalProviderId = fallbackProviderId + let model: LanguageModelV2 + + // 🎯 处理 OpenAI 模式选择逻辑 (从 ModelCreator 迁移) + if (fallbackProviderId === 'openai' && providerOptions?.mode === 'chat') { + // 检查是否支持 chat 模式且不是只支持 chat 的模型 + if (!isOpenAIChatCompletionOnlyModel(modelId)) { + finalProviderId = 'openai-chat' + } + // 否则使用默认的 openai (responses 模式) } - // 传统格式:使用fallbackProviderId + modelId (openai + gpt-4) - return this.resolveTraditionalModel(fallbackProviderId, modelId) + // 检查是否是命名空间格式 + if (modelId.includes(':')) { + model = this.resolveNamespacedModel(modelId) + } else { + // 传统格式:使用处理后的 providerId + modelId + model = this.resolveTraditionalModel(finalProviderId, modelId) + } + + // 🎯 应用中间件(如果有) + if (middlewares && middlewares.length > 0) { + model = wrapModelWithMiddlewares(model, middlewares) + } + + return model } /** * 解析文本嵌入模型 */ - async resolveTextEmbeddingModel( - modelId: string, - fallbackProviderId: string, - _providerOptions?: any - ): Promise> { - if (modelId.includes('>')) { + async resolveTextEmbeddingModel(modelId: string, fallbackProviderId: string): Promise> { + if (modelId.includes(':')) { return this.resolveNamespacedEmbeddingModel(modelId) } @@ -48,8 +68,8 @@ export class ModelResolver { /** * 解析图像模型 */ - async resolveImageModel(modelId: string, fallbackProviderId: string, _providerOptions?: any): Promise { - if (modelId.includes('>')) { + async resolveImageModel(modelId: string, fallbackProviderId: string): Promise { + if (modelId.includes(':')) { return this.resolveNamespacedImageModel(modelId) } @@ -58,49 +78,49 @@ export class ModelResolver { /** * 解析命名空间格式的语言模型 - * aihubmix>anthropic>claude-3 -> globalDynamicRegistry.languageModel('aihubmix>anthropic>claude-3') + * aihubmix:anthropic:claude-3 -> globalRegistryManagement.languageModel('aihubmix:anthropic:claude-3') */ private resolveNamespacedModel(modelId: string): LanguageModelV2 { - return globalDynamicRegistry.languageModel(modelId) + return globalRegistryManagement.languageModel(modelId as any) } /** * 解析传统格式的语言模型 - * providerId: 'openai', modelId: 'gpt-4' -> globalDynamicRegistry.languageModel('openai>gpt-4') + * providerId: 'openai', modelId: 'gpt-4' -> globalRegistryManagement.languageModel('openai:gpt-4') */ private resolveTraditionalModel(providerId: string, modelId: string): LanguageModelV2 { - const fullModelId = `${providerId}>${modelId}` - return globalDynamicRegistry.languageModel(fullModelId) + const fullModelId = `${providerId}:${modelId}` + return globalRegistryManagement.languageModel(fullModelId as any) } /** * 解析命名空间格式的嵌入模型 */ private resolveNamespacedEmbeddingModel(modelId: string): EmbeddingModelV2 { - return globalDynamicRegistry.textEmbeddingModel(modelId) + return globalRegistryManagement.textEmbeddingModel(modelId as any) } /** * 解析传统格式的嵌入模型 */ private resolveTraditionalEmbeddingModel(providerId: string, modelId: string): EmbeddingModelV2 { - const fullModelId = `${providerId}>${modelId}` - return globalDynamicRegistry.textEmbeddingModel(fullModelId) + const fullModelId = `${providerId}:${modelId}` + return globalRegistryManagement.textEmbeddingModel(fullModelId as any) } /** * 解析命名空间格式的图像模型 */ private resolveNamespacedImageModel(modelId: string): ImageModelV2 { - return globalDynamicRegistry.imageModel(modelId) + return globalRegistryManagement.imageModel(modelId as any) } /** * 解析传统格式的图像模型 */ private resolveTraditionalImageModel(providerId: string, modelId: string): ImageModelV2 { - const fullModelId = `${providerId}>${modelId}` - return globalDynamicRegistry.imageModel(fullModelId) + const fullModelId = `${providerId}:${modelId}` + return globalRegistryManagement.imageModel(fullModelId as any) } } diff --git a/packages/aiCore/src/core/providers/DynamicProviderRegistry.ts b/packages/aiCore/src/core/providers/DynamicProviderRegistry.ts deleted file mode 100644 index bd44e55780..0000000000 --- a/packages/aiCore/src/core/providers/DynamicProviderRegistry.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 动态Provider注册表 - 完全基于AI SDK原生 - * 每次变更都重建registry,让AI SDK处理所有逻辑 - */ - -import { EmbeddingModelV2, ImageModelV2, LanguageModelV2, ProviderV2 } from '@ai-sdk/provider' -import { createProviderRegistry } from 'ai' - -export class DynamicProviderRegistry { - private providers: Record = {} - private separator: string - private registry: any | null = null - - constructor(options: { separator?: string } = {}) { - this.separator = options.separator || '>' - } - - /** - * 动态注册provider - 立即重建registry - */ - registerProvider(id: string, provider: ProviderV2): this { - this.providers[id] = provider - this.rebuildRegistry() - return this - } - - /** - * 批量注册providers - */ - registerProviders(providers: Record): this { - Object.assign(this.providers, providers) - this.rebuildRegistry() - return this - } - - /** - * 移除provider - */ - unregisterProvider(id: string): this { - delete this.providers[id] - this.rebuildRegistry() - return this - } - - /** - * 立即重建registry - 每次变更都重建 - */ - private rebuildRegistry(): void { - if (Object.keys(this.providers).length === 0) { - this.registry = null - return - } - - this.registry = createProviderRegistry(this.providers, { - separator: this.separator - }) - } - - /** - * 获取语言模型 - AI SDK原生方法 - */ - languageModel(id: string): LanguageModelV2 { - if (!this.registry) { - throw new Error('No providers registered') - } - return this.registry.languageModel(id) - } - - /** - * 获取文本嵌入模型 - AI SDK原生方法 - */ - textEmbeddingModel(id: string): EmbeddingModelV2 { - if (!this.registry) { - throw new Error('No providers registered') - } - return this.registry.textEmbeddingModel(id) - } - - /** - * 获取图像模型 - AI SDK原生方法 - */ - imageModel(id: string): ImageModelV2 { - if (!this.registry) { - throw new Error('No providers registered') - } - return this.registry.imageModel(id) - } - - /** - * 获取转录模型 - AI SDK原生方法 - */ - transcriptionModel(id: string): any { - if (!this.registry) { - throw new Error('No providers registered') - } - return this.registry.transcriptionModel(id) - } - - /** - * 获取语音模型 - AI SDK原生方法 - */ - speechModel(id: string): any { - if (!this.registry) { - throw new Error('No providers registered') - } - return this.registry.speechModel(id) - } - - /** - * 获取已注册的provider列表 - */ - getRegisteredProviders(): string[] { - return Object.keys(this.providers) - } - - /** - * 检查是否有已注册的providers - */ - hasProviders(): boolean { - return Object.keys(this.providers).length > 0 - } - - /** - * 清除所有providers - */ - clear(): this { - this.providers = {} - this.registry = null - return this - } -} - -/** - * 全局动态registry实例 - */ -export const globalDynamicRegistry = new DynamicProviderRegistry({ separator: '>' }) diff --git a/packages/aiCore/src/core/providers/HubProvider.ts b/packages/aiCore/src/core/providers/HubProvider.ts new file mode 100644 index 0000000000..a5fd77888f --- /dev/null +++ b/packages/aiCore/src/core/providers/HubProvider.ts @@ -0,0 +1,148 @@ +/** + * Hub Provider - 支持路由到多个底层provider + * + * 支持格式: hubId:providerId:modelId + * 例如: aihubmix:anthropic:claude-3.5-sonnet + */ + +import { ProviderV2 } from '@ai-sdk/provider' +import { customProvider } from 'ai' + +import { globalRegistryManagement } from './RegistryManagement' + +export interface HubProviderConfig { + /** Hub的唯一标识符 */ + hubId: string + /** 是否启用调试日志 */ + debug?: boolean +} + +export class HubProviderError extends Error { + constructor( + message: string, + public readonly hubId: string, + public readonly providerId?: string, + public readonly originalError?: Error + ) { + super(message) + this.name = 'HubProviderError' + } +} + +/** + * 解析Hub模型ID + */ +function parseHubModelId(modelId: string): { provider: string; actualModelId: string } { + const parts = modelId.split(':') + if (parts.length !== 2) { + throw new HubProviderError(`Invalid hub model ID format. Expected "provider:modelId", got: ${modelId}`, 'unknown') + } + return { + provider: parts[0], + actualModelId: parts[1] + } +} + +/** + * 创建Hub Provider + */ +export function createHubProvider(config: HubProviderConfig): ProviderV2 { + const { hubId, debug = false } = config + + function logDebug(message: string, ...args: any[]) { + if (debug) { + console.log(`[HubProvider:${hubId}] ${message}`, ...args) + } + } + + function getTargetProvider(providerId: string): ProviderV2 { + // 从全局注册表获取provider实例 + try { + const provider = (globalRegistryManagement as any).getProvider(providerId) + if (!provider) { + throw new HubProviderError( + `Provider "${providerId}" is not initialized. Please call initializeProvider("${providerId}", options) first.`, + hubId, + providerId + ) + } + return provider + } catch (error) { + throw new HubProviderError( + `Failed to get provider "${providerId}": ${error instanceof Error ? error.message : 'Unknown error'}`, + hubId, + providerId, + error instanceof Error ? error : undefined + ) + } + } + + return customProvider({ + fallbackProvider: { + languageModel: (modelId: string) => { + logDebug('Resolving language model:', modelId) + + const { provider, actualModelId } = parseHubModelId(modelId) + const targetProvider = getTargetProvider(provider) + + if (!targetProvider.languageModel) { + throw new HubProviderError(`Provider "${provider}" does not support language models`, hubId, provider) + } + + return targetProvider.languageModel(actualModelId) + }, + + textEmbeddingModel: (modelId: string) => { + logDebug('Resolving text embedding model:', modelId) + + const { provider, actualModelId } = parseHubModelId(modelId) + const targetProvider = getTargetProvider(provider) + + if (!targetProvider.textEmbeddingModel) { + throw new HubProviderError(`Provider "${provider}" does not support text embedding models`, hubId, provider) + } + + return targetProvider.textEmbeddingModel(actualModelId) + }, + + imageModel: (modelId: string) => { + logDebug('Resolving image model:', modelId) + + const { provider, actualModelId } = parseHubModelId(modelId) + const targetProvider = getTargetProvider(provider) + + if (!targetProvider.imageModel) { + throw new HubProviderError(`Provider "${provider}" does not support image models`, hubId, provider) + } + + return targetProvider.imageModel(actualModelId) + }, + + transcriptionModel: (modelId: string) => { + logDebug('Resolving transcription model:', modelId) + + const { provider, actualModelId } = parseHubModelId(modelId) + const targetProvider = getTargetProvider(provider) + + if (!targetProvider.transcriptionModel) { + throw new HubProviderError(`Provider "${provider}" does not support transcription models`, hubId, provider) + } + + return targetProvider.transcriptionModel(actualModelId) + }, + + speechModel: (modelId: string) => { + logDebug('Resolving speech model:', modelId) + + const { provider, actualModelId } = parseHubModelId(modelId) + const targetProvider = getTargetProvider(provider) + + if (!targetProvider.speechModel) { + throw new HubProviderError(`Provider "${provider}" does not support speech models`, hubId, provider) + } + + return targetProvider.speechModel(actualModelId) + } + } + }) +} diff --git a/packages/aiCore/src/core/providers/RegistryManagement.ts b/packages/aiCore/src/core/providers/RegistryManagement.ts new file mode 100644 index 0000000000..bbde41e3e7 --- /dev/null +++ b/packages/aiCore/src/core/providers/RegistryManagement.ts @@ -0,0 +1,150 @@ +/** + * Provider 注册表管理器 + * 纯粹的管理功能:存储、检索已配置好的 provider 实例 + * 基于 AI SDK 原生的 createProviderRegistry + */ + +import { EmbeddingModelV2, ImageModelV2, LanguageModelV2, ProviderV2 } from '@ai-sdk/provider' +import { createProviderRegistry, type ProviderRegistryProvider } from 'ai' + +type PROVIDERS = Record + +export const DEFAULT_SEPARATOR = ':' + +// export type MODEL_ID = `${string}${typeof DEFAULT_SEPARATOR}${string}` + +export class RegistryManagement { + private providers: PROVIDERS = {} + private separator: SEPARATOR + private registry: ProviderRegistryProvider | null = null + + constructor(options: { separator: SEPARATOR } = { separator: DEFAULT_SEPARATOR as SEPARATOR }) { + this.separator = options.separator + } + + /** + * 注册已配置好的 provider 实例 + */ + registerProvider(id: string, provider: ProviderV2): this { + this.providers[id] = provider + this.rebuildRegistry() + return this + } + + /** + * 获取已注册的provider实例 + */ + getProvider(id: string): ProviderV2 | undefined { + return this.providers[id] + } + + /** + * 批量注册 providers + */ + registerProviders(providers: Record): this { + Object.assign(this.providers, providers) + this.rebuildRegistry() + return this + } + + /** + * 移除 provider + */ + unregisterProvider(id: string): this { + delete this.providers[id] + this.rebuildRegistry() + return this + } + + /** + * 立即重建 registry - 每次变更都重建 + */ + private rebuildRegistry(): void { + if (Object.keys(this.providers).length === 0) { + this.registry = null + return + } + + this.registry = createProviderRegistry(this.providers, { + separator: this.separator + }) + } + + /** + * 获取语言模型 - AI SDK 原生方法 + */ + languageModel(id: `${string}${SEPARATOR}${string}`): LanguageModelV2 { + if (!this.registry) { + throw new Error('No providers registered') + } + return this.registry.languageModel(id) + } + + /** + * 获取文本嵌入模型 - AI SDK 原生方法 + */ + textEmbeddingModel(id: `${string}${SEPARATOR}${string}`): EmbeddingModelV2 { + if (!this.registry) { + throw new Error('No providers registered') + } + return this.registry.textEmbeddingModel(id) + } + + /** + * 获取图像模型 - AI SDK 原生方法 + */ + imageModel(id: `${string}${SEPARATOR}${string}`): ImageModelV2 { + if (!this.registry) { + throw new Error('No providers registered') + } + return this.registry.imageModel(id) + } + + /** + * 获取转录模型 - AI SDK 原生方法 + */ + transcriptionModel(id: `${string}${SEPARATOR}${string}`): any { + if (!this.registry) { + throw new Error('No providers registered') + } + return this.registry.transcriptionModel(id) + } + + /** + * 获取语音模型 - AI SDK 原生方法 + */ + speechModel(id: `${string}${SEPARATOR}${string}`): any { + if (!this.registry) { + throw new Error('No providers registered') + } + return this.registry.speechModel(id) + } + + /** + * 获取已注册的 provider 列表 + */ + getRegisteredProviders(): string[] { + return Object.keys(this.providers) + } + + /** + * 检查是否有已注册的 providers + */ + hasProviders(): boolean { + return Object.keys(this.providers).length > 0 + } + + /** + * 清除所有 providers + */ + clear(): this { + this.providers = {} + this.registry = null + return this + } +} + +/** + * 全局注册表管理器实例 + */ +export const globalRegistryManagement = new RegistryManagement<':'>() diff --git a/packages/aiCore/src/core/providers/creator.ts b/packages/aiCore/src/core/providers/creator.ts deleted file mode 100644 index d64f4e8e31..0000000000 --- a/packages/aiCore/src/core/providers/creator.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Provider Creator - * 负责根据 ProviderConfig 创建 provider 实例 - */ - -import { type ProviderConfig } from './types' - -// 错误类型 -export class ProviderCreationError extends Error { - constructor( - message: string, - public providerId?: string, - public cause?: Error - ) { - super(message) - this.name = 'ProviderCreationError' - } -} - -/** - * 创建 Provider 实例 - * 支持两种模式:直接提供 creator 函数,或动态导入 + 函数名 - */ -export async function createProvider(config: ProviderConfig, options: any): Promise { - try { - // 验证配置 - if (!validateProviderConfig(config)) { - throw new ProviderCreationError( - 'Invalid provider configuration: must provide either creator function or import configuration', - config.id - ) - } - - config.validateOptions?.(options) - - // 方式一:直接使用 creator 函数 - if (config.creator) { - return config.creator(options) - } - - // 方式二:动态导入 + 函数名 - if (config.import && config.creatorFunctionName) { - const module = (await config.import()) as Record - const creatorFunction = module[config.creatorFunctionName] - - if (typeof creatorFunction !== 'function') { - throw new ProviderCreationError( - `Creator function "${config.creatorFunctionName}" not found in the imported module`, - config.id - ) - } - - return creatorFunction(options) - } - - throw new ProviderCreationError('Unexpected provider configuration state', config.id) - } catch (error) { - if (error instanceof ProviderCreationError) { - throw error - } - throw new ProviderCreationError( - `Failed to create provider "${config.id}": ${error instanceof Error ? error.message : 'Unknown error'}`, - config.id, - error instanceof Error ? error : undefined - ) - } -} - -/** - * 创建图像生成 Provider 实例 - */ -export async function createImageProvider(config: ProviderConfig, options: any): Promise { - try { - if (!config.supportsImageGeneration) { - throw new ProviderCreationError(`Provider "${config.id}" does not support image generation`, config.id) - } - - // 如果有专门的图像 creator - if (config.imageCreator) { - return config.imageCreator(options) - } - - // 否则使用普通的 provider 创建流程 - return await createProvider(config, options) - } catch (error) { - if (error instanceof ProviderCreationError) { - throw error - } - throw new ProviderCreationError( - `Failed to create image provider "${config.id}": ${error instanceof Error ? error.message : 'Unknown error'}`, - config.id, - error instanceof Error ? error : undefined - ) - } -} - -/** - * 验证 Provider 配置 - */ -export function validateProviderConfig(config: ProviderConfig): boolean { - if (!config.id || !config.name) { - return false - } - - if (!config.creator && !(config.import && config.creatorFunctionName)) { - return false - } - - return true -} diff --git a/packages/aiCore/src/core/providers/index.ts b/packages/aiCore/src/core/providers/index.ts index 59c3b1ce89..779f73e46c 100644 --- a/packages/aiCore/src/core/providers/index.ts +++ b/packages/aiCore/src/core/providers/index.ts @@ -2,21 +2,45 @@ * Providers 模块统一导出 - 现代化架构 */ -// ==================== 新版动态Registry(推荐使用)==================== +// ==================== 新版架构(推荐使用)==================== -// 基于AI SDK原生的动态Provider注册表 -export { DynamicProviderRegistry, globalDynamicRegistry } from './DynamicProviderRegistry' +// Provider 注册表管理器 +export { globalRegistryManagement, RegistryManagement } from './RegistryManagement' + +// Provider 初始化器(核心功能) +export { + clearAllProviders, + getImageModel, + getInitializedProviders, + getLanguageModel, + getProviderInfo, + getSupportedProviders, + getTextEmbeddingModel, + hasInitializedProviders, + // initializeImageProvider, // deprecated: 使用 initializeProvider 即可 + initializeProvider, + initializeProviders, + isProviderInitialized, + isProviderSupported, + ProviderInitializationError, + ProviderInitializer, + providerRegistry, + reinitializeProvider +} from './registry' // ==================== 保留的导出(兼容性)==================== // 基础Provider数据源 export { baseProviderIds, baseProviders } from './schemas' +// Hub Provider 功能 +export { createHubProvider, type HubProviderConfig, HubProviderError } from './HubProvider' + // 类型定义(可能被其他模块使用) export type { ProviderConfig, ProviderId, ProviderSettingsMap } from './types' -// Provider创建功能(可能被其他地方使用) -export { createImageProvider, createProvider, ProviderCreationError, validateProviderConfig } from './creator' +// Provider验证功能(使用更好的Zod版本) +export { validateProviderConfig } from './schemas' // 验证功能(可能被其他地方使用) export { validateProviderId } from './schemas' diff --git a/packages/aiCore/src/core/providers/registry.ts b/packages/aiCore/src/core/providers/registry.ts index 5e512e2a8e..1ddf360533 100644 --- a/packages/aiCore/src/core/providers/registry.ts +++ b/packages/aiCore/src/core/providers/registry.ts @@ -1,211 +1,232 @@ /** - * AI Provider 注册表 - * - 使用 schemas 提供的验证函数 - * - 专注于状态管理和业务逻辑 - * - 数据驱动的 Provider 初始化 + * Provider 初始化器 + * 负责根据配置创建 providers 并注册到全局管理器 + * 集成了来自 ModelCreator 的特殊处理逻辑 */ -import { - baseProviders, - type DynamicProviderRegistration, - type ProviderConfig, - type ProviderId, - validateDynamicProviderRegistration, - validateProviderId -} from './schemas' +import { customProvider } from 'ai' -export class AiProviderRegistry { - private static instance: AiProviderRegistry - private registry = new Map() - // 动态注册扩展 - private dynamicMappings = new Map() - private dynamicProviders = new Set() +import { isOpenAIChatCompletionOnlyModel } from '../../utils/model' +import { globalRegistryManagement } from './RegistryManagement' +import { baseProviders } from './schemas' - private constructor() { - this.initializeProviders() - } - - public static getInstance(): AiProviderRegistry { - if (!AiProviderRegistry.instance) { - AiProviderRegistry.instance = new AiProviderRegistry() - } - return AiProviderRegistry.instance - } - - /** - * 初始化所有支持的 Providers - * 使用 schemas 中的 baseProviders 数据驱动 - */ - private initializeProviders(): void { - baseProviders.forEach((config) => { - this.registry.set(config.id, config as ProviderConfig) - }) - } - - /** - * 获取所有已注册的 Providers - */ - public getAllProviders(): ProviderConfig[] { - return Array.from(this.registry.values()) - } - - /** - * 根据 ID 获取 Provider 配置 - */ - public getProvider(id: string): ProviderConfig | undefined { - return this.registry.get(id) || this.registry.get('openai-compatible') - } - - /** - * 检查 Provider 是否支持(是否已注册) - */ - public isSupported(id: string): boolean { - // 首先检查是否在注册表中 - if (this.registry.has(id)) { - return true - } - - // 然后检查是否是有效的 provider ID(可能是新的动态 provider) - return validateProviderId(id) - } - - /** - * 注册新的 Provider(用于扩展) - */ - public registerProvider(config: ProviderConfig): void { - // 使用 schemas 的验证函数 - if (!validateProviderId(config.id)) { - throw new Error(`Invalid provider ID: ${config.id}`) - } - - // 验证:不能同时提供两种方式 - if (config.creator && config.import) { - console.warn('Both creator and import provided, creator will take precedence') - } - - this.registry.set(config.id, config) - } - - /** - * 动态注册Provider并支持映射关系 - */ - public registerDynamicProvider(config: DynamicProviderRegistration): boolean { - try { - // 使用 schemas 的验证函数 - const validatedConfig = validateDynamicProviderRegistration(config) - if (!validatedConfig) { - console.error('Invalid dynamic provider configuration') - return false - } - - // 注册provider - this.registerProvider(validatedConfig) - - // 记录为动态provider - this.dynamicProviders.add(validatedConfig.id) - - // 添加映射关系(如果提供) - if (validatedConfig.mappings) { - Object.entries(validatedConfig.mappings).forEach(([key, value]) => { - this.dynamicMappings.set(key, value) - }) - } - - return true - } catch (error) { - console.error(`Failed to register provider ${config.id}:`, error) - return false - } - } - - /** - * 批量注册多个动态Providers - */ - public registerMultipleProviders(configs: DynamicProviderRegistration[]): number { - let successCount = 0 - configs.forEach((config) => { - if (this.registerDynamicProvider(config)) { - successCount++ - } - }) - return successCount - } - - /** - * 获取Provider映射(包括动态映射) - */ - public getProviderMapping(providerId: string): string | undefined { - return this.dynamicMappings.get(providerId) || (this.dynamicProviders.has(providerId) ? providerId : undefined) - } - - /** - * 检查是否为动态注册的Provider - */ - public isDynamicProvider(providerId: string): boolean { - return this.dynamicProviders.has(providerId) - } - - /** - * 获取所有动态Provider映射 - */ - public getAllDynamicMappings(): Record { - return Object.fromEntries(this.dynamicMappings) - } - - /** - * 获取所有动态注册的Providers - */ - public getDynamicProviders(): string[] { - return Array.from(this.dynamicProviders) - } - - /** - * 获取所有有效的 Provider IDs(包括基础和动态) - */ - public getAllValidProviderIds(): string[] { - return [...Array.from(this.registry.keys()), ...this.dynamicProviders] - } - - /** - * 验证 Provider ID 是否有效 - */ - public validateProviderId(id: string): boolean { - return validateProviderId(id) - } - - /** - * 清理资源 - 接管所有状态管理 - */ - public cleanup(): void { - this.registry.clear() - this.dynamicProviders.clear() - this.dynamicMappings.clear() - // 重新初始化基础 providers - this.initializeProviders() +/** + * Provider 初始化错误类型 + */ +class ProviderInitializationError extends Error { + constructor( + message: string, + public providerId?: string, + public cause?: Error + ) { + super(message) + this.name = 'ProviderInitializationError' } } -// 导出单例实例 -export const aiProviderRegistry = AiProviderRegistry.getInstance() +/** + * Provider 初始化器类 + */ +export class ProviderInitializer { + /** + * 初始化单个 provider 并注册 + */ + static initializeProvider(providerId: string, options: any): void { + try { + // 1. 从 schemas 获取 provider 配置 + const providerConfig = baseProviders.find((p) => p.id === providerId) + if (!providerConfig) { + throw new ProviderInitializationError(`Provider configuration for '${providerId}' not found`, providerId) + } -// 便捷函数 -export const getProvider = (id: string) => aiProviderRegistry.getProvider(id) -export const getAllProviders = () => aiProviderRegistry.getAllProviders() -export const isProviderSupported = (id: string) => aiProviderRegistry.isSupported(id) -export const registerProvider = (config: ProviderConfig) => aiProviderRegistry.registerProvider(config) -export const validateProviderIdRegistry = (id: string) => aiProviderRegistry.validateProviderId(id) -export const getAllValidProviderIds = () => aiProviderRegistry.getAllValidProviderIds() + // 2. 使用 creator 函数创建已配置的 provider + const configuredProvider = providerConfig.creator(options) -// 动态注册相关便捷函数 -export const registerDynamicProvider = (config: DynamicProviderRegistration) => - aiProviderRegistry.registerDynamicProvider(config) -export const registerMultipleProviders = (configs: DynamicProviderRegistration[]) => - aiProviderRegistry.registerMultipleProviders(configs) -export const getProviderMapping = (providerId: string) => aiProviderRegistry.getProviderMapping(providerId) -export const isDynamicProvider = (providerId: string) => aiProviderRegistry.isDynamicProvider(providerId) -export const getAllDynamicMappings = () => aiProviderRegistry.getAllDynamicMappings() -export const getDynamicProviders = () => aiProviderRegistry.getDynamicProviders() -export const cleanup = () => aiProviderRegistry.cleanup() + // 3. 处理特殊逻辑并注册到全局管理器 + this.handleProviderSpecificLogic(configuredProvider, providerId) + } catch (error) { + if (error instanceof ProviderInitializationError) { + throw error + } + throw new ProviderInitializationError( + `Failed to initialize provider ${providerId}: ${error instanceof Error ? error.message : 'Unknown error'}`, + providerId, + error instanceof Error ? error : undefined + ) + } + } -// 导出类型 -export type { DynamicProviderRegistration, ProviderConfig, ProviderId } + /** + * 批量初始化 providers + */ + static initializeProviders(providers: Record): void { + Object.entries(providers).forEach(([providerId, options]) => { + try { + this.initializeProvider(providerId, options) + } catch (error) { + console.error(`Failed to initialize provider ${providerId}:`, error) + } + }) + } + + /** + * 处理特定 provider 的特殊逻辑 (从 ModelCreator 迁移并改进) + */ + private static handleProviderSpecificLogic(provider: any, providerId: string): void { + if (providerId === 'openai') { + // 🎯 OpenAI 默认注册 (responses 模式) + globalRegistryManagement.registerProvider('openai', provider) + + // 🎯 使用 AI SDK 官方的 customProvider 创建 chat 模式变体 + const openaiChatProvider = customProvider({ + fallbackProvider: { + ...provider, + // 覆盖 languageModel 方法指向 chat + languageModel: (modelId: string) => provider.chat(modelId) + } + }) + + globalRegistryManagement.registerProvider('openai-chat', openaiChatProvider) + } else { + // 其他 provider 直接注册 + globalRegistryManagement.registerProvider(providerId, provider) + } + } + + /** + * 初始化图像生成 provider (从 ModelCreator 迁移) + * + * @deprecated 不再需要单独的图像provider初始化,使用 initializeProvider() 即可 + * 一个provider实例可以同时支持文本和图像功能,无需分别初始化 + * + * TODO: 考虑在下个版本中删除此方法 + */ + // static initializeImageProvider(providerId: string, options: any): void { + // try { + // const providerConfig = baseProviders.find((p) => p.id === providerId) + // if (!providerConfig) { + // throw new ProviderInitializationError(`Provider configuration for '${providerId}' not found`, providerId) + // } + + // if (!providerConfig.supportsImageGeneration) { + // throw new ProviderInitializationError(`Provider "${providerId}" does not support image generation`, providerId) + // } + + // const provider = providerConfig.creator(options) + + // // 注册图像 provider (使用特殊前缀区分) + // globalRegistryManagement.registerProvider(`${providerId}-image`, provider as any) + // } catch (error) { + // if (error instanceof ProviderInitializationError) { + // throw error + // } + // throw new ProviderInitializationError( + // `Failed to initialize image provider ${providerId}: ${error instanceof Error ? error.message : 'Unknown error'}`, + // providerId, + // error instanceof Error ? error : undefined + // ) + // } + // } + + /** + * 检查 provider 是否已初始化 + */ + static isProviderInitialized(providerId: string): boolean { + return globalRegistryManagement.getRegisteredProviders().includes(providerId) + } + + /** + * 重新初始化 provider(更新配置) + */ + static reinitializeProvider(providerId: string, options: any): void { + this.initializeProvider(providerId, options) // 会覆盖已有的 + } + + /** + * 清除所有已初始化的 providers + */ + static clearAllProviders(): void { + globalRegistryManagement.clear() + } +} + +// ==================== 便捷函数导出 ==================== + +export const initializeProvider = ProviderInitializer.initializeProvider.bind(ProviderInitializer) +export const initializeProviders = ProviderInitializer.initializeProviders.bind(ProviderInitializer) +// export const initializeImageProvider = ProviderInitializer.initializeImageProvider.bind(ProviderInitializer) // deprecated: 使用 initializeProvider 即可 +export const isProviderInitialized = ProviderInitializer.isProviderInitialized.bind(ProviderInitializer) +export const reinitializeProvider = ProviderInitializer.reinitializeProvider.bind(ProviderInitializer) +export const clearAllProviders = ProviderInitializer.clearAllProviders.bind(ProviderInitializer) + +// ==================== 全局管理器导出 ==================== + +export { globalRegistryManagement as providerRegistry } + +// ==================== 便捷访问方法 ==================== + +export const getLanguageModel = (id: string) => globalRegistryManagement.languageModel(id as any) +export const getTextEmbeddingModel = (id: string) => globalRegistryManagement.textEmbeddingModel(id as any) +export const getImageModel = (id: string) => globalRegistryManagement.imageModel(id as any) + +// ==================== 工具函数 (从 ModelCreator 迁移) ==================== + +/** + * 获取支持的 Providers 列表 (从 ModelCreator 迁移) + */ +export function getSupportedProviders(): Array<{ + id: string + name: string +}> { + return baseProviders.map((provider) => ({ + id: provider.id, + name: provider.name + })) +} + +/** + * 检查 Provider 是否被支持 + */ +export function isProviderSupported(providerId: string): boolean { + return getProviderInfo(providerId).isSupported +} + +/** + * 获取 Provider 信息 (从 ModelCreator 迁移并改进) + */ +export function getProviderInfo(providerId: string): { + id: string + name: string + isSupported: boolean + isInitialized: boolean + effectiveProvider: string +} { + const provider = baseProviders.find((p) => p.id === providerId) + const isInitialized = globalRegistryManagement.getRegisteredProviders().includes(providerId) + + return { + id: providerId, + name: provider?.name || providerId, + isSupported: !!provider, + isInitialized, + effectiveProvider: isInitialized ? providerId : 'openai-compatible' + } +} + +/** + * 获取所有已初始化的 providers + */ +export function getInitializedProviders(): string[] { + return globalRegistryManagement.getRegisteredProviders() +} + +/** + * 检查是否有任何已初始化的 providers + */ +export function hasInitializedProviders(): boolean { + return globalRegistryManagement.hasProviders() +} + +// ==================== 导出错误类型和工具函数 ==================== + +export { isOpenAIChatCompletionOnlyModel, ProviderInitializationError } diff --git a/packages/aiCore/src/core/runtime/executor.ts b/packages/aiCore/src/core/runtime/executor.ts index 0ee79e1b4f..dfe0a800f2 100644 --- a/packages/aiCore/src/core/runtime/executor.ts +++ b/packages/aiCore/src/core/runtime/executor.ts @@ -14,9 +14,9 @@ import { import { type ProviderId } from '../../types' import { globalModelResolver } from '../models' -import { getProviderInfo } from '../models/ModelCreator' import { type ModelConfig } from '../models/types' import { type AiPlugin, type AiRequestContext, definePlugin } from '../plugins' +import { getProviderInfo } from '../providers/registry' import { ImageGenerationError, ImageModelResolutionError } from './errors' import { PluginEngine } from './pluginEngine' import { type RuntimeConfig } from './types' @@ -43,10 +43,9 @@ export class RuntimeExecutor { name: '_internal_resolveModel', enforce: 'post', - resolveModel: async (modelId: string, context: AiRequestContext) => { - // 从 context 中读取由用户插件注入的 extraModelConfig - const extraModelConfig = context.extraModelConfig || {} - return await this.resolveModel(modelId, middlewares, extraModelConfig) + resolveModel: async (modelId: string) => { + // 注意:extraModelConfig 暂时不支持,已在新架构中移除 + return await this.resolveModel(modelId, middlewares) } }) } @@ -272,15 +271,15 @@ export class RuntimeExecutor { */ private async resolveModel( modelOrId: LanguageModel, - middlewares?: LanguageModelV2Middleware[], - extraModelConfig?: Record + middlewares?: LanguageModelV2Middleware[] ): Promise { if (typeof modelOrId === 'string') { - // 字符串modelId,使用新的ModelResolver解析 + // 🎯 字符串modelId,使用新的ModelResolver解析,传递完整参数 return await globalModelResolver.resolveLanguageModel( - modelOrId, // 支持 'gpt-4' 和 'aihubmix>anthropic>claude-3' + modelOrId, // 支持 'gpt-4' 和 'aihubmix:anthropic:claude-3.5-sonnet' this.config.providerId, // fallback provider - this.config.providerSettings // provider options + this.config.providerSettings, // provider options + middlewares // 中间件数组 ) } else { // 已经是模型,直接返回 @@ -296,9 +295,8 @@ export class RuntimeExecutor { if (typeof modelOrId === 'string') { // 字符串modelId,使用新的ModelResolver解析 return await globalModelResolver.resolveImageModel( - modelOrId, // 支持 'dall-e-3' 和 'aihubmix>openai>dall-e-3' - this.config.providerId, // fallback provider - this.config.providerSettings // provider options + modelOrId, // 支持 'dall-e-3' 和 'aihubmix:openai:dall-e-3' + this.config.providerId // fallback provider ) } else { // 已经是模型,直接返回 diff --git a/packages/aiCore/src/index.ts b/packages/aiCore/src/index.ts index b2f42a7ab5..297af35fe2 100644 --- a/packages/aiCore/src/index.ts +++ b/packages/aiCore/src/index.ts @@ -4,11 +4,7 @@ */ // 导入内部使用的类和函数 -import { - getProviderInfo as factoryGetProviderInfo, - getSupportedProviders as factoryGetSupportedProviders -} from './core/models' -import { aiProviderRegistry, isProviderSupported } from './core/providers/registry' +import { getSupportedProviders, isProviderSupported } from './core/providers/registry' import type { ProviderId } from './core/providers/types' import type { ProviderSettingsMap } from './core/providers/types' import { createExecutor } from './core/runtime' @@ -24,7 +20,7 @@ export { } from './core/runtime' // ==================== 高级API ==================== -export { createModel } from './core/models' +export { globalModelResolver as modelResolver } from './core/models' // ==================== 插件系统 ==================== export type { AiPlugin, AiRequestContext, HookResult, PluginManagerConfig } from './core/plugins' @@ -33,14 +29,7 @@ export { createContext, definePlugin, PluginManager } from './core/plugins' export { PluginEngine } from './core/runtime/pluginEngine' // ==================== 低级 API ==================== -export { - createBaseModel as createApiClient, - createImageModel, - getProviderInfo as getClientInfo, - getSupportedProviders, - ModelCreationError -} from './core/models' -export { aiProviderRegistry } from './core/providers/registry' +export { providerRegistry } from './core/providers/registry' // ==================== 类型定义 ==================== export type { ProviderConfig } from './core/providers/types' @@ -123,33 +112,32 @@ export { type TypedProviderOptions } from './core/options' -// ==================== 工具函数 ==================== +// ==================== Provider 初始化和管理 ==================== export { - getAllDynamicMappings, - getAllProviders, - getAllValidProviderIds, - getDynamicProviders, - getProvider, - getProviderMapping, - isDynamicProvider, + clearAllProviders, + getImageModel, + getInitializedProviders, + // 访问功能 + getLanguageModel, + getProviderInfo, + getTextEmbeddingModel, + hasInitializedProviders, + // initializeImageProvider, // deprecated: 使用 initializeProvider 即可 + // 初始化功能 + initializeProvider, + initializeProviders, + isProviderInitialized, isProviderSupported, - // 动态注册功能 - registerDynamicProvider, - registerMultipleProviders, - registerProvider, - // Zod 验证相关 - validateProviderIdRegistry + // 错误类型 + ProviderInitializationError, + reinitializeProvider } from './core/providers/registry' // ==================== Zod Schema 和验证 ==================== -export { - type BaseProviderId, - baseProviderIds, - type DynamicProviderId, - type DynamicProviderRegistration, - validateDynamicProviderRegistration, - validateProviderId -} from './core/providers' +export { baseProviderIds, validateProviderId } from './core/providers' + +// ==================== Hub Provider ==================== +export { createHubProvider, type HubProviderConfig, HubProviderError } from './core/providers/HubProvider' // ==================== Provider 配置工厂 ==================== export { @@ -177,17 +165,11 @@ export const AiCore = { // 获取支持的providers getSupportedProviders() { - return factoryGetSupportedProviders() + return getSupportedProviders() }, - // 检查provider支持 - isSupported(providerId: string) { + isSupported(providerId: ProviderId) { return isProviderSupported(providerId) - }, - - // 获取客户端信息 - getClientInfo(providerId: string) { - return factoryGetProviderInfo(providerId) } } @@ -210,42 +192,18 @@ export const createXAIExecutor = (options: ProviderSettingsMap['xai'], plugins?: // ==================== 调试和开发工具 ==================== export const DevTools = { - // 列出所有注册的providers + // 列出所有支持的providers listProviders() { - return aiProviderRegistry.getAllProviders().map((p) => ({ - id: p.id, - name: p.name - })) - }, - - // 测试provider连接 - async testProvider(providerId: ProviderId, options: ProviderSettingsMap[ProviderId]) { - try { - const executor = createExecutor(providerId, options) - const info = executor.getClientInfo() - return { - success: true, - providerId: info.id, - name: info.name, - isSupported: info.isSupported - } - } catch (error) { - return { - success: false, - providerId, - error: error instanceof Error ? error.message : 'Unknown error' - } - } + return getSupportedProviders() }, // 获取provider详细信息 getProviderDetails() { - const providers = aiProviderRegistry.getAllProviders() + const supportedProviders = getSupportedProviders() return { - supportedProviders: providers.length, - registeredProviders: providers.length, - providers: providers.map((p) => ({ + supportedProviders: supportedProviders.length, + providers: supportedProviders.map((p) => ({ id: p.id, name: p.name })) diff --git a/src/renderer/src/aiCore/index_new.ts b/src/renderer/src/aiCore/index_new.ts index 1758d5d201..35589f9d7f 100644 --- a/src/renderer/src/aiCore/index_new.ts +++ b/src/renderer/src/aiCore/index_new.ts @@ -8,7 +8,7 @@ * 3. 暂时保持接口兼容性 */ -import { createExecutor, generateImage, StreamTextParams } from '@cherrystudio/ai-core' +import { createExecutor, generateImage, initializeProvider, StreamTextParams } from '@cherrystudio/ai-core' import { loggerService } from '@logger' import { isNotSupportedImageSizeModel } from '@renderer/config/models' import { addSpan, endSpan } from '@renderer/services/SpanManagerService' @@ -36,6 +36,21 @@ export default class ModernAiProvider { // 只保存配置,不预先创建executor this.config = providerToAiSdkConfig(this.actualProvider) + + // 初始化 provider 到全局管理器 + try { + initializeProvider(this.config.providerId, this.config.options) + logger.debug('Provider initialized successfully', { + providerId: this.config.providerId, + hasOptions: !!this.config.options + }) + } catch (error) { + // 如果 provider 已经初始化过,可能会抛出错误,这里可以忽略 + logger.debug('Provider initialization skipped (may already be initialized)', { + providerId: this.config.providerId, + error: error instanceof Error ? error.message : String(error) + }) + } } public getActualProvider() { diff --git a/src/renderer/src/aiCore/provider/factory.ts b/src/renderer/src/aiCore/provider/factory.ts index bf44a8b02c..88255b112e 100644 --- a/src/renderer/src/aiCore/provider/factory.ts +++ b/src/renderer/src/aiCore/provider/factory.ts @@ -1,10 +1,9 @@ -import { AiCore, getProviderMapping, type ProviderId } from '@cherrystudio/ai-core' +import { AiCore, type ProviderId } from '@cherrystudio/ai-core' import { Provider } from '@renderer/types' -import { initializeNewProviders } from './providerConfigs' - +// TODO // 初始化新的Provider注册系统 -initializeNewProviders() +// initializeNewProviders() // 静态Provider映射 - 核心providers const STATIC_PROVIDER_MAPPING: Record = { @@ -21,24 +20,24 @@ export function getAiSdkProviderId(provider: Provider): ProviderId | 'openai-com if (staticProviderId) { return staticProviderId } - + // TODO // 2. 检查动态注册的provider映射(使用aiCore的函数) - const dynamicProviderId = getProviderMapping(provider.id) - if (dynamicProviderId) { - return dynamicProviderId as ProviderId - } + // const dynamicProviderId = getProviderMapping(provider.id) + // if (dynamicProviderId) { + // return dynamicProviderId as ProviderId + // } // 3. 检查provider.type的静态映射 const staticProviderType = STATIC_PROVIDER_MAPPING[provider.type] if (staticProviderType) { return staticProviderType } - + // TODO // 4. 检查provider.type的动态映射 - const dynamicProviderType = getProviderMapping(provider.type) - if (dynamicProviderType) { - return dynamicProviderType as ProviderId - } + // const dynamicProviderType = getProviderMapping(provider.type) + // if (dynamicProviderType) { + // return dynamicProviderType as ProviderId + // } // 5. 检查AiCore是否直接支持 if (AiCore.isSupported(provider.id)) { diff --git a/src/renderer/src/aiCore/provider/providerConfigs.ts b/src/renderer/src/aiCore/provider/providerConfigs.ts index 306f86ea43..efb37d5685 100644 --- a/src/renderer/src/aiCore/provider/providerConfigs.ts +++ b/src/renderer/src/aiCore/provider/providerConfigs.ts @@ -1,4 +1,4 @@ -import { type ProviderConfig, registerMultipleProviders } from '@cherrystudio/ai-core' +import { type ProviderConfig } from '@cherrystudio/ai-core' import { loggerService } from '@logger' const logger = loggerService.withContext('ProviderConfigs') @@ -43,18 +43,19 @@ export const NEW_PROVIDER_CONFIGS: (ProviderConfig & { } ] as const -/** - * 初始化新的Providers - * 使用aiCore的动态注册功能 - */ -export async function initializeNewProviders(): Promise { - try { - const successCount = registerMultipleProviders(NEW_PROVIDER_CONFIGS) +// TODO +// /** +// * 初始化新的Providers +// * 使用aiCore的动态注册功能 +// */ +// export async function initializeNewProviders(): Promise { +// try { +// const successCount = registerMultipleProviders(NEW_PROVIDER_CONFIGS) - if (successCount < NEW_PROVIDER_CONFIGS.length) { - logger.warn('Some providers failed to register. Check previous error logs.') - } - } catch (error) { - logger.error('Failed to initialize new providers:', error as Error) - } -} +// if (successCount < NEW_PROVIDER_CONFIGS.length) { +// logger.warn('Some providers failed to register. Check previous error logs.') +// } +// } catch (error) { +// logger.error('Failed to initialize new providers:', error as Error) +// } +// }