billion-context 0.1.88-pr.558.418 → 0.1.88-pr.571.415

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
@@ -112,6 +112,25 @@ turn is a semantic mismatch the model tolerates (it is clearly marked
112
112
  summaries (their tool call isn't in the agent's history and the agent's view
113
113
  skips `acp_summary`) — but that needs the id match above, which doesn't occur.
114
114
 
115
+ ### Preflight hold (#568)
116
+
117
+ Preflight compression runs **before** the proxy sends any response bytes, and on
118
+ big sessions it takes minutes (summarization round-trips to the same upstream).
119
+ That exceeds client-side header timeouts (undici's default is 300s): the client
120
+ aborts, the proxy drops the in-flight compression and never forwards — a
121
+ repeating death loop every few minutes.
122
+
123
+ So when preflight outlives a grace period (default **30s**, override with
124
+ `BILI_PREFLIGHT_HOLD_MS`), the proxy commits the response early: streams get
125
+ `200` + `text/event-stream` + SSE comment keep-alives (`: bili-preflight`, every
126
+ 15s); non-stream requests get `200` + `application/json` + whitespace padding.
127
+ Early commits carry an `x-bili-preflight: compressing` header. Once the status
128
+ is committed, late failures can no longer change it, so they are delivered
129
+ **in-band** instead: a protocol error event (`preflight_compress_failed`) for
130
+ streams, the identical structured error body under the already-sent 200 for
131
+ JSON. Fast paths (preflight finishes within the grace) are unchanged and keep
132
+ full status-code fidelity.
133
+
115
134
  ## Which do I need?
116
135
 
117
136
  Pick by your client:
package/dist/index.js CHANGED
@@ -51147,11 +51147,6 @@ function estimateRawBodyTokens(parsed) {
51147
51147
  walk(parsed);
51148
51148
  return tokens;
51149
51149
  }
51150
- function estimateCoreMessagesUpper(messages) {
51151
- let chars = 0;
51152
- for (const m2 of messages) chars += (m2.text ?? "").length;
51153
- return chars;
51154
- }
51155
51150
  function rangeChars2(messages, startIdx, endIdx) {
51156
51151
  let chars = 0;
51157
51152
  for (let i = startIdx; i <= endIdx && i < messages.length; i++) {
@@ -51171,16 +51166,16 @@ ${text}`);
51171
51166
  }
51172
51167
  return parts.join("\n\n");
51173
51168
  }
51174
- function splitChunks(messages, startIdx, endIdx, budget, minUnits, countText = defaultCountTokens) {
51169
+ function splitChunks(messages, startIdx, endIdx, budgetTokens) {
51175
51170
  const chunks = [];
51176
51171
  let cur = startIdx;
51177
51172
  while (cur <= endIdx) {
51178
- let total = 0;
51173
+ let tokens = 0;
51179
51174
  let last = cur;
51180
51175
  for (let i = cur; i <= endIdx; i++) {
51181
- const t = countText(messages[i].text ?? "");
51182
- if (total + t > budget && i > cur && (minUnits <= 0 || total >= minUnits)) break;
51183
- total += t;
51176
+ const t = defaultCountTokens(messages[i].text ?? "");
51177
+ if (tokens + t > budgetTokens && i > cur) break;
51178
+ tokens += t;
51184
51179
  last = i;
51185
51180
  }
51186
51181
  chunks.push([cur, last]);
@@ -51267,14 +51262,11 @@ function noEmergencyTruncate(config) {
51267
51262
  }
51268
51263
  async function preflightCompress(deps, messages) {
51269
51264
  const limit = deps.config.modelContextLimit;
51270
- const result = { compressedRanges: 0, savedTokens: 0, payloadEstimate: estimateCoreMessages(messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0), fitsWindow: true };
51265
+ const result = { compressedRanges: 0, savedTokens: 0, payloadEstimate: estimateCoreMessages(messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0) };
51271
51266
  if (limit <= 0) return result;
51272
51267
  const budget = Math.max(MIN_CHUNK_TOKENS, Math.floor(limit * CHUNK_FRACTION));
51273
51268
  const minChars = deps.config.compress.minCompressRange;
51274
- const baselineKnown = deps.unknownBaseline !== true;
51275
- const countText = baselineKnown ? defaultCountTokens : (text) => text.length;
51276
- let currentTokens = baselineKnown ? deps.session.stats.lastInputTokens : estimateCoreMessagesUpper(messages);
51277
- let finalUpper = baselineKnown ? 0 : estimateCoreMessagesUpper(messages);
51269
+ let currentTokens = deps.session.stats.lastInputTokens;
51278
51270
  let startTokens = -1;
51279
51271
  let failure;
51280
51272
  let activeConfig = deps.config;
@@ -51298,10 +51290,6 @@ async function preflightCompress(deps, messages) {
51298
51290
  });
51299
51291
  deps.session.state = turn.state;
51300
51292
  currentTokens = Math.max(deps.session.stats.lastInputTokens, estimateCoreMessages(turn.messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0));
51301
- if (!baselineKnown) {
51302
- finalUpper = estimateCoreMessagesUpper(turn.messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0);
51303
- currentTokens = Math.max(currentTokens, finalUpper);
51304
- }
51305
51293
  result.payloadEstimate = estimateCoreMessages(turn.messages) + (deps.imageFloor ?? 0) + (deps.wireOverhead ?? 0);
51306
51294
  if (startTokens < 0) startTokens = currentTokens;
51307
51295
  if (currentTokens < limit) break;
@@ -51337,7 +51325,7 @@ async function preflightCompress(deps, messages) {
51337
51325
  continue;
51338
51326
  }
51339
51327
  rangesTried += 1;
51340
- for (const [cs2, ce2] of splitChunks(messages, startIdx, endIdx, budget, baselineKnown ? 0 : minChars, countText)) {
51328
+ for (const [cs2, ce2] of splitChunks(messages, startIdx, endIdx, budget)) {
51341
51329
  if (currentTokens < limit) break;
51342
51330
  if (deps.signal?.aborted) {
51343
51331
  failure = ABORTED_FAILURE;
@@ -51396,8 +51384,7 @@ async function preflightCompress(deps, messages) {
51396
51384
  break;
51397
51385
  }
51398
51386
  const compressed = deps.session.stats.compressCreditTokens - creditBefore;
51399
- const folded = baselineKnown ? compressed : rangeChars2(messages, cs2, ce2);
51400
- currentTokens = Math.max(0, currentTokens - folded + countText(summary));
51387
+ currentTokens = Math.max(0, currentTokens - compressed + defaultCountTokens(summary));
51401
51388
  deps.session.stats.lastInputTokens += defaultCountTokens(summary);
51402
51389
  appliedThisRound += 1;
51403
51390
  result.compressedRanges += 1;
@@ -51422,7 +51409,6 @@ async function preflightCompress(deps, messages) {
51422
51409
  if (result.compressedRanges > 0) deps.session.stats.lastInputTokens = currentTokens;
51423
51410
  result.savedTokens = Math.max(0, startTokens - currentTokens);
51424
51411
  if (currentTokens >= limit) result.failure = failure;
51425
- result.fitsWindow = baselineKnown ? result.payloadEstimate < limit : finalUpper < limit;
51426
51412
  return result;
51427
51413
  }
51428
51414
 
@@ -54717,6 +54703,35 @@ data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "end_turn"
54717
54703
  safeWrite(res, `event: message_stop
54718
54704
  data: ${JSON.stringify({ type: "message_stop" })}
54719
54705
 
54706
+ `);
54707
+ }
54708
+ } catch {
54709
+ } finally {
54710
+ try {
54711
+ res.end();
54712
+ } catch {
54713
+ }
54714
+ }
54715
+ }
54716
+ function emitPreflightError(res, protocol, error, log2) {
54717
+ const err2 = { type: "server_error", code: "preflight_compress_failed", message: error.message, retryable: error.retryable };
54718
+ log2?.(`[acp-proxy: preflight failed after early response commit \u2014 delivering in-band: ${error.message}]`);
54719
+ try {
54720
+ if (protocol === "openai") {
54721
+ safeWrite(res, `data: ${JSON.stringify({ error: err2 })}
54722
+
54723
+ data: [DONE]
54724
+
54725
+ `);
54726
+ } else if (protocol === "responses") {
54727
+ safeWrite(res, `event: error
54728
+ data: ${JSON.stringify({ type: "error", code: err2.code, message: err2.message })}
54729
+
54730
+ `);
54731
+ } else {
54732
+ safeWrite(res, `event: error
54733
+ data: ${JSON.stringify({ type: "error", error: { type: "server_error", code: err2.code, message: err2.message } })}
54734
+
54720
54735
  `);
54721
54736
  }
54722
54737
  } catch {
@@ -58356,24 +58371,41 @@ ${bodyBuffer.toString("utf8")}`);
58356
58371
  parsed.model,
58357
58372
  route,
58358
58373
  affinity,
58359
- anonAffinity !== null,
58360
58374
  log2,
58361
58375
  instanceId
58362
58376
  );
58363
58377
  if (isPreflightFailFast(outcome)) {
58364
- if (outcome.respond && !res.headersSent && !res.destroyed) {
58365
- res.writeHead(outcome.status, {
58366
- "content-type": "application/json",
58367
- ...outcome.status === 503 ? { "retry-after": "30" } : {}
58368
- });
58369
- res.end(JSON.stringify({
58370
- error: {
58371
- type: "server_error",
58372
- code: "preflight_compress_failed",
58373
- message: outcome.message,
58374
- retryable: outcome.retryable
58378
+ if (outcome.respond && !res.destroyed) {
58379
+ if (res.headersSent) {
58380
+ if (prepared.stream) {
58381
+ emitPreflightError(res, prepared.protocol, { message: outcome.message, retryable: outcome.retryable }, (m2) => log2("warn", m2));
58382
+ } else {
58383
+ try {
58384
+ res.end(JSON.stringify({
58385
+ error: {
58386
+ type: "server_error",
58387
+ code: "preflight_compress_failed",
58388
+ message: outcome.message,
58389
+ retryable: outcome.retryable
58390
+ }
58391
+ }));
58392
+ } catch {
58393
+ }
58375
58394
  }
58376
- }));
58395
+ } else {
58396
+ res.writeHead(outcome.status, {
58397
+ "content-type": "application/json",
58398
+ ...outcome.status === 503 ? { "retry-after": "30" } : {}
58399
+ });
58400
+ res.end(JSON.stringify({
58401
+ error: {
58402
+ type: "server_error",
58403
+ code: "preflight_compress_failed",
58404
+ message: outcome.message,
58405
+ retryable: outcome.retryable
58406
+ }
58407
+ }));
58408
+ }
58377
58409
  }
58378
58410
  return;
58379
58411
  }
@@ -58492,11 +58524,6 @@ function armHostUsageCredit(session, originalMessages, processedMessages, log2)
58492
58524
  log2("info", `[${session.id}] host usage backfill armed: +${session.hostCreditTokens} tok (forwarded view is folded); host usage will report the uncompressed baseline`);
58493
58525
  }
58494
58526
  }
58495
- function effectiveTokenCount(session, msgs) {
58496
- if (session.stats.lastInputTokens > 0) return session.stats.lastInputTokens;
58497
- if (!session.metadata.anonymousPrefixAffinity) return 0;
58498
- return estimateCoreMessagesUpper(msgs);
58499
- }
58500
58527
  function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, session, pluginMode) {
58501
58528
  const sessionId = session.id;
58502
58529
  const stream2 = parsed.stream === true;
@@ -58521,7 +58548,7 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
58521
58548
  const { msgs, cacheControls } = anthropicToCore(parsed);
58522
58549
  originalMessages = msgs;
58523
58550
  extractSystem(parsed.system);
58524
- const tokenCount = effectiveTokenCount(session, msgs);
58551
+ const tokenCount = session.stats.lastInputTokens;
58525
58552
  const activeBefore = new Set(session.state.blocks.filter((b2) => b2.active).map((b2) => b2.blockId));
58526
58553
  const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
58527
58554
  session.state = turn.state;
@@ -58642,7 +58669,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
58642
58669
  const { msgs, systemText } = openaiToCore(parsed);
58643
58670
  openaiSystemText = systemText;
58644
58671
  originalMessages = msgs;
58645
- const tokenCount = effectiveTokenCount(session, msgs);
58672
+ const tokenCount = session.stats.lastInputTokens;
58646
58673
  const activeBefore = new Set(session.state.blocks.filter((b2) => b2.active).map((b2) => b2.blockId));
58647
58674
  const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags: "text-only" });
