@skaile/transform-proxy 0.1.0 → 0.2.1
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/CHANGELOG.md +9 -0
- package/README.md +28 -2
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{config-BKD-ILIz.mjs → config-Xdn_LYpZ.mjs} +716 -54
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +1 -1
- package/dist/omp/index.d.mts +89 -0
- package/dist/omp/index.mjs +345 -0
- package/dist/{server-D5nBnO7T.d.mts → server-C_eKyCTV.d.mts} +1 -1
- package/package.json +24 -3
|
@@ -304,60 +304,60 @@ var UnsupportedContentError = class extends Error {
|
|
|
304
304
|
}
|
|
305
305
|
};
|
|
306
306
|
// src/wire/anthropic.ts
|
|
307
|
-
const isObj = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
307
|
+
const isObj$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
308
308
|
/** Block types that carry attachments the proxy cannot pseudonymize. */
|
|
309
309
|
const REFUSED_BLOCKS = {
|
|
310
310
|
image: true,
|
|
311
311
|
document: true,
|
|
312
312
|
search_result: true
|
|
313
313
|
};
|
|
314
|
-
const signatureKey = (signature) => `anthropic:${signature}`;
|
|
315
|
-
function refuse(block) {
|
|
314
|
+
const signatureKey$1 = (signature) => `anthropic:${signature}`;
|
|
315
|
+
function refuse$1(block) {
|
|
316
316
|
if (typeof block.type === "string" && REFUSED_BLOCKS[block.type] === true) throw new UnsupportedContentError(`Anthropic ${block.type} content cannot be transformed`);
|
|
317
317
|
}
|
|
318
|
-
async function outbound(transform, text, leaf) {
|
|
318
|
+
async function outbound$2(transform, text, leaf) {
|
|
319
319
|
return text === "" ? text : transform.outbound(text, leaf);
|
|
320
320
|
}
|
|
321
321
|
/** A string, or an array of blocks whose `text` blocks are rewritten. */
|
|
322
322
|
async function rewriteText(content, leaf, transform) {
|
|
323
|
-
if (typeof content === "string") return outbound(transform, content, leaf);
|
|
323
|
+
if (typeof content === "string") return outbound$2(transform, content, leaf);
|
|
324
324
|
if (!Array.isArray(content)) return content;
|
|
325
325
|
const out = [];
|
|
326
326
|
for (const block of content) {
|
|
327
|
-
if (!isObj(block)) {
|
|
327
|
+
if (!isObj$2(block)) {
|
|
328
328
|
out.push(block);
|
|
329
329
|
continue;
|
|
330
330
|
}
|
|
331
|
-
refuse(block);
|
|
331
|
+
refuse$1(block);
|
|
332
332
|
out.push(block.type === "text" && typeof block.text === "string" ? {
|
|
333
333
|
...block,
|
|
334
|
-
text: await outbound(transform, block.text, leaf)
|
|
334
|
+
text: await outbound$2(transform, block.text, leaf)
|
|
335
335
|
} : block);
|
|
336
336
|
}
|
|
337
337
|
return out;
|
|
338
338
|
}
|
|
339
339
|
/** Every string leaf of a JSON value (tool-call input); keys stay as they are. */
|
|
340
|
-
async function rewriteStrings(value, leaf, transform) {
|
|
341
|
-
if (typeof value === "string") return outbound(transform, value, leaf);
|
|
340
|
+
async function rewriteStrings$2(value, leaf, transform) {
|
|
341
|
+
if (typeof value === "string") return outbound$2(transform, value, leaf);
|
|
342
342
|
if (Array.isArray(value)) {
|
|
343
343
|
const out = [];
|
|
344
|
-
for (const item of value) out.push(await rewriteStrings(item, leaf, transform));
|
|
344
|
+
for (const item of value) out.push(await rewriteStrings$2(item, leaf, transform));
|
|
345
345
|
return out;
|
|
346
346
|
}
|
|
347
|
-
if (isObj(value)) {
|
|
347
|
+
if (isObj$2(value)) {
|
|
348
348
|
const out = {};
|
|
349
|
-
for (const [key, item] of Object.entries(value)) out[key] = await rewriteStrings(item, leaf, transform);
|
|
349
|
+
for (const [key, item] of Object.entries(value)) out[key] = await rewriteStrings$2(item, leaf, transform);
|
|
350
350
|
return out;
|
|
351
351
|
}
|
|
352
352
|
return value;
|
|
353
353
|
}
|
|
354
354
|
/** Every string leaf of a JSON value, restored inbound. */
|
|
355
|
-
function restoreStrings(value, transform) {
|
|
355
|
+
function restoreStrings$1(value, transform) {
|
|
356
356
|
if (typeof value === "string") return transform.inbound(value);
|
|
357
|
-
if (Array.isArray(value)) return value.map((item) => restoreStrings(item, transform));
|
|
358
|
-
if (isObj(value)) {
|
|
357
|
+
if (Array.isArray(value)) return value.map((item) => restoreStrings$1(item, transform));
|
|
358
|
+
if (isObj$2(value)) {
|
|
359
359
|
const out = {};
|
|
360
|
-
for (const [key, item] of Object.entries(value)) out[key] = restoreStrings(item, transform);
|
|
360
|
+
for (const [key, item] of Object.entries(value)) out[key] = restoreStrings$1(item, transform);
|
|
361
361
|
return out;
|
|
362
362
|
}
|
|
363
363
|
return value;
|
|
@@ -365,7 +365,7 @@ function restoreStrings(value, transform) {
|
|
|
365
365
|
/** A thinking block: swaps in the cached model text when its signature is known, else rewrites it. */
|
|
366
366
|
async function rewriteThinking(block, ctx) {
|
|
367
367
|
if (typeof block.signature === "string") {
|
|
368
|
-
const cached = ctx.signatures.get(signatureKey(block.signature));
|
|
368
|
+
const cached = ctx.signatures.get(signatureKey$1(block.signature));
|
|
369
369
|
if (cached !== void 0) return {
|
|
370
370
|
...block,
|
|
371
371
|
thinking: cached
|
|
@@ -374,25 +374,25 @@ async function rewriteThinking(block, ctx) {
|
|
|
374
374
|
if (typeof block.thinking !== "string") return block;
|
|
375
375
|
return {
|
|
376
376
|
...block,
|
|
377
|
-
thinking: await outbound(ctx.transform, block.thinking, { kind: "thinking" })
|
|
377
|
+
thinking: await outbound$2(ctx.transform, block.thinking, { kind: "thinking" })
|
|
378
378
|
};
|
|
379
379
|
}
|
|
380
|
-
async function rewriteContent(content, role, toolNames, ctx) {
|
|
380
|
+
async function rewriteContent$2(content, role, toolNames, ctx) {
|
|
381
381
|
const textKind = role === "assistant" ? "assistant" : "user";
|
|
382
|
-
if (typeof content === "string") return outbound(ctx.transform, content, { kind: textKind });
|
|
382
|
+
if (typeof content === "string") return outbound$2(ctx.transform, content, { kind: textKind });
|
|
383
383
|
if (!Array.isArray(content)) return content;
|
|
384
384
|
const out = [];
|
|
385
385
|
for (const block of content) {
|
|
386
|
-
if (!isObj(block)) {
|
|
386
|
+
if (!isObj$2(block)) {
|
|
387
387
|
out.push(block);
|
|
388
388
|
continue;
|
|
389
389
|
}
|
|
390
|
-
refuse(block);
|
|
390
|
+
refuse$1(block);
|
|
391
391
|
switch (block.type) {
|
|
392
392
|
case "text":
|
|
393
393
|
out.push(typeof block.text === "string" ? {
|
|
394
394
|
...block,
|
|
395
|
-
text: await outbound(ctx.transform, block.text, { kind: textKind })
|
|
395
|
+
text: await outbound$2(ctx.transform, block.text, { kind: textKind })
|
|
396
396
|
} : block);
|
|
397
397
|
break;
|
|
398
398
|
case "thinking":
|
|
@@ -402,7 +402,7 @@ async function rewriteContent(content, role, toolNames, ctx) {
|
|
|
402
402
|
const toolName = typeof block.name === "string" ? block.name : void 0;
|
|
403
403
|
out.push({
|
|
404
404
|
...block,
|
|
405
|
-
input: await rewriteStrings(block.input, {
|
|
405
|
+
input: await rewriteStrings$2(block.input, {
|
|
406
406
|
kind: "toolCall",
|
|
407
407
|
toolName
|
|
408
408
|
}, ctx.transform)
|
|
@@ -429,18 +429,23 @@ async function rewriteContent(content, role, toolNames, ctx) {
|
|
|
429
429
|
function toolNamesById(messages) {
|
|
430
430
|
const names = /* @__PURE__ */ new Map();
|
|
431
431
|
for (const message of messages) {
|
|
432
|
-
if (!isObj(message) || !Array.isArray(message.content)) continue;
|
|
433
|
-
for (const block of message.content) if (isObj(block) && block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") names.set(block.id, block.name);
|
|
432
|
+
if (!isObj$2(message) || !Array.isArray(message.content)) continue;
|
|
433
|
+
for (const block of message.content) if (isObj$2(block) && block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") names.set(block.id, block.name);
|
|
434
434
|
}
|
|
435
435
|
return names;
|
|
436
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Appends the instruction after the host's system prompt. Gateways that serve only Claude Code
|
|
439
|
+
* traffic check that the system prompt starts with the Claude Code blocks (TeamClaude answers
|
|
440
|
+
* 429 for Sonnet otherwise), so the host's leading blocks must stay first.
|
|
441
|
+
*/
|
|
437
442
|
function withInstruction(system, instruction) {
|
|
438
443
|
if (system === void 0) return instruction;
|
|
439
|
-
if (typeof system === "string") return `${
|
|
440
|
-
if (Array.isArray(system)) return [{
|
|
444
|
+
if (typeof system === "string") return `${system}\n\n${instruction}`;
|
|
445
|
+
if (Array.isArray(system)) return [...system, {
|
|
441
446
|
type: "text",
|
|
442
447
|
text: instruction
|
|
443
|
-
}
|
|
448
|
+
}];
|
|
444
449
|
return system;
|
|
445
450
|
}
|
|
446
451
|
function newBlockState(kind, transform) {
|
|
@@ -465,7 +470,7 @@ function tailDelta(kind, tail) {
|
|
|
465
470
|
partial_json: tail
|
|
466
471
|
};
|
|
467
472
|
}
|
|
468
|
-
function createStreamTransformer(ctx) {
|
|
473
|
+
function createStreamTransformer$2(ctx) {
|
|
469
474
|
const blocks = /* @__PURE__ */ new Map();
|
|
470
475
|
const handle = (block) => {
|
|
471
476
|
let payload;
|
|
@@ -474,12 +479,12 @@ function createStreamTransformer(ctx) {
|
|
|
474
479
|
} catch {
|
|
475
480
|
return block.raw;
|
|
476
481
|
}
|
|
477
|
-
if (!isObj(payload)) return block.raw;
|
|
482
|
+
if (!isObj$2(payload)) return block.raw;
|
|
478
483
|
const index = typeof payload.index === "number" ? payload.index : void 0;
|
|
479
484
|
switch (payload.type) {
|
|
480
485
|
case "content_block_start": {
|
|
481
486
|
const content = payload.content_block;
|
|
482
|
-
if (index === void 0 || !isObj(content)) return block.raw;
|
|
487
|
+
if (index === void 0 || !isObj$2(content)) return block.raw;
|
|
483
488
|
if (content.type === "text" || content.type === "thinking" || content.type === "tool_use") {
|
|
484
489
|
const kind = content.type === "tool_use" ? "tool" : content.type;
|
|
485
490
|
blocks.set(index, newBlockState(kind, ctx.transform));
|
|
@@ -489,7 +494,7 @@ function createStreamTransformer(ctx) {
|
|
|
489
494
|
case "content_block_delta": {
|
|
490
495
|
const state = index === void 0 ? void 0 : blocks.get(index);
|
|
491
496
|
const delta = payload.delta;
|
|
492
|
-
if (state === void 0 || !isObj(delta)) return block.raw;
|
|
497
|
+
if (state === void 0 || !isObj$2(delta)) return block.raw;
|
|
493
498
|
if (state.kind === "text" && delta.type === "text_delta" && typeof delta.text === "string") return formatSseEvent(block.event, {
|
|
494
499
|
...payload,
|
|
495
500
|
delta: {
|
|
@@ -508,7 +513,7 @@ function createStreamTransformer(ctx) {
|
|
|
508
513
|
});
|
|
509
514
|
}
|
|
510
515
|
if (state.kind === "thinking" && delta.type === "signature_delta" && typeof delta.signature === "string") {
|
|
511
|
-
ctx.signatures.set(signatureKey(delta.signature), state.raw);
|
|
516
|
+
ctx.signatures.set(signatureKey$1(delta.signature), state.raw);
|
|
512
517
|
return block.raw;
|
|
513
518
|
}
|
|
514
519
|
if (state.kind === "tool" && delta.type === "input_json_delta" && typeof delta.partial_json === "string") return formatSseEvent(block.event, {
|
|
@@ -552,31 +557,31 @@ function createStreamTransformer(ctx) {
|
|
|
552
557
|
}
|
|
553
558
|
const anthropicWalker = {
|
|
554
559
|
async request(body, ctx) {
|
|
555
|
-
if (!isObj(body)) return body;
|
|
560
|
+
if (!isObj$2(body)) return body;
|
|
556
561
|
const out = { ...body };
|
|
557
562
|
if (body.system !== void 0) out.system = await rewriteText(body.system, { kind: "system" }, ctx.transform);
|
|
558
563
|
if (ctx.transform.instruction !== void 0) out.system = withInstruction(out.system, ctx.transform.instruction);
|
|
559
564
|
if (Array.isArray(body.messages)) {
|
|
560
565
|
const toolNames = toolNamesById(body.messages);
|
|
561
566
|
const messages = [];
|
|
562
|
-
for (const message of body.messages) messages.push(isObj(message) ? {
|
|
567
|
+
for (const message of body.messages) messages.push(isObj$2(message) ? {
|
|
563
568
|
...message,
|
|
564
|
-
content: await rewriteContent(message.content, message.role, toolNames, ctx)
|
|
569
|
+
content: await rewriteContent$2(message.content, message.role, toolNames, ctx)
|
|
565
570
|
} : message);
|
|
566
571
|
out.messages = messages;
|
|
567
572
|
}
|
|
568
573
|
return out;
|
|
569
574
|
},
|
|
570
575
|
response(body, ctx) {
|
|
571
|
-
if (!isObj(body) || !Array.isArray(body.content)) return body;
|
|
576
|
+
if (!isObj$2(body) || !Array.isArray(body.content)) return body;
|
|
572
577
|
const content = body.content.map((block) => {
|
|
573
|
-
if (!isObj(block)) return block;
|
|
578
|
+
if (!isObj$2(block)) return block;
|
|
574
579
|
if (block.type === "text" && typeof block.text === "string") return {
|
|
575
580
|
...block,
|
|
576
581
|
text: ctx.transform.inbound(block.text)
|
|
577
582
|
};
|
|
578
583
|
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
579
|
-
if (typeof block.signature === "string") ctx.signatures.set(signatureKey(block.signature), block.thinking);
|
|
584
|
+
if (typeof block.signature === "string") ctx.signatures.set(signatureKey$1(block.signature), block.thinking);
|
|
580
585
|
return {
|
|
581
586
|
...block,
|
|
582
587
|
thinking: ctx.transform.inbound(block.thinking)
|
|
@@ -584,7 +589,7 @@ const anthropicWalker = {
|
|
|
584
589
|
}
|
|
585
590
|
if (block.type === "tool_use") return {
|
|
586
591
|
...block,
|
|
587
|
-
input: restoreStrings(block.input, ctx.transform)
|
|
592
|
+
input: restoreStrings$1(block.input, ctx.transform)
|
|
588
593
|
};
|
|
589
594
|
return block;
|
|
590
595
|
});
|
|
@@ -593,6 +598,651 @@ const anthropicWalker = {
|
|
|
593
598
|
content
|
|
594
599
|
};
|
|
595
600
|
},
|
|
601
|
+
stream(ctx) {
|
|
602
|
+
return createStreamTransformer$2(ctx);
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
// src/wire/openai-chat.ts
|
|
606
|
+
const isObj$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
607
|
+
/** Content part types that carry attachments the proxy cannot pseudonymize. */
|
|
608
|
+
const REFUSED_PARTS$1 = {
|
|
609
|
+
image_url: true,
|
|
610
|
+
input_audio: true,
|
|
611
|
+
file: true
|
|
612
|
+
};
|
|
613
|
+
/** Reasoning fields used by OpenAI-compatible providers. */
|
|
614
|
+
const REASONING_FIELDS = ["reasoning_content", "reasoning"];
|
|
615
|
+
async function outbound$1(transform, text, leaf) {
|
|
616
|
+
return text === "" ? text : transform.outbound(text, leaf);
|
|
617
|
+
}
|
|
618
|
+
/** A string, or an array of parts whose `text` parts are rewritten. */
|
|
619
|
+
async function rewriteContent$1(content, leaf, transform) {
|
|
620
|
+
if (typeof content === "string") return outbound$1(transform, content, leaf);
|
|
621
|
+
if (!Array.isArray(content)) return content;
|
|
622
|
+
const out = [];
|
|
623
|
+
for (const part of content) {
|
|
624
|
+
if (!isObj$1(part)) {
|
|
625
|
+
out.push(part);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (typeof part.type === "string" && REFUSED_PARTS$1[part.type] === true) throw new UnsupportedContentError(`OpenAI chat ${part.type} content cannot be transformed`);
|
|
629
|
+
out.push(part.type === "text" && typeof part.text === "string" ? {
|
|
630
|
+
...part,
|
|
631
|
+
text: await outbound$1(transform, part.text, leaf)
|
|
632
|
+
} : part);
|
|
633
|
+
}
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
async function rewriteStrings$1(value, leaf, transform) {
|
|
637
|
+
if (typeof value === "string") return outbound$1(transform, value, leaf);
|
|
638
|
+
if (Array.isArray(value)) {
|
|
639
|
+
const out = [];
|
|
640
|
+
for (const item of value) out.push(await rewriteStrings$1(item, leaf, transform));
|
|
641
|
+
return out;
|
|
642
|
+
}
|
|
643
|
+
if (isObj$1(value)) {
|
|
644
|
+
const out = {};
|
|
645
|
+
for (const [key, item] of Object.entries(value)) out[key] = await rewriteStrings$1(item, leaf, transform);
|
|
646
|
+
return out;
|
|
647
|
+
}
|
|
648
|
+
return value;
|
|
649
|
+
}
|
|
650
|
+
function restoreStrings(value, transform) {
|
|
651
|
+
if (typeof value === "string") return transform.inbound(value);
|
|
652
|
+
if (Array.isArray(value)) return value.map((item) => restoreStrings(item, transform));
|
|
653
|
+
if (isObj$1(value)) {
|
|
654
|
+
const out = {};
|
|
655
|
+
for (const [key, item] of Object.entries(value)) out[key] = restoreStrings(item, transform);
|
|
656
|
+
return out;
|
|
657
|
+
}
|
|
658
|
+
return value;
|
|
659
|
+
}
|
|
660
|
+
/** Tool-call arguments are a JSON string: rewrite its string leaves, or the raw text when it is not JSON. */
|
|
661
|
+
async function rewriteArguments$1(args, leaf, transform) {
|
|
662
|
+
let parsed;
|
|
663
|
+
try {
|
|
664
|
+
parsed = JSON.parse(args);
|
|
665
|
+
} catch {
|
|
666
|
+
return outbound$1(transform, args, leaf);
|
|
667
|
+
}
|
|
668
|
+
return JSON.stringify(await rewriteStrings$1(parsed, leaf, transform));
|
|
669
|
+
}
|
|
670
|
+
function restoreArguments$1(args, transform) {
|
|
671
|
+
let parsed;
|
|
672
|
+
try {
|
|
673
|
+
parsed = JSON.parse(args);
|
|
674
|
+
} catch {
|
|
675
|
+
return transform.inbound(args);
|
|
676
|
+
}
|
|
677
|
+
return JSON.stringify(restoreStrings(parsed, transform));
|
|
678
|
+
}
|
|
679
|
+
async function rewriteToolCalls(calls, transform, names) {
|
|
680
|
+
if (!Array.isArray(calls)) return calls;
|
|
681
|
+
const out = [];
|
|
682
|
+
for (const call of calls) {
|
|
683
|
+
if (!isObj$1(call) || !isObj$1(call.function)) {
|
|
684
|
+
out.push(call);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
const fn = call.function;
|
|
688
|
+
const toolName = typeof fn.name === "string" ? fn.name : void 0;
|
|
689
|
+
if (typeof call.id === "string" && toolName !== void 0) names.set(call.id, toolName);
|
|
690
|
+
out.push(typeof fn.arguments === "string" ? {
|
|
691
|
+
...call,
|
|
692
|
+
function: {
|
|
693
|
+
...fn,
|
|
694
|
+
arguments: await rewriteArguments$1(fn.arguments, {
|
|
695
|
+
kind: "toolCall",
|
|
696
|
+
toolName
|
|
697
|
+
}, transform)
|
|
698
|
+
}
|
|
699
|
+
} : call);
|
|
700
|
+
}
|
|
701
|
+
return out;
|
|
702
|
+
}
|
|
703
|
+
async function rewriteMessage(message, transform, names) {
|
|
704
|
+
const out = { ...message };
|
|
705
|
+
switch (message.role) {
|
|
706
|
+
case "system":
|
|
707
|
+
case "developer":
|
|
708
|
+
out.content = await rewriteContent$1(message.content, { kind: "system" }, transform);
|
|
709
|
+
break;
|
|
710
|
+
case "user":
|
|
711
|
+
out.content = await rewriteContent$1(message.content, { kind: "user" }, transform);
|
|
712
|
+
break;
|
|
713
|
+
case "assistant":
|
|
714
|
+
for (const field of REASONING_FIELDS) {
|
|
715
|
+
const value = message[field];
|
|
716
|
+
if (typeof value === "string") out[field] = await outbound$1(transform, value, { kind: "thinking" });
|
|
717
|
+
}
|
|
718
|
+
out.content = await rewriteContent$1(message.content, { kind: "assistant" }, transform);
|
|
719
|
+
out.tool_calls = await rewriteToolCalls(message.tool_calls, transform, names);
|
|
720
|
+
if (message.tool_calls === void 0) delete out.tool_calls;
|
|
721
|
+
break;
|
|
722
|
+
case "tool": {
|
|
723
|
+
const toolName = typeof message.tool_call_id === "string" ? names.get(message.tool_call_id) : void 0;
|
|
724
|
+
out.content = await rewriteContent$1(message.content, {
|
|
725
|
+
kind: "toolResult",
|
|
726
|
+
toolName
|
|
727
|
+
}, transform);
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
default: return message;
|
|
731
|
+
}
|
|
732
|
+
if (message.content === void 0) delete out.content;
|
|
733
|
+
return out;
|
|
734
|
+
}
|
|
735
|
+
/** The delta carrying every held tail of one choice, or undefined when nothing is held. */
|
|
736
|
+
function flushChoice(state) {
|
|
737
|
+
const delta = {};
|
|
738
|
+
if (state.content !== void 0) {
|
|
739
|
+
const tail = state.content.flush();
|
|
740
|
+
if (tail !== "") delta.content = tail;
|
|
741
|
+
}
|
|
742
|
+
for (const [field, restore] of state.reasoning) {
|
|
743
|
+
const tail = restore.flush();
|
|
744
|
+
if (tail !== "") delta[field] = tail;
|
|
745
|
+
}
|
|
746
|
+
const tools = [];
|
|
747
|
+
for (const [index, restore] of state.tools) {
|
|
748
|
+
const tail = restore.flush();
|
|
749
|
+
if (tail !== "") tools.push({
|
|
750
|
+
index,
|
|
751
|
+
function: { arguments: tail }
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
if (tools.length > 0) delta.tool_calls = tools;
|
|
755
|
+
state.content = void 0;
|
|
756
|
+
state.reasoning.clear();
|
|
757
|
+
state.tools.clear();
|
|
758
|
+
return Object.keys(delta).length === 0 ? void 0 : delta;
|
|
759
|
+
}
|
|
760
|
+
function createStreamTransformer$1(ctx) {
|
|
761
|
+
const choices = /* @__PURE__ */ new Map();
|
|
762
|
+
/** The latest chunk's envelope (id, model, ...), reused for tail chunks. */
|
|
763
|
+
let envelope = { object: "chat.completion.chunk" };
|
|
764
|
+
const choiceState = (index) => {
|
|
765
|
+
let state = choices.get(index);
|
|
766
|
+
if (state === void 0) {
|
|
767
|
+
state = {
|
|
768
|
+
reasoning: /* @__PURE__ */ new Map(),
|
|
769
|
+
tools: /* @__PURE__ */ new Map()
|
|
770
|
+
};
|
|
771
|
+
choices.set(index, state);
|
|
772
|
+
}
|
|
773
|
+
return state;
|
|
774
|
+
};
|
|
775
|
+
const tailEvent = (event, index, delta) => formatSseEvent(event, {
|
|
776
|
+
...envelope,
|
|
777
|
+
choices: [{
|
|
778
|
+
index,
|
|
779
|
+
delta,
|
|
780
|
+
finish_reason: null
|
|
781
|
+
}]
|
|
782
|
+
});
|
|
783
|
+
const flushAll = (event) => {
|
|
784
|
+
let out = "";
|
|
785
|
+
for (const [index, state] of choices) {
|
|
786
|
+
const delta = flushChoice(state);
|
|
787
|
+
if (delta !== void 0) out += tailEvent(event, index, delta);
|
|
788
|
+
}
|
|
789
|
+
choices.clear();
|
|
790
|
+
return out;
|
|
791
|
+
};
|
|
792
|
+
const rewriteDelta = (delta, state) => {
|
|
793
|
+
const out = { ...delta };
|
|
794
|
+
let changed = false;
|
|
795
|
+
if (typeof delta.content === "string") {
|
|
796
|
+
state.content ??= ctx.transform.inboundStream();
|
|
797
|
+
out.content = state.content.push(delta.content);
|
|
798
|
+
changed = true;
|
|
799
|
+
}
|
|
800
|
+
for (const field of REASONING_FIELDS) {
|
|
801
|
+
const value = delta[field];
|
|
802
|
+
if (typeof value !== "string") continue;
|
|
803
|
+
let restore = state.reasoning.get(field);
|
|
804
|
+
if (restore === void 0) {
|
|
805
|
+
restore = ctx.transform.inboundStream();
|
|
806
|
+
state.reasoning.set(field, restore);
|
|
807
|
+
}
|
|
808
|
+
out[field] = restore.push(value);
|
|
809
|
+
changed = true;
|
|
810
|
+
}
|
|
811
|
+
if (Array.isArray(delta.tool_calls)) out.tool_calls = delta.tool_calls.map((call) => {
|
|
812
|
+
if (!isObj$1(call) || !isObj$1(call.function) || typeof call.function.arguments !== "string") return call;
|
|
813
|
+
const index = typeof call.index === "number" ? call.index : 0;
|
|
814
|
+
let restore = state.tools.get(index);
|
|
815
|
+
if (restore === void 0) {
|
|
816
|
+
restore = createJsonArgRestorer(() => ctx.transform.inboundStream());
|
|
817
|
+
state.tools.set(index, restore);
|
|
818
|
+
}
|
|
819
|
+
changed = true;
|
|
820
|
+
return {
|
|
821
|
+
...call,
|
|
822
|
+
function: {
|
|
823
|
+
...call.function,
|
|
824
|
+
arguments: restore.push(call.function.arguments)
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
});
|
|
828
|
+
return {
|
|
829
|
+
delta: out,
|
|
830
|
+
changed
|
|
831
|
+
};
|
|
832
|
+
};
|
|
833
|
+
const handle = (block) => {
|
|
834
|
+
if (block.data.trim() === "[DONE]") return flushAll(block.event) + block.raw;
|
|
835
|
+
let payload;
|
|
836
|
+
try {
|
|
837
|
+
payload = JSON.parse(block.data);
|
|
838
|
+
} catch {
|
|
839
|
+
return block.raw;
|
|
840
|
+
}
|
|
841
|
+
if (!isObj$1(payload) || !Array.isArray(payload.choices)) return block.raw;
|
|
842
|
+
const { choices: _choices, ...rest } = payload;
|
|
843
|
+
envelope = rest;
|
|
844
|
+
let changed = false;
|
|
845
|
+
let before = "";
|
|
846
|
+
const rewritten = payload.choices.map((choice) => {
|
|
847
|
+
if (!isObj$1(choice)) return choice;
|
|
848
|
+
const index = typeof choice.index === "number" ? choice.index : 0;
|
|
849
|
+
const state = choiceState(index);
|
|
850
|
+
let next = choice;
|
|
851
|
+
if (isObj$1(choice.delta)) {
|
|
852
|
+
const result = rewriteDelta(choice.delta, state);
|
|
853
|
+
if (result.changed) {
|
|
854
|
+
next = {
|
|
855
|
+
...choice,
|
|
856
|
+
delta: result.delta
|
|
857
|
+
};
|
|
858
|
+
changed = true;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (choice.finish_reason !== null && choice.finish_reason !== void 0) {
|
|
862
|
+
const tail = flushChoice(state);
|
|
863
|
+
choices.delete(index);
|
|
864
|
+
if (tail !== void 0) before += tailEvent(block.event, index, tail);
|
|
865
|
+
}
|
|
866
|
+
return next;
|
|
867
|
+
});
|
|
868
|
+
return before + (changed ? formatSseEvent(block.event, {
|
|
869
|
+
...payload,
|
|
870
|
+
choices: rewritten
|
|
871
|
+
}) : block.raw);
|
|
872
|
+
};
|
|
873
|
+
return createSseFramer(handle, () => flushAll(void 0));
|
|
874
|
+
}
|
|
875
|
+
const openaiChatWalker = {
|
|
876
|
+
async request(body, ctx) {
|
|
877
|
+
if (!isObj$1(body)) return body;
|
|
878
|
+
const out = { ...body };
|
|
879
|
+
if (Array.isArray(body.messages)) {
|
|
880
|
+
const names = /* @__PURE__ */ new Map();
|
|
881
|
+
const messages = [];
|
|
882
|
+
for (const message of body.messages) messages.push(isObj$1(message) ? await rewriteMessage(message, ctx.transform, names) : message);
|
|
883
|
+
if (ctx.transform.instruction !== void 0) messages.unshift({
|
|
884
|
+
role: "system",
|
|
885
|
+
content: ctx.transform.instruction
|
|
886
|
+
});
|
|
887
|
+
out.messages = messages;
|
|
888
|
+
}
|
|
889
|
+
return out;
|
|
890
|
+
},
|
|
891
|
+
response(body, ctx) {
|
|
892
|
+
if (!isObj$1(body) || !Array.isArray(body.choices)) return body;
|
|
893
|
+
const choices = body.choices.map((choice) => {
|
|
894
|
+
if (!isObj$1(choice) || !isObj$1(choice.message)) return choice;
|
|
895
|
+
const message = { ...choice.message };
|
|
896
|
+
if (typeof message.content === "string") message.content = ctx.transform.inbound(message.content);
|
|
897
|
+
for (const field of REASONING_FIELDS) {
|
|
898
|
+
const value = message[field];
|
|
899
|
+
if (typeof value === "string") message[field] = ctx.transform.inbound(value);
|
|
900
|
+
}
|
|
901
|
+
if (Array.isArray(message.tool_calls)) message.tool_calls = message.tool_calls.map((call) => {
|
|
902
|
+
if (!isObj$1(call) || !isObj$1(call.function) || typeof call.function.arguments !== "string") return call;
|
|
903
|
+
return {
|
|
904
|
+
...call,
|
|
905
|
+
function: {
|
|
906
|
+
...call.function,
|
|
907
|
+
arguments: restoreArguments$1(call.function.arguments, ctx.transform)
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
});
|
|
911
|
+
return {
|
|
912
|
+
...choice,
|
|
913
|
+
message
|
|
914
|
+
};
|
|
915
|
+
});
|
|
916
|
+
return {
|
|
917
|
+
...body,
|
|
918
|
+
choices
|
|
919
|
+
};
|
|
920
|
+
},
|
|
921
|
+
stream(ctx) {
|
|
922
|
+
return createStreamTransformer$1(ctx);
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
// src/wire/openai-responses.ts
|
|
926
|
+
const isObj = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
927
|
+
/** Part types that carry attachments the proxy cannot pseudonymize. */
|
|
928
|
+
const REFUSED_PARTS = {
|
|
929
|
+
input_image: true,
|
|
930
|
+
input_file: true,
|
|
931
|
+
input_audio: true
|
|
932
|
+
};
|
|
933
|
+
const signatureKey = (itemId) => `responses:${itemId}`;
|
|
934
|
+
function refuse(part) {
|
|
935
|
+
if (typeof part.type === "string" && REFUSED_PARTS[part.type] === true) throw new UnsupportedContentError(`OpenAI responses ${part.type} content cannot be transformed`);
|
|
936
|
+
}
|
|
937
|
+
async function outbound(transform, text, leaf) {
|
|
938
|
+
return text === "" ? text : transform.outbound(text, leaf);
|
|
939
|
+
}
|
|
940
|
+
/** A string, or an array of parts whose `input_text`/`output_text` parts are rewritten. */
|
|
941
|
+
async function rewriteContent(content, leaf, transform) {
|
|
942
|
+
if (typeof content === "string") return outbound(transform, content, leaf);
|
|
943
|
+
if (!Array.isArray(content)) return content;
|
|
944
|
+
const out = [];
|
|
945
|
+
for (const part of content) {
|
|
946
|
+
if (!isObj(part)) {
|
|
947
|
+
out.push(part);
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
refuse(part);
|
|
951
|
+
out.push((part.type === "input_text" || part.type === "output_text") && typeof part.text === "string" ? {
|
|
952
|
+
...part,
|
|
953
|
+
text: await outbound(transform, part.text, leaf)
|
|
954
|
+
} : part);
|
|
955
|
+
}
|
|
956
|
+
return out;
|
|
957
|
+
}
|
|
958
|
+
async function rewriteStrings(value, leaf, transform) {
|
|
959
|
+
if (typeof value === "string") return outbound(transform, value, leaf);
|
|
960
|
+
if (Array.isArray(value)) {
|
|
961
|
+
const out = [];
|
|
962
|
+
for (const item of value) out.push(await rewriteStrings(item, leaf, transform));
|
|
963
|
+
return out;
|
|
964
|
+
}
|
|
965
|
+
if (isObj(value)) {
|
|
966
|
+
const out = {};
|
|
967
|
+
for (const [key, item] of Object.entries(value)) out[key] = await rewriteStrings(item, leaf, transform);
|
|
968
|
+
return out;
|
|
969
|
+
}
|
|
970
|
+
return value;
|
|
971
|
+
}
|
|
972
|
+
/** Tool-call arguments: a JSON string whose string leaves are rewritten; raw text when it is not JSON. */
|
|
973
|
+
async function rewriteArguments(args, leaf, transform) {
|
|
974
|
+
let parsed;
|
|
975
|
+
try {
|
|
976
|
+
parsed = JSON.parse(args);
|
|
977
|
+
} catch {
|
|
978
|
+
return outbound(transform, args, leaf);
|
|
979
|
+
}
|
|
980
|
+
return JSON.stringify(await rewriteStrings(parsed, leaf, transform));
|
|
981
|
+
}
|
|
982
|
+
/** Restores the string values of a JSON text, keeping its formatting; restored originals are JSON-escaped. */
|
|
983
|
+
function restoreArguments(args, transform) {
|
|
984
|
+
const restorer = createJsonArgRestorer(() => transform.inboundStream());
|
|
985
|
+
return restorer.push(args) + restorer.flush();
|
|
986
|
+
}
|
|
987
|
+
function summaryTexts(item) {
|
|
988
|
+
if (!Array.isArray(item.summary)) return void 0;
|
|
989
|
+
const texts = [];
|
|
990
|
+
for (const part of item.summary) {
|
|
991
|
+
if (!isObj(part) || typeof part.text !== "string") return void 0;
|
|
992
|
+
texts.push(part.text);
|
|
993
|
+
}
|
|
994
|
+
return texts;
|
|
995
|
+
}
|
|
996
|
+
function cachedSummary(item, ctx) {
|
|
997
|
+
if (typeof item.id !== "string") return void 0;
|
|
998
|
+
const cached = ctx.signatures.get(signatureKey(item.id));
|
|
999
|
+
if (cached === void 0) return void 0;
|
|
1000
|
+
const texts = JSON.parse(cached);
|
|
1001
|
+
if (!Array.isArray(texts) || !Array.isArray(item.summary) || texts.length !== item.summary.length) return void 0;
|
|
1002
|
+
return texts;
|
|
1003
|
+
}
|
|
1004
|
+
async function rewriteReasoning(item, ctx) {
|
|
1005
|
+
if (!Array.isArray(item.summary)) return item;
|
|
1006
|
+
const cached = cachedSummary(item, ctx);
|
|
1007
|
+
const summary = [];
|
|
1008
|
+
for (const [i, part] of item.summary.entries()) if (!isObj(part) || typeof part.text !== "string") summary.push(part);
|
|
1009
|
+
else if (cached !== void 0) summary.push({
|
|
1010
|
+
...part,
|
|
1011
|
+
text: cached[i]
|
|
1012
|
+
});
|
|
1013
|
+
else summary.push({
|
|
1014
|
+
...part,
|
|
1015
|
+
text: await outbound(ctx.transform, part.text, { kind: "thinking" })
|
|
1016
|
+
});
|
|
1017
|
+
return {
|
|
1018
|
+
...item,
|
|
1019
|
+
summary
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function roleKind(role) {
|
|
1023
|
+
if (role === "developer" || role === "system") return "system";
|
|
1024
|
+
if (role === "user") return "user";
|
|
1025
|
+
if (role === "assistant") return "assistant";
|
|
1026
|
+
}
|
|
1027
|
+
function toolNamesByCallId(items) {
|
|
1028
|
+
const names = /* @__PURE__ */ new Map();
|
|
1029
|
+
for (const item of items) if (isObj(item) && item.type === "function_call" && typeof item.call_id === "string" && typeof item.name === "string") names.set(item.call_id, item.name);
|
|
1030
|
+
return names;
|
|
1031
|
+
}
|
|
1032
|
+
async function rewriteItem(item, toolNames, ctx) {
|
|
1033
|
+
if (item.type === "message" || item.type === void 0 && item.role !== void 0) {
|
|
1034
|
+
const kind = roleKind(item.role);
|
|
1035
|
+
if (kind === void 0) return item;
|
|
1036
|
+
return {
|
|
1037
|
+
...item,
|
|
1038
|
+
content: await rewriteContent(item.content, { kind }, ctx.transform)
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
switch (item.type) {
|
|
1042
|
+
case "function_call": {
|
|
1043
|
+
if (typeof item.arguments !== "string") return item;
|
|
1044
|
+
const toolName = typeof item.name === "string" ? item.name : void 0;
|
|
1045
|
+
return {
|
|
1046
|
+
...item,
|
|
1047
|
+
arguments: await rewriteArguments(item.arguments, {
|
|
1048
|
+
kind: "toolCall",
|
|
1049
|
+
toolName
|
|
1050
|
+
}, ctx.transform)
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
case "function_call_output": {
|
|
1054
|
+
const toolName = typeof item.call_id === "string" ? toolNames.get(item.call_id) : void 0;
|
|
1055
|
+
return {
|
|
1056
|
+
...item,
|
|
1057
|
+
output: await rewriteContent(item.output, {
|
|
1058
|
+
kind: "toolResult",
|
|
1059
|
+
toolName
|
|
1060
|
+
}, ctx.transform)
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
case "reasoning": return rewriteReasoning(item, ctx);
|
|
1064
|
+
default: return item;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
/** Restores one output item; reasoning items store their raw summary in the signature cache first. */
|
|
1068
|
+
function restoreItem(item, ctx) {
|
|
1069
|
+
if (!isObj(item)) return item;
|
|
1070
|
+
const transform = ctx.transform;
|
|
1071
|
+
switch (item.type) {
|
|
1072
|
+
case "message":
|
|
1073
|
+
if (!Array.isArray(item.content)) return item;
|
|
1074
|
+
return {
|
|
1075
|
+
...item,
|
|
1076
|
+
content: item.content.map((part) => restorePart(part, transform))
|
|
1077
|
+
};
|
|
1078
|
+
case "reasoning": {
|
|
1079
|
+
const texts = summaryTexts(item);
|
|
1080
|
+
if (texts === void 0) return item;
|
|
1081
|
+
if (typeof item.id === "string") ctx.signatures.set(signatureKey(item.id), JSON.stringify(texts));
|
|
1082
|
+
return {
|
|
1083
|
+
...item,
|
|
1084
|
+
summary: item.summary.map((part) => ({
|
|
1085
|
+
...part,
|
|
1086
|
+
text: transform.inbound(part.text)
|
|
1087
|
+
}))
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
case "function_call": return typeof item.arguments === "string" ? {
|
|
1091
|
+
...item,
|
|
1092
|
+
arguments: restoreArguments(item.arguments, transform)
|
|
1093
|
+
} : item;
|
|
1094
|
+
default: return item;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
function restorePart(part, transform) {
|
|
1098
|
+
return isObj(part) && part.type === "output_text" && typeof part.text === "string" ? {
|
|
1099
|
+
...part,
|
|
1100
|
+
text: transform.inbound(part.text)
|
|
1101
|
+
} : part;
|
|
1102
|
+
}
|
|
1103
|
+
function restoreResponse(body, ctx) {
|
|
1104
|
+
const out = { ...body };
|
|
1105
|
+
if (Array.isArray(body.output)) out.output = body.output.map((item) => restoreItem(item, ctx));
|
|
1106
|
+
if (typeof body.output_text === "string") out.output_text = ctx.transform.inbound(body.output_text);
|
|
1107
|
+
return out;
|
|
1108
|
+
}
|
|
1109
|
+
function createStreamTransformer(ctx) {
|
|
1110
|
+
const streams = /* @__PURE__ */ new Map();
|
|
1111
|
+
const open = (key, factory) => {
|
|
1112
|
+
let state = streams.get(key);
|
|
1113
|
+
if (state === void 0) {
|
|
1114
|
+
state = { restore: factory() };
|
|
1115
|
+
streams.set(key, state);
|
|
1116
|
+
}
|
|
1117
|
+
return state;
|
|
1118
|
+
};
|
|
1119
|
+
/** Emits the tail delta (if any) followed by the rewritten `.done` event. */
|
|
1120
|
+
const closeWith = (block, payload, key, deltaType, done) => {
|
|
1121
|
+
const state = streams.get(key);
|
|
1122
|
+
streams.delete(key);
|
|
1123
|
+
let out = "";
|
|
1124
|
+
const tail = state?.restore.flush() ?? "";
|
|
1125
|
+
if (tail !== "") {
|
|
1126
|
+
const { text: _text, arguments: _args, ...rest } = payload;
|
|
1127
|
+
out += formatSseEvent(block.event === void 0 ? void 0 : deltaType, {
|
|
1128
|
+
...rest,
|
|
1129
|
+
type: deltaType,
|
|
1130
|
+
delta: tail
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
return out + formatSseEvent(block.event, done);
|
|
1134
|
+
};
|
|
1135
|
+
const handle = (block) => {
|
|
1136
|
+
let payload;
|
|
1137
|
+
try {
|
|
1138
|
+
payload = JSON.parse(block.data);
|
|
1139
|
+
} catch {
|
|
1140
|
+
return block.raw;
|
|
1141
|
+
}
|
|
1142
|
+
if (!isObj(payload)) return block.raw;
|
|
1143
|
+
const itemId = typeof payload.item_id === "string" ? payload.item_id : void 0;
|
|
1144
|
+
const textKey = `text:${itemId}:${String(payload.content_index)}`;
|
|
1145
|
+
const summaryKey = `summary:${itemId}:${String(payload.summary_index)}`;
|
|
1146
|
+
const argsKey = `args:${itemId}`;
|
|
1147
|
+
const textStream = () => ctx.transform.inboundStream();
|
|
1148
|
+
const argStream = () => createJsonArgRestorer(() => ctx.transform.inboundStream());
|
|
1149
|
+
switch (payload.type) {
|
|
1150
|
+
case "response.output_text.delta":
|
|
1151
|
+
if (itemId === void 0 || typeof payload.delta !== "string") return block.raw;
|
|
1152
|
+
return formatSseEvent(block.event, {
|
|
1153
|
+
...payload,
|
|
1154
|
+
delta: open(textKey, textStream).restore.push(payload.delta)
|
|
1155
|
+
});
|
|
1156
|
+
case "response.reasoning_summary_text.delta":
|
|
1157
|
+
if (itemId === void 0 || typeof payload.delta !== "string") return block.raw;
|
|
1158
|
+
return formatSseEvent(block.event, {
|
|
1159
|
+
...payload,
|
|
1160
|
+
delta: open(summaryKey, textStream).restore.push(payload.delta)
|
|
1161
|
+
});
|
|
1162
|
+
case "response.function_call_arguments.delta":
|
|
1163
|
+
if (itemId === void 0 || typeof payload.delta !== "string") return block.raw;
|
|
1164
|
+
return formatSseEvent(block.event, {
|
|
1165
|
+
...payload,
|
|
1166
|
+
delta: open(argsKey, argStream).restore.push(payload.delta)
|
|
1167
|
+
});
|
|
1168
|
+
case "response.output_text.done":
|
|
1169
|
+
if (itemId === void 0 || typeof payload.text !== "string") return block.raw;
|
|
1170
|
+
return closeWith(block, payload, textKey, "response.output_text.delta", {
|
|
1171
|
+
...payload,
|
|
1172
|
+
text: ctx.transform.inbound(payload.text)
|
|
1173
|
+
});
|
|
1174
|
+
case "response.reasoning_summary_text.done":
|
|
1175
|
+
if (itemId === void 0 || typeof payload.text !== "string") return block.raw;
|
|
1176
|
+
return closeWith(block, payload, summaryKey, "response.reasoning_summary_text.delta", {
|
|
1177
|
+
...payload,
|
|
1178
|
+
text: ctx.transform.inbound(payload.text)
|
|
1179
|
+
});
|
|
1180
|
+
case "response.function_call_arguments.done":
|
|
1181
|
+
if (itemId === void 0 || typeof payload.arguments !== "string") return block.raw;
|
|
1182
|
+
return closeWith(block, payload, argsKey, "response.function_call_arguments.delta", {
|
|
1183
|
+
...payload,
|
|
1184
|
+
arguments: restoreArguments(payload.arguments, ctx.transform)
|
|
1185
|
+
});
|
|
1186
|
+
case "response.content_part.done": return isObj(payload.part) ? formatSseEvent(block.event, {
|
|
1187
|
+
...payload,
|
|
1188
|
+
part: restorePart(payload.part, ctx.transform)
|
|
1189
|
+
}) : block.raw;
|
|
1190
|
+
case "response.reasoning_summary_part.done": return isObj(payload.part) && typeof payload.part.text === "string" ? formatSseEvent(block.event, {
|
|
1191
|
+
...payload,
|
|
1192
|
+
part: {
|
|
1193
|
+
...payload.part,
|
|
1194
|
+
text: ctx.transform.inbound(payload.part.text)
|
|
1195
|
+
}
|
|
1196
|
+
}) : block.raw;
|
|
1197
|
+
case "response.output_item.done": return isObj(payload.item) ? formatSseEvent(block.event, {
|
|
1198
|
+
...payload,
|
|
1199
|
+
item: restoreItem(payload.item, ctx)
|
|
1200
|
+
}) : block.raw;
|
|
1201
|
+
case "response.completed":
|
|
1202
|
+
case "response.incomplete": return isObj(payload.response) ? formatSseEvent(block.event, {
|
|
1203
|
+
...payload,
|
|
1204
|
+
response: restoreResponse(payload.response, ctx)
|
|
1205
|
+
}) : block.raw;
|
|
1206
|
+
default: return block.raw;
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
const finish = () => {
|
|
1210
|
+
let out = "";
|
|
1211
|
+
for (const [key, state] of streams) {
|
|
1212
|
+
const tail = state.restore.flush();
|
|
1213
|
+
if (tail === "") continue;
|
|
1214
|
+
const [kind, itemId] = key.split(":");
|
|
1215
|
+
const type = kind === "text" ? "response.output_text.delta" : kind === "summary" ? "response.reasoning_summary_text.delta" : "response.function_call_arguments.delta";
|
|
1216
|
+
out += formatSseEvent(type, {
|
|
1217
|
+
type,
|
|
1218
|
+
item_id: itemId,
|
|
1219
|
+
delta: tail
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
streams.clear();
|
|
1223
|
+
return out;
|
|
1224
|
+
};
|
|
1225
|
+
return createSseFramer(handle, finish);
|
|
1226
|
+
}
|
|
1227
|
+
const openaiResponsesWalker = {
|
|
1228
|
+
async request(body, ctx) {
|
|
1229
|
+
if (!isObj(body)) return body;
|
|
1230
|
+
const out = { ...body };
|
|
1231
|
+
if (typeof body.instructions === "string") out.instructions = await outbound(ctx.transform, body.instructions, { kind: "system" });
|
|
1232
|
+
const instruction = ctx.transform.instruction;
|
|
1233
|
+
if (instruction !== void 0) out.instructions = typeof out.instructions === "string" && out.instructions !== "" ? `${instruction}\n\n${out.instructions}` : instruction;
|
|
1234
|
+
if (typeof body.input === "string") out.input = await outbound(ctx.transform, body.input, { kind: "user" });
|
|
1235
|
+
else if (Array.isArray(body.input)) {
|
|
1236
|
+
const toolNames = toolNamesByCallId(body.input);
|
|
1237
|
+
const input = [];
|
|
1238
|
+
for (const item of body.input) input.push(isObj(item) ? await rewriteItem(item, toolNames, ctx) : item);
|
|
1239
|
+
out.input = input;
|
|
1240
|
+
}
|
|
1241
|
+
return out;
|
|
1242
|
+
},
|
|
1243
|
+
response(body, ctx) {
|
|
1244
|
+
return isObj(body) ? restoreResponse(body, ctx) : body;
|
|
1245
|
+
},
|
|
596
1246
|
stream(ctx) {
|
|
597
1247
|
return createStreamTransformer(ctx);
|
|
598
1248
|
}
|
|
@@ -600,18 +1250,30 @@ const anthropicWalker = {
|
|
|
600
1250
|
// src/routes.ts
|
|
601
1251
|
/**
|
|
602
1252
|
* The route table. Routes are matched in order, so longer suffixes come first.
|
|
603
|
-
*
|
|
604
|
-
* those paths, and embeddings, answer 404 `unsupported_route`.
|
|
1253
|
+
* Any other path, embeddings included, answers 404 `unsupported_route`.
|
|
605
1254
|
*/
|
|
606
|
-
const WIRE_ROUTES = [
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1255
|
+
const WIRE_ROUTES = [
|
|
1256
|
+
{
|
|
1257
|
+
suffix: "/messages/count_tokens",
|
|
1258
|
+
wire: anthropicWalker,
|
|
1259
|
+
rewriteResponse: false
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
suffix: "/messages",
|
|
1263
|
+
wire: anthropicWalker,
|
|
1264
|
+
rewriteResponse: true
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
suffix: "/chat/completions",
|
|
1268
|
+
wire: openaiChatWalker,
|
|
1269
|
+
rewriteResponse: true
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
suffix: "/responses",
|
|
1273
|
+
wire: openaiResponsesWalker,
|
|
1274
|
+
rewriteResponse: true
|
|
1275
|
+
}
|
|
1276
|
+
];
|
|
615
1277
|
/** Classifies a request by method and path. `undefined` means the route is unsupported. */
|
|
616
1278
|
function classify(method, pathname) {
|
|
617
1279
|
if (method === "GET" && pathname.endsWith("/models")) return { kind: "passthrough" };
|
|
@@ -1057,4 +1719,4 @@ function requireStringList(value, field) {
|
|
|
1057
1719
|
function isRecord(value) {
|
|
1058
1720
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1059
1721
|
}
|
|
1060
|
-
export {
|
|
1722
|
+
export { composeTransforms as _, parseProxyConfig as a, SESSION_HEADER as c, createTransformProxy as d, openaiResponsesWalker as f, createSessionFacts as g, UnsupportedContentError as h, loadProxyConfig as i, TOKEN_HEADER as l, anthropicWalker as m, configCandidates as n, resolveProxyConfig as o, openaiChatWalker as p, findProxyConfig as r, PIPELINE_HEADER as s, CONFIG_FILE_NAME as t, UPSTREAM_HEADER as u, errorMessage as v, silentLogger as y };
|