@infersec/conduit 1.109.0 → 1.109.2

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/cli.js CHANGED
@@ -69402,6 +69402,7 @@ function validateAndExtract(type, data, schemas) {
69402
69402
  function containsStatusOnly(payload) {
69403
69403
  return !!payload && typeof payload === "object" && "statusText" in payload;
69404
69404
  }
69405
+ const KEEPALIVE_INTERVAL_MS = 15_000;
69405
69406
  function sendTextStreamResponse({ logger, res, stream }) {
69406
69407
  const failStream = (error) => {
69407
69408
  if (res.writableEnded || res.destroyed)
@@ -69427,10 +69428,38 @@ function sendTextStreamResponse({ logger, res, stream }) {
69427
69428
  })}\n\n`);
69428
69429
  res.end();
69429
69430
  };
69431
+ let keepalive = null;
69432
+ let streamEnded = false;
69433
+ const stopKeepalive = () => {
69434
+ if (keepalive !== null) {
69435
+ clearInterval(keepalive);
69436
+ keepalive = null;
69437
+ }
69438
+ };
69439
+ res.on("close", stopKeepalive);
69440
+ res.flushHeaders();
69441
+ stream.on("close", () => {
69442
+ stopKeepalive();
69443
+ if (!streamEnded && !res.writableEnded && !res.destroyed) {
69444
+ failStream(new Error("Upstream stream closed prematurely"));
69445
+ }
69446
+ });
69447
+ stream.on("end", () => {
69448
+ streamEnded = true;
69449
+ stopKeepalive();
69450
+ });
69430
69451
  stream.on("error", error => {
69452
+ stopKeepalive();
69431
69453
  failStream(error instanceof Error ? error : new Error(String(error)));
69432
69454
  });
69433
69455
  stream.pipe(res);
69456
+ keepalive = setInterval(() => {
69457
+ if (res.writableEnded || res.destroyed) {
69458
+ stopKeepalive();
69459
+ return;
69460
+ }
69461
+ res.write(": keepalive\n\n");
69462
+ }, KEEPALIVE_INTERVAL_MS);
69434
69463
  }
69435
69464
  function implementSingleEndpoint({ endpoint, handler, logger, method, mount, route }) {
69436
69465
  const expressRoute = getHTTPMethod(mount, method);
@@ -126370,6 +126399,17 @@ async function streamResponse({ apiURL, configuration, logger, requestID, reques
126370
126399
  function encodeBinaryChunk(chunk) {
126371
126400
  return chunk.toString("base64");
126372
126401
  }
126402
+ const FLUSH_MAX_BATCH = 16;
126403
+ const FLUSH_WINDOW_MS = 60;
126404
+ /**
126405
+ * Decides whether the chunk queue must flush at a chunk arrival. Age-based,
126406
+ * so cadence adapts to generation speed: slow streams flush almost
126407
+ * immediately, fast streams accumulate into larger batches. The max batch
126408
+ * guards bursts faster than any flush roundtrip can absorb.
126409
+ */
126410
+ function shouldFlushChunkBatch({ ageMs, count, isTerminal, maxBatch, windowMs }) {
126411
+ return isTerminal || ageMs >= windowMs || count >= maxBatch;
126412
+ }
126373
126413
  async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126374
126414
  const streamURL = `${apiURL}/conduit/api/v1/source/${configuration.inferenceSourceID}/requests/${requestID}/stream`;
126375
126415
  const maxFlushAttempts = 3;
@@ -126378,6 +126418,7 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126378
126418
  let isClosed = false;
126379
126419
  let activeAbortController = null;
126380
126420
  let consecutiveFlushFailures = 0;
126421
+ let pendingSince = null;
126381
126422
  const chunks = [];
126382
126423
  const sendChunk = async (payload) => {
126383
126424
  if (isAborted || isClosed) {
@@ -126393,9 +126434,22 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126393
126434
  ...response,
126394
126435
  sequence: payload.sequence
126395
126436
  });
126437
+ const now = Date.now();
126438
+ if (pendingSince === null) {
126439
+ pendingSince = now;
126440
+ }
126396
126441
  chunks.push(Buffer.from(chunk + "\n"));
126397
- if (payload.data === null || chunks.length >= 10) {
126442
+ if (shouldFlushChunkBatch({
126443
+ ageMs: now - pendingSince,
126444
+ count: chunks.length,
126445
+ isTerminal: payload.data === null,
126446
+ maxBatch: FLUSH_MAX_BATCH,
126447
+ windowMs: FLUSH_WINDOW_MS
126448
+ })) {
126398
126449
  await flushChunks();
126450
+ if (chunks.length === 0) {
126451
+ pendingSince = null;
126452
+ }
126399
126453
  }
126400
126454
  };
126401
126455
  const flushChunks = async () => {
@@ -126464,6 +126518,7 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126464
126518
  activeAbortController.abort();
126465
126519
  }
126466
126520
  chunks.length = 0;
126521
+ pendingSince = null;
126467
126522
  if (error) {
126468
126523
  logger.error("Chunk stream aborted", {
126469
126524
  error: asError(error)
package/dist/cli.sea.cjs CHANGED
@@ -69417,6 +69417,7 @@ function validateAndExtract(type, data, schemas) {
69417
69417
  function containsStatusOnly(payload) {
69418
69418
  return !!payload && typeof payload === "object" && "statusText" in payload;
69419
69419
  }
69420
+ const KEEPALIVE_INTERVAL_MS = 15_000;
69420
69421
  function sendTextStreamResponse({ logger, res, stream }) {
69421
69422
  const failStream = (error) => {
69422
69423
  if (res.writableEnded || res.destroyed)
@@ -69442,10 +69443,38 @@ function sendTextStreamResponse({ logger, res, stream }) {
69442
69443
  })}\n\n`);
