Cherry Studio AI Core 2.0 演进解析:AI SDK v6 迁移与 Provider 插件化架构重构
【免费下载链接】cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio
本篇技术指南以 Cherry Studio 仓库内 packages/aiCore/CHANGELOG.md 为主线,结合 packages/aiCore 包源码,完整梳理@cherrystudio/ai-core从 2.0.0 到 2.0.1 的架构演进:包括 AI SDK v6 迁移带来的破坏性变更、Provider Extension / Variant 类型系统重构、toolFactories 工具工厂机制,以及插件化运行时的生命周期模型。读完本文,你将理解该包"模型层 → 运行时层"的设计脉络,掌握ProviderVariant<TSettings, TProvider, TOutput>泛型设计与azure-anthropic变体修复的底层原理,并能在自己的项目中正确安装、注册与调用该统一 AI Provider 接口。
版本演进总览:一次破坏性重构与一次精准修复
@cherrystudio/ai-core(即packages/aiCore)是 Cherry Studio 基于 Vercel AI SDK 构建的统一 AI Provider 接口包,其 package.json 声明的主版本演进记录了两个关键节点:
- 2.0.0(Major Changes):由 PR #12235 引入,完成向 AI SDK v6 的整体迁移,对 Provider 与中间件架构做了"完整重写"(complete rewrite),同时包含若干破坏性变更;
- 2.0.1(Patch Changes):由 PR #14087 引入,集中修复
azure-anthropic变体的工具工厂与类型系统问题,是理解 Variant 泛型设计的最佳案例。
除版本自身内容外,2.0.0 还包含两项 Patch:PR #13787 补充缺失的@openrouter/ai-sdk-provider依赖以修复包构建,PR #12783 作为基线发布(Baseline release),将此前未纳管的历史变更收编,并正式引入基于 changesets 的发布流程。从 changelog 可见,2.0.0 同时将@cherrystudio/ai-sdk-provider依赖更新至 0.1.6,二者属于同仓库 workspace 联动演进(见 pnpm-workspace.yaml)。
2.0.0 核心:AI SDK v6 迁移的五大变化
1. 移除遗留 API 客户端与中间件管线(破坏性变更)
2.0.0 明确标注BREAKING:移除所有遗留 API clients、middleware pipeline 以及 barrel 形式的index.ts。这意味着旧版中自维护的 HTTP 客户端封装、自建的中间件链均被废弃,取而代之的是直接复用 AI SDK v6 的能力。
从当前源码可以印证这一转向:包的主入口 packages/aiCore/src/index.ts 已不再是一份厚重的 barrel 文件,而是按模块分区(runtime / plugins / providers / context / errors)做轻量再导出;核心执行器 RuntimeExecutor 直接调用 AI SDK 的streamText、generateText、generateImage、embedMany、rerank等顶层函数,不再存在自定义的网络层。
2. 图像生成:迁移到原生 generateImage / editImage
changelog 指出,图像生成从"遗留 image middleware"迁移为 AI SDK 原生的generateImage/editImage。源码中 RuntimeExecutor.generateImage 正是这一迁移的落点:它支持字符串模型 ID 或模型对象两种入参,通过pluginEngine.executeImageWithPlugins执行,并借助wrapImageModel的 v3 middleware 实现了onProviderCall观测回调(记录 requestId、providerId、modelId、imageCount、usage 与耗时指标),观测逻辑通过 best-effort 方式保证不干扰正常 AI 结果(见 emitProviderCall)。
3. Embedding:迁移到 embedMany
changelog 记录 Embedding 从遗留客户端迁移到 AI SDK 的embedMany(并移除了遗留 embedding clients)。这与 AI SDK v6 的能力边界一致——源码注释明确写着"AI SDK v6 只有 embedMany,没有 embed"(见 packages/aiCore/src/core/runtime/index.ts)。RuntimeExecutor.embedMany 对模型 ID 使用registry.embeddingModel('${providerId}:${modelId}')解析,同样支持onProviderCall观测;构造函数中还针对部分 v3 provider(如@openrouter/ai-sdk-provider)只暴露textEmbeddingModel而非embeddingModel的情况做了兼容补丁。
4. 模型列表:ModelListService 重构为 Strategy Registry 模式
2.0.0 将ModelListService重构为Strategy Registry 模式,并"consolidate schema files"(合并 schema 文件)。这是架构层面的收敛:把不同 provider 的模型列取策略统一注册到注册表中,避免服务类中的分支膨胀。这一思路与包内 Extension Registry 一脉相承——ExtensionRegistry 是全局单例extensionRegistry,负责 provider 创建与模型解析器的集中管理。
5. 命名收敛与 OpenRouter / GitHub Copilot 适配
- 重命名:
index_new.ts→AiProvider.ts,ModelListService.ts→listModels.ts,消除了过渡期命名; - OpenRouter 图像:通过
@openrouter/ai-sdk-provider2.3.3 提供原生图像端点支持(generateImage/editImage),包依赖同步升级至^2.10.0(见 package.json); - GitHub Copilot:通过移除
ProviderV2cast 与wrapProvider简化扩展,全面拥抱 V3 模型协议——resolveModel 会强制校验模型必须是 V3,否则抛出 "Model must be V3" 错误。
2.0.1 修复深度解读:ProviderVariant 的 TOutput 泛型与工具工厂
2.0.1 的修复集中在 Provider Variant(变体)系统,changelog 将其概括为三个要点:
- 为
ProviderVariant增加TOutput泛型,使transform的输出类型能够流向toolFactories与resolveModel; - 为
azure-anthropic变体补充 Anthropic 专属的toolFactories,修复provider.tools.webSearchPreview is not a function报错; - 修复
urlContextfactory 被错误映射到webSearch工具键的问题,并修正BedrockExtension的satisfies类型。
TOutput 泛型的设计意图
在 packages/aiCore/src/core/providers/types/index.ts 中,ProviderVariant声明为:
export interface ProviderVariant< TSettings = any, TProvider extends ProviderV3 = ProviderV3, TOutput extends ProviderV3 = TProvider > { suffix: string name: string /** 类型安全的模型解析:provider.responses(modelId) / provider.chat(modelId) */ resolveModel?: (provider: TOutput, modelId: string) => LanguageModel /** 替换整个 provider(如 azure-anthropic),简单方法切换用 resolveModel */ transform?: (baseProvider: TProvider, settings?: TSettings) => TOutput | Promise<TOutput> toolFactories?: ToolFactoryMap<TOutput> }关键点在于:当transform返回的 provider 类型与输入不同(即TOutput不等于TProvider)时,toolFactories与resolveModel必须基于TOutput而非 TProvider 做类型推导。这正是azure-anthropic场景——Azure 变体通过createAnthropic整体重建 provider,输出是AnthropicProvider而不是AzureOpenAIProvider,因此其webSearch、urlContext工厂必须接收 Anthropic 的 provider 实例。
azure-anthropic 变体的完整实现
见 packages/aiCore/src/core/providers/core/initialization.ts:
{ suffix: 'anthropic', name: 'Azure Anthropic', transform: async (_provider, settings) => (await import('@ai-sdk/anthropic')).createAnthropic({ baseURL: (settings?.baseURL ?? '') + '/anthropic/v1', apiKey: settings?.apiKey ?? '', headers: settings?.headers, // 转发调用方注入的 fetch(如代理感知的 customFetch), // 避免变体重建 provider 后请求静默回退到 SDK 默认 fetch fetch: settings?.fetch }), toolFactories: { webSearch: (provider) => (config: NonNullable<Parameters<AnthropicProvider['tools']['webSearch_20260209']>[0]>) => ({ tools: { webSearch: provider.tools.webSearch_20260209(config) } }), urlContext: (provider) => (config: NonNullable<Parameters<AnthropicProvider['tools']['webFetch_20260209']>[0]>) => ({ tools: { urlContext: provider.tools.webFetch_20260209(config) } }) } } satisfies ProviderVariant<AzureOpenAIProviderSettings, AzureOpenAIProvider, AnthropicProvider>该实现揭示了修复的三个层面:
- 类型层:
satisfies ProviderVariant<AzureOpenAIProviderSettings, AzureOpenAIProvider, AnthropicProvider>显式声明 TOutput 为AnthropicProvider,让toolFactories与resolveModel的参数类型自动收敛到 Anthropic 类型; - 行为层:此前
azure-anthropic变体未提供自己的 toolFactories,ExtensionRegistry.getToolFactory会回退到 base extension 的工厂(见 ExtensionRegistry.ts),而 base Azure 工厂使用AzureOpenAIProvider['tools']['webSearchPreview'],在 Anthropic provider 上调用即触发webSearchPreview is not a function;补充 Anthropic 工厂后,webSearch_20260209与webFetch_20260209得以正确调用; - 映射层:
urlContext工厂此前误映射到webSearch工具键,修复后正确输出tools: { urlContext: ... }。
工具工厂的解析优先级
ExtensionRegistry.resolveTool 体现了工具工厂的查找策略:先看 provider 自身的 toolFactories(variant 级别优先于 base extension),失败后再沿 provider 分段逐级回退(如azure-anthropic→azure→ 基础扩展),最终尝试从 provider 对象上直接取方法。测试用例 ExtensionRegistry.test.ts 覆盖了 variant 级工厂、回退与 undefined 场景。此外,providerToolPlugin.ts 会把工厂返回的ToolFactoryPatch(tools / providerOptions)合并进请求参数,打通"工具工厂 → 插件 → 请求"链路。
插件系统:请求生命周期的四类钩子
2.0.0 重构后的运行时以插件为第一公民。插件接口定义在 packages/aiCore/src/core/plugins/types.ts,按执行语义分为四类:
| 钩子类别 | 钩子名称 | 执行语义 |
|---|---|---|
| First(首个命中) | resolveModel、loadTemplate | 串行遍历,返回第一个非空结果 |
| Sequential(串行链式) | configureContext、transformParams、transformResult | 逐个执行,后者接收前者的输出 |
| Parallel(并行副作用) | onRequestStart、onRequestEnd、onError | Promise.all并发执行,互不依赖 |
| Stream(流处理) | transformStream | 基于 AI SDK 流变换,收集后统一传入experimental_transform |
插件的排序规则为pre → normal → post(由enforce字段控制),实现在 PluginManager.sortPlugins。请求上下文AiRequestContext携带 providerId、model、originalParams、requestId、递归深度控制(默认最大 10 层,防止栈溢出)以及可选的 MCP tools(见 types.ts),并预留recursiveCall供插件内部发起递归调用。
运行时侧,PluginEngine 是插件与 AI SDK 调用的桥梁:RuntimeExecutor.streamText/generateText会依据入参是字符串模型 ID 还是模型对象,决定是否注入_internal_resolveModel插件,最终把插件链产出的模型对象、转换后的参数与流变换一并交给 AI SDK(见 executor.ts)。
从 CHANGELOG 到实践:安装与接入
安装与依赖边界
@cherrystudio/ai-core通过 pnpm workspace 管理,peerDependencies 要求 AI SDK 生态的版本对齐(ai ^6.0.116、@ai-sdk/openai ^3.0.109、@ai-sdk/google ^3.0.113),内部依赖则覆盖 Anthropic、Azure、DeepSeek、OpenAI-Compatible、xAI、OpenRouter 等 provider 包(见 package.json),产物同时导出dist/index.cjs(CommonJS)与dist/index.mjs(ESM),并声明了react-native入口,Node 运行环境要求>=18.0.0。独立的./built-in/plugins与./provider子路径导出允许按需引入插件或 provider 能力。
在 React Native 环境中使用该包时,需要在metro.config.js中补充resolverMainFields = ['react-native', 'browser', 'main']与平台列表,详见 packages/aiCore/README.md。
最小可运行示例
import { createExecutor, streamText } from '@cherrystudio/ai-core' // 函数式:直接流式生成 const result = await streamText( 'openai', { apiKey: 'your-api-key' }, { model: 'gpt-4', messages: [{ role: 'user', content: 'Hello!' }] } ) // 实例式:可复用的执行器(内部自动确保 provider 已初始化) const executor = await createExecutor('anthropic', { apiKey: 'your-key' }) const res = await executor.generateText({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'Hello!' }] })从源码看,createExecutor会先校验extensionRegistry.has(providerId),再调用extensionRegistry.createProvider创建 provider,并从 variant 声明中提取类型安全的模型解析器(见 packages/aiCore/src/core/runtime/index.ts)——这也解释了 changelog 中"TOutput类型流向resolveModel"的工程价值:变体的模型解析行为随类型系统一起被约束。
注册自定义 Provider
对非内置 provider,可通过registerProvider注册(支持直接传入 creator 或动态 import 两种方式),随后即可像内置 provider 一样通过AiCore.create(id, { apiKey })调用。完整的注册示例与插件示例(webSearchPlugin、loggingPlugin、definePlugin 自定义插件)同样见 packages/aiCore/README.md 的「扩展 Provider 注册」与「插件系统」章节。
结语
从 2.0.0 的 AI SDK v6 全量迁移到 2.0.1 的 Variant 泛型修复,@cherrystudio/ai-core的演进主线十分清晰:砍掉自维护的客户端与中间件层,把能力下沉到 AI SDK 原生 API,同时用类型系统(ProviderVariant 的 TOutput 泛型、toolFactories、satisfies 约束)与插件运行时(First / Sequential / Parallel / Stream 四类钩子)重新织起 Cherry Studio 自己的抽象。对想要阅读或复用该包的人,建议按以下顺序深入:先读 packages/aiCore/CHANGELOG.md 把握演进脉络,再看 packages/aiCore/src/core/runtime/executor.ts 理解执行器与插件引擎的协作,最后对照 initialization.ts 与 ExtensionRegistry.ts 研读 Provider Extension 的注册、变体与工具工厂机制。
【免费下载链接】cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考