billion-context 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +40 -30
  2. package/README.zh-CN.md +288 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [English](./README.md) | [中文](./README.zh-CN.md)
2
+
1
3
  # billion-context
2
4
 
3
5
  Universal context-compression proxy for AI coding agents.
@@ -132,10 +134,44 @@ Disable permanently via config (`"autoUpdate": false`) or env
132
134
 
133
135
  ## Configuration
134
136
 
135
- Configuration is read from a JSON file with env-var overrides. Priority:
136
- **env var > config file > built-in default**.
137
+ The proxy is configured via **environment variables** (the recommended way
138
+ for most setups) **or** a JSON config file. Both are fully supported; pick one.
139
+ Priority (highest wins): **CLI flag > env var > config file > built-in default**.
140
+
141
+ - **Env vars** — quickest, great for a single provider, easy to script
142
+ (`.env`, systemd unit, docker `--env`). Just `export ACP_…` and run `bili`.
143
+ - **JSON file** — better when you have many providers with per-model context
144
+ windows (the only place to declare those). A handful of keys (notably
145
+ `providers.*.models` context windows) have no env equivalent.
146
+
147
+ Both can coexist: env vars override individual file keys.
137
148
 
138
- ### Config file
149
+ ### Environment variables (recommended)
150
+
151
+ Every config key has an env override. Set to override the file value (or to run
152
+ with no file at all).
153
+
154
+ | Env | Default | Description |
155
+ |-----|---------|-------------|
156
+ | `ACP_PORT` / `PORT` | `8787` | Listen port |
157
+ | `ACP_HOST` | `127.0.0.1` | Listen host |
158
+ | `ACP_UPSTREAM` | `https://api.anthropic.com` | Default upstream |
159
+ | `ACP_PROVIDERS` | *(none)* | Path to a legacy providers JSON file (overrides `providers` in config) |
160
+ | `ACP_MODEL_CONTEXT_LIMIT` | `200000` | Global fallback context window (only used when no provider/model match) |
161
+ | `ACP_SESSION_HEADER` | `x-acp-session` | Conversation-id header name |
162
+ | `ACP_COMPRESS_TOOL` | `1` | Set `0` to disable injecting the compress tool |
163
+ | `ACP_COMPRESS_NUDGE` | `1` | Set `0` to disable compression nudges |
164
+ | `ACP_DEBUG` | `0` | Set `1` for verbose logging |
165
+ | `ACP_PASSTHROUGH` | `0` | Set `1` to forward without compression |
166
+ | `ACP_AUTO_UPDATE` | `1` | Set `0` to disable background self-update |
167
+ | `ACP_LOG_FILE` | *XDG state path* | Log file path (`off` disables the file, keeps stderr) |
168
+ | `ACP_DUMP_SSE` | *(none)* | Directory to dump SSE for debugging |
169
+ | `BILI_PERSIST` | `1` | Set `0` to disable session persistence (in-memory only, lost on restart) |
170
+ | `BILI_PERSIST_DEBOUNCE_MS` | `500` | Debounce window for writes to disk (ms) |
171
+ | `BILI_MAX_SESSIONS` | `256` | Max sessions held in memory (LRU eviction; disk is source of truth) |
172
+ | `BILI_SESSIONS_DIR` | *(XDG data dir)* | Directory for persisted session state |
173
+
174
+ ### Config file (optional)
139
175
 
140
176
  Location (XDG Base Directory):
141
177
 
@@ -245,32 +281,6 @@ codex
245
281
  The `/zhipu/...` prefix tells the proxy to route to the `zhipu` provider; the
246
282
  remaining `/api/coding/paas/v4/...` path is preserved.
247
283
 
