billion-context 0.1.92 → 0.1.93-pr.630.451

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/dist/index.js CHANGED
@@ -5632,7 +5632,7 @@ var require_util2 = __commonJS({
5632
5632
  callback();
5633
5633
  }
5634
5634
  };
5635
- function createInflate(zlibOptions) {
5635
+ function createInflate2(zlibOptions) {
5636
5636
  return new InflateStream(zlibOptions);
5637
5637
  }
5638
5638
  function extractMimeType(headers) {
@@ -5764,7 +5764,7 @@ var require_util2 = __commonJS({
5764
5764
  readAllBytes,
5765
5765
  simpleRangeHeaderValue,
5766
5766
  buildContentRange,
5767
- createInflate,
5767
+ createInflate: createInflate2,
5768
5768
  extractMimeType,
5769
5769
  getDecodeSplit,
5770
5770
  environmentSettingsObject,
@@ -13122,7 +13122,7 @@ var require_mock_interceptor = __commonJS({
13122
13122
  var require_mock_client = __commonJS({
13123
13123
  "node_modules/undici/lib/mock/mock-client.js"(exports, module) {
13124
13124
  "use strict";
13125
- var { promisify: promisify2 } = __require("util");
13125
+ var { promisify } = __require("util");
13126
13126
  var Client = require_client();
13127
13127
  var { buildMockDispatch } = require_mock_utils();
13128
13128
  var {
@@ -13170,7 +13170,7 @@ var require_mock_client = __commonJS({
13170
13170
  this[kDispatches] = [];
13171
13171
  }
13172
13172
  async [kClose]() {
13173
- await promisify2(this[kOriginalClose])();
13173
+ await promisify(this[kOriginalClose])();
13174
13174
  this[kConnected] = 0;
13175
13175
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
13176
13176
  }
@@ -13383,7 +13383,7 @@ var require_mock_call_history = __commonJS({
13383
13383
  var require_mock_pool = __commonJS({
13384
13384
  "node_modules/undici/lib/mock/mock-pool.js"(exports, module) {
13385
13385
  "use strict";
13386
- var { promisify: promisify2 } = __require("util");
13386
+ var { promisify } = __require("util");
13387
13387
  var Pool2 = require_pool();
13388
13388
  var { buildMockDispatch } = require_mock_utils();
13389
13389
  var {
@@ -13431,7 +13431,7 @@ var require_mock_pool = __commonJS({
13431
13431
  this[kDispatches] = [];
13432
13432
  }
13433
13433
  async [kClose]() {
13434
- await promisify2(this[kOriginalClose])();
13434
+ await promisify(this[kOriginalClose])();
13435
13435
  this[kConnected] = 0;
13436
13436
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
13437
13437
  }
@@ -17545,17 +17545,17 @@ var require_cache2 = __commonJS({
17545
17545
  var require_decompress = __commonJS({
17546
17546
  "node_modules/undici/lib/interceptor/decompress.js"(exports, module) {
17547
17547
  "use strict";
17548
- var { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = __require("zlib");
17548
+ var { createInflate: createInflate2, createGunzip: createGunzip2, createBrotliDecompress: createBrotliDecompress2, createZstdDecompress } = __require("zlib");
17549
17549
  var { pipeline } = __require("stream");
17550
17550
  var DecoratorHandler = require_decorator_handler();
17551
17551
  var { runtimeFeatures } = require_runtime_features();
17552
17552
  var supportedEncodings = {
17553
- gzip: createGunzip,
17554
- "x-gzip": createGunzip,
17555
- br: createBrotliDecompress,
17556
- deflate: createInflate,
17557
- compress: createInflate,
17558
- "x-compress": createInflate,
17553
+ gzip: createGunzip2,
17554
+ "x-gzip": createGunzip2,
17555
+ br: createBrotliDecompress2,
17556
+ deflate: createInflate2,
17557
+ compress: createInflate2,
17558
+ "x-compress": createInflate2,
17559
17559
  ...runtimeFeatures.has("zstd") ? { zstd: createZstdDecompress } : {}
17560
17560
  };
17561
17561
  var defaultSkipStatusCodes = (
@@ -20384,7 +20384,7 @@ var require_fetch = __commonJS({
20384
20384
  clampAndCoarsenConnectionTimingInfo,
20385
20385
  simpleRangeHeaderValue,
20386
20386
  buildContentRange,
20387
- createInflate,
20387
+ createInflate: createInflate2,
20388
20388
  extractMimeType,
20389
20389
  hasAuthenticationEntry,
20390
20390
  includesCredentials,
@@ -21366,7 +21366,7 @@ var require_fetch = __commonJS({
21366
21366
  finishFlush: zlib.constants.Z_SYNC_FLUSH
21367
21367
  }));
21368
21368
  } else if (coding === "deflate") {
21369
- decoders.push(createInflate({
21369
+ decoders.push(createInflate2({
21370
21370
  flush: zlib.constants.Z_SYNC_FLUSH,
21371
21371
  finishFlush: zlib.constants.Z_SYNC_FLUSH
21372
21372
  }));
@@ -23432,7 +23432,7 @@ var require_connection = __commonJS({
23432
23432
  var require_permessage_deflate = __commonJS({
23433
23433
  "node_modules/undici/lib/web/websocket/permessage-deflate.js"(exports, module) {
23434
23434
  "use strict";
23435
- var { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __require("zlib");
23435
+ var { createInflateRaw: createInflateRaw2, Z_DEFAULT_WINDOWBITS } = __require("zlib");
23436
23436
  var { isValidClientWindowBits } = require_util5();
23437
23437
  var { MessageSizeExceededError } = require_errors();
23438
23438
  var tail = Buffer.from([0, 0, 255, 255]);
@@ -23468,7 +23468,7 @@ var require_permessage_deflate = __commonJS({
23468
23468
  windowBits = Number.parseInt(this.#options.serverMaxWindowBits);
23469
23469
  }
23470
23470
  try {
23471
- this.#inflate = createInflateRaw({ windowBits });
23471
+ this.#inflate = createInflateRaw2({ windowBits });
23472
23472
  } catch (err2) {
23473
23473
  callback(err2);
23474
23474
  return;
@@ -38339,7 +38339,7 @@ var require_tls = __commonJS({
38339
38339
  }
38340
38340
  return rval;
38341
38341
  };
38342
- var inflate2 = function(c, record, s3) {
38342
+ var inflate = function(c, record, s3) {
38343
38343
  var rval = false;
38344
38344
  try {
38345
38345
  var bytes = c.inflate(record.fragment.getBytes());
@@ -39484,7 +39484,7 @@ var require_tls = __commonJS({
39484
39484
  case tls4.CompressionMethod.none:
39485
39485
  break;
39486
39486
  case tls4.CompressionMethod.deflate:
39487
- state.read.compressFunction = inflate2;
39487
+ state.read.compressFunction = inflate;
39488
39488
  state.write.compressFunction = deflate;
39489
39489
  break;
39490
39490
  default:
@@ -50329,6 +50329,7 @@ function buildAlertMessage(dir, count) {
50329
50329
  // src/persist.ts
50330
50330
  var PERSIST_VERSION = 3;
50331
50331
  var MIGRATION_MARKER = ".bili-migration-286.done";
50332
+ var STALE_WARN_THROTTLE_MS = 6e4;
50332
50333
  function mergeState(parsed) {
50333
50334
  const fresh = createInitialState();
50334
50335
  return {
@@ -50362,11 +50363,14 @@ var SessionStore = class {
50362
50363
  enabled;
50363
50364
  dir;
50364
50365
  store;
50366
+ log;
50367
+ staleWarnAt = /* @__PURE__ */ new Map();
50365
50368
  constructor(opts) {
50366
50369
  const debounceMs = opts?.debounceMs ?? defaultDebounce();
50367
50370
  this.enabled = (opts?.enabled ?? true) && debounceMs >= 0;
50368
50371
  this.dir = opts?.dir ?? defaultDir();
50369
50372
  const baseLog = opts?.log ?? defaultLogger;
50373
+ this.log = baseLog;
50370
50374
  const epermAlert = new PersistEpermAlert({
50371
50375
  dir: this.dir,
50372
50376
  threshold: epermAlertThreshold(),
@@ -50516,40 +50520,83 @@ var SessionStore = class {
50516
50520
  });
50517
50521
  }
50518
50522
  }
50523
+ /** Shared envelope probe for the sync read paths: namespaced path
50524
+ * first, then the _unknown/ location, letting the kernel store layer
50525
+ * also check its discovered map and the flat legacy name. */
50526
+ loadEnvelope(id, meta) {
50527
+ const envelopes = [
50528
+ this.store.loadSync(id, relPathFor(id, meta?.protocol, meta?.upstreamOrigin)),
50529
+ meta?.protocol ? this.store.loadSync(id, relPathFor(id)) : null
50530
+ ];
50531
+ for (const envelope of envelopes) {
50532
+ if (envelope) return envelope;
50533
+ }
50534
+ return null;
50535
+ }
50519
50536
  /** Synchronous reload of a single session. Used on a memory miss (after
50520
50537
  * LRU eviction). Sync fs is acceptable here because a miss is rare and
50521
50538
  * reads a single small file (~1ms). Returns null if missing/corrupt or the
50522
50539
  * body id does not match what we asked for. */
50523
50540
  loadSync(id, meta) {
50524
50541
  if (!this.enabled) return null;
50525
- const envelopes = [
50526
- this.store.loadSync(id, relPathFor(id, meta?.protocol, meta?.upstreamOrigin)),
50527
- meta?.protocol ? this.store.loadSync(id, relPathFor(id)) : null
50528
- ];
50529
- for (const envelope of envelopes) {
50530
- if (envelope) {
50531
- const session = buildSession(envelope.payload);
50532
- if (hasNegativePersistedTokens(envelope.payload)) {
50533
- this.scheduleSave(session);
50534
- log("info", `[persist] clamped negative token stats on reload for ${id} (#408)`);
50535
- }
50536
- return session;
50537
- }
50542
+ const envelope = this.loadEnvelope(id, meta);
50543
+ if (!envelope) return null;
50544
+ const session = buildSession(envelope.payload);
50545
+ if (hasNegativePersistedTokens(envelope.payload)) {
50546
+ this.scheduleSave(session);
50547
+ log("info", `[persist] clamped negative token stats on reload for ${id} (#408)`);
50548
+ }
50549
+ return session;
50550
+ }
50551
+ /** #405 fix #4: dual-instance rollback guard. When two proxy processes
50552
+ * share BILI_SESSIONS_DIR, whoever saves last used to win — an instance
50553
+ * holding a STALE in-memory copy would roll counters back (requests:3 →
50554
+ * 2). Both signals below are monotonic per session id, so either being
50555
+ * strictly smaller proves staleness. Returns the fresher-on-disk payload
50556
+ * when the incoming record is stale, else null. */
50557
+ staleDiskPayload(incoming) {
50558
+ const envelope = this.loadEnvelope(incoming.id, incoming.meta);
50559
+ if (!envelope) return null;
50560
+ const disk = envelope.payload;
50561
+ const diskRequests = disk.stats?.requests ?? disk.requests ?? 0;
50562
+ const incRequests = incoming.stats?.requests ?? incoming.requests ?? 0;
50563
+ if (incRequests < diskRequests) return disk;
50564
+ if (incRequests === diskRequests) {
50565
+ const diskBlocks = disk.state?.nextBlockId ?? 0;
50566
+ const incBlocks = incoming.state?.nextBlockId ?? 0;
50567
+ if (incBlocks < diskBlocks) return disk;
50538
50568
  }
50539
50569
  return null;
50540
50570
  }
50571
+ guardedBuild(session) {
50572
+ return () => {
50573
+ const record = buildRecord(session);
50574
+ const disk = this.staleDiskPayload(record);
50575
+ if (disk === null) return record;
50576
+ const now = Date.now();
50577
+ const last = this.staleWarnAt.get(record.id) ?? 0;
50578
+ if (now - last >= STALE_WARN_THROTTLE_MS) {
50579
+ this.staleWarnAt.set(record.id, now);
50580
+ this.log(
50581
+ "warn",
50582
+ `[persist] rejected stale snapshot for session ${record.id}: in-memory copy is older than the on-disk one (another bili instance holds newer state) \u2014 keeping disk state, no rollback (#405)`
50583
+ );
50584
+ }
50585
+ return disk;
50586
+ };
50587
+ }
50541
50588
  /** Schedule a debounced write for a session. Multiple calls within the
50542
50589
  * window coalesce; the record is built at WRITE time, so the freshest
50543
50590
  * session state is persisted. Safe to call on the hot path. No-op if
50544
50591
  * disabled. */
50545
50592
  scheduleSave(session) {
50546
- this.store.scheduleSave(session.id, () => buildRecord(session));
50593
+ this.store.scheduleSave(session.id, this.guardedBuild(session));
50547
50594
  }
50548
50595
  /** Asynchronously persist a session right now (skips the debounce). Throws
50549
50596
  * on write failure so callers can react (e.g. avoid evicting). Serialized
50550
50597
  * per-session by the kernel store's write chains. */
50551
50598
  async writeNow(session) {
50552
- await this.store.writeNow(session.id, () => buildRecord(session));
50599
+ await this.store.writeNow(session.id, this.guardedBuild(session));
50553
50600
  }
50554
50601
  /** Synchronous flush for a single session. Used on memory eviction so a
50555
50602
  * dirty evicted session is not lost. Sync because eviction runs in the
@@ -50557,7 +50604,7 @@ var SessionStore = class {
50557
50604
  * Returns true on success, false on failure (caller must NOT evict on
50558
50605
  * failure for a never-persisted session or it is lost permanently). */
50559
50606
  flushSync(session) {
50560
- return this.store.flushSync(session.id, () => buildRecord(session));
50607
+ return this.store.flushSync(session.id, this.guardedBuild(session));
50561
50608
  }
50562
50609
  /** Flush all dirty sessions with a pending debounce timer. Called on
50563
50610
  * SIGTERM/SIGINT for graceful shutdown. The kernel store flushes its own
@@ -57416,8 +57463,7 @@ function tunnelAllowlistFromEnv(env = process.env) {
57416
57463
  }
57417
57464
 
57418
57465
  // src/content-encoding.ts
57419
- import { promisify } from "util";
57420
- import { brotliDecompress, gunzip, inflate, inflateRaw } from "zlib";
57466
+ import { createBrotliDecompress, createGunzip, createInflate, createInflateRaw } from "zlib";
57421
57467
 
57422
57468
  // node_modules/fzstd/esm/index.mjs
57423
57469
  var ab = ArrayBuffer;
@@ -58090,31 +58136,54 @@ var Decompress = /* @__PURE__ */ (function() {
58090
58136
  })();
58091
58137
 
58092
58138
  // src/content-encoding.ts
58093
- var gunzipAsync = promisify(gunzip);
58094
- var inflateAsync = promisify(inflate);
58095
- var inflateRawAsync = promisify(inflateRaw);
58096
- var brotliAsync = promisify(brotliDecompress);
58097
- async function decodeZstd(input, maxOutputBytes) {
58139
+ var DecompressedTooLargeError = class extends Error {
58140
+ constructor(limit) {
58141
+ super(`decompressed request exceeds ${limit} bytes`);
58142
+ this.limit = limit;
58143
+ this.name = "DecompressedTooLargeError";
58144
+ }
58145
+ limit;
58146
+ };
58147
+ function streamDecode(factory, input, max) {
58148
+ return new Promise((resolve, reject) => {
58149
+ const chunks = [];
58150
+ let size = 0;
58151
+ const decoder = factory();
58152
+ decoder.on("data", (chunk) => {
58153
+ size += chunk.length;
58154
+ if (size > max) {
58155
+ decoder.destroy();
58156
+ reject(new DecompressedTooLargeError(max));
58157
+ return;
58158
+ }
58159
+ chunks.push(chunk);
58160
+ });
58161
+ decoder.on("end", () => resolve(Buffer.concat(chunks, size)));
58162
+ decoder.on("error", (err2) => reject(err2));
58163
+ decoder.end(input);
58164
+ });
58165
+ }
58166
+ async function decodeZstd(input, max) {
58098
58167
  const chunks = [];
58099
58168
  let size = 0;
58100
58169
  const decoder = new Decompress((chunk) => {
58101
58170
  size += chunk.byteLength;
58102
- if (size > maxOutputBytes) throw new Error(`decompressed request exceeds ${maxOutputBytes} bytes`);
58171
+ if (size > max) throw new DecompressedTooLargeError(max);
58103
58172
  chunks.push(Buffer.from(chunk));
58104
58173
  });
58105
58174
  decoder.push(input, true);
58106
58175
  return Buffer.concat(chunks, size);
58107
58176
  }
58108
- async function decodeOne(coding, input, maxOutputBytes) {
58109
- const options = { maxOutputLength: maxOutputBytes };
58110
- if (coding === "gzip" || coding === "x-gzip") return gunzipAsync(input, options);
58111
- if (coding === "br") return brotliAsync(input, options);
58112
- if (coding === "zstd") return decodeZstd(input, maxOutputBytes);
58177
+ async function decodeOne(coding, input, max) {
58178
+ if (coding === "gzip" || coding === "x-gzip") return streamDecode(createGunzip, input, max);
58179
+ if (coding === "br") return streamDecode(createBrotliDecompress, input, max);
58180
+ if (coding === "zstd") return decodeZstd(input, max);
58113
58181
  if (coding === "deflate") {
58114
58182
  try {
58115
- return await inflateAsync(input, options);
58116
- } catch {
58117
- return inflateRawAsync(input, options);
58183
+ return await streamDecode(createInflate, input, max);
58184
+ } catch (err2) {
58185
+ if (err2 instanceof DecompressedTooLargeError) throw err2;
58186
+ return streamDecode(createInflateRaw, input, max);
58118
58187
  }
58119
58188
  }
58120
58189
  throw new Error(`unsupported request content-encoding: ${coding}`);
@@ -58125,9 +58194,7 @@ async function decodeRequestBody(contentEncoding, input, maxOutputBytes) {
58125
58194
  let body = input;
58126
58195
  for (const coding of codings.reverse()) {
58127
58196
  body = await decodeOne(coding, body, maxOutputBytes);
58128
- if (body.byteLength > maxOutputBytes) {
58129
- throw new Error(`decompressed request exceeds ${maxOutputBytes} bytes`);
58130
- }
58197
+ if (body.byteLength > maxOutputBytes) throw new DecompressedTooLargeError(maxOutputBytes);
58131
58198
  }
58132
58199
  return { body, decoded: true };
58133
58200
  }
@@ -58707,18 +58774,24 @@ async function handle(req, res, opts, core, config, log2, instanceId, instanceSt
58707
58774
  upstreamOrigin = route ? route.upstream : /^https?:\/\//i.test(url) ? new URL(url).origin : opts.upstream;
58708
58775
  protocol = route?.explicitProtocol ?? (req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") || responsesCompact ? "responses" : null : null);
58709
58776
  if (protocol !== null && bodyBuffer.length > 0) {
58710
- const decoded = await decodeRequestBody(headerValue2(req, "content-encoding"), bodyBuffer, MAX_REQUEST_BYTES);
58711
- bodyBuffer = decoded.body;
58712
- if (decoded.decoded) delete req.headers["content-encoding"];
58777
+ try {
58778
+ const decoded = await decodeRequestBody(headerValue2(req, "content-encoding"), bodyBuffer, MAX_REQUEST_BYTES);
58779
+ bodyBuffer = decoded.body;
58780
+ if (decoded.decoded) delete req.headers["content-encoding"];
58781
+ } catch (decErr) {
58782
+ if (decErr instanceof DecompressedTooLargeError) throw decErr;
58783
+ protocol = null;
58784
+ log2("warn", `decode body failed (${String(decErr)}) - forwarding raw body verbatim to ${upstreamOrigin}`);
58785
+ }
58713
58786
  }
58714
58787
  } catch (err2) {
58715
- if (err2 instanceof BodyTooLargeError) {
58788
+ if (err2 instanceof BodyTooLargeError || err2 instanceof DecompressedTooLargeError) {
58716
58789
  log2("warn", `413: request body exceeds ${err2.limit} bytes`);
58717
58790
  res.writeHead(413, { "content-type": "application/json" });
58718
58791
  res.end(JSON.stringify({ error: { type: "request_too_large", message: err2.message } }));
58719
58792
  return;
58720
58793
  }
58721
- log2("warn", `read/decode body failed: ${String(err2)}`);
58794
+ log2("warn", `failed to prepare inbound request (${String(err2)}) - 400`);
58722
58795
  res.writeHead(400, { "content-type": "application/json" });
58723
58796
  res.end(JSON.stringify({ error: { type: "invalid_request", message: String(err2) } }));
58724
58797
  return;