Context7 TypeScript SDK 版本演进解析:从 0.1.0 到 0.3.1 的 API 简化与错误处理加固
@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 raw SyntaxError when the server returns a non-JSON error body. HttpClient.request() now wraps the error-path res.json() in a .catch, so non-JSON responses (HTML 502s, plain-text 429s, Cloudflare challenge pages) fall back to res.statusText and always surface as a typed Context7Error.”
问题背景
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 + 全局 mock fetch 覆盖了三条回退路径:
- 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) * 50 ms,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 声明。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00