AI SDK 预发布周期(Pre-Release Cycle)完整指南:从创建维护分支到发布下一个大版本
【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai
导读
本指南基于 contributing/pre-release-cycle.md 展开,完整讲解 AI SDK(The AI Toolkit for TypeScript)如何围绕"大版本升级(Major Release)"组织一次预发布(beta)周期:从创建维护分支、进入 changeset 预发布模式、为新版 Provider 规范播种 v4 规格目录,到适配器与 mock 测试工具、文档站版本化部署,再到周期内的日常开发约定与收尾发布。读完本文,你将掌握在main分支上并行开发下一个大版本、同时为当前稳定版持续回传补丁的完整工程流程,并能对照仓库源码理解每个步骤背后的真实实现。
一、为什么需要预发布周期
AI SDK 的每一个大版本发布都会引入一个新的Provider 规范版本(provider specification version),例如 V3 到 V4。演进规范正是大版本发布存在的理由:它允许对 Provider 接口做出破坏性变更,同时为 Provider 作者提供清晰的迁移目标。
在仓库的 packages/provider/src/ 中,这一点体现得极为直观:以语言模型为例,packages/provider/src/language-model/ 下并排存在着v2、v3、v4三个版本目录,每个目录内的类型都通过specificationVersion字面量标明版本,例如 embedding-model-v3.ts 中readonly specificationVersion: 'v3',而 embedding-model-v4.ts 中则是'v4'。
预发布周期解决的核心问题是双轨并行:
main分支发布 beta 版本(例如ai@7.0.0-beta.1);- 一个维护分支(例如
release-v6.0)接收回传(backport)的补丁并发布稳定版本。
这样既能让破坏性变更在main上持续推进,又不阻塞当前稳定版用户的紧急修复。
二、启动一个预发布周期
1. 创建维护分支
从当前mainHEAD 创建一个分支,让当前稳定版本可以继续接收补丁:
git checkout main git pull origin main git checkout -b release-v<current-major>.0 # e.g. release-v6.0 git push origin release-v<current-major>.0仓库中的发布工作流 已经配置在release-v*分支上运行(branches: [main, release-v*]),因此无需修改任何工作流文件,维护分支上的合并会自动触发稳定版发布。
2. 在维护分支上设置 npm dist-tag
在新建的维护分支上,更新根目录package.json中的ci:release脚本,使其以版本专属的 npm dist-tag 发布。这一步防止维护版本抢占 npm 上的latest标签:
- "ci:release": "turbo clean && turbo build && changeset publish", + "ci:release": "turbo clean && turbo build && changeset publish --tag ai-v<current-major>",例如,对于release-v6.0分支使用--tag ai-v6。直接将该改动提交并推送到维护分支。
仓库现状佐证:当前根 package.json 中的脚本为
"ci:release": "turbo clean && npm run build:packages && changeset publish",并没有--tag后缀——这正是"维护分支上追加版本专属 tag"需要改动的行;同时ci:version脚本(changeset version && node .github/scripts/cleanup-examples-changesets.mjs && pnpm install --no-frozen-lockfile)会在版本 PR 合并时统一执行版本号提升与依赖重装。
3. 在main上进入预发布模式
切回main,进入 changeset 的预发布模式:
git checkout main pnpm changeset pre enter beta该命令会修改.changeset/pre.json。其中initialVersions字段应当只包含来自packages/*/package.json的包;需要移除其他任何条目(例如@example/*、tools/*或嵌套的测试包)。提交并推送该改动(或开一个 PR)。
4. 创建一个大版本 changeset
创建一个将每个已发布包提升到下一个大版本的 changeset:
pnpm changeset选择packages/*/package.json中的所有包(跳过@example/*、tools/*以及任何不在packages/下的条目——它们是私有的、不会被发布),并为每个包选择major。写一个类似这样的摘要:
Start v7 pre-release
提交生成的.changeset/*.md文件。
5. 播种新的规范版本(spec version)
每个大版本都会引入一个新的 Provider 规范版本(例如 V3 → V4)。必须为packages/provider/src/下每一个包含版本化子目录的规范目录创建新版本目录。可以用下面的命令找到它们:
ls -d packages/provider/src/*/v3截至本文写作时,这些目录为:embedding-model、embedding-model-middleware、image-model、image-model-middleware、language-model、language-model-middleware、provider、reranking-model、shared、speech-model、transcription-model、video-model。
仓库现状佐证:在当前仓库执行上述命令,实际返回了 12 个
v3目录,与文档列出的清单完全一致(packages/provider/src/embedding-model/v3、packages/provider/src/language-model/v3、packages/provider/src/provider/v3、packages/provider/src/shared/v3等)。同时language-model目录下已存在v4,印证了 V3→V4 迁移在仓库中已经落地。
对于每个目录:
- 将当前规范目录(例如
v3/)复制为新的版本目录(例如v4/)。 - 将所有文件从旧版本重命名为新版本(例如
language-model-v3.ts→language-model-v4.ts)。 - 在每个文件内部,将所有旧版本字样替换为新版本(例如类型名、import 路径中的
V3→V4、v3→v4,以及specificationVersion字面量)。 - 在父级
index.ts中,于 v3 导出之前添加export * from './v4/index';。 - 更新交叉引用:如果
providerv4 规范导入了其他模型类型,确保它从新的 v4 路径导入(而不是 v3)。
通过在packages/provider中运行pnpm build验证——所有新类型都应出现在构建出的.d.ts输出中。
6. 创建 mock 测试工具
为packages/ai/src/test/中的每一个 mock 文件创建 V4 对应版本(例如mock-language-model-v3.ts→mock-language-model-v4.ts)。更新packages/ai/test/index.ts以导出新的 V4 mocks。
仓库现状佐证:在 packages/ai/src/test/ 中可以看到完整的 V2/V3/V4 mock 矩阵,例如
mock-language-model-v2.ts、mock-language-model-v3.ts、mock-language-model-v4.ts,以及mock-provider-v2.ts、mock-provider-v3.ts、mock-provider-v4.ts等,说明 V3→V4 迁移中"三版本并存、测试各自覆盖"的模式在仓库中已经真实落地。
7. 更新packages/ai以支持新规范版本
核心的packages/ai包需要适配函数、更新的公共 API 以及测试更新,来在旧版本之外同时支持新的规范版本。
适配函数(Adapter Functions)
在packages/ai/src/model/中为每种模型类型创建 V4 适配文件。这些适配器使用Proxy通过覆盖specificationVersion将 V3 模型转换为 V4:
as-language-model-v4.tsas-embedding-model-v4.tsas-image-model-v4.tsas-speech-model-v4.tsas-transcription-model-v4.tsas-reranking-model-v4.tsas-video-model-v4.tsas-provider-v4.ts(通过包装所有模型工厂方法,将 V3 Provider 转换为 V4)
每个适配器都会检查specificationVersion:如果已经是 V4 则原样返回模型,否则将其包装在Proxy中。
以 as-language-model-v4.ts 为例,源码清晰地展示了这一模式:
export function asLanguageModelV4( model: LanguageModelV2 | LanguageModelV3 | LanguageModelV4, ): LanguageModelV4 { if (model.specificationVersion === 'v4') { return model; } // first convert v2 to v3, then proxy v3 as v4: const v3Model = model.specificationVersion === 'v2' ? asLanguageModelV3(model) : model; return new Proxy(v3Model, { get(target, prop: keyof LanguageModelV3) { if (prop === 'specificationVersion') return 'v4'; return target[prop]; }, }) as unknown as LanguageModelV4; }可以看到:V4 直接透传(identity);V2 先经asLanguageModelV3升到 V3,再通过 Proxy 伪装为 V4;Proxy 的get拦截器只在读取specificationVersion时返回'v4',其余属性与方法一律透传,因此模型行为完全保留。
而 as-provider-v4.ts 展示了 Provider 级别的转换:先确保得到 V3 Provider(必要时调用asProviderV3),再返回一个全新的对象,其中specificationVersion: 'v4',并且每个模型工厂方法(languageModel、embeddingModel、imageModel、transcriptionModel、speechModel、rerankingModel)都通过对应的 V4 适配器包装;对于 Provider 未提供的可选模型类型(如transcriptionModel、speechModel、rerankingModel),会保留undefined语义。
每个适配器还应有对应的测试文件(例如as-language-model-v4.test.ts),验证以下行为:
- V4 输入原样返回(用
.toBe()做身份校验); - V3 输入被代理且
specificationVersion变为'v4'; - V2 输入(如适用)先转为 V3 再转为 V4;
- 属性和方法在通过 Proxy 后保持完好。
公共 API 更新
更新以下文件,使其公共边界接受V2 | V3 | V4模型,并在内部用适配器转换为 V4:
- packages/ai/src/middleware/wrap-language-model.ts —— 接受
LanguageModelV2 | V3 | V4 packages/ai/src/middleware/wrap-image-model.ts—— 接受ImageModelV2 | V3 | V4packages/ai/src/middleware/wrap-embedding-model.ts—— 接受EmbeddingModelV3 | V4(V2 是通用类型、不包含;保留 V3 以向后兼容)packages/ai/src/registry/custom-provider.ts—— 所有模型 map 中接受 V2/V3/V4 模型packages/ai/src/registry/provider-registry.ts—— 接受ProviderV2 | V3 | V4,用asProviderV4转换packages/ai/src/types/language-model-middleware.ts—— 放宽为同时接受 V3 和 V4 中间件
从 wrap-language-model.ts 的源码可以看到这一演进的落地形态:其wrapLanguageModel的入参类型为LanguageModelV2 | LanguageModelV3 | LanguageModelV4,函数体第一行即调用asLanguageModelV4(inputModel)统一转成 V4,随后所有内部逻辑(LanguageModelV4CallOptions、LanguageModelV4GenerateResult、LanguageModelV4StreamResult)都基于 V4 类型展开,返回的包装对象也显式声明specificationVersion: 'v4'。
测试更新
- 为每个 V4 适配器创建测试文件(例如
as-language-model-v4.test.ts),验证身份透传、V3→V4 转换和 V2→V4 转换。 - 更新
resolve-model.test.ts,用独立的测试块分别测试 V3→V4 转换(使用 V3 mocks)和 V4 透传(使用 V4 mocks)。 - 更新其他测试文件,在代码现在返回 V4 模型的地方改用 V4 mocks(例如
custom-provider.test.ts、provider-registry.test.ts、中间件测试)。任何对返回模型做引用相等性(.toBe())校验的测试都应使用 V4 mocks。
在packages/ai中运行pnpm test,并在工作区根目录运行pnpm type-check:full(对应根 package.json 中的tsc --build tsconfig.with-examples.json)来验证。
8. 配置文档站点(ai-sdk.dev)
文档站点托管在ai-studio仓库中,通过一个指向本仓库的 Git submodule 引用本仓库。在预发布周期内,站点需要同时为稳定版和 beta 版文档提供版本化分支与 Vercel 部署。
在vercel/ai仓库中:
- 更新
.github/workflows/update-sdk-submodule-v6.yml,使其跟踪release-v6.0分支而非main。 - 创建
.github/workflows/update-sdk-submodule-v7.yml—— 该工作流拉取main、在ai-studio中检出sdk/v7分支并推送到origin sdk/v7。
在ai-studio仓库中:
- 创建
sdk/v7分支(默认分支暂时保持sdk/v6,这样生产站点继续提供稳定版文档)。 - 在 Vercel 中创建一个连接到
sdk/v7分支的 v7 预览部署(例如v7.ai-sdk.dev)。
9. 合并到main
打开一个包含第 3-7 步全部改动的 PR。合并后,第一个 beta 版本(例如ai@7.0.0-beta.1)将自动发布。
三、预发布周期内的日常开发
日常开发约定
- 所有功能 PR 继续指向
main。 - 每个 PR 仍然需要 changeset(默认使用
patch)。 - 当Version PackagesPR 被合并时,beta 版本自动发布。
新增一个包
在main处于预发布模式时引入新包,将其package.json中的初始version设为纯0.0.0——绝不要设成0.0.0-canary.0(或任何-<tag>.N后缀)。预发布后缀会使版本成为 "premajor",而 semver 会把对 premajor 的major/minor/patch提升视为仅仅去掉后缀,于是包会卡在0.0.0-canary.N而无法前进(例如一个majorchangeset 本应产生1.0.0-canary.0)。详见 add-new-provider.md → When in pre-release mode。
将修复回传到稳定版
要把main上的修复回传到维护分支,给已合并的 PR 添加backport标签即可。这会自动创建一个指向维护分支的新 PR。
仓库现状佐证:这一行为由 .github/workflows/backport.yml 实现。工作流同时监听
pull_request的labeled与closed事件:labeled捕获"合并后补打标签"的场景,closed捕获"合并时已带标签"的场景,两者都以事件载荷中的 PR 号为准,避免竞态。它会自动解析目标维护分支(main的 PR 回传到最新release-v*分支,release-vN的 PR 回传到更早一个),执行git cherry-pick -m 1,并通过 GitHub GraphQLcreateCommitOnBranch创建签名提交与 backport PR。注意两点限制:来自 fork 的 PR 需要用workflow_dispatch手动触发;修改.github/workflows/的 PR 因GITHUB_TOKEN缺少workflows权限无法自动回传。
发布稳定补丁
合并进维护分支的补丁会触发发布工作流并发布稳定补丁版本。
四、结束预发布周期
当新大版本准备好发布稳定版时:
1. 退出预发布模式
git checkout main pnpm changeset pre exit这会移除.changeset/pre.json。提交并推送(或开一个 PR)。
仓库现状佐证:当前仓库根目录的 .changeset/ 下只有
README.md、config.json与几个常规 changeset 文件(如gentle-images-shine.md),并无pre.json——说明仓库当前并不处于预发布模式,这正是"进入/退出模式由pre.json存在与否决定"的旁证。
2. 发布稳定版
退出预发布模式的 PR 合并后,下一个Version PackagesPR 将产生稳定版本(例如ai@7.0.0)。合并它即可发布。
3. 切换文档站点
在ai-studio仓库中,将默认分支从sdk/v6改为sdk/v7,使生产站点提供新大版本的文档。同步更新 Vercel 生产部署。
4. 归档维护分支
维护分支(例如release-v6.0)可以保留以应对紧急补丁,但不再接收常规回传。
五、小结:一份可复用的预发布检查清单
| 阶段 | 关键动作 | 验证方式 |
|---|---|---|
| 启动 | 创建release-v<major>.0分支并推送 | 分支上 CI 自动运行 |
| 启动 | 维护分支ci:release追加--tag ai-v<major> | 检查 npm dist-tag 未被覆盖 |
| 启动 | pnpm changeset pre enter beta并清理pre.json的initialVersions | 检查pre.json内容 |
| 启动 | 创建全包majorchangeset | 检查生成的.changeset/*.md |
| 启动 | 为全部 12 个规范目录播种 v4、重命名文件、替换版本字样、更新index.ts导出与交叉引用 | packages/provider中pnpm build通过 |
| 启动 | 创建 V4 mock 并导出 | pnpm test通过 |
| 启动 | 创建 8 个 V4 适配器及测试,更新公共 API 边界与相关测试 | pnpm test+pnpm type-check:full通过 |
| 启动 | 文档站点版本化分支与 Vercel 部署 | 预览域名可访问 beta 文档 |
| 周期内 | 功能 PR 均指向main,默认patchchangeset | Version Packages PR 自动发布 beta |
| 周期内 | 新包初始版本用纯0.0.0 | 避免 premajor 卡版本 |
| 周期内 | 已合并 PR 加backport标签 | backport PR 自动创建 |
| 结束 | pnpm changeset pre exit删除pre.json | Version Packages 产生稳定版本 |
| 结束 | 文档站默认分支切换到sdk/v<new-major> | 生产站点提供新版文档 |
这套流程的底层设计意图清晰:规范版本化(spec versioning)是 AI SDK 大版本演进的锚点——从packages/provider/src/*/vN的目录结构,到packages/ai/src/model/中以Proxy实现的版本适配器,再到packages/ai/src/test/中 V2/V3/V4 三套并行的 mock 矩阵,整条链路保证了 Provider 作者可以渐进迁移,而核心packages/ai的公共 API 能同时在旧版本与新版本之上稳定运行。理解并复现这个周期,是任何希望在 AI SDK 生态中跟进或贡献大版本演进的关键能力。
【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考