Context7 TypeScript SDK 版本演进解析:从 0.1.0 到 0.3.1 的 API 简化与错误处理加固
【免费下载链接】context7Context7 Platform -- Up-to-date code documentation for LLMs and AI code editors项目地址: https://gitcode.com/gh_mirrors/co/context7
@upstash/context7-sdk是 Context7 平台面向 TypeScript 的官方 SDK,用于在 AI Agent、RAG 管线中检索实时、带版本号的开源库文档。本文基于当前仓库中 packages/sdk/CHANGELOG.md 的完整版本记录,逐版本解读每次发布引入的 API 变化——包括 0.2.0 的破坏性 API 简化、0.3.0 默认响应类型从txt切换到json、0.3.1 的非 JSON 错误体加固——并结合 入口实现、HTTP 客户端 与对应测试用例,说明每个变更在源码中的落点与迁移注意点。读完后你可以明确:当前版本(0.3.1)的完整 API 面、各版本之间的不兼容边界,以及错误路径的行为契约。
版本演进总览
packages/sdk/CHANGELOG.md 记录了 4 个发布版本,与 packages/sdk/package.json 中"version": "0.3.1"一致。总览如下:
| 版本 | 变更级别 | 核心变更 |
|---|---|---|
| 0.1.0 | Minor(首次发布) | 提供 HTTP/REST 客户端、searchLibrary()与getDocs()、环境变量 API Key 支持 |
| 0.2.0 | Minor(破坏性 API 简化) | getDocs()更名为getContext(query, libraryId, options);searchLibrary()改为双参数;响应类型统一为Library/Documentation;移除分页、mode、topic、limit 等选项 |
| 0.3.0 | Minor | searchLibrary与getContext的默认响应类型从"txt"改为"json";AI SDK 工具显式使用type: "txt"获取 LLM 友好的纯文本 |
| 0.3.1 | Patch | 服务器返回非 JSON 错误体时不再抛出裸SyntaxError,统一包装为带类型的Context7Error |
需要注意:packages/sdk/README.md与 docs/sdks/ts/getting-started.mdx 均明确标注该 SDK 处于Work in Progress状态,“API 仍在活跃开发中,未来版本可能引入破坏性变更”。因此 0.1.0 到 0.3.0 虽然语义上是 Minor/Patch 级发布,0.2.0 与 0.3.0 实际上都改变了默认行为或方法签名,跨版本升级时必须对照下文逐节确认。
0.1.0:初始发布奠定 HTTP/REST 客户端基线
CHANGELOG 对 0.1.0(提交5e11d35)的描述是“Context7 TypeScript SDK 首次发布”,包含三项能力:
- HTTP/REST 客户端(对接 Context7 API)
searchLibrary()—— 在 Context7 数据库内搜索库getDocs()—— 带过滤选项地拉取文档- API Key 的环境变量配置支持
这四项在源码中均可一一对应。当前 packages/sdk/src/client.ts 中的Context7类在构造时完成三件事:
- 按
config.apiKey→process.env.CONTEXT7_API_KEY的顺序解析 API Key,两者都缺失时抛出Context7Error(“API key is required...”),这正是 0.1.0 承诺的“环境变量支持”的落地; - 对 Key 做前缀校验——非
ctx7sk前缀会打印API key should start with 'ctx7sk'警告(源码中API_KEY_PREFIX = "ctx7sk"); - 内部实例化
HttpClient,固定baseUrl为https://context7.com/api,并预置 Bearer 认证头、5 次重试与Math.exp(retryCount) * 50毫秒的指数退避、以及cache: "no-store"缓存策略。
HttpClient(packages/sdk/src/http/index.ts)是 SDK 唯一的网络出口,封装了fetch调用、重试循环、响应解析与错误归一化。0.1.0 的getDocs()在 0.2.0 中已被移除,其“过滤选项”(分页、mode、topic、limit)也随之取消——下文 0.2.0 小节会详细说明这一取舍。
0.2.0:破坏性的 API 简化——从 getDocs 到 getContext
0.2.0(提交b3cd38a)是一次以“简化”为目标的接口重构,CHANGELOG 列出的每一条变更都可以在当前源码中找到对应形态:
方法签名重构
getDocs()被替换为getContext(query, libraryId, options)。新签名要求传入query参数(用户的问题或任务),用于服务端做相关性排序检索,而不再是无差别拉取。对照 packages/sdk/src/commands/get-context/index.ts:命令构造时把query、libraryId、type组装成 GET 查询参数,请求v2/context端点。searchLibrary(query, libraryName)改为双参数。此前只需库名,现在必须同时提供“相关性 query”与“库名”。packages/sdk/src/commands/search-library/index.ts 在构造函数中显式校验:query或libraryName为空即抛出Context7Error("query and libraryName are required"),请求命中v2/libs/search端点。
响应类型统一
CHANGELOG 说明响应类型被替换为Library与Documentation两个模型,取代旧版SearchResult、CodeDocsResponse、InfoDocsResponse等分散类型。当前定义集中在 packages/sdk/src/commands/types.ts:
export interface Library { id: string; // Context7 库 ID,如 "/react/react" name: string; // 显示名 description: string; // 库描述 totalSnippets: number; // 可用文档片段数 trustScore: number; // 来源可信度分数(0-10) benchmarkScore: number; // 质量指标分数(0-100) versions?: string[]; // 可用版本/标签 } export interface Documentation { title: string; // 文档片段标题 content: string; // 文档内容(可含 Markdown 代码块) source: string; // 来源 URL 或标识 }从 packages/sdk/src/utils/format.ts 的格式化函数可以看清“统一”的具体做法:服务端返回的codeSnippets(含codeTitle、codeDescription、codeList、codeId等原始字段)被formatCodeSnippet拼装成{ title, content, source }——代码块以 ```language 围栏重新封装进content,描述前置;infoSnippets由formatInfoSnippet映射为同构形状(面包屑作为title,缺失时回退为"Documentation")。两类原始响应在 GetContextCommand.exec 中被合并为单一Documentation[]返回([...codeDocs, ...infoDocs]),调用方不再需要区分“代码文档”与“信息文档”两种类型。
移除分页与过滤选项
CHANGELOG 明确写道:“Remove pagination, mode, topic, and limit options from context retrieval”,并且GetContextOptions被简化到只剩type: "json" | "txt"一个字段。这一点在 types.ts 中得到印证——GetContextOptions与SearchLibraryOptions均只有一个可选的type属性。检索的分页/模式/数量控制被上收到服务端默认策略,SDK 调用方只关心“问题 + 库 + 返回格式”。
迁移提示:如果你的代码仍在使用 0.1.0 的
getDocs(libraryId, { mode, topic, limit, page })形态,需要改写为getContext(query, libraryId, { type }),并把原来依赖分页遍历的逻辑改为“一次相关性检索”。
0.3.0:默认响应类型从 txt 切换到 json
0.3.0(提交9412e62)的变更一句话概括:“Change SDK default response type from 'txt' to 'json' for both searchLibrary and getContext methods. AI SDK tools now explicitly use type: 'txt' for LLM-friendly text responses.”
默认值变更的源码落点
两个命令各自定义了DEFAULT_TYPE = "json":
- GetContextCommand:
const responseType = options?.type ?? DEFAULT_TYPE; - SearchLibraryCommand:
this.responseType = options?.type ?? DEFAULT_TYPE;
配合 client.ts 中的三重载签名,TypeScript 层面能按type字面量推导出精确返回类型:
// type: "json" → Library[] async searchLibrary(query: string, libraryName: string, options: SearchLibraryOptions & { type: "json" }): Promise<Library[]>; // type: "txt" → string async searchLibrary(query: string, libraryName: string, options: SearchLibraryOptions & { type: "txt" }): Promise<string>; // 不传 options → 默认 JSON async searchLibrary(query: string, libraryName: string, options?: SearchLibraryOptions): Promise<Library[]>;getContext的三重载结构与之完全对称(Documentation[]/string/ 默认Documentation[]),见 client.ts。这意味着 0.3.0 之后,不传 options 的调用方拿到的是结构化数组而不是字符串——对以 0.2.x 时代txt为默认写的代码是一次隐性破坏:原本const text = await client.getContext(...)得到的字符串,升级后变成Documentation[],需要显式传{ type: "txt" }或改为遍历数组。仓库中的测试(如 packages/sdk/src/client.test.ts、packages/sdk/src/commands/get-context/index.test.ts)均以{ type: "txt" }显式断言文本路径。
设计动机:JSON 给代码,TXT 给 LLM
从仓库结构看,这一拆分的另一侧消费者是 AI SDK 工具包。docs/agentic-tools/ai-sdk/agents/tools 下的resolve-library-id.mdx与query-docs.mdx文档描述了@upstash/context7-tools-ai-sdk提供的两个工具,其实现位于 packages/tools-ai-sdk/src/tools:这些 Agent 工具内部调用 SDK 时显式传type: "txt",因为纯文本格式(库搜索结果由 formatLibrariesAsText 渲染为 “Title / library ID / Description / Trust Score…” 的分节文本)可以直接塞进 LLM prompt,无需模型自己解析 JSON。而程序化场景(RAG 管线、需要逐片段处理或统计 token 的调用方)则受益于默认json结构化的Documentation[]。简言之,0.3.0 把“默认格式”的决策权交还给了调用场景,而不是替所有场景默认选文本。
txt 路径的附赠能力:分页响应头
值得留意的是,即便 0.2.0 移除了客户端分页参数,txt路径仍保留分页元数据。HttpClient.request 对非 JSON 响应会解析x-context7-page、x-context7-limit、x-context7-total-pages、x-context7-has-next、x-context7-has-prev、x-context7-total-tokens六个响应头,聚合成TxtResponseHeaders对象随结果一并返回(类型定义)。从源码结构看,这是为 txt 分页游标语义预留的通道——服务端仍在按页返回文本,SDK 只是把“跳页”的能力留给了响应头而非请求参数。
0.3.1:错误路径加固——非 JSON 错误体不再裸抛 SyntaxError
0.3.1(提交f327589)是当前版本,修复了一个生产环境常见的崩溃源。CHANGELOG 原文:“Avoid throwing a rawSyntaxErrorwhen the server returns a non-JSON error body.HttpClient.request()now wraps the error-pathres.json()in a.catch, so non-JSON responses (HTML 502s, plain-text 429s, Cloudflare challenge pages) fall back tores.statusTextand always surface as a typedContext7Error.”
问题背景
HTTP 客户端在!res.ok时习惯性地执行await res.json()提取错误信息。但当中间层(负载均衡、CDN 边缘节点、限流网关)返回 HTML 502 页面、纯文本 429 或 Cloudflare 质询页时,res.json()解析失败会抛出 JavaScript 原生SyntaxError: Unexpected token <...——这不是业务错误,调用方的catch (e) { if (e instanceof Context7Error) ... }分支无法捕获语义,日志里也丢失了真实 HTTP 状态信息。
修复实现
修复位于 packages/sdk/src/http/index.ts 的错误分支:
if (!res.ok) { const errorBody = (await res.json().catch(() => ({}))) as { error?: string; message?: string; }; throw new Context7Error(errorBody.error || errorBody.message || res.statusText); }三级回退链非常明确:
- JSON 错误体的
error字段; - JSON 错误体的
message字段; - 都不是(含非 JSON 响应导致
.catch(() => ({}))兜底)时,回退到res.statusText(如 "Bad Gateway"、"Service Unavailable")。
任何情况下抛出的都是 Context7Error(继承Error,name = "Context7Error"),与 docs/sdks/ts/getting-started.mdx 中“Error Handling”一节承诺的error instanceof Context7Error判定契约完全一致。
测试佐证
packages/sdk/src/http/index.test.ts 用 vitest + 全局 mockfetch覆盖了三条回退路径:
- 429 JSON 错误体:断言
Context7Error("rate limit exceeded")(error字段路径); - 400 仅含
message字段:断言回退到message; - 502 HTML 错误体:断言抛出的是
Context7Error且不是SyntaxError,message为"Bad Gateway"——这条用例正是 0.3.1 修复行为的直接回归测试; - 503 空错误体:断言回退到
statusText("Service Unavailable")。
对使用者的实际影响是:在 Agent 或长驻服务中集成 SDK 时,try/catch Context7Error一个分支就能兜住所有 API 侧故障,无需再防御性处理SyntaxError。
当前版本(0.3.1)API 速查与迁移清单
综合四个版本的变更,0.3.1 的对外 API 面收敛为:一个Context7客户端类 + 两个方法 + 一组导出类型 + 一个错误类。
import { Context7, Context7Error } from "@upstash/context7-sdk"; const client = new Context7({ apiKey: "ctx7sk-..." }); // 或依赖环境变量:CONTEXT7_API_KEY="ctx7sk-..." 后 new Context7() // 1) 搜索库(默认 JSON → Library[]) const libs = await client.searchLibrary("I need to build a UI with components", "react"); console.log(libs[0].id); // 例如 "/facebook/react" // 2) 获取文档上下文(默认 JSON → Documentation[]) const docs = await client.getContext("How do I use hooks?", "/facebook/react"); docs.forEach((d) => console.log(d.title, d.content, d.source)); // 3) LLM prompt 场景用纯文本 const context = await client.getContext("How do I use hooks?", "/facebook/react", { type: "txt", });关键参数与默认值(依据 types.ts 与 client.ts):
| 项 | 说明 | 默认值 |
|---|---|---|
Context7Config.apiKey | API Key;缺失时读取CONTEXT7_API_KEY,再缺失则抛Context7Error | 无(必填其一) |
searchLibrary(query, libraryName, options?) | 双参数均为必填,空值抛错;命中v2/libs/search | type: "json" |
getContext(query, libraryId, options?) | query用于相关性排序,libraryId形如/facebook/react;命中v2/context | type: "json" |
options.type | "json"返回结构化数组 /"txt"返回可直接入 prompt 的文本 | "json" |
| 内部重试 | 5 次重试,退避exp(n) * 50ms,cache: "no-store"(构造函数内固定,不对外暴露) | — |
跨版本迁移检查单:
- 从 0.1.x 升级:
getDocs()已不存在,替换为getContext(query, libraryId);searchLibrary需补第二个参数query;删除所有mode/topic/limit/ 分页传参。 - 从 0.2.x 升级:核对未传
type的调用——0.3.0 起默认返回结构化数组,需要文本的调用点显式补{ type: "txt" }。 - 所有版本:统一用
error instanceof Context7Error捕获 API 侧错误,0.3.1 之后该判定覆盖非 JSON 错误体场景。
小结
packages/sdk/CHANGELOG.md这条从 0.1.0 到 0.3.1 的演进线,呈现了一个典型的 SDK 成熟过程:0.1.0 建立“Key 管理 + 搜索 + 取文档”三件套;0.2.0 用一次破坏性简化把分散的响应类型收敛为Library/Documentation,并用相关性query取代手工分页/过滤;0.3.0 把默认响应格式从 LLM 文本切回程序友好的 JSON,把txt显式让渡给 AI SDK 工具类消费者(如 packages/tools-ai-sdk/src/tools 中的实现);0.3.1 则补齐了网络错误路径的类型化契约。当前代码、类型定义与 http 层测试 三者一致地印证了 CHANGELOG 的每一项描述,读者若基于该 SDK 构建文档驱动的 Agent,建议锁定 0.3.1 的上述行为契约,并留意 README 中“API 仍可能破坏性变更”的 WIP 声明。
【免费下载链接】context7Context7 Platform -- Up-to-date code documentation for LLMs and AI code editors项目地址: https://gitcode.com/gh_mirrors/co/context7
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考