Headroom TypeScript SDK 全解:通过代理压缩 LLM 上下文,无缝接入 Vercel AI、OpenAI 与 Anthropic SDK
Headroom 的 TypeScript SDK(npm 包 headroom-ai)让任意 JavaScript/TypeScript 应用能够在消息发送给 LLM 之前完成上下文压缩:省 token、降成本、把更多上下文塞进每次请求。本文基于仓库中的 TypeScript SDK 文档、SDK 源码 与 代理端点实现,完整讲解 compress() 核心 API、可复用客户端、三个框架适配器、错误与回退机制,以及多轮对话下的缓存安全注意事项。
架构定位:SDK 是一个纯 HTTP 客户端
理解 TypeScript SDK 的第一步是认清它的边界:压缩逻辑不在 Node.js 里运行。当你调用 compress() 时,SDK 把消息 POST 到 Headroom 代理的 /v1/compress 端点,代理在内部执行完整的压缩管线(ContentRouter 及各压缩器,包括 SmartCrusher),再把压缩后的消息返回。
Your TypeScript App
│
│ compress(messages)
▼
headroom-ai (npm) ← HTTP client
│
│ POST /v1/compress
▼
Headroom Proxy (loopback) ← compression pipeline (Python)
│
│ compressed messages
▼
Your TypeScript App
│
│ openai.chat.completions.create(compressed)
▼
LLM Provider
这条数据流在源码中有直接对应:
- SDK 入口 compress():自动检测输入格式(OpenAI / Anthropic / Vercel AI / Gemini),统一转成 OpenAI 格式,经 HeadroomClient 发起
POST /v1/compress,完成后转回原格式。 - 代理路由注册:
/v1/compress默认挂载Depends(_require_loopback)依赖——默认仅回环地址(loopback)可访问。非回环调用方会收到404(而不是 403,路由对扫描器保持不可见)。若你要让同一内网里的 sidecar/网关(如 Kong、LiteLLM)访问该端点,需要启动代理时设置HEADROOM_COMPRESS_ALLOW_REMOTE=1,该开关只解除这一条路由(连同/v1/usage)的回环限制,其余回环路由不受影响;HEADROOM_PROXY_TOKEN入站鉴权仍然生效。
/v1/compress 是一个"仅压缩"端点:它从不向 LLM 提供商发起补全请求,因此不需要提供商 API key;但它会运行本地 ML 模型(Kompress 编码器做 token 保留度打分、Magika 做内容类型分类),这是 代理文档 明确说明的行为。
端点请求/响应契约
TypeScript SDK 封装了如下请求体(字段说明来自 proxy 文档,SDK 侧的构造逻辑见 client.ts 的 _doCompress):
{
"messages": [ ... ], // OpenAI 或 Anthropic 两种线格式均可
"model": "gpt-4o", // 选择 tokenizer 与上下文上限(可带网关前缀,如 bedrock/anthropic.claude-3-5-sonnet)
"token_budget": 8000, // 可选:覆盖上下文上限(对应 SDK 的 tokenBudget 参数)
"config": { // 可选
"mode": "lossy_inline", // ccr | lossy_inline | lossless_then_lossy
"frozen_message_count": 12, // 固定已缓存的头部前缀
"compress_user_messages": false,
"target_ratio": 0.5,
"protect_recent": 2,
"protect_analysis_context": true
}
}
响应字段:
{
"messages": [ ... ], // 压缩后的消息
"tokens_before": 15000,
"tokens_after": 3500,
"tokens_saved": 11500,
"compression_ratio": 0.23, // tokens_after / tokens_before,越低越好
"transforms_applied": ["router:smart_crusher:0.35"],
"transforms_summary": {"router:smart_crusher:0.35": 1},
"ccr_hashes": [] // 仅 mode="ccr" 时非空
}
从源码看,SDK 的 CompressResult 与代理响应的对应关系是 camelCase 一一映射(tokensBefore/tokensAfter/tokensSaved/compressionRatio/transformsApplied/ccrHashes),并在成功路径上把 compressed 置为 true;回退路径则返回 compressed: false。
两个容易踩坑的契约细节(proxy 文档 原文强调):
- 不做格式转换:
messages传什么形状就返回什么形状。OpenAI 形状(role: "tool"+tool_call_id)或 Anthropic 形状(tool_use/tool_result内容块)都可以,但混用会出问题。 system与tools被忽略:Anthropic 会把二者放在带外传输,该端点接收但不压缩、不返回——需要自行保留。若需要 system prompt 压缩或 tool-schema 压缩,应把 Headroom 当作完整代理(passthrough 模式)运行,而不是仅调用/v1/compress。
端点还有两个运维特性:Fail-open——压缩超时时返回 200 并附 compression_skipped: true、skip_reason: "compression_timeout",原始消息原样返回;x-headroom-bypass: true 请求头可跳过压缩。错误响应为 400(字段缺失/非法)、401(HEADROOM_PROXY_TOKEN 校验失败)、404(非回环且未开启 HEADROOM_COMPRESS_ALLOW_REMOTE)、503(压缩失败)。
安装与运行前提
npm install headroom-ai
前提是一个运行中的 Headroom 代理(Python 侧 headroom proxy)。从 package.json 可以确认:
- 运行时要求 Node.js
>= 18(engines字段); - 包名
headroom-ai,当前仓库版本 0.37.0,Apache-2.0 许可; - 零运行时依赖:Vercel AI、OpenAI、Anthropic SDK 全部是
peerDependencies且标记optional: true——用哪个就装哪个,不用就不装; - 子路径导出有四个:
headroom-ai/vercel-ai、headroom-ai/openai、headroom-ai/anthropic,此外源码中还有 Gemini 适配器 并暴露为headroom-ai/gemini(这一点在 wiki 文档中未展开,以 package.json exports 与适配器源码为准)。
SDK 支持 CJS 与 ESM 双格式输出(main/module 分别指向 dist/index.cjs 与 dist/index.js),构建工具为 tsup,测试框架为 vitest(见 vitest 配置)。
快速上手:compress()
import { compress } from 'headroom-ai';
const result = await compress(messages, { model: 'gpt-4o' });
console.log(`Saved ${result.tokensSaved} tokens`);
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: result.messages,
});
完整参数与默认值(默认值已与 client.ts 中的常量 核对):
import { compress } from 'headroom-ai';
const result = await compress(messages, {
model: 'gpt-4o', // 模型名(用于 token 计数)
baseUrl: 'http://localhost:8787', // 代理地址(默认值)
apiKey: 'your-api-key', // 可选,用于启用鉴权的端点
timeout: 30000, // 毫秒(默认 30_000)
fallback: true, // 代理不可达时返回未压缩消息(默认 true)
retries: 1, // 瞬时错误重试次数(默认 1,即最多请求 2 次)
});
result.messages // 压缩后的消息(与输入同格式)
result.tokensBefore // 原始 token 数
result.tokensAfter // 压缩后 token 数
result.tokensSaved // 节省的 token 数
result.compressionRatio // tokensAfter / tokensBefore
result.transformsApplied // 例如 ['router:smart_crusher:0.35']
result.compressed // 回退触发时为 false
消息使用标准 OpenAI chat 格式:{ role, content, tool_calls?, tool_call_id? }。类型定义见 types.ts 中的 OpenAIMessage 联合类型,覆盖 system/user/assistant(可带 tool_calls)/tool(必带 tool_call_id)四种角色,user 消息的 content 还可为 text + image_url 内容块数组。
除了 wiki 文档列出的六项,从 CompressOptions 类型 看还有三个可选参数:
tokenBudget:压缩到不超过该 token 预算(用于会话压缩/compaction 场景),会写入请求体token_budget;hooks:压缩前后钩子(preCompress/computeBiases/postCompress),可注入逐消息压缩偏置;stack:集成来源标识,会以X-Headroom-Stack请求头发送(如"adapter_ts_openai"),便于代理侧区分流量来源。
环境变量
不传 options 时可改用环境变量(client.ts 构造逻辑 的解析顺序:显式 options → 环境变量 → 默认值):
HEADROOM_BASE_URL— 代理地址,默认http://localhost:8787;HEADROOM_API_KEY— 鉴权端点的可选 API key,会以Authorization: Bearer <key>发送。
可复用客户端 HeadroomClient
对高频调用场景,创建一个客户端实例复用其连接配置与超时设置:
import { HeadroomClient } from 'headroom-ai';
const client = new HeadroomClient({
baseUrl: 'http://localhost:8787',
apiKey: 'your-api-key',
});
const r1 = await client.compress(messages1, { model: 'gpt-4o' });
const r2 = await client.compress(messages2, { model: 'gpt-4o' });
compress() 每次调用都会新建一个临时 HeadroomClient(见 compress.ts 第 59 行),因此多调用场景复用实例可以省去重复的环境解析,也可通过 client 选项把已有实例注入 compress()。
从源码看,HeadroomClient 远不止一个压缩客户端,它同时是代理运维 API 的 TypeScript 门面:
- 透传调用:
client.chat.completions.create()(走POST /v1/chat/completions)与client.messages.create()(走POST /v1/messages,Anthropic 风格),支持流式(stream: true时返回 SSE 解析器),并支持headroomMode(audit/optimize/simulate)等x-headroom-mode控制头; - 观测端点:
health()、proxyStats()、prometheusMetrics()、statsHistory()、memoryUsage(); - CCR 取回:
retrieve(hash)从压缩缓存中取回被 CCR 模式移出上下文的原始内容(POST /v1/retrieve),以及getCCRStats()、handleToolCall()(处理 LLM 发出的headroom_retrieve工具调用); - 遥测/反馈/TOIN:
telemetry.*、feedback.getHints(toolName)、toin.getPatterns()等统计接口; - simulate():
client.chat.completions.simulate({ model, messages })以default_mode: "simulate"调用/v1/compress并请求生成 diff 工件,用于"不调 LLM 先看看压缩结果"的演练。
框架适配器
Vercel AI SDK
headroomMiddleware 直接对接 Vercel AI SDK 的 wrapLanguageModel():
import { headroomMiddleware } from 'headroom-ai/vercel-ai';
import { wrapLanguageModel, generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
const model = wrapLanguageModel({
model: openai('gpt-4o'),
middleware: headroomMiddleware(),
});
// 经过该模型的所有调用自动压缩
const { text } = await generateText({ model, messages });
从 适配器源码 看,中间件实现的是 transformParams 钩子:取出 Vercel 内部 prompt → vercelToOpenAI() 转成 OpenAI 格式 → 调 compress()(自动带 stack: "adapter_ts_vercel_ai" 标识)→ 仅在 result.compressed 为真时把 openAIToVercel(result.messages) 写回 prompt;回退时原样返回 params,业务代码零改动。
也可以绕过中间件直接压缩 Vercel 消息:
import { compressVercelMessages } from 'headroom-ai/vercel-ai';
const result = await compressVercelMessages(modelMessages, { model: 'gpt-4o' });
// result.messages 保持 Vercel ModelMessage[] 格式
OpenAI SDK
用 withHeadroom() 包裹 OpenAI 客户端,每次 chat.completions.create() 自动压缩:
import { withHeadroom } from 'headroom-ai/openai';
import OpenAI from 'openai';
const client = withHeadroom(new OpenAI());
// 消息在发送前被压缩——对业务代码透明
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: longConversation,
});
OpenAI 适配器 的实现是三层 Proxy 对象(client → chat → completions),仅劫持 create 方法:取出 params.messages 调 compress()(stack: "adapter_ts_openai"),再用压缩结果替换 messages 后调用原方法。其余方法(embeddings、images、audio 等)全部原样透传。
Anthropic SDK
同样的包裹模式:
import { withHeadroom } from 'headroom-ai/anthropic';
import Anthropic from '@anthropic-ai/sdk';
const client = withHeadroom(new Anthropic());
const response = await client.messages.create({
model: 'claude-sonnet-4-5-20250929',
messages: longConversation,
max_tokens: 1024,
});
仅 messages.create() 被拦截,适配器负责在 Anthropic 内容块格式与 OpenAI 格式之间自动转换。
更多示例
仓库的 examples 目录 提供了 12 个可直接参考的示例脚本,包括基本压缩(basic-compress.ts)、CCR 取回(ccr-retrieve.ts)、多提供者(multi-provider.ts)、OpenAI/Anthropic 适配器(openai-anthropic-adapters.ts)、Vercel 中间件(with-headroom-vercel.ts)、流式对话(streaming-chat.ts)、工具调用 Agent(tool-calling-agent.ts)、共享上下文多 Agent(shared-context-multi-agent.ts)以及模拟演练(simulation-dry-run.ts)等。
错误处理与回退行为
SDK 的错误层次与 Python 侧的 headroom.exceptions 对齐,定义见 errors.ts:
import { compress, HeadroomConnectionError, HeadroomAuthError } from 'headroom-ai';
try {
const result = await compress(messages, { model: 'gpt-4o', fallback: false });
} catch (error) {
if (error instanceof HeadroomAuthError) {
// API key 无效(401)
} else if (error instanceof HeadroomConnectionError) {
// 代理不可达
}
}
除 HeadroomAuthError、HeadroomConnectionError 外,还有携带 statusCode 与 errorType 的 HeadroomCompressError,以及一组按代理错误类型映射的子类:ConfigurationError、ProviderError、StorageError、TokenizationError、CacheError、ValidationError、TransformError。映射逻辑在 mapProxyError:401 一律映射为 HeadroomAuthError,其余按代理返回的 error.type 字符串查表,查不到则兜底为 HeadroomCompressError。
回退矩阵
默认 fallback: true 时,compress() 永远不会阻塞应用。行为矩阵(与 client.ts 的重试循环 一致):
| 场景 | fallback: true(默认) |
fallback: false |
|---|---|---|
| 代理不可达 | 返回未压缩消息,compressed: false |
抛出 HeadroomConnectionError |
| 代理 503 | 重试后返回未压缩消息 | 抛出 HeadroomCompressError |
| API key 无效(401) | 抛出 HeadroomAuthError |
抛出 HeadroomAuthError |
| 请求非法(400) | 抛出 HeadroomCompressError |
抛出 HeadroomCompressError |
源码里的重试语义值得注意:maxAttempts = 1 + retries,即默认 retries: 1 表示最多尝试 2 次;循环中 401(HeadroomAuthError)和所有 4xx(statusCode < 500 的 HeadroomCompressError)立即抛出、不重试——只有连接错误和 5xx 才消耗重试次数。回退结果由 makeFallbackResult 构造:原消息原样返回,tokensBefore/After/Saved 归零,compressionRatio 为 1.0,compressed: false。
多轮调用:别把前缀缓存打爆
/v1/compress 是无状态端点:它不像代理自身的请求路径那样运行 CacheAligner 并跨轮跟踪提供商缓存命中。由于提供商缓存的是你转发出去的字节(已被压缩修改过),你的原始消息与缓存前缀已不再相同,而且压缩强度随消息位置变化(旧的工具结果可能随对话变长而滑出"近期读取保护窗口",被压得更狠),因此"重新压缩原始消息"不能保证复现上一轮的输出。代理文档 给出两条规则:
- 传
config.frozen_message_count= 上游已缓存的头部消息数量; - 回传上一轮转发出去的消息,而不是原始消息。
frozen_message_count会把头部消息按传入内容原样返回,喂原始消息等于给提供商喂了与上轮不同的字节,缓存照样失效。
forwarded = []
def next_turn(new_messages):
r = requests.post(
f"{proxy}/v1/compress",
json={
"messages": forwarded + new_messages,
"model": "claude-sonnet-4-6",
"config": {"frozen_message_count": len(forwarded)},
},
).json()
forwarded[:] = r["messages"] # 下一轮的冻结前缀
return forwarded
在 TypeScript 侧,config 对象可以经 ExtendedClientOptions.config(HeadroomConfig 类型,会 deepSnakeCase 后写入请求体)传入,frozen_message_count 等字段即通过该通道下发。
与 Python SDK 的对比
| 特性 | Python SDK | TypeScript SDK |
|---|---|---|
compress() |
原生(本地运行) | HTTP 客户端(调用代理) |
| 代理 | 内置服务器 | 连接已有代理 |
| Vercel AI SDK | N/A | 中间件适配器 |
| OpenAI SDK | HeadroomClient 封装 |
withHeadroom() 封装 |
| Anthropic SDK | HeadroomClient 封装 |
withHeadroom() 封装 |
| LangChain | HeadroomChatModel |
直接调用 compress() |
| 记忆系统 | 完整(SQLite + HNSW) | 暂无(走代理) |
| MCP 服务器 | 内置 | 暂无 |
| CLI 工具 | headroom proxy、headroom wrap 等 |
N/A(使用 Python CLI) |
OpenClaw 插件:SDK 的生产级用法
headroom-ai 同时驱动 headroom-openclaw 插件。该插件在 assemble() 生命周期钩子中调用 HeadroomClient 压缩 OpenClaw Agent 的上下文。推荐安装方式是 headroom wrap openclaw;直接安装插件的命令为 openclaw plugins install --dangerously-force-unsafe-install headroom-ai/openclaw。插件源码见仓库 plugins/openclaw 目录。
小结
TypeScript SDK 的设计取舍非常清晰:Node 侧只保留一个零依赖的 HTTP 客户端、格式转换层和框架适配器,所有压缩智能留在代理侧。落地时的关键检查清单是——代理已在本机 8787 端口运行;跨主机部署时配置 HEADROOM_COMPRESS_ALLOW_REMOTE=1 并保留 HEADROOM_PROXY_TOKEN;生产代码依赖 fallback: true 的静默降级(用 result.compressed 判断是否真正压缩);多轮会话按 frozen_message_count + "回传已转发消息"两条规则保护前缀缓存。想进一步验证行为,仓库提供了完整的单元测试(client.test.ts、compress.test.ts、errors.test.ts 及四个适配器的测试),以及 12 个可运行的集成示例。
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 StartedRust0626
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