☰
Mastra 内部 vendor AI SDK v5:`@internal/ai-sdk-v5` 包的源码级解析
2026/10/10 2:33:19 网站建设 项目流程
  • 人工智能
  • Agent 框架
  • AI Agent
  • RAG
  • 后端

【免费下载链接】mastra

Mastra is the modern TypeScript framework for AI-powered applications and agents.

项目地址:https://gitcode.com/GitHub_Trending/ma/mastra
点击查看免费下载

导读

在 Mastra 的 monorepo 中,packages/_vendored/ai_v5目录维护着一个特殊的内部包@internal/ai-sdk-v5,它的存在只有一个目的:把 ai-sdk v5 以 vendor(内嵌)方式固化进代码库,使任何公开包都无需直接声明对ai的运行时依赖。本文基于该包的 README 与真实源码、构建配置和测试用例,完整拆解它的三个职责——提供 v5 API 的安全包装、同步并修复 ai 包的.d.ts类型文件、以及在测试与构建中充当统一入口,并说明它在 core 包 Agent 体系中的实际消费方式。


一、包定位:为什么 Mastra 要“vendor”而不是“依赖”ai-sdk v5

@internal/ai-sdk-v5的 README 只有一句话,却点明了整个包的设计意图:

Package to vendor ai-sdk v5 into our codebase so we don't have to use it in any package as a dependency

也就是说,这个包不是面向用户的 API 库,而是仓库内部的基础设施。它的目标是把 ai-sdk v5 的能力“装进”Mastra 自己的代码库,从而让上层各包(尤其是@mastra/core)在源码层面直接 import AI SDK 的 API,却不必在自己的package.json里声明对ai的依赖。

这一点可以从 packages/core/package.json 得到印证:core 包以workspace:*协议同时声明了@internal/ai-sdk-v4、@internal/ai-sdk-v5等多个 vendor 包,而真正指向 npm 生态的ai依赖被隔离在_vendored内部包中。

而且 Mastra 并不是只 vendor 了一个版本。packages/_vendored/目录下同时存在ai_v4/、ai_v5/、ai_v6/、ai_v7/四个目录,形成一条完整的“多版本并行 vendor”体系,分别对应 AI SDK 的不同大版本(packages/_vendored)。@internal/ai-sdk-v5就是这条体系里锁定 v5 的那一环——从 package.json 可以看到,它把ai@^5.0.221、@ai-sdk/gateway@2.0.120、@ai-sdk/provider@2.0.3、@ai-sdk/provider-utils@3.0.30以及@opentelemetry/api、zod等一并作为该包自身的依赖锁定。

从源码结构看,这种“每个大版本一个内部包”的做法,让 Mastra 可以同时在代码库里保留多个 AI SDK 世代,供 Agent、语音、工作流等不同模块按需选用,同时避免版本冲突在公共依赖图中扩散。


二、入口设计:index.ts/internal.ts/test.ts三个子路径

@internal/ai-sdk-v5的源码只有三个入口文件,分别对应 AI SDK 的三大使用场景(src 目录):

入口内容用途
src/index.tsexport * from 'ai'+ 重写的generateText/streamText业务代码的标准入口,提供 v5 全量 API 并叠加安全约束
src/internal.tsexport * from 'ai/internal'暴露 AI SDK 的内部实现细节,供 Mastra 框架层使用
src/test.tsexport * from 'ai/test'测试专用工具(如MockLanguageModelV2、convertArrayToReadableStream)

