AI SDK 仓库测试指南:手动验证、Provider 测试夹具的生成与加载
AI SDK 仓库测试指南手动验证、Provider 测试夹具的生成与加载【免费下载链接】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 SDKThe AI Toolkit for TypeScript仓库的贡献者测试指南整理系统讲解该仓库的测试方法论如何利用examples/ai-functions与examples/ai-e2e-next完成generateText、streamText与 Web UI 的手动回归以及如何为各 Provider 的响应解析测试生成真实响应夹具Test Fixtures并在单元测试中按不同的 SSE 格式加载这些夹具。读完本文你将掌握一套可复用的真实响应 → 夹具化 → 离线断言的测试工作流能够为新增或修改的 Provider 功能补齐全链路验证。一、手动测试三条必测路径仓库贡献指南要求任何变更或新功能至少覆盖三类手动测试场景分别对应 CLI 与 Web UI 两条通道场景通道验证目标generateText测试命令行一次性文本生成的结果正确性streamText测试命令行流式输出的逐块正确性UI 测试含追问Web UI助手回复后发送 follow-up message确保上下文能正确回传给 LLM这三类场景可直接使用仓库中的两个示例工程examples/ai-functions纯命令行/脚本式示例集覆盖generate-text、stream-text、embed-many、generate-image、generate-speech、transcribe、upload-file、workflow-agent等大量子目录是生成夹具与手动冒烟测试的主战场examples/ai-e2e-next基于 Next.js 的端到端示例app/下包含完整的 UI 页面agent/下是 Agent 相关实现用于验证带 UI 的多轮对话链路。第三类 UI 测试的核心在于回复一轮还不够必须在助手输出之后再发送一条追问以此确认多轮消息messages 数组能正确、完整地作为上下文返回给 LLM——这是许多仅测单轮的功能最容易出问题的地方。二、Provider 单元测试与测试夹具体系2.1 为什么坚持真实响应夹具对于 Provider 响应解析类测试仓库的硬性约定是测试夹具必须来自 Provider 的真实响应除非响应体过大此时允许做不改变语义的裁剪。这条约定写在 contributing/testing.md 的 Test Fixtures 一节目的是避免用手工捏造的响应来测试解析逻辑——只有真实响应才能暴露字段缺失、格式漂移等只在生产环境出现的问题。夹具统一存放在各包内的__fixtures__子目录中例如OpenAI Responses APIpackages/openai/src/responses/__fixtures__/从该目录的文件名如openai-client-tool-search.1.json、openai-client-tool-search.1.chunks.txt、parallel-tool-call-wrapper.1.chunks.txt可以看出命名约定名称.序号.后缀测试辅助代码的写法以packages/openai/src/responses/openai-responses-language-model.test.ts为范本。2.2 三种方式生成夹具指南给出了三种生成夹具的典型方式对应ai包的三种核心 API。下面逐一展开并补充仓库中的底层实现佐证。generateText直接落盘响应体对于非流式生成把原始响应 body 打印到控制台再复制进新的夹具文件即可import { openai } from ai-sdk/openai; import { generateText } from ai; import { run } from ../lib/run; run(async () { const result await generateText({ model: openai(gpt-5-nano), prompt: Invent a new holiday and describe its traditions., }); console.log(JSON.stringify(result.response.body, null, 2)); });上述代码中的run辅助函数实现在 examples/ai-functions/src/lib/run.ts它执行传入的异步函数若结果可被录制即同时满足包含fullStream或steps字段这一结构特征见 examples/ai-functions/src/lib/record-fixture.ts 的isRecordableResult就会自动调用recordFixture把响应体写入output/目录出错时则打印错误并输出请求体、响应体便于定位问题。streamTextincludeRawChunkssaveRawChunks流式场景必须拿到未被 SSE 解包前的原始 chunk因此需要开启原始块透传选项并借助专门的saveRawChunks辅助函数import { openai } from ai-sdk/openai; import { streamText } from ai; import { run } from ../lib/run; import { saveRawChunks } from ../lib/save-raw-chunks; run(async () { const result streamText({ model: openai(gpt-5-nano), prompt: Invent a new holiday and describe its traditions., includeRawChunks: true, }); await saveRawChunks({ result, filename: openai-gpt-5-nano }); });运行方式在examples/ai-functions目录下执行pnpm tsx src/stream-text/script-name.ts生成结果会写入examples/ai-functions/output目录将其复制到目标包的__fixtures__目录并按约定重命名即可。关于底层机制两点值得展开saveRawChunks的实现examples/ai-functions/src/lib/save-raw-chunks.ts遍历result.stream只收集type raw的 chunk 的rawValue每行序列化一个 JSON 对象写入output/filename.chunks.txt——注意这里不包含任何 SSE 信封无data:前缀、无[DONE]还原工作由测试侧的加载器完成。API 的新旧两种写法本文档示例中的includeRawChunks: true是流式选项的既有形式而仓库较新的代码已支持include: { rawChunks: true }的写法。例如 packages/ai/src/agent/tool-loop-agent.test.ts 中即通过include: { rawChunks: true }断言doStreamOptions.includeRawChunks trueexamples/ai-functions/src/e2e/raw-chunks.test.ts 也用该写法对 OpenAI、Anthropic、Google 三个 Provider 做了端到端验证开启时流中恰好出现 1 个rawchunk关闭时为 0。写新代码时建议优先采用include: { rawChunks: true }。embedMany注意复数responses字段向量化接口的夹具采集有一个易错点embedMany返回的是responses复数数组不是responseimport { openai } from ai-sdk/openai; import { embedMany } from ai; import { run } from ../lib/run; run(async () { const result await embedMany({ model: openai.embedding(text-embedding-3-small), values: [sunny day at the beach, rainy day in the city], }); console.log(JSON.stringify(result.responses?.[0]?.body, null, 2)); });同时由于 embedding 向量通常体积巨大指南建议只保留每个向量前几个数值例如 5 个用于夹具其余结构保持不变从而在保真与体积之间取得平衡。2.3 自动录制recordFixture 的约定除了手动打印/复制仓库还提供了一套自动化录制逻辑examples/ai-functions/src/lib/record-fixture.ts其行为与上述约定严格一致夹具文件名取自被执行的示例脚本文件名path.basename(process.argv[1])去掉扩展名按请求序号生成.1、.2…… 多个文件streamText产生name.n.chunks.txt按start-step分组、每个 step 一个文件generateText产生name.n.json每个 step 的response.body一个文件输出目录为output/已被 gitignore首次运行时会自动mkdirSync创建。这套逻辑让跑一次示例即得一套夹具成为可能命名天然与单元测试的读取路径对应。三、在单元测试中加载夹具按 SSE 格式还原流saveRawChunks写出的文件是每行一个 JSON 对象没有 SSE 信封。因此测试侧的 chunk loader 必须根据 Provider 实际使用的 SSE 协议把原始 chunk 重新包装回 Provider 期望的格式。不同 Provider 的差异正是这里的关键。3.1 OpenAI 风格 SSEdata:前缀 [DONE]哨兵OpenAI、DeepSeek、Groq、xAI 等 Provider 使用标准的data:前缀并以[DONE]作为流结束哨兵。加载器实现如下节选自 packages/openai/src/responses/openai-responses-language-model.test.tsfunction prepareChunksFixtureResponse(filename: string) { const chunks fs .readFileSync(src/__fixtures__/${filename}.chunks.txt, utf8) .split(\n) .filter(line line.trim().length 0) .map(line data: ${line}\n\n); chunks.push(data: [DONE]\n\n); server.urls[api-url].response { type: stream-chunks, chunks, }; }要点逐行读取、过滤空行、为每行补上data:前缀与空行分隔最后追加data: [DONE]\n\n。随后将组装好的 chunk 数组挂到server.urls[api-url]的响应上由ai-sdk/test-server提供的createTestServer以stream-chunks类型逐块吐出驱动被测模型的流式解析逻辑。3.2 事件类型 SSE从 chunk 的type提取event:字段以 Cohere 为代表的部分 Provider 使用带事件类型的 SSE需要从每个 chunk 的type属性还原event:字段function prepareChunksFixtureResponse(filename: string) { const chunks fs .readFileSync(src/__fixtures__/${filename}.chunks.txt, utf8) .split(\n) .filter(line line.trim() ! ) .map(line { const parsed JSON.parse(line); return event: ${parsed.type}\ndata: ${line}\n\n; }); server.urls[api-url].response { type: stream-chunks, chunks, }; }两种加载器一对比即可看出核心差异OpenAI 风格只包data:事件风格还要额外带event: type行——type来自 JSON chunk 解析后的type属性。3.3 如何确认 Provider 用的是哪种格式指南给出了一个非常实用的判别方法查看 Provider 的doStream实现看它用的是哪个 SSE 解析器。在 AI SDK 的 Provider 包中流式响应统一通过createEventSourceResponseHandler等处理器构建例如 OpenAI Responses 的流式成功响应处理器即位于 packages/openai/src/responses/openai-responses-language-model.tscreateEventSourceResponseHandler的导入见该文件第 20 行。加载器必须与 Provider 侧使用的解析器格式一一对应否则解包会错位、解析直接失败。四、测试基础设施速览夹具加载离不开测试服务器。上述示例中的server来自ai-sdk/test-servermonorepo 中的 packages/test-server配合 vitest 使用时可从ai-sdk/test-server/with-vitest导入createTestServer与TestResponseController再结合ai-sdk/provider-utils/test的convertReadableStreamToArray、mockId等工具完成流断言。该模式在openai-responses-language-model.test.ts等文件中被反复使用是编写新 Provider 解析测试时的标准样板。此外仓库还在 examples/ai-functions/src/e2e 维护了一批真实联网的端到端测试如openai.test.ts、anthropic.test.ts、cohere.test.ts、raw-chunks.test.ts等它们依赖.env中的 API Key通过dotenv/config加载适合在 CI 有凭证时做全链路冒烟而__fixtures__夹具测试则完全离线、稳定可重复两者互补。五、实践清单给贡献者一份可照做的操作清单功能改动前先在examples/ai-functions下跑通generateText/streamText/embedMany三类示例验证真实 Provider 行为生成夹具generateText打印并保存response.bodystreamText开启include: { rawChunks: true }或includeRawChunks: true后由saveRawChunks落盘embedMany记得取responses[0].body并裁剪向量入库夹具按名称.序号.json|chunks.txt命名放入packages/provider/src/.../__fixtures__/编写加载器先读目标 Provider 的doStream确认 SSE 格式OpenAI 风格 vs 事件风格再套用对应的prepareChunksFixtureResponse样板离线断言通过createTestServer的stream-chunks响应驱动解析配合快照__snapshots__与convertReadableStreamToArray校验输出回归 UI 链路在examples/ai-e2e-next中验证回复 → 追问 → 上下文正确回传的多轮闭环。这套流程的价值在于真实响应保证了解析逻辑的保真度夹具化让测试彻底离线、可重复、可 diff而手动测试则兜底覆盖了单元测试难以触及的 UI 与多轮交互链路——三者共同构成 AI SDK 各 Provider 包的质量防线。延伸阅读贡献总览CONTRIBUTING.md构建新功能时的测试要求contributing/building-new-features.md新增 Provider 指南contributing/add-new-provider.md测试基础设施包packages/test-server核心工具包含测试辅助packages/provider-utils【免费下载链接】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),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →