@copilotkit/aimock 1.44.0 → 1.45.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +42 -0
- package/README.md +6 -0
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/aimock-cli.cjs +31 -5
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +31 -5
- package/dist/aimock-cli.js.map +1 -1
- package/dist/bedrock-converse.cjs +312 -39
- 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 +313 -40
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +195 -42
- 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 +196 -43
- package/dist/bedrock.js.map +1 -1
- package/dist/chaos.cjs +1 -0
- package/dist/chaos.js +1 -1
- package/dist/cli.cjs +152 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +155 -8
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +132 -71
- 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 +133 -72
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +62 -4
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +37 -4
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +37 -4
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +62 -4
- package/dist/config-loader.js.map +1 -1
- package/dist/fakes-plugin.cjs +131 -0
- package/dist/fakes-plugin.cjs.map +1 -0
- package/dist/fakes-plugin.d.cts +23 -0
- package/dist/fakes-plugin.d.cts.map +1 -0
- package/dist/fakes-plugin.d.ts +23 -0
- package/dist/fakes-plugin.d.ts.map +1 -0
- package/dist/fakes-plugin.js +127 -0
- package/dist/fakes-plugin.js.map +1 -0
- package/dist/fixture-loader-services.cjs +1 -1
- package/dist/fixture-loader-services.cjs.map +1 -1
- package/dist/fixture-loader-services.js +1 -1
- package/dist/fixture-loader-services.js.map +1 -1
- package/dist/fixture-loader.cjs +389 -27
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +47 -3
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +47 -3
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +384 -29
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +250 -52
- 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 +251 -53
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +226 -37
- 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 +227 -38
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +881 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +85 -4
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +85 -4
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +839 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -3
- package/dist/jest.cjs +31 -7
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +43 -3
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +43 -3
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +33 -9
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +135 -13
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +38 -4
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +38 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +135 -13
- package/dist/journal.js.map +1 -1
- package/dist/live-sanitize.cjs +4 -32
- package/dist/live-sanitize.cjs.map +1 -1
- package/dist/live-sanitize.js +4 -32
- package/dist/live-sanitize.js.map +1 -1
- package/dist/llmock.cjs +123 -30
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +16 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +16 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +125 -32
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +1 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -1
- package/dist/mcp-fakes-mount.js +1 -0
- package/dist/mcp-fakes-mount.js.map +1 -1
- package/dist/mcp-fakes-report.cjs +111 -0
- package/dist/mcp-fakes-report.cjs.map +1 -0
- package/dist/mcp-fakes-report.d.cts +40 -0
- package/dist/mcp-fakes-report.d.cts.map +1 -0
- package/dist/mcp-fakes-report.d.ts +40 -0
- package/dist/mcp-fakes-report.d.ts.map +1 -0
- package/dist/mcp-fakes-report.js +107 -0
- package/dist/mcp-fakes-report.js.map +1 -0
- package/dist/mcp-fakes.cjs +331 -8
- package/dist/mcp-fakes.cjs.map +1 -1
- package/dist/mcp-fakes.d.cts +406 -3
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/mcp-fakes.d.ts +406 -3
- package/dist/mcp-fakes.d.ts.map +1 -1
- package/dist/mcp-fakes.js +331 -8
- package/dist/mcp-fakes.js.map +1 -1
- package/dist/mcp-handler.cjs +216 -17
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.d.cts +49 -0
- package/dist/mcp-handler.d.cts.map +1 -0
- package/dist/mcp-handler.d.ts +49 -0
- package/dist/mcp-handler.d.ts.map +1 -0
- package/dist/mcp-handler.js +214 -18
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +112 -1
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +33 -2
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +33 -2
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +112 -1
- package/dist/mcp-mock.js.map +1 -1
- package/dist/mcp-recorder.cjs +1224 -0
- package/dist/mcp-recorder.cjs.map +1 -0
- package/dist/mcp-recorder.d.cts +108 -0
- package/dist/mcp-recorder.d.cts.map +1 -0
- package/dist/mcp-recorder.d.ts +108 -0
- package/dist/mcp-recorder.d.ts.map +1 -0
- package/dist/mcp-recorder.js +1215 -0
- package/dist/mcp-recorder.js.map +1 -0
- package/dist/messages.cjs +362 -56
- 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 +363 -58
- package/dist/messages.js.map +1 -1
- package/dist/misbehavior.cjs +1523 -0
- package/dist/misbehavior.cjs.map +1 -0
- package/dist/misbehavior.d.cts +43 -0
- package/dist/misbehavior.d.cts.map +1 -0
- package/dist/misbehavior.d.ts +43 -0
- package/dist/misbehavior.d.ts.map +1 -0
- package/dist/misbehavior.js +1511 -0
- package/dist/misbehavior.js.map +1 -0
- package/dist/ollama.cjs +310 -112
- 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 +311 -113
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +30 -2
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +30 -2
- package/dist/openrouter-video.js.map +1 -1
- package/dist/record-sanitize.cjs +231 -0
- package/dist/record-sanitize.cjs.map +1 -0
- package/dist/record-sanitize.js +224 -0
- package/dist/record-sanitize.js.map +1 -0
- package/dist/recorder.cjs +106 -5
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +2 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +2 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +107 -7
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +667 -171
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +5 -13
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +5 -13
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +665 -174
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +24 -2
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +25 -4
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +572 -76
- 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 +574 -78
- package/dist/server.js.map +1 -1
- package/dist/stream-collapse.cjs +236 -34
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +39 -4
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +39 -4
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +235 -35
- package/dist/stream-collapse.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +315 -4
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +315 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-cli.cjs +36 -4
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +37 -5
- package/dist/validate-cli.js.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/version.cjs +45 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.js +44 -0
- package/dist/version.js.map +1 -0
- package/dist/vitest.cjs +66 -10
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +35 -4
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +35 -4
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +67 -11
- package/dist/vitest.js.map +1 -1
- package/dist/watcher.cjs +6 -2
- package/dist/watcher.cjs.map +1 -1
- package/dist/watcher.js +6 -2
- package/dist/watcher.js.map +1 -1
- package/dist/ws-gemini-live.cjs +210 -101
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +9 -3
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts +9 -3
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +211 -102
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +175 -62
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +8 -3
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +8 -3
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +176 -63
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +109 -16
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts +8 -12
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts +8 -12
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +111 -18
- package/dist/ws-responses.js.map +1 -1
- package/package.json +10 -2
- package/skills/write-fixtures/SKILL.md +179 -21
package/dist/responses.cjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
1
2
|
const require_model_utils = require('./model-utils.cjs');
|
|
2
3
|
const require_helpers = require('./helpers.cjs');
|
|
3
4
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
4
5
|
const require_chaos = require('./chaos.cjs');
|
|
6
|
+
const require_misbehavior = require('./misbehavior.cjs');
|
|
5
7
|
const require_router = require('./router.cjs');
|
|
6
8
|
const require_recorder = require('./recorder.cjs');
|
|
7
9
|
const require_interruption = require('./interruption.cjs');
|
|
10
|
+
let node_crypto = require("node:crypto");
|
|
8
11
|
|
|
9
12
|
//#region src/responses.ts
|
|
10
13
|
function extractTextContent(content) {
|
|
@@ -12,7 +15,36 @@ function extractTextContent(content) {
|
|
|
12
15
|
if (typeof content === "string") return content;
|
|
13
16
|
return content.filter((p) => p.type === "input_text" || p.type === "output_text").map((p) => p.text ?? "").join("");
|
|
14
17
|
}
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* A `custom_tool_call_output.output` is a string or a list of content parts
|
|
20
|
+
* (`input_text` / `input_image` / `input_file`). Flatten a list to its
|
|
21
|
+
* concatenated `input_text` text so text matchers such as
|
|
22
|
+
* `toolResultContains` see it.
|
|
23
|
+
*
|
|
24
|
+
* The value comes straight from the request body, so it is read as `unknown`.
|
|
25
|
+
* Like `function_call_output`, a malformed output never fails the request:
|
|
26
|
+
* any other value (null, an object, a number) yields empty text, and list
|
|
27
|
+
* entries that are not `input_text` parts with string text are skipped.
|
|
28
|
+
*/
|
|
29
|
+
function customToolOutputText(output) {
|
|
30
|
+
if (typeof output === "string") return output;
|
|
31
|
+
if (!Array.isArray(output)) return "";
|
|
32
|
+
return output.map((p) => {
|
|
33
|
+
if (p === null || typeof p !== "object") return "";
|
|
34
|
+
const part = p;
|
|
35
|
+
return part.type === "input_text" && typeof part.text === "string" ? part.text : "";
|
|
36
|
+
}).join("");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert Responses input items into chat messages for fixture matching. The
|
|
40
|
+
* default is the conversion of earlier releases: `custom_tool_call` and
|
|
41
|
+
* `custom_tool_call_output` items are skipped, and history namespaces are not
|
|
42
|
+
* kept. With `extended`, a custom tool call becomes an assistant message with
|
|
43
|
+
* `custom_tool_calls` (so turn counting sees it), its output becomes a `tool`
|
|
44
|
+
* message (so `hasToolResult` and `toolCallId` see it), and a `function_call`
|
|
45
|
+
* keeps its `namespace`.
|
|
46
|
+
*/
|
|
47
|
+
function responsesInputToMessages(req, extended = false) {
|
|
16
48
|
const messages = [];
|
|
17
49
|
const itemReferencePlaceholders = /* @__PURE__ */ new WeakSet();
|
|
18
50
|
if (req.instructions) messages.push({
|
|
@@ -26,6 +58,25 @@ function responsesInputToMessages(req) {
|
|
|
26
58
|
});
|
|
27
59
|
return messages;
|
|
28
60
|
}
|
|
61
|
+
/** A synthesized call for an output item that has no matching call. */
|
|
62
|
+
const attachSynthesizedCall = (target, item) => {
|
|
63
|
+
const id = item.call_id ?? require_helpers.generateToolCallId();
|
|
64
|
+
if (item.type === "custom_tool_call_output") (target.custom_tool_calls ??= []).push({
|
|
65
|
+
id,
|
|
66
|
+
type: "custom",
|
|
67
|
+
name: "",
|
|
68
|
+
input: ""
|
|
69
|
+
});
|
|
70
|
+
else (target.tool_calls ??= []).push({
|
|
71
|
+
id,
|
|
72
|
+
type: "function",
|
|
73
|
+
function: {
|
|
74
|
+
name: "",
|
|
75
|
+
arguments: ""
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
const hasCalls = (m) => m.tool_calls !== void 0 || m.custom_tool_calls !== void 0;
|
|
29
80
|
for (const item of req.input) if (item.role === "system" || item.role === "developer") messages.push({
|
|
30
81
|
role: "system",
|
|
31
82
|
content: extractTextContent(item.content)
|
|
@@ -44,61 +95,55 @@ function responsesInputToMessages(req) {
|
|
|
44
95
|
tool_calls: [{
|
|
45
96
|
id: item.call_id ?? require_helpers.generateToolCallId(),
|
|
46
97
|
type: "function",
|
|
98
|
+
...extended && typeof item.namespace === "string" ? { namespace: item.namespace } : {},
|
|
47
99
|
function: {
|
|
48
100
|
name: item.name ?? "",
|
|
49
101
|
arguments: item.arguments ?? ""
|
|
50
102
|
}
|
|
51
103
|
}]
|
|
52
104
|
});
|
|
53
|
-
else if (item.type === "
|
|
54
|
-
|
|
105
|
+
else if (extended && item.type === "custom_tool_call") messages.push({
|
|
106
|
+
role: "assistant",
|
|
107
|
+
content: null,
|
|
108
|
+
custom_tool_calls: [{
|
|
109
|
+
id: item.call_id ?? require_helpers.generateToolCallId(),
|
|
110
|
+
type: "custom",
|
|
111
|
+
name: item.name ?? "",
|
|
112
|
+
input: typeof item.input === "string" ? item.input : "",
|
|
113
|
+
...typeof item.namespace === "string" ? { namespace: item.namespace } : {}
|
|
114
|
+
}]
|
|
115
|
+
});
|
|
116
|
+
else if (item.type === "function_call_output" || extended && item.type === "custom_tool_call_output") {
|
|
117
|
+
if (!messages.some((m) => m.role === "assistant" && (m.tool_calls?.some((tc) => tc.id === item.call_id) || m.custom_tool_calls?.some((tc) => tc.id === item.call_id)))) {
|
|
55
118
|
const lastMsg = messages[messages.length - 1];
|
|
56
|
-
if (lastMsg && lastMsg.role === "assistant" && itemReferencePlaceholders.has(lastMsg) && !lastMsg
|
|
119
|
+
if (lastMsg && lastMsg.role === "assistant" && itemReferencePlaceholders.has(lastMsg) && !hasCalls(lastMsg)) {
|
|
57
120
|
lastMsg.content = null;
|
|
58
|
-
lastMsg
|
|
59
|
-
id: item.call_id ?? require_helpers.generateToolCallId(),
|
|
60
|
-
type: "function",
|
|
61
|
-
function: {
|
|
62
|
-
name: "",
|
|
63
|
-
arguments: ""
|
|
64
|
-
}
|
|
65
|
-
}];
|
|
121
|
+
attachSynthesizedCall(lastMsg, item);
|
|
66
122
|
itemReferencePlaceholders.delete(lastMsg);
|
|
67
123
|
} else {
|
|
68
124
|
let appended = false;
|
|
69
125
|
for (let k = messages.length - 1; k >= 0; k--) {
|
|
70
126
|
const m = messages[k];
|
|
71
|
-
if (m.role === "assistant" && m
|
|
72
|
-
m
|
|
73
|
-
id: item.call_id ?? require_helpers.generateToolCallId(),
|
|
74
|
-
type: "function",
|
|
75
|
-
function: {
|
|
76
|
-
name: "",
|
|
77
|
-
arguments: ""
|
|
78
|
-
}
|
|
79
|
-
});
|
|
127
|
+
if (m.role === "assistant" && hasCalls(m)) {
|
|
128
|
+
attachSynthesizedCall(m, item);
|
|
80
129
|
appended = true;
|
|
81
130
|
break;
|
|
82
131
|
}
|
|
83
132
|
if (m.role === "user") break;
|
|
84
133
|
}
|
|
85
|
-
if (!appended)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
arguments: ""
|
|
94
|
-
}
|
|
95
|
-
}]
|
|
96
|
-
});
|
|
134
|
+
if (!appended) {
|
|
135
|
+
const synthesized = {
|
|
136
|
+
role: "assistant",
|
|
137
|
+
content: null
|
|
138
|
+
};
|
|
139
|
+
attachSynthesizedCall(synthesized, item);
|
|
140
|
+
messages.push(synthesized);
|
|
141
|
+
}
|
|
97
142
|
}
|
|
98
143
|
}
|
|
99
144
|
messages.push({
|
|
100
145
|
role: "tool",
|
|
101
|
-
content: item.output ?? "",
|
|
146
|
+
content: item.type === "custom_tool_call_output" ? customToolOutputText(item.output) : item.output ?? "",
|
|
102
147
|
tool_call_id: item.call_id
|
|
103
148
|
});
|
|
104
149
|
} else if (item.type === "item_reference") {
|
|
@@ -111,6 +156,7 @@ function responsesInputToMessages(req) {
|
|
|
111
156
|
}
|
|
112
157
|
return messages;
|
|
113
158
|
}
|
|
159
|
+
/** The request tools of earlier releases: top-level `function` tools only. */
|
|
114
160
|
function responsesToolsToCompletionsTools(tools) {
|
|
115
161
|
if (!tools || tools.length === 0) return void 0;
|
|
116
162
|
return tools.filter((t) => t.type === "function").map((t) => ({
|
|
@@ -122,17 +168,146 @@ function responsesToolsToCompletionsTools(tools) {
|
|
|
122
168
|
}
|
|
123
169
|
}));
|
|
124
170
|
}
|
|
125
|
-
function
|
|
171
|
+
/** One Responses `function` or `custom` tool in aimock's normalized form. */
|
|
172
|
+
function normalizeResponsesTool(tool, namespace) {
|
|
173
|
+
if (tool === null || typeof tool !== "object") return void 0;
|
|
174
|
+
const t = tool;
|
|
175
|
+
const ns = namespace !== void 0 ? { namespace } : {};
|
|
176
|
+
if (t.type === "function") {
|
|
177
|
+
const fn = t;
|
|
178
|
+
return {
|
|
179
|
+
type: "function",
|
|
180
|
+
...ns,
|
|
181
|
+
function: {
|
|
182
|
+
name: fn.name,
|
|
183
|
+
description: fn.description,
|
|
184
|
+
parameters: fn.parameters
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (t.type === "custom") {
|
|
189
|
+
const custom = t;
|
|
190
|
+
return {
|
|
191
|
+
type: "custom",
|
|
192
|
+
name: custom.name,
|
|
193
|
+
...custom.description !== void 0 ? { description: custom.description } : {},
|
|
194
|
+
...ns,
|
|
195
|
+
...custom.format !== void 0 ? { format: custom.format } : {}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Input items whose `tools` add to the request's tools:
|
|
201
|
+
* `additional_tools` (Codex responses-lite) and `tool_search_output` (tools
|
|
202
|
+
* loaded by tool search, e.g. Codex's deferred MCP namespaces). Both carry the
|
|
203
|
+
* same function / custom / namespace tool shapes as `req.tools`.
|
|
204
|
+
*/
|
|
205
|
+
function isToolCarryingItemType(type) {
|
|
206
|
+
return type === "additional_tools" || type === "tool_search_output";
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Why a `namespace` tool cannot be flattened, or undefined when it can: its
|
|
210
|
+
* name is not a non-empty string (OpenAI's `NamespaceToolParam` requires
|
|
211
|
+
* `name` with `minLength: 1`), or its `tools` is not an array.
|
|
212
|
+
*/
|
|
213
|
+
function namespaceToolProblem(tool) {
|
|
214
|
+
if (typeof tool.name !== "string" || tool.name === "") return "name is not a non-empty string";
|
|
215
|
+
if (!Array.isArray(tool.tools)) return "tools is not an array";
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Flatten every Responses tool a request offers, for extended matching and for
|
|
219
|
+
* `toolNamespace`. Sources, in order: `req.tools`, then the `tools` of every
|
|
220
|
+
* `additional_tools` or `tool_search_output` input item, in input order.
|
|
221
|
+
* `namespace` tools contribute one entry per inner function/custom tool, each
|
|
222
|
+
* carrying the namespace; other tool types are ignored.
|
|
223
|
+
*
|
|
224
|
+
* Malformed items never fail the request (earlier releases ignored them): a
|
|
225
|
+
* `namespace` tool without a non-empty string name or with a non-array
|
|
226
|
+
* `tools`, a `null` entry, and a tool-carrying item whose `tools` is not an
|
|
227
|
+
* array are dropped, with a warning when `warn` is given.
|
|
228
|
+
*/
|
|
229
|
+
function flattenResponsesTools(req, warn) {
|
|
230
|
+
const tools = [];
|
|
231
|
+
const customTools = [];
|
|
232
|
+
const add = (normalized) => {
|
|
233
|
+
if (!normalized) return;
|
|
234
|
+
if (normalized.type === "custom") customTools.push(normalized);
|
|
235
|
+
else tools.push(normalized);
|
|
236
|
+
};
|
|
237
|
+
const sources = [];
|
|
238
|
+
if (Array.isArray(req.tools)) req.tools.forEach((tool, i) => sources.push({
|
|
239
|
+
tool,
|
|
240
|
+
path: `tools[${i}]`
|
|
241
|
+
}));
|
|
242
|
+
if (Array.isArray(req.input)) req.input.forEach((item, i) => {
|
|
243
|
+
if (!isToolCarryingItemType(item?.type) || item?.tools === void 0) return;
|
|
244
|
+
if (!Array.isArray(item.tools)) {
|
|
245
|
+
warn?.(`Ignoring input[${i}].tools: not an array`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
item.tools.forEach((tool, j) => sources.push({
|
|
249
|
+
tool,
|
|
250
|
+
path: `input[${i}].tools[${j}]`
|
|
251
|
+
}));
|
|
252
|
+
});
|
|
253
|
+
for (const { tool, path } of sources) {
|
|
254
|
+
const t = tool;
|
|
255
|
+
if (t === null) {
|
|
256
|
+
warn?.(`Ignoring ${path}: null tool`);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (t?.type === "namespace") {
|
|
260
|
+
const problem = namespaceToolProblem(t);
|
|
261
|
+
if (problem) {
|
|
262
|
+
warn?.(`Ignoring namespace tool ${path}: ${problem}`);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
t.tools.forEach((inner, k) => {
|
|
266
|
+
if (inner === null) warn?.(`Ignoring ${path}.tools[${k}]: null tool`);
|
|
267
|
+
else add(normalizeResponsesTool(inner, t.name));
|
|
268
|
+
});
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
add(normalizeResponsesTool(tool));
|
|
272
|
+
}
|
|
126
273
|
return {
|
|
274
|
+
tools,
|
|
275
|
+
customTools,
|
|
276
|
+
sourceCount: sources.length
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function responsesToCompletionRequest(req, options = {}) {
|
|
280
|
+
const extended = options.extended === true;
|
|
281
|
+
const all = flattenResponsesTools(req, extended && options.logger ? (message) => options.logger?.warn(`Responses request: ${message}`) : void 0);
|
|
282
|
+
const completionReq = {
|
|
127
283
|
model: req.model,
|
|
128
|
-
messages: responsesInputToMessages(req),
|
|
284
|
+
messages: responsesInputToMessages(req, extended),
|
|
129
285
|
stream: req.stream,
|
|
130
286
|
temperature: req.temperature,
|
|
131
287
|
max_tokens: req.max_output_tokens,
|
|
132
|
-
tools: responsesToolsToCompletionsTools(req.tools),
|
|
288
|
+
tools: extended ? all.sourceCount > 0 ? all.tools : void 0 : responsesToolsToCompletionsTools(req.tools),
|
|
289
|
+
...extended && all.customTools.length > 0 ? { customTools: all.customTools } : {},
|
|
133
290
|
tool_choice: req.tool_choice,
|
|
134
291
|
response_format: req.response_format
|
|
135
292
|
};
|
|
293
|
+
require_router.setResponsesOfferedTools(completionReq, {
|
|
294
|
+
tools: all.tools,
|
|
295
|
+
customTools: all.customTools
|
|
296
|
+
});
|
|
297
|
+
return completionReq;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* The HTTP request check of earlier releases for `tools`: keep the
|
|
301
|
+
* converter's empty/falsy bypass, and reject only a collection the legacy
|
|
302
|
+
* converter would throw on (not an array, or a `null` entry). Malformed
|
|
303
|
+
* nested tools (inside a `namespace` tool or a tool-carrying input item) are
|
|
304
|
+
* dropped by the conversion instead.
|
|
305
|
+
*/
|
|
306
|
+
function validateResponsesTools(tools) {
|
|
307
|
+
if (tools && tools.length !== 0) {
|
|
308
|
+
if (!Array.isArray(tools)) return "tools must be an array";
|
|
309
|
+
if (tools.some((tool) => tool === null)) return "tools entries must not be null";
|
|
310
|
+
}
|
|
136
311
|
}
|
|
137
312
|
function responsesStatus(finishReason, defaultStatus) {
|
|
138
313
|
if (!finishReason) return defaultStatus;
|
|
@@ -184,56 +359,101 @@ function requireFixtureToolArguments(value) {
|
|
|
184
359
|
if (typeof value !== "string") throw new Error("Invalid fixture tool call: \"arguments\" must be a string after normalization");
|
|
185
360
|
return value;
|
|
186
361
|
}
|
|
362
|
+
function requireFixtureToolInput(value) {
|
|
363
|
+
if (typeof value !== "string") throw new Error("Invalid fixture tool call: \"input\" must be a string for a custom tool call");
|
|
364
|
+
return value;
|
|
365
|
+
}
|
|
366
|
+
function isCustomEmittable(call) {
|
|
367
|
+
if (call === null || typeof call !== "object") return false;
|
|
368
|
+
return call.type === "custom" || call.type === "customToolCall";
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* A Responses `function_call` item. `namespace` is added only when the fixture
|
|
372
|
+
* sets it, so a call without one keeps the keys and key order it had before
|
|
373
|
+
* namespaces existed (existing fixtures stay byte-identical on the wire).
|
|
374
|
+
*/
|
|
375
|
+
function functionCallItem(call, fcId, callId, args, status) {
|
|
376
|
+
return {
|
|
377
|
+
type: "function_call",
|
|
378
|
+
id: fcId,
|
|
379
|
+
call_id: callId,
|
|
380
|
+
...call.namespace !== void 0 ? { namespace: call.namespace } : {},
|
|
381
|
+
name: call.name,
|
|
382
|
+
arguments: args,
|
|
383
|
+
status
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* `ctc_…` item id, the prefix the OpenAI guide and Codex's item-id map use for
|
|
388
|
+
* custom tool calls. The underscore separator is deliberate: it matches the
|
|
389
|
+
* real id format, so this does not use `generateId` (which would give
|
|
390
|
+
* `ctc-…`, like aimock's own `fc-…` ids).
|
|
391
|
+
*/
|
|
392
|
+
function customToolCallItemId() {
|
|
393
|
+
return `ctc_${(0, node_crypto.randomBytes)(12).toString("base64url")}`;
|
|
394
|
+
}
|
|
395
|
+
/** A Responses `custom_tool_call` item; `namespace` is added only when set. */
|
|
396
|
+
function customToolCallItem(call, ctcId, callId, input, status) {
|
|
397
|
+
return {
|
|
398
|
+
type: "custom_tool_call",
|
|
399
|
+
id: ctcId,
|
|
400
|
+
call_id: callId,
|
|
401
|
+
...call.namespace !== void 0 ? { namespace: call.namespace } : {},
|
|
402
|
+
name: call.name,
|
|
403
|
+
input,
|
|
404
|
+
status
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* A `toolCalls` entry as earlier releases served it: a function call whose
|
|
409
|
+
* `type`, `input` and `namespace` are ignored. The exported builders take
|
|
410
|
+
* fixtures in that form; the handler passes `namespace` (extended mode) and
|
|
411
|
+
* custom calls to the internal builders instead.
|
|
412
|
+
*/
|
|
413
|
+
function legacyFunctionCall(call) {
|
|
414
|
+
if (call === null || typeof call !== "object") return call;
|
|
415
|
+
const { name, arguments: args, id } = call;
|
|
416
|
+
return id !== void 0 ? {
|
|
417
|
+
name,
|
|
418
|
+
arguments: args,
|
|
419
|
+
id
|
|
420
|
+
} : {
|
|
421
|
+
name,
|
|
422
|
+
arguments: args
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* The exported builders' `toolCalls` as legacy function calls. It walks
|
|
427
|
+
* `toolCalls` by index up to `toolCalls.length`, as earlier releases did, so an
|
|
428
|
+
* untyped caller's non-array value gives the same calls: none for an object
|
|
429
|
+
* without `length`, and entries by index for a string or array-like.
|
|
430
|
+
*/
|
|
431
|
+
function legacyFunctionCalls(toolCalls) {
|
|
432
|
+
const calls = [];
|
|
433
|
+
for (let idx = 0; idx < toolCalls.length; idx++) calls.push(legacyFunctionCall(toolCalls[idx]));
|
|
434
|
+
return calls;
|
|
435
|
+
}
|
|
436
|
+
/** Resolve exported-builder `blocks` as earlier releases did (no namespace emitted). */
|
|
437
|
+
function legacyOrderedBlocks(blocks) {
|
|
438
|
+
if (!blocks || blocks.length === 0) return void 0;
|
|
439
|
+
return require_helpers.resolveFixtureBlocks(blocks).map((block) => {
|
|
440
|
+
if (block.type === "text" || block.namespace === void 0) return block;
|
|
441
|
+
const copy = { ...block };
|
|
442
|
+
delete copy.namespace;
|
|
443
|
+
return copy;
|
|
444
|
+
});
|
|
445
|
+
}
|
|
187
446
|
function buildToolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
447
|
+
return toolCallStreamEvents(legacyFunctionCalls(toolCalls), model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
448
|
+
}
|
|
449
|
+
function toolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
450
|
+
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
188
451
|
const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
189
452
|
const fcOutputItems = [];
|
|
190
453
|
for (let idx = 0; idx < toolCalls.length; idx++) {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const outputIndex = nextOutputIndex + idx;
|
|
195
|
-
events.push({
|
|
196
|
-
type: "response.output_item.added",
|
|
197
|
-
output_index: outputIndex,
|
|
198
|
-
item: {
|
|
199
|
-
type: "function_call",
|
|
200
|
-
id: fcId,
|
|
201
|
-
call_id: callId,
|
|
202
|
-
name: tc.name,
|
|
203
|
-
arguments: "",
|
|
204
|
-
status: "in_progress"
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
const args = requireFixtureToolArguments(tc.arguments);
|
|
208
|
-
for (let i = 0; i < args.length; i += chunkSize) {
|
|
209
|
-
const slice = args.slice(i, i + chunkSize);
|
|
210
|
-
events.push({
|
|
211
|
-
type: "response.function_call_arguments.delta",
|
|
212
|
-
item_id: fcId,
|
|
213
|
-
output_index: outputIndex,
|
|
214
|
-
delta: slice
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
events.push({
|
|
218
|
-
type: "response.function_call_arguments.done",
|
|
219
|
-
item_id: fcId,
|
|
220
|
-
output_index: outputIndex,
|
|
221
|
-
arguments: args
|
|
222
|
-
});
|
|
223
|
-
const doneItem = {
|
|
224
|
-
type: "function_call",
|
|
225
|
-
id: fcId,
|
|
226
|
-
call_id: callId,
|
|
227
|
-
name: tc.name,
|
|
228
|
-
arguments: args,
|
|
229
|
-
status: "completed"
|
|
230
|
-
};
|
|
231
|
-
events.push({
|
|
232
|
-
type: "response.output_item.done",
|
|
233
|
-
output_index: outputIndex,
|
|
234
|
-
item: doneItem
|
|
235
|
-
});
|
|
236
|
-
fcOutputItems.push(doneItem);
|
|
454
|
+
const { events: callEvents, item } = buildToolCallOutputEvents(toolCalls[idx], chunkSize, nextOutputIndex + idx);
|
|
455
|
+
events.push(...callEvents);
|
|
456
|
+
fcOutputItems.push(item);
|
|
237
457
|
}
|
|
238
458
|
events.push({
|
|
239
459
|
type: "response.completed",
|
|
@@ -464,7 +684,7 @@ function buildWebSearchStreamEvents(queries, startOutputIndex) {
|
|
|
464
684
|
}
|
|
465
685
|
return events;
|
|
466
686
|
}
|
|
467
|
-
function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
687
|
+
function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, forceReasoning = false) {
|
|
468
688
|
const respId = overrides?.id ?? responseId();
|
|
469
689
|
const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
|
|
470
690
|
const effectiveModel = overrides?.model ?? model;
|
|
@@ -493,7 +713,7 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
|
|
|
493
713
|
output: []
|
|
494
714
|
}
|
|
495
715
|
});
|
|
496
|
-
if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) {
|
|
716
|
+
if (reasoning || forceReasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) {
|
|
497
717
|
const reasoningEvents = buildReasoningStreamEvents(reasoning, chunkSize, emitEncryptedReasoning);
|
|
498
718
|
events.push(...reasoningEvents);
|
|
499
719
|
const doneEvent = reasoningEvents.find((e) => e.type === "response.output_item.done" && e.item?.type === "reasoning");
|
|
@@ -589,10 +809,10 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex) {
|
|
|
589
809
|
/**
|
|
590
810
|
* Emit the output_item.added → arguments deltas → arguments.done →
|
|
591
811
|
* output_item.done events for a single function_call at `outputIndex`,
|
|
592
|
-
* returning the completed item for the final `output` array.
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
812
|
+
* returning the completed item for the final `output` array. Every Responses
|
|
813
|
+
* streaming builder (tool-only, legacy content+toolCalls and ordered blocks)
|
|
814
|
+
* reaches this through {@link buildToolCallOutputEvents}, so a given
|
|
815
|
+
* (tool, outputIndex) has the same wire output on every path.
|
|
596
816
|
*/
|
|
597
817
|
function buildFunctionCallOutputEvents(toolCall, chunkSize, outputIndex) {
|
|
598
818
|
const callId = toolCall.id || require_helpers.generateToolCallId();
|
|
@@ -602,14 +822,7 @@ function buildFunctionCallOutputEvents(toolCall, chunkSize, outputIndex) {
|
|
|
602
822
|
events.push({
|
|
603
823
|
type: "response.output_item.added",
|
|
604
824
|
output_index: outputIndex,
|
|
605
|
-
item:
|
|
606
|
-
type: "function_call",
|
|
607
|
-
id: fcId,
|
|
608
|
-
call_id: callId,
|
|
609
|
-
name: toolCall.name,
|
|
610
|
-
arguments: "",
|
|
611
|
-
status: "in_progress"
|
|
612
|
-
}
|
|
825
|
+
item: functionCallItem(toolCall, fcId, callId, "", "in_progress")
|
|
613
826
|
});
|
|
614
827
|
for (let i = 0; i < args.length; i += chunkSize) events.push({
|
|
615
828
|
type: "response.function_call_arguments.delta",
|
|
@@ -623,24 +836,78 @@ function buildFunctionCallOutputEvents(toolCall, chunkSize, outputIndex) {
|
|
|
623
836
|
output_index: outputIndex,
|
|
624
837
|
arguments: args
|
|
625
838
|
});
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
839
|
+
const item = functionCallItem(toolCall, fcId, callId, args, "completed");
|
|
840
|
+
events.push({
|
|
841
|
+
type: "response.output_item.done",
|
|
842
|
+
output_index: outputIndex,
|
|
843
|
+
item
|
|
844
|
+
});
|
|
845
|
+
return {
|
|
846
|
+
events,
|
|
847
|
+
item
|
|
633
848
|
};
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* `text` cut into slices of at most `chunkSize` UTF-16 units, never between
|
|
852
|
+
* the two halves of a surrogate pair: a cut that would land inside a pair
|
|
853
|
+
* moves back before it, or, when that would leave the slice empty
|
|
854
|
+
* (`chunkSize` 1), forward past it. The slices rejoin to `text` exactly.
|
|
855
|
+
*/
|
|
856
|
+
function surrogateSafeChunks(text, chunkSize) {
|
|
857
|
+
const chunks = [];
|
|
858
|
+
for (let i = 0; i < text.length;) {
|
|
859
|
+
let end = Math.min(i + chunkSize, text.length);
|
|
860
|
+
const high = text.charCodeAt(end - 1);
|
|
861
|
+
const low = text.charCodeAt(end);
|
|
862
|
+
if (high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343) end = end - 1 > i ? end - 1 : end + 1;
|
|
863
|
+
chunks.push(text.slice(i, end));
|
|
864
|
+
i = end;
|
|
865
|
+
}
|
|
866
|
+
return chunks;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Emit output_item.added → custom_tool_call_input.delta×N →
|
|
870
|
+
* custom_tool_call_input.done → output_item.done for one `custom_tool_call` at
|
|
871
|
+
* `outputIndex`, returning the completed item. An empty `input`
|
|
872
|
+
* yields no delta event.
|
|
873
|
+
*/
|
|
874
|
+
function buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) {
|
|
875
|
+
const callId = call.id || require_helpers.generateToolCallId();
|
|
876
|
+
const ctcId = customToolCallItemId();
|
|
877
|
+
const input = requireFixtureToolInput(call.input);
|
|
878
|
+
const events = [];
|
|
879
|
+
events.push({
|
|
880
|
+
type: "response.output_item.added",
|
|
881
|
+
output_index: outputIndex,
|
|
882
|
+
item: customToolCallItem(call, ctcId, callId, "", "in_progress")
|
|
883
|
+
});
|
|
884
|
+
for (const delta of surrogateSafeChunks(input, chunkSize)) events.push({
|
|
885
|
+
type: "response.custom_tool_call_input.delta",
|
|
886
|
+
item_id: ctcId,
|
|
887
|
+
output_index: outputIndex,
|
|
888
|
+
delta
|
|
889
|
+
});
|
|
890
|
+
events.push({
|
|
891
|
+
type: "response.custom_tool_call_input.done",
|
|
892
|
+
item_id: ctcId,
|
|
893
|
+
output_index: outputIndex,
|
|
894
|
+
input
|
|
895
|
+
});
|
|
896
|
+
const item = customToolCallItem(call, ctcId, callId, input, "completed");
|
|
634
897
|
events.push({
|
|
635
898
|
type: "response.output_item.done",
|
|
636
899
|
output_index: outputIndex,
|
|
637
|
-
item
|
|
900
|
+
item
|
|
638
901
|
});
|
|
639
902
|
return {
|
|
640
903
|
events,
|
|
641
|
-
|
|
904
|
+
item
|
|
642
905
|
};
|
|
643
906
|
}
|
|
907
|
+
/** Streaming dispatcher: one function or custom tool call at `outputIndex`. */
|
|
908
|
+
function buildToolCallOutputEvents(call, chunkSize, outputIndex) {
|
|
909
|
+
return isCustomEmittable(call) ? buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) : buildFunctionCallOutputEvents(call, chunkSize, outputIndex);
|
|
910
|
+
}
|
|
644
911
|
function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
645
912
|
const output = [];
|
|
646
913
|
if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
|
|
@@ -684,6 +951,7 @@ function buildTextResponse(content, model, reasoning, webSearches, overrides, em
|
|
|
684
951
|
return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning), overrides);
|
|
685
952
|
}
|
|
686
953
|
function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
954
|
+
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
687
955
|
const output = [];
|
|
688
956
|
if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
|
|
689
957
|
summaryText: reasoning,
|
|
@@ -698,21 +966,22 @@ function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrid
|
|
|
698
966
|
query
|
|
699
967
|
}
|
|
700
968
|
});
|
|
701
|
-
for (const tc of toolCalls) output.push(
|
|
702
|
-
type: "function_call",
|
|
703
|
-
id: require_helpers.generateId("fc"),
|
|
704
|
-
call_id: tc.id || require_helpers.generateToolCallId(),
|
|
705
|
-
name: tc.name,
|
|
706
|
-
arguments: requireFixtureToolArguments(tc.arguments),
|
|
707
|
-
status: "completed"
|
|
708
|
-
});
|
|
969
|
+
for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
|
|
709
970
|
return buildResponseEnvelope(model, output, overrides);
|
|
710
971
|
}
|
|
711
972
|
function buildContentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize, reasoning, webSearches, overrides, blocks, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
973
|
+
const ordered = legacyOrderedBlocks(blocks);
|
|
974
|
+
return contentWithToolCallsStreamEvents(content, ordered && ordered.length > 0 ? [] : legacyFunctionCalls(toolCalls), model, chunkSize, reasoning, webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Internal content+tool-calls builder. `ordered` is already resolved (see
|
|
978
|
+
* {@link resolveServedBlockOutcome}); when non-empty it is streamed in array
|
|
979
|
+
* order, otherwise the legacy message-first path runs over `toolCalls`.
|
|
980
|
+
*/
|
|
981
|
+
function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize, reasoning, webSearches, overrides, ordered, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
712
982
|
const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
713
983
|
const orderedOutputItems = [];
|
|
714
|
-
if (
|
|
715
|
-
const ordered = require_helpers.resolveFixtureBlocks(blocks);
|
|
984
|
+
if (ordered && ordered.length > 0) {
|
|
716
985
|
let outputIndex = nextOutputIndex;
|
|
717
986
|
for (const block of ordered) {
|
|
718
987
|
if (block.type === "text") {
|
|
@@ -720,25 +989,21 @@ function buildContentWithToolCallsStreamEvents(content, toolCalls, model, chunkS
|
|
|
720
989
|
events.push(...msgEvents);
|
|
721
990
|
orderedOutputItems.push(msgItem);
|
|
722
991
|
} else {
|
|
723
|
-
const { events:
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
id: block.id
|
|
727
|
-
}, chunkSize, outputIndex);
|
|
728
|
-
events.push(...fcEvents);
|
|
729
|
-
orderedOutputItems.push(fcItem);
|
|
992
|
+
const { events: callEvents, item } = buildToolCallOutputEvents(block, chunkSize, outputIndex);
|
|
993
|
+
events.push(...callEvents);
|
|
994
|
+
orderedOutputItems.push(item);
|
|
730
995
|
}
|
|
731
996
|
outputIndex += 1;
|
|
732
997
|
}
|
|
733
998
|
} else {
|
|
999
|
+
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
734
1000
|
const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex);
|
|
735
1001
|
events.push(...msgEvents);
|
|
736
1002
|
orderedOutputItems.push(msgItem);
|
|
737
1003
|
for (let idx = 0; idx < toolCalls.length; idx++) {
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
orderedOutputItems.push(fcItem);
|
|
1004
|
+
const { events: callEvents, item } = buildToolCallOutputEvents(toolCalls[idx], chunkSize, nextOutputIndex + 1 + idx);
|
|
1005
|
+
events.push(...callEvents);
|
|
1006
|
+
orderedOutputItems.push(item);
|
|
742
1007
|
}
|
|
743
1008
|
}
|
|
744
1009
|
events.push({
|
|
@@ -755,15 +1020,10 @@ function buildContentWithToolCallsStreamEvents(content, toolCalls, model, chunkS
|
|
|
755
1020
|
});
|
|
756
1021
|
return events;
|
|
757
1022
|
}
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
call_id: tc.id || require_helpers.generateToolCallId(),
|
|
763
|
-
name: tc.name,
|
|
764
|
-
arguments: requireFixtureToolArguments(tc.arguments),
|
|
765
|
-
status: "completed"
|
|
766
|
-
};
|
|
1023
|
+
/** Non-streaming dispatcher: the completed function or custom tool call item. */
|
|
1024
|
+
function buildToolCallOutputItem(tc) {
|
|
1025
|
+
if (isCustomEmittable(tc)) return customToolCallItem(tc, customToolCallItemId(), tc.id || require_helpers.generateToolCallId(), requireFixtureToolInput(tc.input), "completed");
|
|
1026
|
+
return functionCallItem(tc, require_helpers.generateId("fc"), tc.id || require_helpers.generateToolCallId(), requireFixtureToolArguments(tc.arguments), "completed");
|
|
767
1027
|
}
|
|
768
1028
|
function buildMessageOutputItem(content) {
|
|
769
1029
|
return {
|
|
@@ -778,9 +1038,8 @@ function buildMessageOutputItem(content) {
|
|
|
778
1038
|
}]
|
|
779
1039
|
};
|
|
780
1040
|
}
|
|
781
|
-
function buildContentWithToolCallsResponse(content, toolCalls, model, reasoning, webSearches, overrides,
|
|
782
|
-
if (
|
|
783
|
-
const ordered = require_helpers.resolveFixtureBlocks(blocks);
|
|
1041
|
+
function buildContentWithToolCallsResponse(content, toolCalls, model, reasoning, webSearches, overrides, ordered, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
|
|
1042
|
+
if (ordered && ordered.length > 0) {
|
|
784
1043
|
const output = [];
|
|
785
1044
|
if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
|
|
786
1045
|
summaryText: reasoning,
|
|
@@ -796,15 +1055,12 @@ function buildContentWithToolCallsResponse(content, toolCalls, model, reasoning,
|
|
|
796
1055
|
}
|
|
797
1056
|
});
|
|
798
1057
|
for (const block of ordered) if (block.type === "text") output.push(buildMessageOutputItem(block.text));
|
|
799
|
-
else output.push(
|
|
800
|
-
name: block.name,
|
|
801
|
-
arguments: block.arguments,
|
|
802
|
-
id: block.id
|
|
803
|
-
}));
|
|
1058
|
+
else output.push(buildToolCallOutputItem(block));
|
|
804
1059
|
return buildResponseEnvelope(model, output, overrides);
|
|
805
1060
|
}
|
|
1061
|
+
require_helpers.assertResponsesToolCalls(toolCalls);
|
|
806
1062
|
const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
807
|
-
for (const tc of toolCalls) output.push(
|
|
1063
|
+
for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
|
|
808
1064
|
return buildResponseEnvelope(model, output, overrides);
|
|
809
1065
|
}
|
|
810
1066
|
async function writeResponsesSSEStream(res, events, optionsOrLatency) {
|
|
@@ -885,12 +1141,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
885
1141
|
break;
|
|
886
1142
|
}
|
|
887
1143
|
}
|
|
888
|
-
|
|
889
|
-
if (responsesReq.tools && responsesReq.tools.length !== 0) {
|
|
890
|
-
if (!Array.isArray(responsesReq.tools)) toolsError = "tools must be an array";
|
|
891
|
-
else if (responsesReq.tools.some((tool) => tool === null)) toolsError = "tools entries must not be null";
|
|
892
|
-
}
|
|
893
|
-
const validationError = inputError ?? toolsError;
|
|
1144
|
+
const validationError = inputError ?? validateResponsesTools(responsesReq.tools);
|
|
894
1145
|
if (validationError) {
|
|
895
1146
|
journal.add({
|
|
896
1147
|
method: req.method ?? "POST",
|
|
@@ -908,7 +1159,11 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
908
1159
|
} }));
|
|
909
1160
|
return;
|
|
910
1161
|
}
|
|
911
|
-
const
|
|
1162
|
+
const extendedTools = defaults.responsesTools === "extended";
|
|
1163
|
+
const completionReq = responsesToCompletionRequest(responsesReq, {
|
|
1164
|
+
extended: extendedTools,
|
|
1165
|
+
logger: defaults.logger
|
|
1166
|
+
});
|
|
912
1167
|
completionReq._endpointType = "chat";
|
|
913
1168
|
completionReq._context = require_helpers.getContext(req);
|
|
914
1169
|
const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);
|
|
@@ -984,27 +1239,116 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
984
1239
|
} }));
|
|
985
1240
|
return;
|
|
986
1241
|
}
|
|
987
|
-
const
|
|
1242
|
+
const resolvedResponse = await require_helpers.resolveResponse(fixture, completionReq);
|
|
1243
|
+
const response = extendedTools ? resolvedResponse : require_helpers.withoutLegacyNamespaces(resolvedResponse);
|
|
1244
|
+
const misbehavior = require_misbehavior.planMisbehavior({
|
|
1245
|
+
wire: "openai-responses",
|
|
1246
|
+
fixture,
|
|
1247
|
+
response,
|
|
1248
|
+
request: completionReq,
|
|
1249
|
+
stream: responsesReq.stream === true,
|
|
1250
|
+
emitsToolCallIds: true,
|
|
1251
|
+
defaults,
|
|
1252
|
+
rawHeaders: req.headers,
|
|
1253
|
+
url: req.url
|
|
1254
|
+
});
|
|
1255
|
+
if (misbehavior.kind === "error") {
|
|
1256
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1257
|
+
entry: journal.add({
|
|
1258
|
+
method: req.method ?? "POST",
|
|
1259
|
+
path: req.url ?? "/v1/responses",
|
|
1260
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1261
|
+
body: completionReq,
|
|
1262
|
+
response: {
|
|
1263
|
+
status: misbehavior.status,
|
|
1264
|
+
fixture
|
|
1265
|
+
}
|
|
1266
|
+
}),
|
|
1267
|
+
summary: misbehavior.summary,
|
|
1268
|
+
defaults,
|
|
1269
|
+
testId
|
|
1270
|
+
});
|
|
1271
|
+
if (!misbehavior.summary?.evaluations.some((evaluation) => evaluation.outcome === "error")) defaults.logger.error(misbehavior.message);
|
|
1272
|
+
require_sse_writer.writeErrorResponse(res, misbehavior.status, JSON.stringify({ error: {
|
|
1273
|
+
message: misbehavior.message,
|
|
1274
|
+
type: "invalid_request_error",
|
|
1275
|
+
code: misbehavior.code
|
|
1276
|
+
} }));
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
988
1279
|
const latency = fixture.latency ?? defaults.latency;
|
|
989
1280
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
990
1281
|
const fixtureTimings = fixture.recordedTimings;
|
|
991
1282
|
const effectiveReplaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
|
|
992
|
-
if (
|
|
993
|
-
const
|
|
994
|
-
|
|
1283
|
+
if (misbehavior.kind === "applied") {
|
|
1284
|
+
const prepared = prepareResponsesMisbehavior(misbehavior);
|
|
1285
|
+
const output = buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
|
|
1286
|
+
const entry = journal.add({
|
|
995
1287
|
method: req.method ?? "POST",
|
|
996
1288
|
path: req.url ?? "/v1/responses",
|
|
997
1289
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
998
1290
|
body: completionReq,
|
|
999
1291
|
response: {
|
|
1000
|
-
status,
|
|
1292
|
+
status: 200,
|
|
1001
1293
|
fixture
|
|
1002
1294
|
}
|
|
1003
1295
|
});
|
|
1296
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1297
|
+
entry,
|
|
1298
|
+
summary: prepared.summary,
|
|
1299
|
+
defaults,
|
|
1300
|
+
testId
|
|
1301
|
+
});
|
|
1302
|
+
if (responsesReq.stream !== true) {
|
|
1303
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1304
|
+
res.end(JSON.stringify(output.response));
|
|
1305
|
+
} else {
|
|
1306
|
+
const appliedTimings = prepared.duplicateId && fixtureTimings ? {
|
|
1307
|
+
...fixtureTimings,
|
|
1308
|
+
interChunkDelaysMs: [...fixtureTimings.interChunkDelaysMs]
|
|
1309
|
+
} : fixtureTimings;
|
|
1310
|
+
if (prepared.duplicateId && appliedTimings) {
|
|
1311
|
+
const lastGap = appliedTimings.interChunkDelaysMs.at(-1) ?? 0;
|
|
1312
|
+
while (appliedTimings.interChunkDelaysMs.length < output.events.length - 1) appliedTimings.interChunkDelaysMs.push(lastGap);
|
|
1313
|
+
}
|
|
1314
|
+
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
1315
|
+
if (!await writeResponsesSSEStream(res, output.events, {
|
|
1316
|
+
latency,
|
|
1317
|
+
streamingProfile: fixture.streamingProfile,
|
|
1318
|
+
recordedTimings: appliedTimings,
|
|
1319
|
+
replaySpeed: effectiveReplaySpeed,
|
|
1320
|
+
signal: interruption?.signal,
|
|
1321
|
+
onChunkSent: interruption?.tick
|
|
1322
|
+
})) {
|
|
1323
|
+
if (!res.writableEnded) res.destroy();
|
|
1324
|
+
entry.response.interrupted = true;
|
|
1325
|
+
entry.response.interruptReason = interruption?.reason();
|
|
1326
|
+
}
|
|
1327
|
+
interruption?.cleanup();
|
|
1328
|
+
}
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
if (require_helpers.isErrorResponse(response)) {
|
|
1332
|
+
const status = response.status ?? 500;
|
|
1333
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1334
|
+
entry: journal.add({
|
|
1335
|
+
method: req.method ?? "POST",
|
|
1336
|
+
path: req.url ?? "/v1/responses",
|
|
1337
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1338
|
+
body: completionReq,
|
|
1339
|
+
response: {
|
|
1340
|
+
status,
|
|
1341
|
+
fixture
|
|
1342
|
+
}
|
|
1343
|
+
}),
|
|
1344
|
+
summary: misbehavior.summary,
|
|
1345
|
+
defaults,
|
|
1346
|
+
testId
|
|
1347
|
+
});
|
|
1004
1348
|
require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
|
|
1005
1349
|
return;
|
|
1006
1350
|
}
|
|
1007
|
-
if (require_helpers.
|
|
1351
|
+
if (require_helpers.isCombinedFixtureResponse(response)) {
|
|
1008
1352
|
const overrides = require_helpers.extractOverrides(response);
|
|
1009
1353
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
1010
1354
|
const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
|
|
@@ -1018,12 +1362,21 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1018
1362
|
fixture
|
|
1019
1363
|
}
|
|
1020
1364
|
});
|
|
1365
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1366
|
+
entry: journalEntry,
|
|
1367
|
+
summary: misbehavior.summary,
|
|
1368
|
+
defaults,
|
|
1369
|
+
testId
|
|
1370
|
+
});
|
|
1371
|
+
const ordered = require_helpers.resolveServedBlockOutcome(response)?.ordered;
|
|
1372
|
+
if (!ordered) require_helpers.assertCustomToolCalls(response);
|
|
1373
|
+
const toolCalls = require_helpers.servedToolCalls(response);
|
|
1021
1374
|
if (responsesReq.stream !== true) {
|
|
1022
|
-
const body = buildContentWithToolCallsResponse(response.content ?? "",
|
|
1375
|
+
const body = buildContentWithToolCallsResponse(response.content ?? "", toolCalls, completionReq.model, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1023
1376
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1024
1377
|
res.end(JSON.stringify(body));
|
|
1025
1378
|
} else {
|
|
1026
|
-
const events =
|
|
1379
|
+
const events = contentWithToolCallsStreamEvents(response.content ?? "", toolCalls, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1027
1380
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
1028
1381
|
if (!await writeResponsesSSEStream(res, events, {
|
|
1029
1382
|
latency,
|
|
@@ -1055,6 +1408,12 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1055
1408
|
fixture
|
|
1056
1409
|
}
|
|
1057
1410
|
});
|
|
1411
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1412
|
+
entry: journalEntry,
|
|
1413
|
+
summary: misbehavior.summary,
|
|
1414
|
+
defaults,
|
|
1415
|
+
testId
|
|
1416
|
+
});
|
|
1058
1417
|
if (responsesReq.stream !== true) {
|
|
1059
1418
|
const body = buildTextResponse(response.content, completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1060
1419
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -1092,12 +1451,19 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1092
1451
|
fixture
|
|
1093
1452
|
}
|
|
1094
1453
|
});
|
|
1454
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1455
|
+
entry: journalEntry,
|
|
1456
|
+
summary: misbehavior.summary,
|
|
1457
|
+
defaults,
|
|
1458
|
+
testId
|
|
1459
|
+
});
|
|
1460
|
+
require_helpers.assertCustomToolCalls(response);
|
|
1095
1461
|
if (responsesReq.stream !== true) {
|
|
1096
|
-
const body = buildToolCallResponse(response
|
|
1462
|
+
const body = buildToolCallResponse(require_helpers.servedToolCalls(response), completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1097
1463
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1098
1464
|
res.end(JSON.stringify(body));
|
|
1099
1465
|
} else {
|
|
1100
|
-
const events =
|
|
1466
|
+
const events = toolCallStreamEvents(require_helpers.servedToolCalls(response), completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
1101
1467
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
1102
1468
|
if (!await writeResponsesSSEStream(res, events, {
|
|
1103
1469
|
latency,
|
|
@@ -1115,29 +1481,159 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
|
|
|
1115
1481
|
}
|
|
1116
1482
|
return;
|
|
1117
1483
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1484
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
1485
|
+
entry: journal.add({
|
|
1486
|
+
method: req.method ?? "POST",
|
|
1487
|
+
path: req.url ?? "/v1/responses",
|
|
1488
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
1489
|
+
body: completionReq,
|
|
1490
|
+
response: {
|
|
1491
|
+
status: 500,
|
|
1492
|
+
fixture
|
|
1493
|
+
}
|
|
1494
|
+
}),
|
|
1495
|
+
summary: misbehavior.summary,
|
|
1496
|
+
defaults,
|
|
1497
|
+
testId
|
|
1127
1498
|
});
|
|
1128
1499
|
require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
|
|
1129
1500
|
message: "Fixture response did not match any known type",
|
|
1130
1501
|
type: "server_error"
|
|
1131
1502
|
} }));
|
|
1132
1503
|
}
|
|
1504
|
+
/** Prepare call IDs once, retaining the engine's effective blocks and exact served-call metadata. */
|
|
1505
|
+
function prepareResponsesMisbehavior(plan) {
|
|
1506
|
+
return require_helpers.prepareOpenAIChatMisbehavior(plan);
|
|
1507
|
+
}
|
|
1508
|
+
/** Build one faulted payload/event sequence for both HTTP and Responses WebSocket consumers. */
|
|
1509
|
+
function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReasoning = false) {
|
|
1510
|
+
const response = plan.response;
|
|
1511
|
+
if (!(require_helpers.isCombinedFixtureResponse(response) || require_helpers.isTextResponse(response) || require_helpers.isToolCallResponse(response))) throw new Error("Responses misbehavior requires a prepared chat response");
|
|
1512
|
+
const overrides = require_helpers.extractOverrides(response);
|
|
1513
|
+
const reasoning = plan.reasoning ?? ("reasoning" in response ? response.reasoning : void 0);
|
|
1514
|
+
const webSearches = "webSearches" in response ? response.webSearches : void 0;
|
|
1515
|
+
const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(request.model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, false, plan.reasoning !== void 0);
|
|
1516
|
+
const output = [...prefixOutputItems];
|
|
1517
|
+
let outputIndex = nextOutputIndex;
|
|
1518
|
+
const incomplete = plan.stop === "length" || plan.stop === "content_filter";
|
|
1519
|
+
if (plan.refusal !== void 0) {
|
|
1520
|
+
const id = itemId();
|
|
1521
|
+
const part = {
|
|
1522
|
+
type: "refusal",
|
|
1523
|
+
refusal: plan.refusal
|
|
1524
|
+
};
|
|
1525
|
+
const item = {
|
|
1526
|
+
id,
|
|
1527
|
+
type: "message",
|
|
1528
|
+
role: "assistant",
|
|
1529
|
+
status: "completed",
|
|
1530
|
+
content: [part]
|
|
1531
|
+
};
|
|
1532
|
+
const position = {
|
|
1533
|
+
item_id: id,
|
|
1534
|
+
output_index: outputIndex,
|
|
1535
|
+
content_index: 0
|
|
1536
|
+
};
|
|
1537
|
+
events.push({
|
|
1538
|
+
type: "response.output_item.added",
|
|
1539
|
+
output_index: outputIndex,
|
|
1540
|
+
item: {
|
|
1541
|
+
...item,
|
|
1542
|
+
status: "in_progress",
|
|
1543
|
+
content: []
|
|
1544
|
+
}
|
|
1545
|
+
}, {
|
|
1546
|
+
type: "response.content_part.added",
|
|
1547
|
+
...position,
|
|
1548
|
+
part: {
|
|
1549
|
+
type: "refusal",
|
|
1550
|
+
refusal: ""
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
for (let i = 0; i < plan.refusal.length; i += chunkSize) events.push({
|
|
1554
|
+
type: "response.refusal.delta",
|
|
1555
|
+
...position,
|
|
1556
|
+
delta: plan.refusal.slice(i, i + chunkSize)
|
|
1557
|
+
});
|
|
1558
|
+
events.push({
|
|
1559
|
+
type: "response.refusal.done",
|
|
1560
|
+
...position,
|
|
1561
|
+
refusal: plan.refusal
|
|
1562
|
+
}, {
|
|
1563
|
+
type: "response.content_part.done",
|
|
1564
|
+
...position,
|
|
1565
|
+
part
|
|
1566
|
+
}, {
|
|
1567
|
+
type: "response.output_item.done",
|
|
1568
|
+
output_index: outputIndex,
|
|
1569
|
+
item
|
|
1570
|
+
});
|
|
1571
|
+
output.push(item);
|
|
1572
|
+
} else if (plan.summary.fault !== "empty-response" && plan.stop !== "content_filter" && plan.reasoning === void 0) {
|
|
1573
|
+
const combined = require_helpers.isCombinedFixtureResponse(response);
|
|
1574
|
+
const blocks = (combined ? require_helpers.resolveServedBlockOutcome(response) : void 0)?.ordered ?? [..."content" in response && typeof response.content === "string" ? [{
|
|
1575
|
+
type: "text",
|
|
1576
|
+
text: response.content
|
|
1577
|
+
}] : [], ...combined || require_helpers.isToolCallResponse(response) ? require_helpers.servedToolCalls(response).map(require_helpers.toolCallFixtureBlock) : []];
|
|
1578
|
+
let callIndex = 0;
|
|
1579
|
+
for (const block of blocks) {
|
|
1580
|
+
if (block.type === "text") {
|
|
1581
|
+
const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex);
|
|
1582
|
+
events.push(...message.events);
|
|
1583
|
+
output.push(message.msgItem);
|
|
1584
|
+
} else {
|
|
1585
|
+
const call = block.type === "customToolCall" ? buildCustomToolCallOutputEvents(block, chunkSize, outputIndex) : buildFunctionCallOutputEvents(block, chunkSize, outputIndex);
|
|
1586
|
+
const cut = plan.summary.fault === "stop-length-mid-tool" && callIndex === plan.target?.index;
|
|
1587
|
+
const item = cut ? {
|
|
1588
|
+
...call.item,
|
|
1589
|
+
status: "incomplete"
|
|
1590
|
+
} : call.item;
|
|
1591
|
+
events.push(...call.events.map((event) => cut && event.type === "response.output_item.done" ? {
|
|
1592
|
+
...event,
|
|
1593
|
+
item
|
|
1594
|
+
} : event));
|
|
1595
|
+
output.push(item);
|
|
1596
|
+
callIndex++;
|
|
1597
|
+
}
|
|
1598
|
+
outputIndex++;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
const usage = require_helpers.resolveOpenAIChatMisbehaviorUsage(plan, request);
|
|
1602
|
+
const body = {
|
|
1603
|
+
id: respId,
|
|
1604
|
+
object: "response",
|
|
1605
|
+
created_at: created,
|
|
1606
|
+
model: overrides?.model ?? request.model,
|
|
1607
|
+
status: incomplete ? "incomplete" : responsesStatus(overrides?.finishReason, "completed"),
|
|
1608
|
+
...incomplete ? { incomplete_details: { reason: plan.stop === "content_filter" ? "content_filter" : "max_output_tokens" } } : {},
|
|
1609
|
+
output,
|
|
1610
|
+
usage: {
|
|
1611
|
+
input_tokens: usage.prompt_tokens,
|
|
1612
|
+
output_tokens: usage.completion_tokens,
|
|
1613
|
+
total_tokens: usage.total_tokens
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
events.push({
|
|
1617
|
+
type: incomplete ? "response.incomplete" : "response.completed",
|
|
1618
|
+
response: body
|
|
1619
|
+
});
|
|
1620
|
+
return {
|
|
1621
|
+
response: body,
|
|
1622
|
+
events
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1133
1625
|
|
|
1134
1626
|
//#endregion
|
|
1135
1627
|
exports.buildContentWithToolCallsStreamEvents = buildContentWithToolCallsStreamEvents;
|
|
1628
|
+
exports.buildResponsesMisbehavior = buildResponsesMisbehavior;
|
|
1136
1629
|
exports.buildTextResponse = buildTextResponse;
|
|
1137
1630
|
exports.buildTextStreamEvents = buildTextStreamEvents;
|
|
1138
1631
|
exports.buildToolCallStreamEvents = buildToolCallStreamEvents;
|
|
1632
|
+
exports.contentWithToolCallsStreamEvents = contentWithToolCallsStreamEvents;
|
|
1139
1633
|
exports.handleResponses = handleResponses;
|
|
1634
|
+
exports.prepareResponsesMisbehavior = prepareResponsesMisbehavior;
|
|
1140
1635
|
exports.requestIncludesEncryptedReasoning = requestIncludesEncryptedReasoning;
|
|
1141
1636
|
exports.requestWantsEncryptedReasoning = requestWantsEncryptedReasoning;
|
|
1142
1637
|
exports.responsesToCompletionRequest = responsesToCompletionRequest;
|
|
1638
|
+
exports.toolCallStreamEvents = toolCallStreamEvents;
|
|
1143
1639
|
//# sourceMappingURL=responses.cjs.map
|