248
- ### Environment variables (override the config file)
249
-
250
- Every config key has an env-var override. Set to override the file value.
251
-
252
- | Env | Default | Description |
253
- |-----|---------|-------------|
254
- | `ACP_PORT` / `PORT` | `8787` | Listen port |
255
- | `ACP_HOST` | `127.0.0.1` | Listen host |
256
- | `ACP_UPSTREAM` | `https://api.anthropic.com` | Default upstream |
257
- | `ACP_PROVIDERS` | *(none)* | Path to a legacy providers JSON file (overrides `providers` in config) |
258
- | `ACP_MODEL_CONTEXT_LIMIT` | `200000` | Global fallback context window (only used when no provider/model match) |
259
- | `ACP_SESSION_HEADER` | `x-acp-session` | Conversation-id header name |
260
- | `ACP_COMPRESS_TOOL` | `1` | Set `0` to disable injecting the compress tool |
261
- | `ACP_COMPRESS_NUDGE` | `1` | Set `0` to disable compression nudges |
262
- | `ACP_CONDENSE_ENABLED` | `1` | Set `0` to disable tool-result condensing |
263
- | `ACP_KEEP_RECENT_TOOL_RESULTS` | `6` | Tool results kept verbatim before condensing |
264
- | `ACP_MIN_CHARS_TO_CONDENSE` | `1500` | Condense tool results longer than this |
265
- | `ACP_MAX_KEPT_CHARS` | `400` | Max chars kept when condensing a tool result |
266
- | `ACP_DEBUG` | `0` | Set `1` for verbose logging |
267
- | `ACP_PASSTHROUGH` | `0` | Set `1` to forward without compression |
268
- | `ACP_DUMP_SSE` | *(none)* | Directory to dump SSE for debugging |
269
- | `BILI_PERSIST` | `1` | Set `0` to disable session persistence (in-memory only, lost on restart) |
270
- | `BILI_PERSIST_DEBOUNCE_MS` | `500` | Debounce window for writes to disk (ms) |
271
- | `BILI_MAX_SESSIONS` | `256` | Max sessions held in memory (LRU eviction; disk is source of truth) |
272
- | `BILI_SESSIONS_DIR` | *(XDG data dir)* | Directory for persisted session state |
273
-
274
284
  ### Notes on provider names
275
285
 
276
286
  - Must start with a letter, contain only letters/digits/`-`/`_`.
@@ -278,7 +288,7 @@ Every config key has an env-var override. Set to override the file value.
278
288
  are rejected to avoid colliding with real API path segments.
279
289
  - The provider name can appear anywhere in the path; the longest match wins.
280
290
 
281
- ### Session identity
291
+ ## How sessions work
282
292
 
283
293
  The proxy needs a stable per-conversation identifier to isolate compression
284
294
  state across concurrent users/accounts. It derives one from four dimensions