58648
58675
  session.state = turn.state;
@@ -58747,7 +58774,7 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
58747
58774
  if (process.env.ACP_DEBUG) {
58748
58775
  log2("info", `[${sessionId}] input items: ${Array.isArray(parsed.input) ? parsed.input.map((i) => i.type).join(",") : "(string)"}`);
58749
58776
  }
58750
- const tokenCount = effectiveTokenCount(session, msgs);
58777
+ const tokenCount = session.stats.lastInputTokens;
58751
58778
  const turn = core.processTurn({ messages: msgs, state: session.state, config, tokenCount, renderTags });
58752
58779
  session.state = turn.state;
58753
58780
  session.stats.compressCreditTokens = 0;
@@ -59045,15 +59072,54 @@ function buildForwardTarget(req, opts, route, affinity, hopMarker) {
59045
59072
  function isPreflightFailFast(outcome) {
59046
59073
  return "failFast" in outcome;
59047
59074
  }
59048
- async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, core, config, model, route, affinity, anonymous, log2, instanceId) {
59075
+ var PREFLIGHT_HOLD_GRACE_DEFAULT_MS = 3e4;
59076
+ var PREFLIGHT_KEEPALIVE_MS = 15e3;
59077
+ function preflightHoldGraceMs() {
59078
+ const raw = process.env.BILI_PREFLIGHT_HOLD_MS;
59079
+ if (!raw) return PREFLIGHT_HOLD_GRACE_DEFAULT_MS;
59080
+ const v2 = Number(raw);
59081
+ return Number.isFinite(v2) && v2 >= 0 ? Math.floor(v2) : PREFLIGHT_HOLD_GRACE_DEFAULT_MS;
59082
+ }
59083
+ function beginPreflightHold(res, prepared, log2) {
59084
+ if (res.headersSent || res.destroyed || res.writableEnded) return void 0;
59085
+ const sid = prepared.session.id;
59086
+ const keepAlive = prepared.stream ? ": bili-preflight\n\n" : " ";
59087
+ try {
59088
+ if (prepared.stream) {
59089
+ res.writeHead(200, {
59090
+ "content-type": "text/event-stream",
59091
+ "cache-control": "no-cache",
59092
+ "x-accel-buffering": "no",
59093
+ "x-bili-preflight": "compressing"
59094
+ });
59095
+ } else {
59096
+ res.writeHead(200, { "content-type": "application/json", "x-bili-preflight": "compressing" });
59097
+ }
59098
+ } catch {
59099
+ return void 0;
59100
+ }
59101
+ log2("info", `[${sid}] preflight still running after ${preflightHoldGraceMs()}ms grace \u2014 committed early ${prepared.stream ? "SSE" : "JSON"} headers + keep-alive to hold the client (#568)`);
59102
+ try {
59103
+ res.write(keepAlive);
59104
+ } catch {
59105
+ }
59106
+ const iv = setInterval(() => {
59107
+ try {
59108
+ res.write(keepAlive);
59109
+ } catch {
59110
+ clearInterval(iv);
59111
+ }
59112
+ }, PREFLIGHT_KEEPALIVE_MS);
59113
+ return () => clearInterval(iv);
59114
+ }
59115
+ async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, core, config, model, route, affinity, log2, instanceId) {
59049
59116
  const session = prepared.session;
59050
59117
  const limit = config.modelContextLimit;
59051
59118
  const imageTokens = imageTokensInRawBody(prepared.protocol, prepared.body);
59052
59119
  const textEstimate = estimateCoreMessages(prepared.processedMessages);
59053
59120
  const overheadEstimate = estimateWireOverhead(prepared.protocol, prepared.body);
59054
59121
  const payloadEstimate = textEstimate + overheadEstimate + imageTokens;
59055
- const unknownBaseline = anonymous && session.stats.lastInputTokens <= 0;
59056
- const tokenCount = unknownBaseline ? estimateCoreMessagesUpper(prepared.processedMessages) + overheadEstimate + imageTokens : Math.max(session.stats.lastInputTokens, payloadEstimate);
59122
+ const tokenCount = Math.max(session.stats.lastInputTokens, payloadEstimate);
59057
59123
  if (limit <= 0 || !model || tokenCount < limit) return prepared;
59058
59124
  const learnedMap = session.metadata.learnedContextLimits;
59059
59125
  const learnedLimit = (model ? learnedMap?.[model] : void 0) ?? session.metadata.learnedContextLimit;
@@ -59068,14 +59134,9 @@ async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, c
59068
59134
  log2("error", `[${session.id}] preflight fail-fast ${status2} (retryable=${retryable}): ${message}`);
59069
59135
  return { failFast: true, status: status2, message, retryable, respond: !res.writableEnded };
59070
59136
  };
