@copilotkit/aimock 1.46.0 → 1.47.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -1
- package/README.md +4 -1
- package/dist/aws-event-stream.cjs +20 -23
- package/dist/aws-event-stream.cjs.map +1 -1
- package/dist/aws-event-stream.d.cts +0 -1
- package/dist/aws-event-stream.d.ts +0 -1
- package/dist/aws-event-stream.js +20 -24
- package/dist/aws-event-stream.js.map +1 -1
- package/dist/bedrock-converse.cjs +79 -82
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +80 -83
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +112 -121
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +113 -122
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +58 -106
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +60 -108
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/fixture-loader.cjs +103 -2
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +104 -4
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +63 -121
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +65 -123
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +242 -155
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +244 -157
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +400 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +51 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +51 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +391 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/messages.cjs +159 -141
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +161 -143
- package/dist/messages.js.map +1 -1
- package/dist/misbehavior.cjs +62 -3
- package/dist/misbehavior.cjs.map +1 -1
- package/dist/misbehavior.d.cts.map +1 -1
- package/dist/misbehavior.d.ts.map +1 -1
- package/dist/misbehavior.js +63 -4
- package/dist/misbehavior.js.map +1 -1
- package/dist/ndjson-writer.cjs +21 -23
- package/dist/ndjson-writer.cjs.map +1 -1
- package/dist/ndjson-writer.d.cts +0 -1
- package/dist/ndjson-writer.d.cts.map +1 -1
- package/dist/ndjson-writer.d.ts +0 -1
- package/dist/ndjson-writer.d.ts.map +1 -1
- package/dist/ndjson-writer.js +21 -24
- package/dist/ndjson-writer.js.map +1 -1
- package/dist/ollama.cjs +153 -147
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +155 -149
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-chat.cjs +1 -1
- package/dist/openrouter-chat.js +1 -1
- package/dist/raw-validation.cjs +177 -0
- package/dist/raw-validation.cjs.map +1 -0
- package/dist/raw-validation.js +176 -0
- package/dist/raw-validation.js.map +1 -0
- package/dist/response/deliver.cjs +144 -0
- package/dist/response/deliver.cjs.map +1 -0
- package/dist/response/deliver.js +143 -0
- package/dist/response/deliver.js.map +1 -0
- package/dist/response/frame-loop.cjs +102 -0
- package/dist/response/frame-loop.cjs.map +1 -0
- package/dist/response/frame-loop.js +100 -0
- package/dist/response/frame-loop.js.map +1 -0
- package/dist/response/lifecycle.cjs +223 -0
- package/dist/response/lifecycle.cjs.map +1 -0
- package/dist/response/lifecycle.js +211 -0
- package/dist/response/lifecycle.js.map +1 -0
- package/dist/response/proxy.cjs +71 -0
- package/dist/response/proxy.cjs.map +1 -0
- package/dist/response/proxy.js +70 -0
- package/dist/response/proxy.js.map +1 -0
- package/dist/response/raw.cjs +228 -0
- package/dist/response/raw.cjs.map +1 -0
- package/dist/response/raw.js +227 -0
- package/dist/response/raw.js.map +1 -0
- package/dist/response/timing.cjs +157 -0
- package/dist/response/timing.cjs.map +1 -0
- package/dist/response/timing.js +150 -0
- package/dist/response/timing.js.map +1 -0
- package/dist/responses.cjs +269 -148
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +270 -149
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +20 -4
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +7 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +7 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +21 -5
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +89 -120
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +91 -122
- package/dist/server.js.map +1 -1
- package/dist/sse-writer.cjs +49 -30
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +0 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +0 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +49 -31
- package/dist/sse-writer.js.map +1 -1
- package/dist/transcription.cjs +19 -33
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +20 -34
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +173 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +173 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ws-framing.cjs +12 -6
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +2 -0
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +2 -0
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +12 -6
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +120 -93
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.js +120 -93
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +80 -70
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +80 -70
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +72 -53
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +73 -54
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +34 -2
package/dist/responses.cjs
CHANGED
|
@@ -8,7 +8,10 @@ const require_chaos = require('./chaos.cjs');
|
|
|
8
8
|
const require_misbehavior = require('./misbehavior.cjs');
|
|
9
9
|
const require_router = require('./router.cjs');
|
|
10
10
|
const require_recorder = require('./recorder.cjs');
|
|
11
|
-
const
|
|
11
|
+
const require_timing = require('./response/timing.cjs');
|
|
12
|
+
const require_deliver = require('./response/deliver.cjs');
|
|
13
|
+
const require_proxy = require('./response/proxy.cjs');
|
|
14
|
+
const require_raw = require('./response/raw.cjs');
|
|
12
15
|
const require_responses = require('./response/usage/codecs/responses.cjs');
|
|
13
16
|
const require_finish = require('./response/finish.cjs');
|
|
14
17
|
let node_crypto = require("node:crypto");
|
|
@@ -325,7 +328,7 @@ function itemId() {
|
|
|
325
328
|
}
|
|
326
329
|
function buildTextStreamEvents(content, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
327
330
|
const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
328
|
-
const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase);
|
|
331
|
+
const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase, buildAnnotations(content, overrides?.citations));
|
|
329
332
|
events.push(...msgEvents);
|
|
330
333
|
events.push({
|
|
331
334
|
type: "response.completed",
|
|
@@ -434,6 +437,7 @@ function buildToolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSe
|
|
|
434
437
|
}
|
|
435
438
|
function toolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
436
439
|
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
440
|
+
require_helpers.fixtureCitations("", overrides?.citations);
|
|
437
441
|
const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
438
442
|
const fcOutputItems = [];
|
|
439
443
|
for (let idx = 0; idx < toolCalls.length; idx++) {
|
|
@@ -670,6 +674,79 @@ function buildWebSearchStreamEvents(queries, startOutputIndex) {
|
|
|
670
674
|
}
|
|
671
675
|
return events;
|
|
672
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* A Responses `image_generation_call` output item for one fixture file (see
|
|
679
|
+
* `ResponseOutputItem.ImageGenerationCall` in the openai SDK): `result` holds
|
|
680
|
+
* the base64 bytes once completed and is null while in progress.
|
|
681
|
+
*/
|
|
682
|
+
function imageGenerationCallItem(id, file, status) {
|
|
683
|
+
return {
|
|
684
|
+
type: "image_generation_call",
|
|
685
|
+
id,
|
|
686
|
+
status,
|
|
687
|
+
result: status === "completed" ? file.data : null
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* A fixture file that is not `image/*` reached the Responses API, which
|
|
692
|
+
* carries generated files only as `image_generation_call` items. Surfaces as
|
|
693
|
+
* the same coded 500 (`aimock_unsupported_generated_file`), naming `mimeType`.
|
|
694
|
+
*/
|
|
695
|
+
var NonImageGeneratedFileError = class extends require_helpers.UnsupportedGeneratedFileError {
|
|
696
|
+
constructor(mimeType) {
|
|
697
|
+
super("the OpenAI Responses API");
|
|
698
|
+
this.message = `aimock: fixture "files" on the OpenAI Responses API carry only image/* files (as image_generation_call items); got mimeType "${mimeType}"`;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
/**
|
|
702
|
+
* Throw {@link NonImageGeneratedFileError} for the first fixture file whose
|
|
703
|
+
* `mimeType` is not `image/*`. Called before any output item is built, so
|
|
704
|
+
* nothing reaches the client.
|
|
705
|
+
*/
|
|
706
|
+
function requireImageFiles(files) {
|
|
707
|
+
const nonImage = files.find((f) => !f.mimeType.toLowerCase().startsWith("image/"));
|
|
708
|
+
if (nonImage) throw new NonImageGeneratedFileError(nonImage.mimeType);
|
|
709
|
+
}
|
|
710
|
+
/** Fixture `files` as completed `image_generation_call` items (non-streaming). */
|
|
711
|
+
function imageGenerationCallItems(files) {
|
|
712
|
+
const validated = require_helpers.fixtureFiles(files);
|
|
713
|
+
requireImageFiles(validated);
|
|
714
|
+
return validated.map((f) => imageGenerationCallItem(require_helpers.generateId("ig"), f, "completed"));
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Streamed form of {@link imageGenerationCallItems}: per file, the item's
|
|
718
|
+
* `output_item.added`, the `image_generation_call` `in_progress`,
|
|
719
|
+
* `generating` and `completed` events, then `output_item.done` carrying the
|
|
720
|
+
* completed item.
|
|
721
|
+
*/
|
|
722
|
+
function buildImageGenerationStreamEvents(files, startOutputIndex) {
|
|
723
|
+
requireImageFiles(files);
|
|
724
|
+
const events = [];
|
|
725
|
+
files.forEach((file, i) => {
|
|
726
|
+
const id = require_helpers.generateId("ig");
|
|
727
|
+
const outputIndex = startOutputIndex + i;
|
|
728
|
+
events.push({
|
|
729
|
+
type: "response.output_item.added",
|
|
730
|
+
output_index: outputIndex,
|
|
731
|
+
item: imageGenerationCallItem(id, file, "in_progress")
|
|
732
|
+
});
|
|
733
|
+
for (const phase of [
|
|
734
|
+
"in_progress",
|
|
735
|
+
"generating",
|
|
736
|
+
"completed"
|
|
737
|
+
]) events.push({
|
|
738
|
+
type: `response.image_generation_call.${phase}`,
|
|
739
|
+
output_index: outputIndex,
|
|
740
|
+
item_id: id
|
|
741
|
+
});
|
|
742
|
+
events.push({
|
|
743
|
+
type: "response.output_item.done",
|
|
744
|
+
output_index: outputIndex,
|
|
745
|
+
item: imageGenerationCallItem(id, file, "completed")
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
return events;
|
|
749
|
+
}
|
|
673
750
|
function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, forceReasoning = false) {
|
|
674
751
|
const respId = overrides?.id ?? responseId();
|
|
675
752
|
const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
|
|
@@ -713,6 +790,13 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
|
|
|
713
790
|
for (const de of doneEvents) prefixOutputItems.push(de.item);
|
|
714
791
|
nextOutputIndex += webSearches.length;
|
|
715
792
|
}
|
|
793
|
+
const files = require_helpers.fixtureFiles(overrides?.files);
|
|
794
|
+
if (files.length > 0) {
|
|
795
|
+
const fileEvents = buildImageGenerationStreamEvents(files, nextOutputIndex);
|
|
796
|
+
events.push(...fileEvents);
|
|
797
|
+
for (const e of fileEvents) if (e.type === "response.output_item.done") prefixOutputItems.push(e.item);
|
|
798
|
+
nextOutputIndex += files.length;
|
|
799
|
+
}
|
|
716
800
|
return {
|
|
717
801
|
respId,
|
|
718
802
|
created,
|
|
@@ -721,7 +805,84 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
|
|
|
721
805
|
nextOutputIndex
|
|
722
806
|
};
|
|
723
807
|
}
|
|
724
|
-
|
|
808
|
+
/**
|
|
809
|
+
* Fixture `citations` as `output_text` annotations for each text in `texts`
|
|
810
|
+
* (one per message item), in fixture order: a URL citation becomes
|
|
811
|
+
* `url_citation`, a file citation (`type: "file"`) becomes `file_citation`
|
|
812
|
+
* (`file_id`, `filename`, `index`; see `ResponseOutputText.FileCitation` in
|
|
813
|
+
* the openai SDK). Offsets index the joined text of all texts, as on every
|
|
814
|
+
* other provider. A URL citation is a range and goes only to the text holding
|
|
815
|
+
* its `startIndex`. A file citation is a point after the cited text, so it
|
|
816
|
+
* goes to the last text starting before its `index`: an `index` equal to a
|
|
817
|
+
* text's end stays at the end of that text (not at 0 of the next text), and
|
|
818
|
+
* an `index` of 0 goes to the first text. Each citation is rebased onto its
|
|
819
|
+
* text since Responses offsets are per `output_text`, with `end_index`
|
|
820
|
+
* clamped to it. Anthropic and Gemini need no point rule: they have no file
|
|
821
|
+
* citation. Anthropic URL citations follow the same start-owns-range rule;
|
|
822
|
+
* Gemini does not, and splits a citation that spans several text parts into
|
|
823
|
+
* one grounding support per part (see geminiGroundingSupports).
|
|
824
|
+
* Missing offsets take the defaults `resolveCitations` applies: a URL
|
|
825
|
+
* citation starts at 0 and ends at the end of the joined text, so one without
|
|
826
|
+
* offsets goes on the first text and covers all of it; a file citation's
|
|
827
|
+
* `index` defaults to the end of the joined text (the last text).
|
|
828
|
+
*/
|
|
829
|
+
function blockAnnotations(texts, citations) {
|
|
830
|
+
const perText = texts.map(() => []);
|
|
831
|
+
if (texts.length === 0) {
|
|
832
|
+
require_helpers.fixtureCitations("", citations);
|
|
833
|
+
return perText;
|
|
834
|
+
}
|
|
835
|
+
const starts = [];
|
|
836
|
+
let offset = 0;
|
|
837
|
+
for (const text of texts) {
|
|
838
|
+
starts.push(offset);
|
|
839
|
+
offset += text.length;
|
|
840
|
+
}
|
|
841
|
+
const joinedLength = offset;
|
|
842
|
+
const ownerOf = (start) => {
|
|
843
|
+
let owner = 0;
|
|
844
|
+
for (let k = 1; k < starts.length; k++) if (starts[k] <= start) owner = k;
|
|
845
|
+
return owner;
|
|
846
|
+
};
|
|
847
|
+
const pointOwnerOf = (index) => {
|
|
848
|
+
let owner = 0;
|
|
849
|
+
for (let k = 1; k < starts.length; k++) if (starts[k] < index) owner = k;
|
|
850
|
+
return owner;
|
|
851
|
+
};
|
|
852
|
+
for (const c of require_helpers.fixtureCitations(texts.join(""), citations)) {
|
|
853
|
+
if (c.type === "file") {
|
|
854
|
+
const index = c.index ?? joinedLength;
|
|
855
|
+
const owner = pointOwnerOf(index);
|
|
856
|
+
perText[owner].push({
|
|
857
|
+
type: "file_citation",
|
|
858
|
+
file_id: c.fileId,
|
|
859
|
+
filename: c.filename,
|
|
860
|
+
index: index - starts[owner]
|
|
861
|
+
});
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
const start = c.startIndex ?? 0;
|
|
865
|
+
const end = c.endIndex ?? joinedLength;
|
|
866
|
+
const owner = ownerOf(start);
|
|
867
|
+
perText[owner].push({
|
|
868
|
+
type: "url_citation",
|
|
869
|
+
url: c.url,
|
|
870
|
+
title: c.title ?? c.url,
|
|
871
|
+
start_index: start - starts[owner],
|
|
872
|
+
end_index: Math.min(end - starts[owner], texts[owner].length)
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
return perText;
|
|
876
|
+
}
|
|
877
|
+
/** {@link blockAnnotations} for a turn with a single text. */
|
|
878
|
+
function buildAnnotations(content, citations) {
|
|
879
|
+
return blockAnnotations([content], citations)[0];
|
|
880
|
+
}
|
|
881
|
+
/** The annotations of each text block in `blocks`, in block order. */
|
|
882
|
+
function orderedBlockAnnotations(blocks, citations) {
|
|
883
|
+
return blockAnnotations(blocks.flatMap((b) => b.type === "text" ? [b.text] : []), citations);
|
|
884
|
+
}
|
|
885
|
+
function buildMessageOutputEvents(content, chunkSize, outputIndex, phase, annotations) {
|
|
725
886
|
const msgId = itemId();
|
|
726
887
|
const events = [];
|
|
727
888
|
events.push({
|
|
@@ -761,6 +922,16 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
|
|
|
761
922
|
content_index: 0,
|
|
762
923
|
text: content
|
|
763
924
|
});
|
|
925
|
+
annotations.forEach((annotation, annotationIndex) => {
|
|
926
|
+
events.push({
|
|
927
|
+
type: "response.output_text.annotation.added",
|
|
928
|
+
item_id: msgId,
|
|
929
|
+
output_index: outputIndex,
|
|
930
|
+
content_index: 0,
|
|
931
|
+
annotation_index: annotationIndex,
|
|
932
|
+
annotation
|
|
933
|
+
});
|
|
934
|
+
});
|
|
764
935
|
events.push({
|
|
765
936
|
type: "response.content_part.done",
|
|
766
937
|
item_id: msgId,
|
|
@@ -769,7 +940,7 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
|
|
|
769
940
|
part: {
|
|
770
941
|
type: "output_text",
|
|
771
942
|
text: content,
|
|
772
|
-
annotations
|
|
943
|
+
annotations
|
|
773
944
|
}
|
|
774
945
|
});
|
|
775
946
|
const msgItem = {
|
|
@@ -781,7 +952,7 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
|
|
|
781
952
|
content: [{
|
|
782
953
|
type: "output_text",
|
|
783
954
|
text: content,
|
|
784
|
-
annotations
|
|
955
|
+
annotations
|
|
785
956
|
}]
|
|
786
957
|
};
|
|
787
958
|
events.push({
|
|
@@ -836,24 +1007,6 @@ function buildFunctionCallOutputEvents(toolCall, chunkSize, outputIndex) {
|
|
|
836
1007
|
};
|
|
837
1008
|
}
|
|
838
1009
|
/**
|
|
839
|
-
* `text` cut into slices of at most `chunkSize` UTF-16 units, never between
|
|
840
|
-
* the two halves of a surrogate pair: a cut that would land inside a pair
|
|
841
|
-
* moves back before it, or, when that would leave the slice empty
|
|
842
|
-
* (`chunkSize` 1), forward past it. The slices rejoin to `text` exactly.
|
|
843
|
-
*/
|
|
844
|
-
function surrogateSafeChunks(text, chunkSize) {
|
|
845
|
-
const chunks = [];
|
|
846
|
-
for (let i = 0; i < text.length;) {
|
|
847
|
-
let end = Math.min(i + chunkSize, text.length);
|
|
848
|
-
const high = text.charCodeAt(end - 1);
|
|
849
|
-
const low = text.charCodeAt(end);
|
|
850
|
-
if (high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343) end = end - 1 > i ? end - 1 : end + 1;
|
|
851
|
-
chunks.push(text.slice(i, end));
|
|
852
|
-
i = end;
|
|
853
|
-
}
|
|
854
|
-
return chunks;
|
|
855
|
-
}
|
|
856
|
-
/**
|
|
857
1010
|
* Emit output_item.added → custom_tool_call_input.delta×N →
|
|
858
1011
|
* custom_tool_call_input.done → output_item.done for one `custom_tool_call` at
|
|
859
1012
|
* `outputIndex`, returning the completed item. An empty `input`
|
|
@@ -869,7 +1022,7 @@ function buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) {
|
|
|
869
1022
|
output_index: outputIndex,
|
|
870
1023
|
item: customToolCallItem(call, ctcId, callId, "", "in_progress")
|
|
871
1024
|
});
|
|
872
|
-
for (const delta of surrogateSafeChunks(input, chunkSize)) events.push({
|
|
1025
|
+
for (const delta of require_helpers.surrogateSafeChunks(input, chunkSize)) events.push({
|
|
873
1026
|
type: "response.custom_tool_call_input.delta",
|
|
874
1027
|
item_id: ctcId,
|
|
875
1028
|
output_index: outputIndex,
|
|
@@ -896,7 +1049,7 @@ function buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) {
|
|
|
896
1049
|
function buildToolCallOutputEvents(call, chunkSize, outputIndex) {
|
|
897
1050
|
return isCustomEmittable(call) ? buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) : buildFunctionCallOutputEvents(call, chunkSize, outputIndex);
|
|
898
1051
|
}
|
|
899
|
-
function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, phase) {
|
|
1052
|
+
function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, phase, citations, files) {
|
|
900
1053
|
const output = [];
|
|
901
1054
|
if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
|
|
902
1055
|
summaryText: reasoning,
|
|
@@ -911,6 +1064,7 @@ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncrypted
|
|
|
911
1064
|
query
|
|
912
1065
|
}
|
|
913
1066
|
});
|
|
1067
|
+
output.push(...imageGenerationCallItems(files));
|
|
914
1068
|
output.push({
|
|
915
1069
|
type: "message",
|
|
916
1070
|
id: itemId(),
|
|
@@ -920,7 +1074,7 @@ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncrypted
|
|
|
920
1074
|
content: [{
|
|
921
1075
|
type: "output_text",
|
|
922
1076
|
text: content,
|
|
923
|
-
annotations:
|
|
1077
|
+
annotations: buildAnnotations(content, citations)
|
|
924
1078
|
}]
|
|
925
1079
|
});
|
|
926
1080
|
return output;
|
|
@@ -937,10 +1091,11 @@ function buildResponseEnvelope(model, output, overrides) {
|
|
|
937
1091
|
};
|
|
938
1092
|
}
|
|
939
1093
|
function buildTextResponse(content, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
940
|
-
return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase), overrides);
|
|
1094
|
+
return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase, overrides?.citations, overrides?.files), overrides);
|
|
941
1095
|
}
|
|
942
1096
|
function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
943
1097
|
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
1098
|
+
require_helpers.fixtureCitations("", overrides?.citations);
|
|
944
1099
|
const output = [];
|
|
945
1100
|
if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
|
|
946
1101
|
summaryText: reasoning,
|
|
@@ -955,6 +1110,7 @@ function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrid
|
|
|
955
1110
|
query
|
|
956
1111
|
}
|
|
957
1112
|
});
|
|
1113
|
+
output.push(...imageGenerationCallItems(overrides?.files));
|
|
958
1114
|
for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
|
|
959
1115
|
return buildResponseEnvelope(model, output, overrides);
|
|
960
1116
|
}
|
|
@@ -972,9 +1128,11 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
|
|
|
972
1128
|
const orderedOutputItems = [];
|
|
973
1129
|
if (ordered && ordered.length > 0) {
|
|
974
1130
|
let outputIndex = nextOutputIndex;
|
|
1131
|
+
const textAnnotations = orderedBlockAnnotations(ordered, overrides?.citations);
|
|
1132
|
+
let textPos = 0;
|
|
975
1133
|
for (const block of ordered) {
|
|
976
1134
|
if (block.type === "text") {
|
|
977
|
-
const { events: msgEvents, msgItem } = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides?.phase);
|
|
1135
|
+
const { events: msgEvents, msgItem } = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides?.phase, textAnnotations[textPos++]);
|
|
978
1136
|
events.push(...msgEvents);
|
|
979
1137
|
orderedOutputItems.push(msgItem);
|
|
980
1138
|
} else {
|
|
@@ -986,7 +1144,7 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
|
|
|
986
1144
|
}
|
|
987
1145
|
} else {
|
|
988
1146
|
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
989
|
-
const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase);
|
|
1147
|
+
const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase, buildAnnotations(content, overrides?.citations));
|
|
990
1148
|
events.push(...msgEvents);
|
|
991
1149
|
orderedOutputItems.push(msgItem);
|
|
992
1150
|
for (let idx = 0; idx < toolCalls.length; idx++) {
|
|
@@ -1014,7 +1172,7 @@ function buildToolCallOutputItem(tc) {
|
|
|
1014
1172
|
if (isCustomEmittable(tc)) return customToolCallItem(tc, customToolCallItemId(), tc.id || require_helpers.generateToolCallId(), requireFixtureToolInput(tc.input), "completed");
|
|
1015
1173
|
return functionCallItem(tc, require_helpers.generateId("fc"), tc.id || require_helpers.generateToolCallId(), requireFixtureToolArguments(tc.arguments), "completed");
|
|
1016
1174
|
}
|
|
1017
|
-
function buildMessageOutputItem(content, phase) {
|
|
1175
|
+
function buildMessageOutputItem(content, phase, annotations) {
|
|
1018
1176
|
return {
|
|
1019
1177
|
type: "message",
|
|
1020
1178
|
id: itemId(),
|
|
@@ -1024,7 +1182,7 @@ function buildMessageOutputItem(content, phase) {
|
|
|
1024
1182
|
content: [{
|
|
1025
1183
|
type: "output_text",
|
|
1026
1184
|
text: content,
|
|
1027
|
-
annotations
|
|
1185
|
+
annotations
|
|
1028
1186
|
}]
|
|
1029
1187
|
};
|
|
1030
1188
|
}
|
|
@@ -1044,40 +1202,18 @@ function buildContentWithToolCallsResponse(content, toolCalls, model, reasoning,
|
|
|
1044
1202
|
query
|
|
1045
1203
|
}
|
|
1046
1204
|
});
|
|
1047
|
-
|
|
1205
|
+
output.push(...imageGenerationCallItems(overrides?.files));
|
|
1206
|
+
const textAnnotations = orderedBlockAnnotations(ordered, overrides?.citations);
|
|
1207
|
+
let textPos = 0;
|
|
1208
|
+
for (const block of ordered) if (block.type === "text") output.push(buildMessageOutputItem(block.text, overrides?.phase, textAnnotations[textPos++]));
|
|
1048
1209
|
else output.push(buildToolCallOutputItem(block));
|
|
1049
1210
|
return buildResponseEnvelope(model, output, overrides);
|
|
1050
1211
|
}
|
|
1051
1212
|
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
1052
|
-
const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase);
|
|
1213
|
+
const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase, overrides?.citations, overrides?.files);
|
|
1053
1214
|
for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
|
|
1054
1215
|
return buildResponseEnvelope(model, output, overrides);
|
|
1055
1216
|
}
|
|
1056
|
-
async function writeResponsesSSEStream(res, events, optionsOrLatency) {
|
|
1057
|
-
const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
|
|
1058
|
-
const latency = opts.latency ?? 0;
|
|
1059
|
-
const profile = opts.streamingProfile;
|
|
1060
|
-
const { recordedTimings, replaySpeed } = opts;
|
|
1061
|
-
const signal = opts.signal;
|
|
1062
|
-
const onChunkSent = opts.onChunkSent;
|
|
1063
|
-
if (res.writableEnded) return true;
|
|
1064
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
1065
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
1066
|
-
res.setHeader("Connection", "keep-alive");
|
|
1067
|
-
let chunkIndex = 0;
|
|
1068
|
-
for (const event of events) {
|
|
1069
|
-
const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
|
|
1070
|
-
if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
|
|
1071
|
-
if (signal?.aborted) return false;
|
|
1072
|
-
if (res.writableEnded) return true;
|
|
1073
|
-
res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
|
|
1074
|
-
onChunkSent?.();
|
|
1075
|
-
if (signal?.aborted) return false;
|
|
1076
|
-
chunkIndex++;
|
|
1077
|
-
}
|
|
1078
|
-
if (!res.writableEnded) res.end();
|
|
1079
|
-
return true;
|
|
1080
|
-
}
|
|
1081
1217
|
async function handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
|
|
1082
1218
|
setCorsHeaders(res);
|
|
1083
1219
|
let responsesReq;
|
|
@@ -1159,7 +1295,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1159
1295
|
const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);
|
|
1160
1296
|
const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);
|
|
1161
1297
|
const testId = require_helpers.getTestId(req);
|
|
1162
|
-
const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform,
|
|
1298
|
+
const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, {
|
|
1299
|
+
...require_router.recordMatchOptions(!!defaults.record, defaults.logger),
|
|
1300
|
+
wire: "openai-responses"
|
|
1301
|
+
});
|
|
1163
1302
|
if (fixture) {
|
|
1164
1303
|
defaults.logger.debug(`Responses fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v1/responses"}`);
|
|
1165
1304
|
journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
@@ -1194,7 +1333,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1194
1333
|
return;
|
|
1195
1334
|
}
|
|
1196
1335
|
if (defaults.record) {
|
|
1197
|
-
const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, "openai", req.url ?? "/v1/responses", fixtures, defaults, raw);
|
|
1336
|
+
const { outcome, clientHungUp } = await require_proxy.withHangUpDetection(req, res, () => require_recorder.proxyAndRecord(req, res, completionReq, "openai", req.url ?? "/v1/responses", fixtures, defaults, raw));
|
|
1198
1337
|
if (outcome === "handled_by_hook") return;
|
|
1199
1338
|
if (outcome !== "not_configured") {
|
|
1200
1339
|
journal.add({
|
|
@@ -1205,7 +1344,8 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1205
1344
|
response: {
|
|
1206
1345
|
status: res.statusCode ?? 200,
|
|
1207
1346
|
fixture: null,
|
|
1208
|
-
source: "proxy"
|
|
1347
|
+
source: "proxy",
|
|
1348
|
+
...require_proxy.proxyInterruption(res, clientHungUp)
|
|
1209
1349
|
}
|
|
1210
1350
|
});
|
|
1211
1351
|
return;
|
|
@@ -1270,9 +1410,23 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1270
1410
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
1271
1411
|
const fixtureTimings = fixture.recordedTimings;
|
|
1272
1412
|
const effectiveReplaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
|
|
1413
|
+
const streamTiming = {
|
|
1414
|
+
latency,
|
|
1415
|
+
streamingProfile: fixture.streamingProfile,
|
|
1416
|
+
recordedTimings: fixtureTimings,
|
|
1417
|
+
replaySpeed: effectiveReplaySpeed
|
|
1418
|
+
};
|
|
1419
|
+
const timedJson = require_timing.nonStreamingTimingEnabled(fixture, "other");
|
|
1420
|
+
const jsonPayloadLength = (r, reasoning) => require_timing.responsesPayloadLength(r, reasoning) + require_timing.responsesOutputFrames(r) * chunkSize;
|
|
1421
|
+
const jsonOpts = (payloadLength, duplicateId = false) => ({
|
|
1422
|
+
surface: "other",
|
|
1423
|
+
payloadLength: timedJson ? payloadLength() : 0,
|
|
1424
|
+
chunkSize,
|
|
1425
|
+
recordedTimings: fixtureTimings,
|
|
1426
|
+
duplicateId,
|
|
1427
|
+
replaySpeed: effectiveReplaySpeed
|
|
1428
|
+
});
|
|
1273
1429
|
if (misbehavior.kind === "applied") {
|
|
1274
|
-
const prepared = prepareResponsesMisbehavior(misbehavior);
|
|
1275
|
-
const output = buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
|
|
1276
1430
|
const entry = journal.add({
|
|
1277
1431
|
method: req.method ?? "POST",
|
|
1278
1432
|
path: req.url ?? "/v1/responses",
|
|
@@ -1283,41 +1437,50 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1283
1437
|
fixture
|
|
1284
1438
|
}
|
|
1285
1439
|
});
|
|
1286
|
-
|
|
1440
|
+
const prepared = prepareResponsesMisbehavior(misbehavior);
|
|
1441
|
+
const build = () => buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
|
|
1442
|
+
const record = () => require_misbehavior.recordMisbehaviorOutcome({
|
|
1287
1443
|
entry,
|
|
1288
1444
|
summary: prepared.summary,
|
|
1289
1445
|
defaults,
|
|
1290
1446
|
testId
|
|
1291
1447
|
});
|
|
1448
|
+
const output = timedJson && responsesReq.stream !== true ? void 0 : build();
|
|
1449
|
+
if (output) record();
|
|
1292
1450
|
if (responsesReq.stream !== true) {
|
|
1293
|
-
|
|
1294
|
-
|
|
1451
|
+
const servedReasoning = prepared.reasoning ?? ("reasoning" in prepared.response ? prepared.response.reasoning : void 0);
|
|
1452
|
+
await require_deliver.deliverJson(res, entry, fixture, () => {
|
|
1453
|
+
if (output) return output.response;
|
|
1454
|
+
const built = build();
|
|
1455
|
+
record();
|
|
1456
|
+
return built.response;
|
|
1457
|
+
}, jsonOpts(() => require_timing.misbehaviorPayloadLength(prepared, servedReasoning, jsonPayloadLength), !!prepared.duplicateId));
|
|
1295
1458
|
} else {
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
const lastGap = appliedTimings.interChunkDelaysMs.at(-1) ?? 0;
|
|
1302
|
-
while (appliedTimings.interChunkDelaysMs.length < output.events.length - 1) appliedTimings.interChunkDelaysMs.push(lastGap);
|
|
1303
|
-
}
|
|
1304
|
-
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
1305
|
-
if (!await writeResponsesSSEStream(res, output.events, {
|
|
1306
|
-
latency,
|
|
1307
|
-
streamingProfile: fixture.streamingProfile,
|
|
1308
|
-
recordedTimings: appliedTimings,
|
|
1309
|
-
replaySpeed: effectiveReplaySpeed,
|
|
1310
|
-
signal: interruption?.signal,
|
|
1311
|
-
onChunkSent: interruption?.tick
|
|
1312
|
-
})) {
|
|
1313
|
-
if (!res.writableEnded) res.destroy();
|
|
1314
|
-
entry.response.interrupted = true;
|
|
1315
|
-
entry.response.interruptReason = interruption?.reason();
|
|
1316
|
-
}
|
|
1317
|
-
interruption?.cleanup();
|
|
1459
|
+
const { events } = output ?? build();
|
|
1460
|
+
await require_deliver.deliverStream(res, entry, fixture, require_sse_writer.typedSseCodec(), events, {
|
|
1461
|
+
...streamTiming,
|
|
1462
|
+
recordedTimings: require_timing.extendRecordedTimings(fixtureTimings, events.length, !!prepared.duplicateId)
|
|
1463
|
+
});
|
|
1318
1464
|
}
|
|
1319
1465
|
return;
|
|
1320
1466
|
}
|
|
1467
|
+
if (require_helpers.isRawResponse(response)) {
|
|
1468
|
+
await require_raw.serveRawResponse({
|
|
1469
|
+
wire: "openai-responses",
|
|
1470
|
+
stream: responsesReq.stream === true,
|
|
1471
|
+
req,
|
|
1472
|
+
res,
|
|
1473
|
+
fixture,
|
|
1474
|
+
response,
|
|
1475
|
+
body: completionReq,
|
|
1476
|
+
journal,
|
|
1477
|
+
defaults,
|
|
1478
|
+
testId,
|
|
1479
|
+
misbehaviorSummary: misbehavior.summary,
|
|
1480
|
+
errorWire: "openai"
|
|
1481
|
+
});
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1321
1484
|
if (require_helpers.isErrorResponse(response)) {
|
|
1322
1485
|
const status = response.status ?? 500;
|
|
1323
1486
|
require_misbehavior.recordMisbehaviorOutcome({
|
|
@@ -1361,26 +1524,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1361
1524
|
const ordered = require_helpers.resolveServedBlockOutcome(response)?.ordered;
|
|
1362
1525
|
if (!ordered) require_helpers.assertCustomToolCalls(response);
|
|
1363
1526
|
const toolCalls = require_helpers.servedToolCalls(response);
|
|
1364
|
-
if (responsesReq.stream !== true)
|
|
1365
|
-
|
|
1366
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1367
|
-
res.end(JSON.stringify(body));
|
|
1368
|
-
} else {
|
|
1527
|
+
if (responsesReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildContentWithToolCallsResponse(response.content ?? "", toolCalls, completionReq.model, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
|
|
1528
|
+
else {
|
|
1369
1529
|
const events = contentWithToolCallsStreamEvents(response.content ?? "", toolCalls, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1370
|
-
|
|
1371
|
-
if (!await writeResponsesSSEStream(res, events, {
|
|
1372
|
-
latency,
|
|
1373
|
-
streamingProfile: fixture.streamingProfile,
|
|
1374
|
-
recordedTimings: fixtureTimings,
|
|
1375
|
-
replaySpeed: effectiveReplaySpeed,
|
|
1376
|
-
signal: interruption?.signal,
|
|
1377
|
-
onChunkSent: interruption?.tick
|
|
1378
|
-
})) {
|
|
1379
|
-
if (!res.writableEnded) res.destroy();
|
|
1380
|
-
journalEntry.response.interrupted = true;
|
|
1381
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
1382
|
-
}
|
|
1383
|
-
interruption?.cleanup();
|
|
1530
|
+
await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
|
|
1384
1531
|
}
|
|
1385
1532
|
return;
|
|
1386
1533
|
}
|
|
@@ -1404,26 +1551,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1404
1551
|
defaults,
|
|
1405
1552
|
testId
|
|
1406
1553
|
});
|
|
1407
|
-
if (responsesReq.stream !== true)
|
|
1408
|
-
|
|
1409
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1410
|
-
res.end(JSON.stringify(body));
|
|
1411
|
-
} else {
|
|
1554
|
+
if (responsesReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildTextResponse(response.content, completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
|
|
1555
|
+
else {
|
|
1412
1556
|
const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1413
|
-
|
|
1414
|
-
if (!await writeResponsesSSEStream(res, events, {
|
|
1415
|
-
latency,
|
|
1416
|
-
streamingProfile: fixture.streamingProfile,
|
|
1417
|
-
recordedTimings: fixtureTimings,
|
|
1418
|
-
replaySpeed: effectiveReplaySpeed,
|
|
1419
|
-
signal: interruption?.signal,
|
|
1420
|
-
onChunkSent: interruption?.tick
|
|
1421
|
-
})) {
|
|
1422
|
-
if (!res.writableEnded) res.destroy();
|
|
1423
|
-
journalEntry.response.interrupted = true;
|
|
1424
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
1425
|
-
}
|
|
1426
|
-
interruption?.cleanup();
|
|
1557
|
+
await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
|
|
1427
1558
|
}
|
|
1428
1559
|
return;
|
|
1429
1560
|
}
|
|
@@ -1448,26 +1579,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1448
1579
|
testId
|
|
1449
1580
|
});
|
|
1450
1581
|
require_helpers.assertCustomToolCalls(response);
|
|
1451
|
-
if (responsesReq.stream !== true)
|
|
1452
|
-
|
|
1453
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1454
|
-
res.end(JSON.stringify(body));
|
|
1455
|
-
} else {
|
|
1582
|
+
if (responsesReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildToolCallResponse(require_helpers.servedToolCalls(response), completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
|
|
1583
|
+
else {
|
|
1456
1584
|
const events = toolCallStreamEvents(require_helpers.servedToolCalls(response), completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1457
|
-
|
|
1458
|
-
if (!await writeResponsesSSEStream(res, events, {
|
|
1459
|
-
latency,
|
|
1460
|
-
streamingProfile: fixture.streamingProfile,
|
|
1461
|
-
recordedTimings: fixtureTimings,
|
|
1462
|
-
replaySpeed: effectiveReplaySpeed,
|
|
1463
|
-
signal: interruption?.signal,
|
|
1464
|
-
onChunkSent: interruption?.tick
|
|
1465
|
-
})) {
|
|
1466
|
-
if (!res.writableEnded) res.destroy();
|
|
1467
|
-
journalEntry.response.interrupted = true;
|
|
1468
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
1469
|
-
}
|
|
1470
|
-
interruption?.cleanup();
|
|
1585
|
+
await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
|
|
1471
1586
|
}
|
|
1472
1587
|
return;
|
|
1473
1588
|
}
|
|
@@ -1502,7 +1617,11 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
|
|
|
1502
1617
|
const overrides = require_helpers.extractOverrides(response);
|
|
1503
1618
|
const reasoning = plan.reasoning ?? ("reasoning" in response ? response.reasoning : void 0);
|
|
1504
1619
|
const webSearches = "webSearches" in response ? response.webSearches : void 0;
|
|
1505
|
-
const
|
|
1620
|
+
const outputWithheld = plan.refusal !== void 0 || plan.summary.fault === "empty-response" || plan.stop === "content_filter" || plan.reasoning !== void 0;
|
|
1621
|
+
const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(request.model, chunkSize, reasoning, webSearches, outputWithheld ? {
|
|
1622
|
+
...overrides,
|
|
1623
|
+
files: void 0
|
|
1624
|
+
} : overrides, emitEncryptedReasoning, false, plan.reasoning !== void 0);
|
|
1506
1625
|
const output = [...prefixOutputItems];
|
|
1507
1626
|
let outputIndex = nextOutputIndex;
|
|
1508
1627
|
const incomplete = plan.stop === "length" || plan.stop === "content_filter";
|
|
@@ -1560,16 +1679,18 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
|
|
|
1560
1679
|
item
|
|
1561
1680
|
});
|
|
1562
1681
|
output.push(item);
|
|
1563
|
-
} else if (
|
|
1682
|
+
} else if (!outputWithheld) {
|
|
1564
1683
|
const combined = require_helpers.isCombinedFixtureResponse(response);
|
|
1565
1684
|
const blocks = (combined ? require_helpers.resolveServedBlockOutcome(response) : void 0)?.ordered ?? [..."content" in response && typeof response.content === "string" ? [{
|
|
1566
1685
|
type: "text",
|
|
1567
1686
|
text: response.content
|
|
1568
1687
|
}] : [], ...combined || require_helpers.isToolCallResponse(response) ? require_helpers.servedToolCalls(response).map(require_helpers.toolCallFixtureBlock) : []];
|
|
1569
1688
|
let callIndex = 0;
|
|
1689
|
+
const textAnnotations = orderedBlockAnnotations(blocks, overrides.citations);
|
|
1690
|
+
let textPos = 0;
|
|
1570
1691
|
for (const block of blocks) {
|
|
1571
1692
|
if (block.type === "text") {
|
|
1572
|
-
const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides.phase);
|
|
1693
|
+
const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides.phase, textAnnotations[textPos++]);
|
|
1573
1694
|
events.push(...message.events);
|
|
1574
1695
|
output.push(message.msgItem);
|
|
1575
1696
|
} else {
|