mslxdff 0.1.29 → 0.1.30
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/package.json +1 -1
- package/src/routes.js +50 -30
package/package.json
CHANGED
package/src/routes.js
CHANGED
|
@@ -89,22 +89,31 @@ async function relay(res, upRes, body, { onFirstChunk, streamTimeoutMs = STREAM_
|
|
|
89
89
|
let first = true;
|
|
90
90
|
let wroteAny = false;
|
|
91
91
|
let timedOut = false;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
let stalled = false;
|
|
93
|
+
let tooLong = false;
|
|
94
|
+
let stallTimer = null;
|
|
95
|
+
const armStall = () => {
|
|
96
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
97
|
+
stallTimer = STALL_TIMEOUT_MS
|
|
98
|
+
? setTimeout(() => {
|
|
99
|
+
stalled = true;
|
|
100
|
+
if (typeof upRes.body.cancel === "function") upRes.body.cancel().catch(() => {});
|
|
101
|
+
}, STALL_TIMEOUT_MS)
|
|
102
|
+
: null;
|
|
103
|
+
};
|
|
101
104
|
const firstTimer = setTimeout(() => {
|
|
102
105
|
timedOut = true;
|
|
103
106
|
if (typeof upRes.body.cancel === "function") upRes.body.cancel().catch(() => {});
|
|
104
107
|
}, streamTimeoutMs);
|
|
108
|
+
const maxTimer = MAX_STREAM_MS
|
|
109
|
+
? setTimeout(() => {
|
|
110
|
+
tooLong = true;
|
|
111
|
+
if (typeof upRes.body.cancel === "function") upRes.body.cancel().catch(() => {});
|
|
112
|
+
}, MAX_STREAM_MS)
|
|
113
|
+
: null;
|
|
105
114
|
try {
|
|
106
115
|
for await (const chunk of upRes.body) {
|
|
107
|
-
if (timedOut ||
|
|
116
|
+
if (timedOut || stalled || tooLong) break;
|
|
108
117
|
if (first) {
|
|
109
118
|
first = false;
|
|
110
119
|
ttf = Math.round(performance.now() - t0);
|
|
@@ -112,24 +121,26 @@ async function relay(res, upRes, body, { onFirstChunk, streamTimeoutMs = STREAM_
|
|
|
112
121
|
}
|
|
113
122
|
wroteAny = true;
|
|
114
123
|
res.write(chunk);
|
|
124
|
+
armStall(); // any fresh chunk resets the stall clock
|
|
115
125
|
}
|
|
116
126
|
} catch (err) {
|
|
117
|
-
// body threw — if
|
|
118
|
-
if (!wroteAny)
|
|
127
|
+
// body threw — if nothing was written, treat it as a first-block timeout
|
|
128
|
+
if (!wroteAny) timedOut = true;
|
|
129
|
+
else stalled = true;
|
|
119
130
|
} finally {
|
|
120
131
|
if (firstTimer) clearTimeout(firstTimer);
|
|
121
|
-
if (
|
|
132
|
+
if (maxTimer) clearTimeout(maxTimer);
|
|
133
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
122
134
|
}
|
|
123
135
|
if (timedOut && !wroteAny) {
|
|
124
136
|
// nothing written to res yet — safe to drop this model and let the
|
|
125
137
|
// caller fail over to the next one. Do NOT write/end res here.
|
|
126
138
|
return { status: STREAM_TIMEOUT_MS, ttfMs: null, totalMs: Math.round(performance.now() - t0), aborted: true };
|
|
127
139
|
}
|
|
128
|
-
if (
|
|
129
|
-
//
|
|
130
|
-
// cleanly fail over a half-written
|
|
131
|
-
//
|
|
132
|
-
// to remember this model as slow.
|
|
140
|
+
if ((stalled || tooLong) && wroteAny) {
|
|
141
|
+
// a response that was flowing either went silent for the stall window
|
|
142
|
+
// or blew the total ceiling — we can't cleanly fail over a half-written
|
|
143
|
+
// body, so end it and let the caller remember this model as slow.
|
|
133
144
|
interrupted = true;
|
|
134
145
|
try { res.end(); } catch { /* ignore */ }
|
|
135
146
|
return { status: 200, ttfMs: ttf, totalMs: Math.round(performance.now() - t0), aborted: false, interrupted };
|
|
@@ -268,13 +279,21 @@ export const STREAM_TIMEOUT_MS = (() => {
|
|
|
268
279
|
return Number.isInteger(n) && n > 0 ? n : 25_000;
|
|
269
280
|
})();
|
|
270
281
|
|
|
271
|
-
// Ceiling on
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
export const
|
|
276
|
-
const n = Number(process.env.
|
|
277
|
-
return Number.isInteger(n) && n > 0 ? n :
|
|
282
|
+
// Ceiling on how long a streamed response may produce no new chunk before we
|
|
283
|
+
// treat the model as stalled and proactively end the stream (clean EOF instead
|
|
284
|
+
// of hanging on a dead upstream). A model that keeps emitting chunks is never
|
|
285
|
+
// cut off — only silence triggers it. Set to 0 to disable.
|
|
286
|
+
export const STALL_TIMEOUT_MS = (() => {
|
|
287
|
+
const n = Number(process.env.MSLXDFF_STALL_TIMEOUT_MS);
|
|
288
|
+
return Number.isInteger(n) && n > 0 ? n : 15_000;
|
|
289
|
+
})();
|
|
290
|
+
|
|
291
|
+
// Loose total ceiling (TTFB + generation) so an unbounded stream can never
|
|
292
|
+
// run forever. Kept much larger than the stall timeout so normally-flowing
|
|
293
|
+
// responses are not truncated. Set to 0 to disable.
|
|
294
|
+
export const MAX_STREAM_MS = (() => {
|
|
295
|
+
const n = Number(process.env.MSLXDFF_MAX_STREAM_MS);
|
|
296
|
+
return Number.isInteger(n) && n > 0 ? n : 120_000;
|
|
278
297
|
})();
|
|
279
298
|
|
|
280
299
|
async function racePeerCandidates(candidates, ctx) {
|
|
@@ -437,11 +456,12 @@ const ROUTES = [
|
|
|
437
456
|
continue;
|
|
438
457
|
}
|
|
439
458
|
if (out.interrupted) {
|
|
440
|
-
// the
|
|
441
|
-
// so the client got a clean EOF instead of
|
|
442
|
-
// model as slow so the next request prefers
|
|
443
|
-
|
|
444
|
-
|
|
459
|
+
// the model went silent past STALL_TIMEOUT_MS (or blew the total
|
|
460
|
+
// ceiling) — we ended it so the client got a clean EOF instead of
|
|
461
|
+
// hanging. Remember this model as slow so the next request prefers
|
|
462
|
+
// a faster one.
|
|
463
|
+
if (auto) await auto.recordError(model, { status: 200, slow: true, note: `stall ${STALL_TIMEOUT_MS}ms` });
|
|
464
|
+
evt("slow-model", { model, elapsedMs: out.totalMs ?? (Date.now() - startedAt), threshold: STALL_TIMEOUT_MS, interrupted: true });
|
|
445
465
|
logCall(model, 200);
|
|
446
466
|
evt("result", { model, status: out.status, via: "local", timing: upRes._t ?? null, ttfMs: out.ttfMs, totalMs: out.totalMs, interrupted: true });
|
|
447
467
|
return;
|