59071
- if ((prepared.nudge?.compressibleRanges ?? []).length === 0) {
59072
- if (!unknownBaseline && payloadEstimate < limit) {
59073
- log2("warn", `[${session.id}] preflight trigger fired on a stale baseline (~${tokenCount}) but the payload fits (~${payloadEstimate}/${limit}); forwarding as-is`);
59074
- return prepared;
59075
- }
59076
- if (unknownBaseline) {
59077
- return failFast(502, "no part of the conversation is compressible (nothing left to fold)", false);
59078
- }
59137
+ if ((prepared.nudge?.compressibleRanges ?? []).length === 0 && payloadEstimate < limit) {
59138
+ log2("warn", `[${session.id}] preflight trigger fired on a stale baseline (~${tokenCount}) but the payload fits (~${payloadEstimate}/${limit}); forwarding as-is`);
59139
+ return prepared;
59079
59140
  }
59080
59141
  log2("warn", `[${session.id}] context ${tokenCount} tokens exceeds model window ${limit} (model=${model}); preflight compressing before forward`);
59081
59142
  const { upstreamUrl, headers, proxyUrl } = buildForwardTarget(req, opts, route, affinity, instanceId);
@@ -59084,32 +59145,41 @@ async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, c
59084
59145
  if (!res.writableEnded) clientAbort.abort();
59085
59146
  });