@@ -0,0 +1,288 @@
1
+ [English](./README.md) | [中文](./README.zh-CN.md)
2
+
3
+ # billion-context
4
+
5
+ AI 编程助手的通用上下文压缩代理。
6
+
7
+ `billion-context` 架在**任意**编程助手与其模型 API 之间,用 [acp-kernel](https://github.com/ranxianglei/acp-kernel) 压缩重写 Anthropic/OpenAI 流。任何能设置 base URL 的助手开箱即用 —— **无需为每个助手写适配代码**。
8
+
9
+ ## 为什么
10
+
11
+ 长编程会话会把上下文撑爆。各家 provider 按 token 计费,一旦超过上下文窗口,会话质量下降甚至崩掉。`billion-context` 把已消耗的对话压缩成分层摘要,让你**一个会话连跑数天** —— 海量 token 穿过同一个上下文窗口。
12
+
13
+ 与宿主自带的摘要器不同,这里的压缩**增量、可逆、对前缀缓存友好**:摘要在小范围内写入,可按需解压,缓存前缀保持完整。
14
+
15
+ ## 工作原理
16
+
17
+ ```
18
+ 编程助手 (Claude Code / Codex / Cursor / Aider ...)
19
+ │ 你把助手的 base URL 指向 proxy
20
+
21
+ ┌─────────────────┐
22
+ │ billion-context│ 1. 解析请求(Anthropic 或 OpenAI 格式)
23
+ │ proxy │ 2. 对对话运行 acp-kernel 压缩
24
+ │ │ 3. 注入 `compress` 工具 + 压缩哲学
25
+ │ │ 4. 转发到真实模型 API
26
+ │ │ 5. 重写流式响应
27
+ └─────────────────┘
28
+
29
+
30
+ 真实模型 API (Anthropic / OpenAI / 兼容厂商)
31
+ ```
32
+
33
+ 代理向对话注入四个上下文管理工具(`compress`、`decompress`、`search_context`、`acp_status`)。模型在对话增长时调用 `compress`,代理在服务端执行 —— 压缩后的范围在下一轮之前折叠进对话历史。
34
+
35
+ ## 安装
36
+
37
+ ```bash
38
+ npm install -g billion-context
39
+ ```
40
+
41
+ 这会安装 `bili` 命令(`bili-proxy` 保留为别名)。
42
+
43
+ ## 用法
44
+
45
+ ### 启动代理
46
+
47
+ ```bash
48
+ bili
49
+ ```
50
+
51
+ 就这么简单。代理从 `~/.config/billion-context/billion-context.json`(XDG)读取配置,监听 `127.0.0.1:8787`。如果配置文件还不存在,会用合理默认值,并打印期望的文件位置。
52
+
53
+ ### 快速覆盖(命令行参数)
54
+
55
+ ```bash
56
+ bili --port 9000 # 改监听端口
57
+ bili --host 0.0.0.0 # 监听所有网卡
58
+ bili --debug # 详细日志(也可在配置里设 "debug": true)
59
+ bili --passthrough # 不压缩直接转发(冒烟测试模式)
60
+ bili --config ~/my-bili.json # 用别的配置文件
61
+ ```
62
+
63
+ 参数优先级高于配置文件和环境变量。`bili --help` 列出全部。
64
+
65
+ ### 把你的助手指向代理
66
+
67
+ 代理按 **URL 路径里的 provider 名**路由。把助手的 base URL 设为 `http://localhost:8787/<provider>/...`,代理就转发到该 provider(如何在配置里声明 provider 见[配置](#配置))。
68
+
69
+ #### Claude Code(Anthropic)
70
+
71
+ ```bash
72
+ export ANTHROPIC_BASE_URL=http://localhost:8787/anthropic
73
+ export ANTHROPIC_API_KEY=sk-ant-... # 真实 key —— 原样透传
74
+ claude
75
+ ```
76
+
77
+ #### Codex / 任意 OpenAI 兼容助手(智谱 / openai / deepseek)
78
+
79
+ ```bash
80
+ export OPENAI_BASE_URL=http://localhost:8787/zhipu/api/coding/paas/v4
81
+ export OPENAI_API_KEY=<你的真实智谱 key> # 原样透传
82
+ codex
83
+ ```
84
+
85
+ `/zhipu/...` 前缀告诉代理路由到 `zhipu` provider;剩余路径保留不变。
86
+
87
+ #### Cursor / Aider / 其他
88
+
89
+ 在助手设置里把 base URL 设为 `http://localhost:8787/<provider>`。
90
+
91
+ ### 调试
92
+
93
+ 三种方式打开详细日志(优先级:参数 > 环境变量 > 配置):
94
+
95
+ 1. **命令行参数**(最快):`bili --debug`
96
+ 2. **环境变量**:`ACP_DEBUG=1 bili`
97
+ 3. **配置文件**:在 `billion-context.json` 里设 `"debug": true`
98
+
99
+ 详细模式会打印每次 `processTurn`(标签计数、token 用量)、nudge 决策(growth/usage/pendingT1/shouldInject)、客户端 headers 和 SSE 重写。
100
+
101
+ ### 日志文件
102
+
103
+ 所有日志**默认同时写入文件**:`~/.local/state/billion-context/bili.log`(XDG state 目录)。同时仍打印到 stderr,所以前台运行 `bili start` 时终端也能看到。
104
+
105
+ ```bash
106
+ bili start # 日志 → ~/.local/state/billion-context/bili.log + 终端
107
+ bili update # (见下文)
108
+ # 配置: "logFile": "/custom/path.log"
109
+ # 环境变量: ACP_LOG_FILE=/custom/path.log (或 ACP_LOG_FILE=off 关闭文件,只保留 stderr)
110
+ ```
111
+
112
+ 文件超过 10 MB 自动轮转(重命名为 `bili.log.old`)。每个请求的缓存命中统计会以 `[acp-usage] round N input=X cached=Y (cache hit Z%)` 打印,可直接从日志衡量前缀缓存健康度。
113
+
114
+ ### 自动更新
115
+
116
+ 代理启动时和每 3 分钟检查 npm 是否有新版本。发现新版本就全局安装(`npm install -g`)并打印通知 —— **重启 `bili` 才能生效**。
117
+
118
+ ```bash
119
+ bili update # 立即检查并安装(手动,跳过 3 分钟节流)
120
+ bili --no-auto-update # 本次启动禁用自动更新
121
+ ```
122
+
123
+ 永久禁用:配置(`"autoUpdate": false`)或环境变量(`ACP_AUTO_UPDATE=0`)。
124
+
125
+ ## 配置
126
+
127
+ 代理通过**环境变量**(大多数场景的推荐方式)**或** JSON 配置文件配置。两者都完全支持,任选其一。优先级(高优先级覆盖低优先级):**命令行参数 > 环境变量 > 配置文件 > 内置默认**。
128
+
129
+ - **环境变量** —— 最快,适合单 provider,易于脚本化(`.env`、systemd unit、docker `--env`)。`export ACP_…` 然后运行 `bili` 即可。
130
+ - **JSON 文件** —— 当你有多个 provider 且需要按模型声明 context 窗口时更合适(这是唯一能声明它们的地方)。少数键(尤其是 `providers.*.models` 的 context 窗口)没有对应的环境变量。
131
+
132
+ 两者可共存:环境变量覆盖文件里的个别键。
133
+
134
+ ### 环境变量(推荐)
135
+
136
+ 每个配置键都有环境变量覆盖。设置后覆盖文件值(或不配文件直接跑)。
137
+
138
+ | 环境变量 | 默认值 | 说明 |
139
+ |-----|---------|-------------|
140
+ | `ACP_PORT` / `PORT` | `8787` | 监听端口 |
141
+ | `ACP_HOST` | `127.0.0.1` | 监听地址 |
142
+ | `ACP_UPSTREAM` | `https://api.anthropic.com` | 默认上游 |
143
+ | `ACP_PROVIDERS` | *(无)* | 旧版 providers JSON 文件路径(覆盖配置里的 `providers`) |
144
+ | `ACP_MODEL_CONTEXT_LIMIT` | `200000` | 全局兜底 context 窗口(仅当 provider/model 都不匹配时用) |
145
+ | `ACP_SESSION_HEADER` | `x-acp-session` | 会话标识 header 名 |
146
+ | `ACP_COMPRESS_TOOL` | `1` | 设 `0` 禁止注入 compress 工具 |
147
+ | `ACP_COMPRESS_NUDGE` | `1` | 设 `0` 禁止压缩 nudge |
148
+ | `ACP_DEBUG` | `0` | 设 `1` 打开详细日志 |
149
+ | `ACP_PASSTHROUGH` | `0` | 设 `1` 不压缩直接转发 |
150
+ | `ACP_AUTO_UPDATE` | `1` | 设 `0` 禁用后台自动更新 |
151
+ | `ACP_LOG_FILE` | *XDG state 路径* | 日志文件路径(`off` 关闭文件,只保留 stderr) |
152
+ | `ACP_DUMP_SSE` | *(无)* | 转储 SSE 用于调试的目录 |
153
+ | `BILI_PERSIST` | `1` | 设 `0` 禁用会话持久化(纯内存,重启丢失) |
154
+ | `BILI_PERSIST_DEBOUNCE_MS` | `500` | 写磁盘的防抖窗口(毫秒) |
155
+ | `BILI_MAX_SESSIONS` | `256` | 内存中保留的最大会话数(LRU 淘汰;磁盘是真相源) |
156
+ | `BILI_SESSIONS_DIR` | *(XDG data 目录)* | 持久化会话状态的目录 |
157
+
158
+ ### 配置文件(可选)
159
+
160
+ 位置(XDG 基础目录):
161
+
162
+ - **Linux:** `~/.config/billion-context/billion-context.json`
163
+ - 用 `XDG_CONFIG_HOME` 或 `BILI_CONFIG_FILE` 覆盖
164
+
165
+ 配置文件是单个 JSON 对象。示例:
166
+
167
+ ```json
168
+ {
169
+ "port": 8787,
170
+ "host": "127.0.0.1",
171
+ "providers": {
172
+ "zhipu": {
173
+ "url": "https://open.bigmodel.cn",
174
+ "models": {
175
+ "glm-5.2": { "context": 1000000, "output": 131072 },
176
+ "glm-5.1": { "context": 200000, "output": 131072 }
177
+ }
178
+ },
179
+ "anthropic": "https://api.anthropic.com",
180
+ "deepseek": "https://api.deepseek.com"
181
+ }
182
+ }
183
+ ```
184
+
185
+ ### 顶层键
186
+
187
+ | 键 | 默认值 | 说明 |
188
+ |------|---------|-------------|
189
+ | `port` | `8787` | 代理监听端口 |
190
+ | `host` | `127.0.0.1` | 代理监听地址 |
191
+ | `upstream` | `https://api.anthropic.com` | 无路由匹配时的默认上游 |
192
+ | `sessionHeader` | `x-acp-session` | 客户端可发来标识会话的 header 名 |
193
+ | `log` | `true` | 启用请求日志 |
194
+ | `debug` | `false` | 详细日志(等同 `ACP_DEBUG=1`) |
195
+ | `passthrough` | `false` | 不压缩直接转发(等同 `ACP_PASSTHROUGH=1`) |
196
+ | `providers` | *(无)* | Provider 路由 —— 见下文 |
197
+ | `compress` | *(见默认值)* | `{ injectTool, injectNudge }` |
198
+
199
+ ### Providers(URL 路由 + 按模型 context)
200
+
201
+ `providers` 把路由名映射到一个纯 URL 字符串(简单)或一个带 `url` + 可选按模型 context 窗口的对象(推荐)。
202
+
203
+ **简单形式** —— provider 名 → URL:
204
+ ```json
205
+ { "deepseek": "https://api.deepseek.com" }
206
+ ```
207
+
208
+ **完整形式** —— provider 名 → `{ url, models }`:
209
+ ```json
210
+ {
211
+ "zhipu": {
212
+ "url": "https://open.bigmodel.cn",
213
+ "models": {
214
+ "glm-5.2": { "context": 1000000, "output": 131072 },
215
+ "glm-5.1": { "context": 200000 }
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ 同一个模型在不同 provider 后面可以有不同 context 窗口(例如 relay 把模型包成更大窗口)。`context` 是**输入 context 上限**(压缩器用它判断何时 nudge);`output` 是最大输出 token。两者都可选;缺失值回退到内置模型表,再回退到 `modelContextLimit`。
222
+
223
+ > **为什么要声明 context?** LLM 的 `/models` API **不返回** context 窗口(已跨 OpenAI、Anthropic、智谱、comfly 验证)。它们是文档级信息。值错了(例如把 GLM-5.2 猜成 128K 而非 1M)会导致频繁误触发压缩。按 provider + 模型声明能让代理匹配客户端自己用的注册表。
224
+
225
+ **API key 永远不存进代理** —— 助手发什么 key,原样透传给上游。
226
+
227
+ ### 路由
228
+
229
+ 用 provider 名作为路径段把任意助手指向代理。代理剥离该名字并转发到该 provider 的根 URL。
230
+
231
+ ```
232
+ 助手 baseURL: http://localhost:8787/zhipu/api/coding/paas/v4
233
+ └──────────┬──────────┘└────────┬────────┘
234
+ 代理 host 剩余路径
235
+ + provider 名 (原样转发)
236
+ ```
237
+
238
+ #### Claude Code(Anthropic)
239
+
240
+ ```bash
241
+ export ANTHROPIC_BASE_URL=http://localhost:8787/anthropic
242
+ export ANTHROPIC_API_KEY=sk-ant-... # 真实 key —— 原样透传
243
+ claude
244
+ ```
245
+
246
+ #### Codex / 任意 OpenAI 兼容助手(智谱 / openai / deepseek)
247
+
248
+ ```bash
249
+ export OPENAI_BASE_URL=http://localhost:8787/zhipu/api/coding/paas/v4
250
+ export OPENAI_API_KEY=<你的真实智谱 key> # 原样透传
251
+ codex
252
+ ```
253
+
254
+ `/zhipu/...` 前缀告诉代理路由到 `zhipu` provider;剩余 `/api/coding/paas/v4/...` 路径保留不变。
255
+
256
+ ### Provider 名注意事项
257
+
258
+ - 必须以字母开头,只含字母/数字/`-`/`_`。
259
+ - 保留字(`v1`、`chat`、`completions`、`messages`、`models`、`api`)被拒绝,以免与真实 API 路径段冲突。
260
+ - provider 名可出现在路径任意位置;最长匹配优先。
261
+
262
+ ## 会话机制
263
+
264
+ 代理需要一个稳定的、按会话标识的 ID,以便在多个用户/账号并发时隔离压缩状态。它从四个维度推导一个(见 `src/session-id.ts`):**协议 × 上游 origin × API key × 会话**。前三个防止跨账号 / 跨 provider 串数据;会话维度来自客户端发送的内容。
265
+
266
+ 不同客户端发送的东西不同:
267
+
268
+ | 客户端 | 发会话 id 吗? | 来源 | 安全性 |
269
+ |---|---|---|---|
270
+ | **Codex**(0.147+) | ✅ 发 | `body.session_id`(按会话 UUID) | ✅ 安全 |
271
+ | **OpenCode** | ✅ 发 | `x-session-affinity` header(`ses_…`) | ✅ 安全 |
272
+ | **pi** | ❌ **不发** | 无 | ⚠️ **有碰撞风险** |
273
+
274
+ 客户端发显式 id 时,代理直接用它。不发时(pi),代理回退到对首条用户消息做哈希 —— 于是两个开头相同的会话会塌缩到同一个 session。这**不会损坏数据**(每条消息的 ref 用独立的内容指纹,保持稳定),但会让 nudge/压缩时机跑偏,偶尔过早回收某个 block。它是自愈的:最坏情况是压缩效率降低,绝不丢数据。
275
+
276
+ 用于上游粘性路由时,客户端不发会话 header 时代理会合成一个(`x-session-id: ses_<hash>`),让缓存池 / 负载均衡器仍能拿到稳定 key。
277
+
278
+ **建议:** Codex 和 OpenCode 可以安全地通过代理并发跑很多会话。pi 单个 agent 没问题,但因碰撞风险**不建议**并发多会话 —— 直到 pi 自己长出 session-id 信号。pi 多 agent 场景下,每个会话发一个显式 `x-acp-session` header 来避免碰撞。
279
+
280
+ ## 状态
281
+
282
+ 早期。协议处理和压缩已通过 mock 测试(141 项通过)。真实模型集成测试是下一里程碑。预期会有粗糙的地方。
283
+
284
+ pi 扩展模式(进程内、更紧密集成、参考实现)见 [billion-context-pi](https://github.com/ranxianglei/billion-context-pi)。
285
+
286
+ ## 许可证
287
+
288
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "billion-context",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Universal context-compression proxy for AI coding agents. Sits between any agent and its model API, rewriting Anthropic/OpenAI streams with acp-kernel compression. Any agent that can set a base URL (Claude Code, Codex, Cursor, Aider) works out of the box.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",