billion-context 0.1.27 → 0.1.28

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.
package/README.md CHANGED
@@ -303,6 +303,7 @@ with no file at all).
303
303
  | `ACP_SESSION_HEADER` | `x-acp-session` | Conversation-id header name |
304
304
  | `ACP_COMPRESS_TOOL` | `1` | Set `0` to disable injecting the compress tool |
305
305
  | `ACP_COMPRESS_NUDGE` | `1` | Set `0` to disable compression nudges |
306
+ | `ACP_REASONING_KEEP` | *(default)* | Responses API only: set `none` to drop all reasoning items. Default routes reasoning through the compression pipeline so it is hidden automatically once its turn is summarized (prevents the unbounded accumulation that broke Codex's prompt-cache prefix). |
306
307
  | `ACP_DEBUG` | `0` | Set `1` for verbose logging |
307
308
  | `ACP_PASSTHROUGH` | `0` | Set `1` to forward without compression |
308
309
  | `ACP_AUTO_UPDATE` | `1` | Set `0` to disable background self-update |
package/README.zh-CN.md CHANGED
@@ -253,6 +253,7 @@ bili --no-auto-update # 本次启动禁用自动更新
253
253
  | `ACP_SESSION_HEADER` | `x-acp-session` | 会话标识 header 名 |
254
254
  | `ACP_COMPRESS_TOOL` | `1` | 设 `0` 禁止注入 compress 工具 |
255
255
  | `ACP_COMPRESS_NUDGE` | `1` | 设 `0` 禁止压缩 nudge |
256
+ | `ACP_REASONING_KEEP` | *(默认)* | 仅 Responses API:设 `none` 丢弃全部 reasoning。默认走压缩管线,turn 被压缩时自动隐藏(防止 reasoning 无限累积撑爆 Codex 的 prompt-cache 前缀)。 |
256
257
  | `ACP_DEBUG` | `0` | 设 `1` 打开详细日志 |
257
258
  | `ACP_PASSTHROUGH` | `0` | 设 `1` 不压缩直接转发 |
258
259
  | `ACP_AUTO_UPDATE` | `1` | 设 `0` 禁用后台自动更新 |
package/dist/index.js CHANGED
@@ -46647,7 +46647,6 @@ function firstImagePart(content) {
46647
46647
  // src/responses.ts
46648
46648
  var OPAQUE_ITEM_TYPES = /* @__PURE__ */ new Set([
46649
46649
  "additional_tools",
46650
- "reasoning",
46651
46650
  "computer_call",
46652
46651
  "computer_call_output",
46653
46652
  "file_search_call",
@@ -46660,6 +46659,9 @@ var OPAQUE_ITEM_TYPES = /* @__PURE__ */ new Set([
46660
46659
  function isOpaqueItem(it2) {
46661
46660
  return OPAQUE_ITEM_TYPES.has(it2.type);
46662
46661
  }
46662
+ function shouldDropAllReasoning() {
46663
+ return (process.env.ACP_REASONING_KEEP ?? "").trim().toLowerCase() === "none";
46664
+ }
46663
46665
  function partText(p2) {
46664
46666
  if (p2.type === "input_text" || p2.type === "output_text") {
46665
46667
  const t = p2.text;
@@ -46691,6 +46693,7 @@ function responsesToCore(body) {
46691
46693
  const systemParts = [];
46692
46694
  const preamble = [];
46693
46695
  const customToolCallIds = /* @__PURE__ */ new Set();
46696
+ let droppedReasoning = 0;
46694
46697
  if (typeof body.instructions === "string" && body.instructions.trim()) {
46695
46698
  systemParts.push(body.instructions);
46696
46699
  }
@@ -46701,7 +46704,7 @@ function responsesToCore(body) {
46701
46704
  const base = deriveMessageId("user", "text", body.input);
46702
46705
  msgs.push({ id: clusters.next(base), role: "user", contentType: "text", text: body.input });
46703
46706
  idx++;
46704
- return { msgs, systemParts, preamble, customToolCallIds };
46707
+ return { msgs, systemParts, preamble, customToolCallIds, droppedReasoning };
46705
46708
  }
46706
46709
  for (const it2 of items) {
46707
46710
  if (isOpaqueItem(it2)) {
@@ -46709,6 +46712,23 @@ function responsesToCore(body) {
46709
46712
  continue;
46710
46713
  }
46711
46714
  switch (it2.type) {
46715
+ case "reasoning": {
46716
+ if (shouldDropAllReasoning()) {
46717
+ droppedReasoning++;
46718
+ break;
46719
+ }
46720
+ const rid = typeof it2.id === "string" ? String(it2.id) : hashId(JSON.stringify(it2));
46721
+ const base = deriveMessageId("assistant", "reasoning", rid);
46722
+ msgs.push({
46723
+ id: clusters.next(base),
46724
+ role: "assistant",
46725
+ contentType: "reasoning",
46726
+ text: rid,
46727
+ rawResponsesItem: it2
46728
+ });
46729
+ idx++;
46730
+ break;
46731
+ }
46712
46732
  case "message": {
46713
46733
  const m2 = it2;
46714
46734
  const text = messageContent(m2.content);
@@ -46809,7 +46829,7 @@ function responsesToCore(body) {
46809
46829
  break;
46810
46830
  }
46811
46831
  }
46812
- return { msgs, systemParts, preamble, customToolCallIds };
46832
+ return { msgs, systemParts, preamble, customToolCallIds, droppedReasoning };
46813
46833
  }
46814
46834
  function coreToResponses(messages, customToolCallIds = /* @__PURE__ */ new Set()) {
46815
46835
  const out = [];
@@ -46843,6 +46863,10 @@ function coreToResponses(messages, customToolCallIds = /* @__PURE__ */ new Set()
46843
46863
  arguments: m2.text ?? ""
46844
46864
  });
46845
46865
  }
46866
+ } else if (m2.contentType === "reasoning") {
46867
+ if (m2.rawResponsesItem) {
46868
+ out.push(m2.rawResponsesItem);
46869
+ }
46846
46870
  }
46847
46871
  } else if (m2.role === "tool") {
46848
46872
  const callId = m2.toolCallId ?? "";
@@ -49796,7 +49820,8 @@ async function handle(req, res, opts, core, config, log2) {
49796
49820
  }
49797
49821
  const url = req.url ?? "";
49798
49822
  const urlPath = url.split("?", 2)[0];
49799
- const protocol = req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") ? "responses" : null : null;
49823
+ const countTokens = isCountTokensRequest(req.method ?? "GET", urlPath, bodyBuffer.length > 0);
49824
+ const protocol = req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") ? "responses" : countTokens ? "anthropic" : null : null;
49800
49825
  const route = resolveUpstream(opts, req.url ?? "", req);
49801
49826
  const upstreamOrigin = route ? route.upstream : opts.upstream;
49802
49827
  let parsed = null;
@@ -49839,7 +49864,7 @@ async function handle(req, res, opts, core, config, log2) {
49839
49864
  const clientLabel = clientConversationHeader(req.headers);
49840
49865
  const session = getSession(sessionId, { protocol, upstreamOrigin, label: clientLabel ?? void 0 });
49841
49866
  await withSessionLock(session, async () => {
49842
- prepared = protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, log2, session) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, log2, session) : prepareResponses(parsed, req, opts, core, reqConfig, log2, session);
49867
+ prepared = countTokens ? prepareCountTokens(parsed, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, log2, session) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, log2, session) : prepareResponses(parsed, req, opts, core, reqConfig, log2, session);
49843
49868
  acquireInFlight(session);
49844
49869
  try {
49845
49870
  await forward(req, res, opts, prepared.body, prepared, core, reqConfig, log2, route, affinity);
@@ -49880,6 +49905,42 @@ function diagNudge(turn, sessionId, tokenCount, limit) {
49880
49905
  const inject = n.shouldInject ? `INJECT T${n.tier ?? "?"}` : "idle";
49881
49906
  return `[${sessionId}] nudge ${inject}: usage=${pct2} (${tokenCount}/${limit}), growth=${growth}/${floor} (ref=${ref}, interval=${interval}), pendingT1=${pendingT1}/${interval}, reason="${n.reason.slice(0, 120)}"`;
49882
49907
  }
49908
+ function isCountTokensRequest(method, urlPath, hasBody) {
49909
+ return method === "POST" && hasBody && process.env.ACP_COUNT_TOKENS_PASSTHROUGH !== "1" && urlPath.endsWith("/messages/count_tokens");
49910
+ }
49911
+ function prepareCountTokens(parsed, core, config, log2, session) {
49912
+ const sessionId = session.id;
49913
+ try {
49914
+ const { msgs, cacheControls } = anthropicToCore(parsed);
49915
+ const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount: session.stats.lastInputTokens, renderTags: "text-only" });
49916
+ const rebuiltMessages = coreToAnthropic(turn.messages, cacheControls);
49917
+ log2("info", `[${sessionId}] count_tokens pruned: ${msgs.length} \u2192 ${turn.messages.length} msgs`);
49918
+ return {
49919
+ body: JSON.stringify({ ...parsed, messages: rebuiltMessages }),
49920
+ session,
49921
+ processedMessages: [],
49922
+ originalMessages: msgs,
49923
+ protocol: "anthropic",
49924
+ stream: false,
49925
+ // MUST stay false + empty processedMessages: keeps forward() on
49926
+ // plain-pipeThrough so usage-capture/markDirty stays unreachable
49927
+ // (else it poisons lastInputTokens with the compressed count and
49928
+ // suppresses the proxy's own nudges).
49929
+ compressInjected: false
49930
+ };
49931
+ } catch (err) {
49932
+ log2("warn", `[${sessionId}] count_tokens prune failed, forwarding unchanged: ${String(err)}`);
49933
+ return {
49934
+ body: JSON.stringify(parsed),
49935
+ session,
49936
+ processedMessages: [],
49937
+ originalMessages: [],
49938
+ protocol: "anthropic",
49939
+ stream: false,
49940
+ compressInjected: false
49941
+ };
49942
+ }
49943
+ }
49883
49944
  function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
49884
49945
  const sessionId = session.id;
49885
49946
  const stream2 = parsed.stream === true;