59086
59147
  const started = Date.now();
59087
- const result = await preflightCompress(
59088
- {
59089
- core,
59090
- session,
59091
- config,
59092
- prompts: prepared.prompts ?? defaultPrompts,
59093
- protocol: prepared.protocol,
59094
- url: upstreamUrl,
59095
- headers,
59096
- model,
59097
- proxyUrl,
59098
- signal: clientAbort.signal,
59099
- log: log2,
59100
- imageFloor: imageTokens,
59101
- wireOverhead: overheadEstimate,
59102
- unknownBaseline
59103
- },
59104
- prepared.originalMessages
59105
- );
59148
+ let stopHold;
59149
+ const holdTimer = setTimeout(() => {
59150
+ stopHold = beginPreflightHold(res, prepared, log2);
59151
+ }, preflightHoldGraceMs());
59152
+ holdTimer.unref();
59153
+ let result;
59154
+ try {
59155
+ result = await preflightCompress(
59156
+ {
59157
+ core,
59158
+ session,
59159
+ config,
59160
+ prompts: prepared.prompts ?? defaultPrompts,
59161
+ protocol: prepared.protocol,
59162
+ url: upstreamUrl,
59163
+ headers,
59164
+ model,
59165
+ proxyUrl,
59166
+ signal: clientAbort.signal,
59167
+ log: log2,
59168
+ imageFloor: imageTokens,
59169
+ wireOverhead: overheadEstimate
59170
+ },
59171
+ prepared.originalMessages
59172
+ );
59173
+ } finally {
59174
+ clearTimeout(holdTimer);
59175
+ stopHold?.();
59176
+ }
59106
59177
  if (result.compressedRanges > 0) {
59107
59178
  log2("info", `[${session.id}] preflight compressed ${result.compressedRanges} range(s), ~${result.savedTokens} tokens saved (${tokenCount} \u2192 ${session.stats.lastInputTokens}) in ${Date.now() - started}ms; rebuilding payload`);
59108
59179
  const rebuilt = runPrepare();
59109
59180
  session.stats.requests -= 1;
59110
- const fits = unknownBaseline ? result.fitsWindow : estimateCoreMessages(rebuilt.processedMessages) + overheadEstimate + imageTokens < limit;
59111
- if (fits) return rebuilt;
59112
- } else if (unknownBaseline ? result.fitsWindow : estimateCoreMessages(prepared.processedMessages) + overheadEstimate + imageTokens < limit) {
59181
+ if (estimateCoreMessages(rebuilt.processedMessages) + overheadEstimate + imageTokens < limit) return rebuilt;
59182
+ } else if (estimateCoreMessages(prepared.processedMessages) + overheadEstimate + imageTokens < limit) {
59113
59183
  log2("warn", `[${session.id}] preflight made no progress but the payload fits; forwarding as-is`);
59114
59184
  return prepared;
59115
59185
  }
