首页
/ Headroom TypeScript SDK 全解:通过代理压缩 LLM 上下文,无缝接入 Vercel AI、OpenAI 与 Anthropic SDK

Headroom TypeScript SDK 全解:通过代理压缩 LLM 上下文,无缝接入 Vercel AI、OpenAI 与 Anthropic SDK

2026-09-06 19:47:03作者:廉皓灿Ida

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 文档 原文强调):

  1. 不做格式转换messages 传什么形状就返回什么形状。OpenAI 形状(role: "tool" + tool_call_id)或 Anthropic 形状(tool_use/tool_result 内容块)都可以,但混用会出问题。
  2. systemtools 被忽略:Anthropic 会把二者放在带外传输,该端点接收但不压缩、不返回——需要自行保留。若需要 system prompt 压缩或 tool-schema 压缩,应把 Headroom 当作完整代理(passthrough 模式)运行,而不是仅调用 /v1/compress

端点还有两个运维特性:Fail-open——压缩超时时返回 200 并附 compression_skipped: trueskip_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 >= 18engines 字段);
  • 包名 headroom-ai,当前仓库版本 0.37.0,Apache-2.0 许可;
  • 零运行时依赖:Vercel AI、OpenAI、Anthropic SDK 全部是 peerDependencies 且标记 optional: true——用哪个就装哪个,不用就不装;
  • 子路径导出有四个:headroom-ai/vercel-aiheadroom-ai/openaiheadroom-ai/anthropic,此外源码中还有 Gemini 适配器 并暴露为 headroom-ai/gemini(这一点在 wiki 文档中未展开,以 package.json exports 与适配器源码为准)。

SDK 支持 CJS 与 ESM 双格式输出(main/module 分别指向 dist/index.cjsdist/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 解析器),并支持 headroomModeaudit/optimize/simulate)等 x-headroom-mode 控制头;
  • 观测端点health()proxyStats()prometheusMetrics()statsHistory()memoryUsage()
  • CCR 取回retrieve(hash) 从压缩缓存中取回被 CCR 模式移出上下文的原始内容(POST /v1/retrieve),以及 getCCRStats()handleToolCall()(处理 LLM 发出的 headroom_retrieve 工具调用);
  • 遥测/反馈/TOINtelemetry.*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 内部 promptvercelToOpenAI() 转成 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.messagescompress()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) {
    // 代理不可达
  }
}

HeadroomAuthErrorHeadroomConnectionError 外,还有携带 statusCodeerrorTypeHeadroomCompressError,以及一组按代理错误类型映射的子类:ConfigurationErrorProviderErrorStorageErrorTokenizationErrorCacheErrorValidationErrorTransformError。映射逻辑在 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 < 500HeadroomCompressError)立即抛出、不重试——只有连接错误和 5xx 才消耗重试次数。回退结果由 makeFallbackResult 构造:原消息原样返回,tokensBefore/After/Saved 归零,compressionRatio 为 1.0,compressed: false

多轮调用:别把前缀缓存打爆

/v1/compress无状态端点:它不像代理自身的请求路径那样运行 CacheAligner 并跨轮跟踪提供商缓存命中。由于提供商缓存的是你转发出去的字节(已被压缩修改过),你的原始消息与缓存前缀已不再相同,而且压缩强度随消息位置变化(旧的工具结果可能随对话变长而滑出"近期读取保护窗口",被压得更狠),因此"重新压缩原始消息"不能保证复现上一轮的输出。代理文档 给出两条规则:

  1. config.frozen_message_count = 上游已缓存的头部消息数量;
  2. 回传上一轮转发出去的消息,而不是原始消息。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.configHeadroomConfig 类型,会 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 proxyheadroom 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.tscompress.test.tserrors.test.ts 及四个适配器的测试),以及 12 个可运行的集成示例

登录后查看全文
热门项目推荐
相关项目推荐