对应地,package.json 的exports字段采用通配符映射:"./*"会把@internal/ai-sdk-v5/test、@internal/ai-sdk-v5/internal等子路径统一解析到dist/*.d.ts与dist/*.js,同时为import和require分别提供 ESM 与 CJS 产物。

这种“一个包、三个子路径”的设计,正是为了让上层代码可以这样按需引入:

// 业务与类型 import { generateText, streamText, tool } from '@internal/ai-sdk-v5'; import type { ModelMessage, ToolChoice } from '@internal/ai-sdk-v5'; // 框架内部实现 import { ... } from '@internal/ai-sdk-v5/internal'; // 测试替身 import { MockLanguageModelV2, convertArrayToReadableStream } from '@internal/ai-sdk-v5/test';

三、核心实现:generateText/streamText的安全包装

@internal/ai-sdk-v5并不是简单转发 AI SDK,而是在 src/index.ts 里对两个最常用的 API 做了安全加固。完整实现如下:

import { generateText as generateTextV5, streamText as streamTextV5 } from 'ai'; export * from 'ai'; // Keep both security overrides after caller options so embedded system-role messages cannot be re-enabled. export const generateText: typeof generateTextV5 = options => generateTextV5({ ...options, allowSystemInMessages: false, }); export const streamText: typeof streamTextV5 = options => streamTextV5({ ...options, allowSystemInMessages: false, });

这里有两个值得注意的工程细节:

  1. 类型对齐:generateText: typeof generateTextV5保证了包装函数与 AI SDK 原生函数签名完全一致,上层代码可以无感替换。
  2. 安全策略:包装函数把allowSystemInMessages硬编码为false,即禁止在prompt或messages字段中混入system角色的消息。这是 AI SDK v5 提供的一个安全开关——默认关闭时,system 消息只能通过顶层的system参数注入,避免来自不可信上下文的 system 指令被模型当作权威指令执行(即防止 system prompt 注入)。

注释中“Keep both security overrides after caller options”点明了顺序意图:安全选项必须覆盖在调用方传入的 options 之后,这样即使调用方显式传入allowSystemInMessages: true,也会被false覆盖,无法重新启用内嵌 system 消息。

这里必须强调的是,@internal/ai-sdk-v5的这套强制约束是 Mastra 内部统一的默认策略。从代码结构看,ai_v4与ai_v6目录内是同构的模板式实现(它们的 README 与 v5 完全相同),因此可以推断这套 vendor 体系的各版本入口都遵循相似的包装思路。

测试验证:安全约束确实生效

该行为由 src/index.test.ts 的测试用例完整锁定,共四类断言:

  • generateText拒绝 system 消息:分别通过prompt和messages字段传入含 system 角色的消息,即使显式传allowSystemInMessages: true,也会抛出System messages are not allowed in the prompt or messages fields,且底层doGenerate不会被调用(模型请求在验证阶段就被拦截);
  • streamText同样拒绝:消费fullStream时触发校验,通过onError回调捕获到同款错误信息,且doStream不会被调用;
  • 顶层system参数仍然可用:通过system: 'trusted system instruction'注入的受信系统指令会被正常接受,并出现在最终传给模型的 prompt 中(测试断言了prompt里包含{ role: 'system', content: 'trusted system instruction' });
  • 流式与一次性生成行为一致:streamText的result.text与generateText的result.text都能正确返回模型输出。

测试里还提供了一个最小可用的 v2 规格模型替身(createMockModel),它实现LanguageModelV2接口的doGenerate/doStream,并返回带stream-start、text-start、text-delta、text-end、finish片段的流,直观展示了 AI SDK v5 流式协议的片段结构。


四、类型同步机制:copy-ai-dts-files.ts与.d.ts修复

vendor 包最棘手的问题不是运行时代码,而是类型。@internal/ai-sdk-v5通过两条链路解决:

4.1 构建期自动拷贝.d.ts

tsdown.config.ts 的onSuccess钩子在每次构建完成后调用 scripts/copy-ai-dts-files.ts:

onSuccess: async () => { const { copyAIDtsFiles } = await import(new URL('./scripts/copy-ai-dts-files.ts', import.meta.url).href); const dtsFiles = await copyAIDtsFiles(); // ... },

而copyAIDtsFiles()的核心逻辑是:

  1. 读取node_modules/ai/package.json的exports表,以及本包package.json的exports表;
  2. 借助resolve.exports按types条件解析出ai 包每个导出子路径对应的.d.ts文件;
  3. 把 ai 包里的.d.ts逐个拷贝到本包对应的导出位置,使@internal/ai-sdk-v5的dist/*.d.ts始终与所锁定的 ai 版本保持一致。

也就是说,发布到dist的类型声明不是手写的,而是从 node_modules 中的 ai 包实时同步而来,从而保证“API 是 v5 的,类型也是 v5 的”。

4.2 类型嵌入与导出修复

拷贝完成后,tsdown 的onSuccess还会对每个.d.ts执行两步后处理(tsdown.config.ts):

  • embedTypes(...):把ai、@ai-sdk/*、@opentelemetry/api、@standard-schema/spec、@types/json-schema、eventsource-parser等外部依赖的类型直接嵌入到本包的.d.ts中,避免消费方需要另行安装这些类型依赖;
  • fixExportBugInDtsFile(...):用ts-morph修复 AI SDK.d.ts中“损坏的 namespace 导出”。该函数会定位以{ identifier as identifier }形式出现的模块块,把它们重建为合法的export ... from声明,并自动导出值为unique symbol的变量,最终输出Fixed N broken namespace export(s)日志。

4.3 构建产物的其他细节

同文件还配置了:

  • 入口:['src/index.ts', 'src/internal.ts', 'src/test.ts'],仅输出esm,target: 'node22';
  • treeshake: true与sourcemap: true;
  • neverBundle: ['msw', 'msw/node', 'vitest'],避免把测试相关依赖打进产物;
  • alias把@vercel/oidc指向oidc-stub.ts(oidc-stub.ts),以 stub 方式消除该可选依赖;
  • define将process.env.NODE_ENV固定为'production'。

五、在 Mastra 中的实际消费方式

@internal/ai-sdk-v5在仓库中最主要的消费者是@mastra/core。既有运行时使用,也有类型级使用,还有测试级使用:

  • 运行时 API:core 的 Agent 测试中直接引入stepCountIs、tool等工具(如 packages/core/src/agent/agent.e2e.test.ts);
  • 类型导入:Agent 核心实现从该包导入ModelMessage、ToolChoice、LanguageModelUsage、ToolSet等类型(如 packages/core/src/agent/agent.ts、packages/core/src/agent/agent.types.ts、packages/core/src/agent/durable/types.ts);
  • 测试替身:core 的几十个测试文件统一从@internal/ai-sdk-v5/test导入MockLanguageModelV2与convertArrayToReadableStream,用来构造可控的语言模型与流式输出(如 packages/core/src/agent/tests/agent-signals.test.ts)。

这种“业务代码、内部实现、测试工具三入口分离 + workspace 协议引入”的形态,保证了所有上层包只依赖@internal/ai-sdk-v5一个名字,而真正的ai依赖、版本锁定与类型同步全部收敛在_vendored/ai_v5一个目录里。

与 fetch 初始化相关的兼容性测试

另外,src/fetch-import.test.ts 用一个子进程测试锁定了运行时兼容性:即使globalThis.fetch被置为undefined,@ai-sdk/provider-utils仍能被安全 import 而不抛错。这保证了 vendor 包在非标准运行时(如早期 Node 版本或不提供 fetch 的环境)下也能正常加载。


六、与其他 vendored 版本的关系

packages/_vendored/目录中与ai_v5并列的还有ai_v4、ai_v6、ai_v7。从目录与 README 看,它们共享同一种模板式的内部包结构(README 文案一致,均为“Package to vendor ai-sdk … into our codebase”),而 core/package.json 分别以@internal/ai-sdk-v4、@internal/ai-sdk-v5、@internal/ai-v6、@internal/ai-v7引入。

其中ai_v7的 src/index.ts 目前只有一行export * from 'ai'(尚未叠加 v5 那样的安全包装),可以推断:不同版本的 vendor 包成熟度不同,v5 承担着 Agent 主路径的职责并带有强制安全策略,而其他版本可能服务于特定的兼容场景或迁移过渡。这种“多版本并行 vendor”模式本身,就是 Mastra 在不牺牲灵活性的前提下管理 AI SDK 快速演进的工程手段。


七、小结

@internal/ai-sdk-v5是一个典型的“基础设施型”内部包,它的价值不在于代码量,而在于工程边界:

  • 依赖隔离:让ai及其生态依赖只出现在_vendored内部,公开包无需声明;
  • 安全策略统一:通过包装generateText/streamText强制allowSystemInMessages: false,并由测试锁定行为;
  • 类型自洽:构建期自动同步ai的.d.ts并做类型嵌入与导出修复,保证类型与锁定版本一致;
  • 入口清晰:index/internal/test三子路径分别服务业务、框架与测试。

对需要在 Mastra 中复用 AI SDK v5 能力的开发者来说,正确姿势是:只 import@internal/ai-sdk-v5(及其/internal、/test子路径),绝不直接依赖 npm 的ai。这样既能获得与 Mastra 一致的版本锁定和安全默认值,也避免版本漂移带来的行为差异。

  • 人工智能
  • Agent 框架
  • AI Agent
  • RAG
  • 后端

【免费下载链接】mastra

Mastra is the modern TypeScript framework for AI-powered applications and agents.

项目地址:https://gitcode.com/GitHub_Trending/ma/mastra
点击查看免费下载

相关推荐

上一篇:G-Helper 完整指南:免费单文件,三步跑起来的华硕笔记本控制工具
下一篇:Wand-Enhancer 完整指南:免费本地增强 Wand(WeMod)的实操手册

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询