@@ -59124,7 +59194,7 @@ async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, c
59124
59194
  async function forward(req, res, opts, body, prepared, core, config, log2, route, instanceId, affinity) {
59125
59195
  if (prepared?.codexForge) {
59126
59196
  log2("info", `[${prepared.session.id}] codex compact served locally (${prepared.codexForge.kind}); upstream not contacted`);
59127
- res.writeHead(200, { "content-type": prepared.codexForge.contentType });
59197
+ if (!res.headersSent) res.writeHead(200, { "content-type": prepared.codexForge.contentType });
59128
59198
  res.end(prepared.codexForge.body);
59129
59199
  return;
59130
59200
  }
@@ -59377,6 +59447,18 @@ ${hdrText}
59377
59447
  if (bodyText.length > 600) snippet += " \u2026";
59378
59448
  if (!snippet) snippet = "(no body)";
59379
59449
  log("warn", `[${errSid}] \u2190 upstream ${upstream.status}${reqIdText}: ${snippet}`);
59450
+ if (res.headersSent) {
59451
+ if (prepared?.stream) {
59452
+ emitStreamError(res, prepared.protocol, `upstream HTTP ${upstream.status}: ${snippet}`, (m2) => log("info", m2));
59453
+ } else {
59454
+ try {
59455
+ res.end(errBody ?? void 0);
59456
+ } catch {
59457
+ }
59458
+ }
59459
+ clearUpstreamTimer();
59460
+ return;
59461
+ }
59380
59462
  const errHeaders = { ...respHeaders };
59381
59463
  delete errHeaders["content-length"];
59382
59464
  delete errHeaders["transfer-encoding"];
@@ -59385,7 +59467,7 @@ ${hdrText}
59385
59467
  clearUpstreamTimer();
59386
59468
  return;
59387
59469
  }
59388
- res.writeHead(upstream.status, respHeaders);
59470
+ if (!res.headersSent) res.writeHead(upstream.status, respHeaders);
59389
59471
  if (!upstream.body) {
59390
59472
  res.end();
59391
59473
  clearUpstreamTimer();