billion-context 0.1.70 → 0.1.71-pr.378.283
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 +338 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -50369,6 +50369,50 @@ function stripItemContent(it2) {
|
|
|
50369
50369
|
}
|
|
50370
50370
|
return it2;
|
|
50371
50371
|
}
|
|
50372
|
+
function stripIfString(v2) {
|
|
50373
|
+
return typeof v2 === "string" ? stripAcpTags(v2) : v2;
|
|
50374
|
+
}
|
|
50375
|
+
function stripOpenaiChatText(obj) {
|
|
50376
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
50377
|
+
const o = obj;
|
|
50378
|
+
if (!Array.isArray(o["choices"])) return obj;
|
|
50379
|
+
o["choices"] = o["choices"].map((c) => {
|
|
50380
|
+
if (!c || typeof c !== "object") return c;
|
|
50381
|
+
const ch = c;
|
|
50382
|
+
for (const holder of ["delta", "message"]) {
|
|
50383
|
+
const h = ch[holder];
|
|
50384
|
+
if (h && typeof h === "object") {
|
|
50385
|
+
const hh = { ...h };
|
|
50386
|
+
hh["content"] = stripIfString(hh["content"]);
|
|
50387
|
+
hh["reasoning_content"] = stripIfString(hh["reasoning_content"]);
|
|
50388
|
+
hh["reasoning"] = stripIfString(hh["reasoning"]);
|
|
50389
|
+
ch[holder] = hh;
|
|
50390
|
+
}
|
|
50391
|
+
}
|
|
50392
|
+
return ch;
|
|
50393
|
+
});
|
|
50394
|
+
return obj;
|
|
50395
|
+
}
|
|
50396
|
+
function stripAnthropicText(obj) {
|
|
50397
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
50398
|
+
const o = obj;
|
|
50399
|
+
const d = o["delta"];
|
|
50400
|
+
if (d && typeof d === "object") {
|
|
50401
|
+
const dd = { ...d };
|
|
50402
|
+
dd["text"] = stripIfString(dd["text"]);
|
|
50403
|
+
dd["thinking"] = stripIfString(dd["thinking"]);
|
|
50404
|
+
o["delta"] = dd;
|
|
50405
|
+
}
|
|
50406
|
+
if (Array.isArray(o["content"])) {
|
|
50407
|
+
o["content"] = o["content"].map((c) => {
|
|
50408
|
+
if (!c || typeof c !== "object") return c;
|
|
50409
|
+
const cc = c;
|
|
50410
|
+
if (typeof cc["text"] !== "string" && typeof cc["thinking"] !== "string") return c;
|
|
50411
|
+
return { ...cc, text: stripIfString(cc["text"]), thinking: stripIfString(cc["thinking"]) };
|
|
50412
|
+
});
|
|
50413
|
+
}
|
|
50414
|
+
return obj;
|
|
50415
|
+
}
|
|
50372
50416
|
function stripResponsesText(obj) {
|
|
50373
50417
|
if (!obj || typeof obj !== "object") return obj;
|
|
50374
50418
|
const o = obj;
|
|
@@ -53207,6 +53251,86 @@ async function compressLoopResponsesJson(initialResponse, ctx, requestBody, requ
|
|
|
53207
53251
|
return current;
|
|
53208
53252
|
}
|
|
53209
53253
|
|
|
53254
|
+
// src/fake-completion.ts
|
|
53255
|
+
var FAKE_COMPLETION_HINT = "[billion-context] Your last reply described a tool call as plain text instead of invoking it, so no tool actually ran. To act, invoke the tool through the proper tool-calling mechanism (emit a tool_use / tool_calls / function_call block). Do not write tool-call markup as text in your reply.";
|
|
53256
|
+
function maxFakeCompletionRetries() {
|
|
53257
|
+
const n = Number.parseInt(process.env.BILI_FAKE_COMPLETION_RETRIES ?? "0", 10);
|
|
53258
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
53259
|
+
}
|
|
53260
|
+
function fakeBufCap() {
|
|
53261
|
+
const n = Number.parseInt(process.env.BILI_FAKE_BUF_CAP ?? String(16 * 1024 * 1024), 10);
|
|
53262
|
+
return Number.isFinite(n) && n > 0 ? n : 16 * 1024 * 1024;
|
|
53263
|
+
}
|
|
53264
|
+
var ANTHROPIC_TOOL_BLOCK = /"type"\s*:\s*"tool_use"/;
|
|
53265
|
+
var OPENAI_TOOL_BLOCK = /"tool_calls"\s*:\s*\[\s*\{/;
|
|
53266
|
+
var RESPONSES_TOOL_BLOCK = /"type"\s*:\s*"function_call"/;
|
|
53267
|
+
function hasToolBlock(protocol, rawText) {
|
|
53268
|
+
switch (protocol) {
|
|
53269
|
+
case "anthropic":
|
|
53270
|
+
return ANTHROPIC_TOOL_BLOCK.test(rawText);
|
|
53271
|
+
case "openai":
|
|
53272
|
+
return OPENAI_TOOL_BLOCK.test(rawText);
|
|
53273
|
+
case "responses":
|
|
53274
|
+
return RESPONSES_TOOL_BLOCK.test(rawText);
|
|
53275
|
+
}
|
|
53276
|
+
}
|
|
53277
|
+
var TOOL_OPEN = /\x3c(?!\s*\/)(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b|\\u003c(?!\s*\/)(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b/i;
|
|
53278
|
+
var TOOL_CLOSE_GLOBAL = /\x3c\/(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b|\\u003c\/(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b/gi;
|
|
53279
|
+
function hasToolCallStructure(text) {
|
|
53280
|
+
if (!containsToolCallXmlFragment(text)) return false;
|
|
53281
|
+
if (TOOL_OPEN.test(text)) return true;
|
|
53282
|
+
const closes = /* @__PURE__ */ new Set();
|
|
53283
|
+
for (const m2 of text.matchAll(TOOL_CLOSE_GLOBAL)) {
|
|
53284
|
+
const name = m2[1] ?? m2[2];
|
|
53285
|
+
if (name) closes.add(name.toLowerCase());
|
|
53286
|
+
}
|
|
53287
|
+
return closes.size >= 2;
|
|
53288
|
+
}
|
|
53289
|
+
function isFakeCompletion(protocol, rawText) {
|
|
53290
|
+
return hasToolCallStructure(rawText) && !hasToolBlock(protocol, rawText);
|
|
53291
|
+
}
|
|
53292
|
+
function injectFakeCompletionHint(protocol, body) {
|
|
53293
|
+
const raw = typeof body === "string" ? body : body.toString("utf8");
|
|
53294
|
+
let obj;
|
|
53295
|
+
try {
|
|
53296
|
+
obj = JSON.parse(raw);
|
|
53297
|
+
} catch {
|
|
53298
|
+
return null;
|
|
53299
|
+
}
|
|
53300
|
+
if (protocol === "responses") {
|
|
53301
|
+
const input = obj.input;
|
|
53302
|
+
if (!Array.isArray(input)) return null;
|
|
53303
|
+
const arr2 = input;
|
|
53304
|
+
const last2 = arr2[arr2.length - 1];
|
|
53305
|
+
if (last2 && typeof last2 === "object" && last2.role === "user") {
|
|
53306
|
+
const c = last2.content;
|
|
53307
|
+
if (typeof c === "string") last2.content = `${c}
|
|
53308
|
+
|
|
53309
|
+
${FAKE_COMPLETION_HINT}`;
|
|
53310
|
+
else if (Array.isArray(c)) last2.content = [...c, { type: "input_text", text: FAKE_COMPLETION_HINT }];
|
|
53311
|
+
else last2.content = FAKE_COMPLETION_HINT;
|
|
53312
|
+
} else {
|
|
53313
|
+
arr2.push({ role: "user", content: [{ type: "input_text", text: FAKE_COMPLETION_HINT }] });
|
|
53314
|
+
}
|
|
53315
|
+
return JSON.stringify(obj);
|
|
53316
|
+
}
|
|
53317
|
+
const messages = obj.messages;
|
|
53318
|
+
if (!Array.isArray(messages)) return null;
|
|
53319
|
+
const arr = messages;
|
|
53320
|
+
const last = arr[arr.length - 1];
|
|
53321
|
+
if (last && typeof last === "object" && last.role === "user") {
|
|
53322
|
+
const c = last.content;
|
|
53323
|
+
if (typeof c === "string") last.content = `${c}
|
|
53324
|
+
|
|
53325
|
+
${FAKE_COMPLETION_HINT}`;
|
|
53326
|
+
else if (Array.isArray(c)) last.content = [...c, { type: "text", text: FAKE_COMPLETION_HINT }];
|
|
53327
|
+
else last.content = FAKE_COMPLETION_HINT;
|
|
53328
|
+
} else {
|
|
53329
|
+
arr.push({ role: "user", content: FAKE_COMPLETION_HINT });
|
|
53330
|
+
}
|
|
53331
|
+
return JSON.stringify(obj);
|
|
53332
|
+
}
|
|
53333
|
+
|
|
53210
53334
|
// src/stream-openai.ts
|
|
53211
53335
|
function rewriteOpenaiJsonResponse(body, ctx) {
|
|
53212
53336
|
if (!body || typeof body !== "object") return body;
|
|
@@ -54174,19 +54298,125 @@ function mergeUsageSample(acc, sample) {
|
|
|
54174
54298
|
if (sample.cachedTokens !== void 0) acc.cachedTokens = sample.cachedTokens;
|
|
54175
54299
|
if (sample.outputTokens !== void 0) acc.outputTokens = sample.outputTokens;
|
|
54176
54300
|
}
|
|
54177
|
-
async function
|
|
54301
|
+
async function pipePluginChatWithStrip(stream2, res, session, protocol, log2) {
|
|
54178
54302
|
const reader = stream2.getReader();
|
|
54179
54303
|
const decoder = new TextDecoder("utf-8");
|
|
54180
54304
|
let buf = "";
|
|
54181
54305
|
const acc = {};
|
|
54306
|
+
const onDrop = (snippet) => {
|
|
54307
|
+
log("warn", `[tag-echo] stripped model-emitted render tag (plugin passthrough): ${snippet.slice(0, 80).replace(/\n/g, " ")}`);
|
|
54308
|
+
log2?.(`[tag-echo] stripped model-emitted render tag from plugin passthrough text`);
|
|
54309
|
+
};
|
|
54310
|
+
const streams = /* @__PURE__ */ new Map();
|
|
54311
|
+
const filterFor = (field, index) => {
|
|
54312
|
+
const key = `${field}:${index}`;
|
|
54313
|
+
let s3 = streams.get(key);
|
|
54314
|
+
if (!s3) {
|
|
54315
|
+
s3 = { filter: createTagEchoFilter(onDrop), field, index };
|
|
54316
|
+
streams.set(key, s3);
|
|
54317
|
+
}
|
|
54318
|
+
return s3;
|
|
54319
|
+
};
|
|
54320
|
+
const anyPending = () => {
|
|
54321
|
+
for (const s3 of streams.values()) if (s3.filter.pending()) return true;
|
|
54322
|
+
return false;
|
|
54323
|
+
};
|
|
54324
|
+
let lastChunkMeta = {};
|
|
54325
|
+
const syntheticTail = (field, index, tail) => {
|
|
54326
|
+
if (protocol === "anthropic") {
|
|
54327
|
+
const deltaType = field === "text" ? "text_delta" : "thinking_delta";
|
|
54328
|
+
return `event: content_block_delta
|
|
54329
|
+
data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: deltaType, [field]: tail } })}
|
|
54330
|
+
|
|
54331
|
+
`;
|
|
54332
|
+
}
|
|
54333
|
+
return `data: ${JSON.stringify({ ...lastChunkMeta, object: "chat.completion.chunk", choices: [{ index, delta: { [field]: tail } }] })}
|
|
54334
|
+
|
|
54335
|
+
`;
|
|
54336
|
+
};
|
|
54337
|
+
const flushTails = () => {
|
|
54338
|
+
let out = "";
|
|
54339
|
+
for (const s3 of streams.values()) {
|
|
54340
|
+
const tail = s3.filter.flush();
|
|
54341
|
+
if (tail.length > 0) out += syntheticTail(s3.field, s3.index, tail);
|
|
54342
|
+
}
|
|
54343
|
+
return out;
|
|
54344
|
+
};
|
|
54345
|
+
const write = (s3) => {
|
|
54346
|
+
if (!res.write(Buffer.from(s3, "utf8"))) {
|
|
54347
|
+
return new Promise((r) => res.once("drain", () => r()));
|
|
54348
|
+
}
|
|
54349
|
+
};
|
|
54350
|
+
const pushField = (field, index, text) => {
|
|
54351
|
+
const s3 = filterFor(field, index);
|
|
54352
|
+
const clean = s3.filter.push(text);
|
|
54353
|
+
return [clean, clean !== text];
|
|
54354
|
+
};
|
|
54355
|
+
const processOpenai = (ev, rawEvent) => {
|
|
54356
|
+
if (typeof ev["id"] === "string" || typeof ev["model"] === "string") {
|
|
54357
|
+
lastChunkMeta = { id: ev["id"], created: ev["created"], model: ev["model"] };
|
|
54358
|
+
}
|
|
54359
|
+
const choices = ev["choices"];
|
|
54360
|
+
if (!Array.isArray(choices)) {
|
|
54361
|
+
return anyPending() ? flushTails() + rawEvent + "\n\n" : rawEvent + "\n\n";
|
|
54362
|
+
}
|
|
54363
|
+
let rebuilt = null;
|
|
54364
|
+
let droppedText = false;
|
|
54365
|
+
let hadText = false;
|
|
54366
|
+
for (let ci2 = 0; ci2 < choices.length; ci2++) {
|
|
54367
|
+
const ch = choices[ci2];
|
|
54368
|
+
const d = ch?.["delta"];
|
|
54369
|
+
if (!d || typeof d !== "object") continue;
|
|
54370
|
+
const dd = d;
|
|
54371
|
+
for (const field of ["content", "reasoning_content", "reasoning"]) {
|
|
54372
|
+
const v2 = dd[field];
|
|
54373
|
+
if (typeof v2 !== "string") continue;
|
|
54374
|
+
hadText = true;
|
|
54375
|
+
if (!containsRenderTagText(v2) && !anyPending()) continue;
|
|
54376
|
+
const index = typeof ch?.["index"] === "number" ? ch["index"] : ci2;
|
|
54377
|
+
const [clean, changed] = pushField(field, index, v2);
|
|
54378
|
+
if (clean.length === 0) droppedText = true;
|
|
54379
|
+
if (changed) {
|
|
54380
|
+
if (!rebuilt) {
|
|
54381
|
+
rebuilt = { ...ev, choices: choices.map((c) => ({ ...c, delta: { ...c["delta"] } })) };
|
|
54382
|
+
}
|
|
54383
|
+
rebuilt["choices"][ci2]["delta"] = { ...rebuilt["choices"][ci2]["delta"], [field]: clean };
|
|
54384
|
+
}
|
|
54385
|
+
}
|
|
54386
|
+
}
|
|
54387
|
+
if (rebuilt) {
|
|
54388
|
+
if (droppedText && !hadTextOtherThanTextFields(rebuilt["choices"])) {
|
|
54389
|
+
return "";
|
|
54390
|
+
}
|
|
54391
|
+
return rebuildEvent(rawEvent, rebuilt);
|
|
54392
|
+
}
|
|
54393
|
+
if (!hadText && anyPending()) return flushTails() + rawEvent + "\n\n";
|
|
54394
|
+
return rawEvent + "\n\n";
|
|
54395
|
+
};
|
|
54396
|
+
const processAnthropic = (ev, rawEvent) => {
|
|
54397
|
+
if (ev["type"] !== "content_block_delta") {
|
|
54398
|
+
return anyPending() ? flushTails() + rawEvent + "\n\n" : rawEvent + "\n\n";
|
|
54399
|
+
}
|
|
54400
|
+
const d = ev["delta"];
|
|
54401
|
+
const index = typeof ev["index"] === "number" ? ev["index"] : 0;
|
|
54402
|
+
const field = d?.["type"] === "thinking_delta" ? "thinking" : d?.["type"] === "text_delta" ? "text" : null;
|
|
54403
|
+
if (field === null || typeof d?.[field] !== "string") {
|
|
54404
|
+
return rawEvent + "\n\n";
|
|
54405
|
+
}
|
|
54406
|
+
const raw = d[field];
|
|
54407
|
+
if (!containsRenderTagText(raw) && !anyPending()) {
|
|
54408
|
+
return rawEvent + "\n\n";
|
|
54409
|
+
}
|
|
54410
|
+
const [clean, changed] = pushField(field, index, raw);
|
|
54411
|
+
if (!changed) return rawEvent + "\n\n";
|
|
54412
|
+
if (clean.length === 0 && Object.keys(d ?? {}).length <= 2) return "";
|
|
54413
|
+
return rebuildEvent(rawEvent, { ...ev, delta: { ...d, [field]: clean } });
|
|
54414
|
+
};
|
|
54182
54415
|
try {
|
|
54183
54416
|
for (; ; ) {
|
|
54184
54417
|
const { done, value } = await reader.read();
|
|
54185
54418
|
if (done) break;
|
|
54186
54419
|
if (value && value.length > 0) {
|
|
54187
|
-
if (!res.write(Buffer.from(value))) {
|
|
54188
|
-
await new Promise((r) => res.once("drain", () => r()));
|
|
54189
|
-
}
|
|
54190
54420
|
buf = normalizeSseLineEndings(buf + decoder.decode(value, { stream: true }));
|
|
54191
54421
|
let idx;
|
|
54192
54422
|
while ((idx = buf.indexOf("\n\n")) !== -1) {
|
|
@@ -54195,17 +54425,28 @@ async function pipeThroughWithUsage(stream2, res, session, protocol) {
|
|
|
54195
54425
|
const dataLines = rawEvent.split("\n").filter((l) => l.startsWith("data:"));
|
|
54196
54426
|
if (dataLines.length === 0) continue;
|
|
54197
54427
|
const jsonStr = dataLines.map((l) => l.slice(5).replace(/^ /, "")).join("\n").trim();
|
|
54198
|
-
if (!jsonStr
|
|
54428
|
+
if (!jsonStr) continue;
|
|
54429
|
+
if (jsonStr === "[DONE]") {
|
|
54430
|
+
await write(flushTails() + rawEvent + "\n\n");
|
|
54431
|
+
continue;
|
|
54432
|
+
}
|
|
54433
|
+
let ev;
|
|
54199
54434
|
try {
|
|
54200
|
-
|
|
54201
|
-
const sample = usageFromSseEvent(ev);
|
|
54202
|
-
if (sample) mergeUsageSample(acc, sample);
|
|
54435
|
+
ev = JSON.parse(jsonStr);
|
|
54203
54436
|
} catch {
|
|
54437
|
+
await write(rawEvent + "\n\n");
|
|
54438
|
+
continue;
|
|
54204
54439
|
}
|
|
54440
|
+
const sample = usageFromSseEvent(ev);
|
|
54441
|
+
if (sample) mergeUsageSample(acc, sample);
|
|
54442
|
+
const out = protocol === "anthropic" ? processAnthropic(ev, rawEvent) : processOpenai(ev, rawEvent);
|
|
54443
|
+
if (out.length > 0) await write(out);
|
|
54205
54444
|
}
|
|
54206
54445
|
}
|
|
54207
54446
|
if (res.destroyed || res.writableEnded) break;
|
|
54208
54447
|
}
|
|
54448
|
+
const rest = flushTails();
|
|
54449
|
+
if (rest.length > 0 && !res.destroyed && !res.writableEnded) await write(rest);
|
|
54209
54450
|
if (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0) {
|
|
54210
54451
|
applyUsageSample(session, acc, protocol);
|
|
54211
54452
|
markDirty(session);
|
|
@@ -54215,6 +54456,20 @@ async function pipeThroughWithUsage(stream2, res, session, protocol) {
|
|
|
54215
54456
|
res.end();
|
|
54216
54457
|
}
|
|
54217
54458
|
}
|
|
54459
|
+
function hadTextOtherThanTextFields(choices) {
|
|
54460
|
+
if (!Array.isArray(choices)) return true;
|
|
54461
|
+
for (const c of choices) {
|
|
54462
|
+
if (!c || typeof c !== "object") continue;
|
|
54463
|
+
const ch = c;
|
|
54464
|
+
if (typeof ch["finish_reason"] === "string") return true;
|
|
54465
|
+
const d = ch["delta"];
|
|
54466
|
+
if (!d) continue;
|
|
54467
|
+
for (const k2 of Object.keys(d)) {
|
|
54468
|
+
if (k2 !== "content" && k2 !== "reasoning_content" && k2 !== "reasoning") return true;
|
|
54469
|
+
}
|
|
54470
|
+
}
|
|
54471
|
+
return false;
|
|
54472
|
+
}
|
|
54218
54473
|
async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
|
|
54219
54474
|
const reader = stream2.getReader();
|
|
54220
54475
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -54353,13 +54608,12 @@ async function pipePluginJson(stream2, res, session, protocol) {
|
|
|
54353
54608
|
}
|
|
54354
54609
|
} catch {
|
|
54355
54610
|
}
|
|
54356
|
-
if (
|
|
54611
|
+
if (containsRenderTagText(text)) {
|
|
54357
54612
|
try {
|
|
54358
54613
|
const json = JSON.parse(text);
|
|
54359
|
-
|
|
54360
|
-
|
|
54361
|
-
|
|
54362
|
-
}
|
|
54614
|
+
const stripped = protocol === "responses" ? stripResponsesText(json) : protocol === "anthropic" ? stripAnthropicText(json) : stripOpenaiChatText(json);
|
|
54615
|
+
res.end(Buffer.from(JSON.stringify(stripped), "utf8"));
|
|
54616
|
+
return;
|
|
54363
54617
|
} catch {
|
|
54364
54618
|
}
|
|
54365
54619
|
}
|
|
@@ -57329,7 +57583,7 @@ ${hdrText}
|
|
|
57329
57583
|
if (prepared.protocol === "responses") {
|
|
57330
57584
|
await pipePluginResponsesWithStrip(upstream.body, res, prepared.session, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
|
|
57331
57585
|
} else {
|
|
57332
|
-
await
|
|
57586
|
+
await pipePluginChatWithStrip(upstream.body, res, prepared.session, prepared.protocol, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
|
|
57333
57587
|
}
|
|
57334
57588
|
} else {
|
|
57335
57589
|
await pipePluginJson(upstream.body, res, prepared.session, prepared.protocol);
|
|
@@ -57337,10 +57591,24 @@ ${hdrText}
|
|
|
57337
57591
|
clearUpstreamTimer();
|
|
57338
57592
|
return;
|
|
57339
57593
|
}
|
|
57594
|
+
let responseBody = upstream.body;
|
|
57595
|
+
if (prepared !== null && prepared.stream && maxFakeCompletionRetries() > 0) {
|
|
57596
|
+
const resolvedBuf = await resolveFakeCompletion(upstream.body, {
|
|
57597
|
+
protocol: prepared.protocol,
|
|
57598
|
+
body,
|
|
57599
|
+
upstreamUrl,
|
|
57600
|
+
reqHeaders: buildForwardHeaders(headers),
|
|
57601
|
+
proxyUrl,
|
|
57602
|
+
signal: clientAbort.signal,
|
|
57603
|
+
session: prepared.session,
|
|
57604
|
+
log: log2
|
|
57605
|
+
});
|
|
57606
|
+
responseBody = bufferToStream(resolvedBuf);
|
|
57607
|
+
}
|
|
57340
57608
|
const useRewriter = prepared !== null && prepared.compressInjected && prepared.processedMessages.length > 0;
|
|
57341
57609
|
if (!useRewriter || prepared === null) {
|
|
57342
57610
|
if (prepared && prepared.resetAfterSuccess) {
|
|
57343
|
-
const [toClient, toObserve] =
|
|
57611
|
+
const [toClient, toObserve] = responseBody.tee();
|
|
57344
57612
|
const observed = observeResponsesTerminalState(toObserve, prepared.stream);
|
|
57345
57613
|
await pipeThrough(toClient, res);
|
|
57346
57614
|
clearUpstreamTimer();
|
|
@@ -57352,7 +57620,7 @@ ${hdrText}
|
|
|
57352
57620
|
log2("warn", `[${prepared.session.id}] native compact response terminal=${terminal}; rebase NOT scheduled`);
|
|
57353
57621
|
}
|
|
57354
57622
|
} else {
|
|
57355
|
-
await pipeThrough(
|
|
57623
|
+
await pipeThrough(responseBody, res);
|
|
57356
57624
|
clearUpstreamTimer();
|
|
57357
57625
|
}
|
|
57358
57626
|
return;
|
|
@@ -57366,10 +57634,10 @@ ${hdrText}
|
|
|
57366
57634
|
debug: opts.debug
|
|
57367
57635
|
};
|
|
57368
57636
|
if (prepared.stream) {
|
|
57369
|
-
let streamToRead =
|
|
57637
|
+
let streamToRead = responseBody;
|
|
57370
57638
|
let dumpRaw;
|
|
57371
57639
|
if (opts.dumpSse) {
|
|
57372
|
-
const [a, b2] =
|
|
57640
|
+
const [a, b2] = responseBody.tee();
|
|
57373
57641
|
streamToRead = a;
|
|
57374
57642
|
dumpRaw = dumpStreamToFile(b2, opts.dumpSse, `${Date.now()}-${safeSessionId(prepared.session.id)}-raw.sse`);
|
|
57375
57643
|
}
|
|
@@ -57495,6 +57763,58 @@ async function readStreamToBuffer(stream2, maxBytes = 1 << 20) {
|
|
|
57495
57763
|
}
|
|
57496
57764
|
return Buffer.concat(chunks);
|
|
57497
57765
|
}
|
|
57766
|
+
function bufferToStream(buf) {
|
|
57767
|
+
return new ReadableStream({
|
|
57768
|
+
start(controller) {
|
|
57769
|
+
controller.enqueue(new Uint8Array(buf));
|
|
57770
|
+
controller.close();
|
|
57771
|
+
}
|
|
57772
|
+
});
|
|
57773
|
+
}
|
|
57774
|
+
async function resolveFakeCompletion(stream2, opts) {
|
|
57775
|
+
let buffer = await readStreamToBuffer(stream2, fakeBufCap());
|
|
57776
|
+
const max = maxFakeCompletionRetries();
|
|
57777
|
+
const sid = opts.session.id;
|
|
57778
|
+
const priorStreak = opts.session.metadata.fakeCompletionStreak ?? 0;
|
|
57779
|
+
if (max > 0 && priorStreak < max && isFakeCompletion(opts.protocol, buffer.toString("utf8"))) {
|
|
57780
|
+
for (let attempt = 1; attempt <= max && !opts.signal.aborted; attempt++) {
|
|
57781
|
+
const hinted = injectFakeCompletionHint(opts.protocol, opts.body);
|
|
57782
|
+
if (hinted === null) break;
|
|
57783
|
+
opts.log("warn", `[${sid}] fake completion (tool-call XML, no tool block); retry ${attempt}/${max} with corrective hint`);
|
|
57784
|
+
let r;
|
|
57785
|
+
try {
|
|
57786
|
+
r = await fetchWithTimeout(
|
|
57787
|
+
opts.upstreamUrl,
|
|
57788
|
+
{
|
|
57789
|
+
method: "POST",
|
|
57790
|
+
headers: opts.reqHeaders,
|
|
57791
|
+
body: hinted,
|
|
57792
|
+
...opts.proxyUrl ? { dispatcher: proxyDispatcher(opts.proxyUrl) } : {}
|
|
57793
|
+
},
|
|
57794
|
+
void 0,
|
|
57795
|
+
opts.signal
|
|
57796
|
+
);
|
|
57797
|
+
} catch (e) {
|
|
57798
|
+
opts.log("warn", `[${sid}] fake-completion retry failed: ${String(e)}; presenting original`);
|
|
57799
|
+
break;
|
|
57800
|
+
}
|
|
57801
|
+
try {
|
|
57802
|
+
if (!r.response.ok || !r.response.body) {
|
|
57803
|
+
opts.log("warn", `[${sid}] fake-completion retry rejected (HTTP ${r.response.status}); presenting original`);
|
|
57804
|
+
break;
|
|
57805
|
+
}
|
|
57806
|
+
buffer = await readStreamToBuffer(r.response.body, fakeBufCap());
|
|
57807
|
+
} finally {
|
|
57808
|
+
r.clearTimer();
|
|
57809
|
+
}
|
|
57810
|
+
if (!isFakeCompletion(opts.protocol, buffer.toString("utf8"))) break;
|
|
57811
|
+
}
|
|
57812
|
+
}
|
|
57813
|
+
const stillFake = isFakeCompletion(opts.protocol, buffer.toString("utf8"));
|
|
57814
|
+
opts.session.metadata.fakeCompletionStreak = stillFake ? priorStreak + 1 : 0;
|
|
57815
|
+
markDirty(opts.session);
|
|
57816
|
+
return buffer;
|
|
57817
|
+
}
|
|
57498
57818
|
async function pipeThrough(stream2, res) {
|
|
57499
57819
|
const reader = stream2.getReader();
|
|
57500
57820
|
try {
|