69443
69444
  res.end();
69444
69445
  };
69446
+ let keepalive = null;
69447
+ let streamEnded = false;
69448
+ const stopKeepalive = () => {
69449
+ if (keepalive !== null) {
69450
+ clearInterval(keepalive);
69451
+ keepalive = null;
69452
+ }
69453
+ };
69454
+ res.on("close", stopKeepalive);
69455
+ res.flushHeaders();
69456
+ stream.on("close", () => {
69457
+ stopKeepalive();
69458
+ if (!streamEnded && !res.writableEnded && !res.destroyed) {
69459
+ failStream(new Error("Upstream stream closed prematurely"));
69460
+ }
69461
+ });
69462
+ stream.on("end", () => {
69463
+ streamEnded = true;
69464
+ stopKeepalive();
69465
+ });
69445
69466
  stream.on("error", error => {
69467
+ stopKeepalive();
69446
69468
  failStream(error instanceof Error ? error : new Error(String(error)));
69447
69469
  });
69448
69470
  stream.pipe(res);
69471
+ keepalive = setInterval(() => {
69472
+ if (res.writableEnded || res.destroyed) {
69473
+ stopKeepalive();
69474
+ return;
69475
+ }
69476
+ res.write(": keepalive\n\n");
69477
+ }, KEEPALIVE_INTERVAL_MS);
69449
69478
  }
69450
69479
  function implementSingleEndpoint({ endpoint, handler, logger, method, mount, route }) {
69451
69480
  const expressRoute = getHTTPMethod(mount, method);
@@ -126385,6 +126414,17 @@ async function streamResponse({ apiURL, configuration, logger, requestID, reques
126385
126414
  function encodeBinaryChunk(chunk) {
126386
126415
  return chunk.toString("base64");
126387
126416
  }
126417
+ const FLUSH_MAX_BATCH = 16;
126418
+ const FLUSH_WINDOW_MS = 60;
126419
+ /**
126420
+ * Decides whether the chunk queue must flush at a chunk arrival. Age-based,
126421
+ * so cadence adapts to generation speed: slow streams flush almost
126422
+ * immediately, fast streams accumulate into larger batches. The max batch
126423
+ * guards bursts faster than any flush roundtrip can absorb.
126424
+ */
126425
+ function shouldFlushChunkBatch({ ageMs, count, isTerminal, maxBatch, windowMs }) {
126426
+ return isTerminal || ageMs >= windowMs || count >= maxBatch;
126427
+ }
126388
126428
  async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126389
126429
  const streamURL = `${apiURL}/conduit/api/v1/source/${configuration.inferenceSourceID}/requests/${requestID}/stream`;
126390
126430
  const maxFlushAttempts = 3;
@@ -126393,6 +126433,7 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126393
126433
  let isClosed = false;
126394
126434
  let activeAbortController = null;
126395
126435
  let consecutiveFlushFailures = 0;
126436
+ let pendingSince = null;
126396
126437
  const chunks = [];
126397
126438
  const sendChunk = async (payload) => {
126398
126439
  if (isAborted || isClosed) {
@@ -126408,9 +126449,22 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126408
126449
  ...response,
126409
126450
  sequence: payload.sequence
126410
126451
  });
126452
+ const now = Date.now();
126453
+ if (pendingSince === null) {
126454
+ pendingSince = now;
126455
+ }
126411
126456
  chunks.push(Buffer.from(chunk + "\n"));
126412
- if (payload.data === null || chunks.length >= 10) {
126457
+ if (shouldFlushChunkBatch({
126458
+ ageMs: now - pendingSince,
126459
+ count: chunks.length,
126460
+ isTerminal: payload.data === null,
126461
+ maxBatch: FLUSH_MAX_BATCH,
126462
+ windowMs: FLUSH_WINDOW_MS
126463
+ })) {
126413
126464
  await flushChunks();
126465
+ if (chunks.length === 0) {
126466
+ pendingSince = null;
126467
+ }
126414
126468
  }
126415
126469
  };
126416
126470
  const flushChunks = async () => {
@@ -126479,6 +126533,7 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
126479
126533
  activeAbortController.abort();
126480
126534
  }
126481
126535
  chunks.length = 0;
126536
+ pendingSince = null;
126482
126537
  if (error) {
126483
126538
  logger.error("Chunk stream aborted", {
126484
126539
  error: asError(error)
@@ -13,3 +13,16 @@ export declare function handleSSERequests({ apiURL, configuration, logger, onReq
13
13
  onRequestStart?: (request: ServerToClientAPIRequest) => Promise<void> | void;
14
14
  signal?: AbortSignal;
15
15
  }): Promise<void>;
16
+ /**
17
+ * Decides whether the chunk queue must flush at a chunk arrival. Age-based,
18
+ * so cadence adapts to generation speed: slow streams flush almost
19
+ * immediately, fast streams accumulate into larger batches. The max batch
20
+ * guards bursts faster than any flush roundtrip can absorb.
21
+ */
22
+ export declare function shouldFlushChunkBatch({ ageMs, count, isTerminal, maxBatch, windowMs }: {
23
+ ageMs: number;
24
+ count: number;
25
+ isTerminal: boolean;
26
+ maxBatch: number;
27
+ windowMs: number;
28
+ }): boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@infersec/conduit",
3
3
  "description": "End user conduit agent for connecting local LLMs to the cloud.",
4
- "version": "1.109.0",
4
+ "version": "1.109.2",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },