@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 +56 -1
- package/dist/cli.sea.cjs +56 -1
- package/dist/sse/handler.d.ts +13 -0
- package/package.json +1 -1
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 (
|
|
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 (
|
|
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)
|
package/dist/sse/handler.d.ts
CHANGED
|
@@ -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;
|