@copilotkit/aimock 1.43.0 → 1.45.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 +46 -0
- package/README.md +14 -2
- package/dist/agui-handler.d.ts.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 +344 -43
- 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 +345 -44
- 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 +9 -5
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +10 -7
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +238 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +242 -13
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +159 -72
- 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 +160 -73
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +63 -5
- 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 +63 -5
- package/dist/config-loader.js.map +1 -1
- package/dist/constants.cjs +8 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +6 -2
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/echo-text.cjs +65 -0
- package/dist/echo-text.cjs.map +1 -0
- package/dist/echo-text.d.cts +22 -0
- package/dist/echo-text.d.cts.map +1 -0
- package/dist/echo-text.d.ts +22 -0
- package/dist/echo-text.d.ts.map +1 -0
- package/dist/echo-text.js +65 -0
- package/dist/echo-text.js.map +1 -0
- package/dist/embeddings.cjs +18 -1
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +18 -1
- package/dist/embeddings.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/files.cjs +9 -11
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +10 -13
- package/dist/files.js.map +1 -1
- package/dist/fine-tuning.cjs +1 -19
- package/dist/fine-tuning.cjs.map +1 -1
- package/dist/fine-tuning.d.cts.map +1 -1
- package/dist/fine-tuning.d.ts.map +1 -1
- package/dist/fine-tuning.js +2 -20
- package/dist/fine-tuning.js.map +1 -1
- package/dist/fixture-loader-services.cjs +116 -0
- package/dist/fixture-loader-services.cjs.map +1 -0
- package/dist/fixture-loader-services.d.cts +48 -0
- package/dist/fixture-loader-services.d.cts.map +1 -0
- package/dist/fixture-loader-services.d.ts +48 -0
- package/dist/fixture-loader-services.d.ts.map +1 -0
- package/dist/fixture-loader-services.js +114 -0
- package/dist/fixture-loader-services.js.map +1 -0
- package/dist/fixture-loader.cjs +552 -29
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +140 -2
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +140 -2
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +542 -31
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +42 -2
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +43 -3
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +288 -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 +289 -53
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +234 -38
- 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 +235 -39
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +1157 -12
- 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 +1113 -14
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +3 -2
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +3 -2
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +39 -1
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +11 -4
- package/dist/jest.cjs +43 -14
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +47 -5
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +47 -5
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +45 -16
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +155 -13
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +54 -4
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +54 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +155 -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 +204 -39
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +51 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +51 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +206 -41
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +217 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -0
- package/dist/mcp-fakes-mount.js +211 -0
- package/dist/mcp-fakes-mount.js.map +1 -0
- 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 +1942 -0
- package/dist/mcp-fakes.cjs.map +1 -0
- package/dist/mcp-fakes.d.cts +504 -0
- package/dist/mcp-fakes.d.cts.map +1 -0
- package/dist/mcp-fakes.d.ts +504 -0
- package/dist/mcp-fakes.d.ts.map +1 -0
- package/dist/mcp-fakes.js +1937 -0
- package/dist/mcp-fakes.js.map +1 -0
- package/dist/mcp-handler.cjs +534 -27
- 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 +530 -28
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +326 -47
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +97 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +97 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +327 -48
- 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/mcp-stub.cjs +5 -1
- package/dist/mcp-stub.d.cts +3 -1
- package/dist/mcp-stub.d.ts +3 -1
- package/dist/mcp-stub.js +3 -1
- package/dist/mcp-types.d.cts +6 -0
- package/dist/mcp-types.d.ts +6 -0
- package/dist/message-text.cjs +192 -0
- package/dist/message-text.cjs.map +1 -0
- package/dist/message-text.d.cts +31 -0
- package/dist/message-text.d.cts.map +1 -0
- package/dist/message-text.d.ts +31 -0
- package/dist/message-text.d.ts.map +1 -0
- package/dist/message-text.js +184 -0
- package/dist/message-text.js.map +1 -0
- package/dist/messages.cjs +400 -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 +401 -58
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +46 -66
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +14 -6
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +14 -6
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +32 -40
- package/dist/metrics.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 +315 -113
- 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 +316 -114
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +3405 -0
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +30 -0
- package/dist/openapi.d.cts.map +1 -0
- package/dist/openapi.d.ts +30 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +3404 -0
- package/dist/openapi.js.map +1 -0
- package/dist/openrouter-video.cjs +4 -4
- 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 +4 -4
- 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 +115 -6
- 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 +116 -8
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +701 -166
- 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 +699 -169
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +951 -0
- package/dist/route-registry.cjs.map +1 -0
- package/dist/route-registry.d.cts +33 -0
- package/dist/route-registry.d.cts.map +1 -0
- package/dist/route-registry.d.ts +33 -0
- package/dist/route-registry.d.ts.map +1 -0
- package/dist/route-registry.js +866 -0
- package/dist/route-registry.js.map +1 -0
- 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 +1159 -264
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +4 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1067 -172
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +4 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +4 -2
- package/dist/speech.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/suite.cjs +1 -1
- package/dist/suite.js +1 -1
- package/dist/transcription.cjs +2 -2
- 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 +2 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +516 -5
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +516 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-cli.cjs +123 -33
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +125 -35
- package/dist/validate-cli.js.map +1 -1
- package/dist/vector-stores.cjs +1180 -0
- package/dist/vector-stores.cjs.map +1 -0
- package/dist/vector-stores.d.cts +106 -0
- package/dist/vector-stores.d.cts.map +1 -0
- package/dist/vector-stores.d.ts +106 -0
- package/dist/vector-stores.d.ts.map +1 -0
- package/dist/vector-stores.js +1164 -0
- package/dist/vector-stores.js.map +1 -0
- package/dist/vector-types.d.cts.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/video.cjs +19 -0
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +19 -0
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +77 -16
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +38 -5
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +38 -5
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +78 -17
- 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 +121 -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 +123 -18
- package/dist/ws-responses.js.map +1 -1
- package/package.json +13 -2
- package/skills/write-fixtures/SKILL.md +179 -21
package/dist/ws-responses.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_constants = require('./constants.cjs');
|
|
2
2
|
const require_helpers = require('./helpers.cjs');
|
|
3
3
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
4
|
+
const require_misbehavior = require('./misbehavior.cjs');
|
|
4
5
|
const require_router = require('./router.cjs');
|
|
5
6
|
require('./journal.cjs');
|
|
6
7
|
const require_interruption = require('./interruption.cjs');
|
|
@@ -24,15 +25,21 @@ function handleWebSocketResponses(ws, fixtures, journal, defaults) {
|
|
|
24
25
|
const { logger } = defaults;
|
|
25
26
|
let pending = Promise.resolve();
|
|
26
27
|
ws.on("message", (raw) => {
|
|
27
|
-
pending = pending.then(
|
|
28
|
-
const msg = err instanceof Error ? err.message : "Internal error";
|
|
29
|
-
logger.error(`WebSocket responses error: ${msg}`);
|
|
28
|
+
pending = pending.then(async () => {
|
|
30
29
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
defaults.beforeProcessMessage?.();
|
|
31
|
+
await processMessage(raw, ws, fixtures, journal, defaults);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
34
|
+
logger.error(`WebSocket responses error: ${msg}`);
|
|
35
|
+
try {
|
|
36
|
+
const code = require_helpers.isFixtureToolCallError(err) ? require_helpers.fixtureToolCallErrorCode(err) : void 0;
|
|
37
|
+
ws.send(JSON.stringify(buildErrorEvent(msg, "server_error", code)));
|
|
38
|
+
} catch (sendErr) {
|
|
39
|
+
defaults.logger.debug(`Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : "unknown"}`);
|
|
40
|
+
}
|
|
34
41
|
}
|
|
35
|
-
})
|
|
42
|
+
});
|
|
36
43
|
});
|
|
37
44
|
}
|
|
38
45
|
async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
@@ -62,7 +69,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
62
69
|
};
|
|
63
70
|
const emitEncryptedReasoning = require_responses.requestWantsEncryptedReasoning(responsesReq);
|
|
64
71
|
const synthesizeSummarylessReasoning = require_responses.requestIncludesEncryptedReasoning(responsesReq);
|
|
65
|
-
const
|
|
72
|
+
const extendedTools = defaults.responsesTools === "extended";
|
|
73
|
+
const completionReq = require_responses.responsesToCompletionRequest(responsesReq, {
|
|
74
|
+
extended: extendedTools,
|
|
75
|
+
logger: defaults.logger
|
|
76
|
+
});
|
|
66
77
|
completionReq._endpointType = "chat";
|
|
67
78
|
const contextHeader = defaults.upgradeHeaders?.["x-aimock-context"];
|
|
68
79
|
completionReq._context = typeof contextHeader === "string" ? contextHeader : Array.isArray(contextHeader) && contextHeader.length > 0 ? contextHeader[0] : void 0;
|
|
@@ -101,13 +112,87 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
101
112
|
ws.send(JSON.stringify(buildErrorEvent("No fixture matched", "invalid_request_error", "no_fixture_match")));
|
|
102
113
|
return;
|
|
103
114
|
}
|
|
104
|
-
const
|
|
115
|
+
const resolvedResponse = await require_helpers.resolveResponse(fixture, completionReq);
|
|
116
|
+
const response = extendedTools ? resolvedResponse : require_helpers.withoutLegacyNamespaces(resolvedResponse);
|
|
117
|
+
const misbehaviorDefaults = {
|
|
118
|
+
...defaults,
|
|
119
|
+
replaySpeed: defaults.replaySpeed ?? 1
|
|
120
|
+
};
|
|
121
|
+
const misbehavior = require_misbehavior.planMisbehavior({
|
|
122
|
+
wire: "openai-responses",
|
|
123
|
+
fixture,
|
|
124
|
+
response,
|
|
125
|
+
request: completionReq,
|
|
126
|
+
stream: true,
|
|
127
|
+
emitsToolCallIds: true,
|
|
128
|
+
defaults: misbehaviorDefaults,
|
|
129
|
+
rawHeaders: {
|
|
130
|
+
...defaults.upgradeHeaders,
|
|
131
|
+
"x-test-id": testId
|
|
132
|
+
},
|
|
133
|
+
url: "/v1/responses"
|
|
134
|
+
});
|
|
135
|
+
const recordOutcome = (entry) => require_misbehavior.recordMisbehaviorOutcome({
|
|
136
|
+
entry,
|
|
137
|
+
summary: misbehavior.summary,
|
|
138
|
+
defaults: misbehaviorDefaults,
|
|
139
|
+
testId
|
|
140
|
+
});
|
|
141
|
+
if (misbehavior.kind === "error") {
|
|
142
|
+
recordOutcome(journal.add({
|
|
143
|
+
method: "WS",
|
|
144
|
+
path: "/v1/responses",
|
|
145
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
146
|
+
body: completionReq,
|
|
147
|
+
response: {
|
|
148
|
+
status: misbehavior.status,
|
|
149
|
+
fixture
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
if (!misbehavior.summary?.evaluations.length) defaults.logger.error(`[misbehavior] ${misbehavior.code}: ${misbehavior.message} (testId=${testId})`);
|
|
153
|
+
ws.send(JSON.stringify(buildErrorEvent(misbehavior.message, "invalid_request_error", misbehavior.code)));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
105
156
|
const latency = fixture.latency ?? defaults.latency;
|
|
106
157
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
158
|
+
if (misbehavior.kind === "applied") {
|
|
159
|
+
const prepared = require_responses.prepareResponsesMisbehavior(misbehavior);
|
|
160
|
+
const output = require_responses.buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
|
|
161
|
+
const entry = journal.add({
|
|
162
|
+
method: "WS",
|
|
163
|
+
path: "/v1/responses",
|
|
164
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
165
|
+
body: completionReq,
|
|
166
|
+
response: {
|
|
167
|
+
status: 200,
|
|
168
|
+
fixture
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
require_misbehavior.recordMisbehaviorOutcome({
|
|
172
|
+
entry,
|
|
173
|
+
summary: prepared.summary,
|
|
174
|
+
defaults: misbehaviorDefaults,
|
|
175
|
+
testId
|
|
176
|
+
});
|
|
177
|
+
const timings = fixture.recordedTimings;
|
|
178
|
+
const gaps = timings?.interChunkDelaysMs;
|
|
179
|
+
const appliedTimings = prepared.duplicateId && timings && gaps ? {
|
|
180
|
+
...timings,
|
|
181
|
+
interChunkDelaysMs: Array.from({ length: Math.max(gaps.length, output.events.length - 1) }, (_, index) => gaps[index] ?? gaps.at(-1) ?? 0)
|
|
182
|
+
} : timings;
|
|
183
|
+
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
184
|
+
if (!await sendEvents(ws, output.events, latency, interruption?.signal, interruption?.tick, appliedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
185
|
+
ws.destroy();
|
|
186
|
+
entry.response.interrupted = true;
|
|
187
|
+
entry.response.interruptReason = interruption?.reason();
|
|
188
|
+
}
|
|
189
|
+
interruption?.cleanup();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
107
192
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders);
|
|
108
193
|
if (require_helpers.isErrorResponse(response)) {
|
|
109
194
|
const status = response.status ?? 500;
|
|
110
|
-
journal.add({
|
|
195
|
+
recordOutcome(journal.add({
|
|
111
196
|
method: "WS",
|
|
112
197
|
path: "/v1/responses",
|
|
113
198
|
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
@@ -116,11 +201,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
116
201
|
status,
|
|
117
202
|
fixture
|
|
118
203
|
}
|
|
119
|
-
});
|
|
204
|
+
}));
|
|
120
205
|
ws.send(JSON.stringify(buildErrorEvent(response.error.message, response.error.type, response.error.code)));
|
|
121
206
|
return;
|
|
122
207
|
}
|
|
123
|
-
if (require_helpers.
|
|
208
|
+
if (require_helpers.isCombinedFixtureResponse(response)) {
|
|
124
209
|
const journalEntry = journal.add({
|
|
125
210
|
method: "WS",
|
|
126
211
|
path: "/v1/responses",
|
|
@@ -131,7 +216,17 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
131
216
|
fixture
|
|
132
217
|
}
|
|
133
218
|
});
|
|
134
|
-
|
|
219
|
+
recordOutcome(journalEntry);
|
|
220
|
+
let events;
|
|
221
|
+
try {
|
|
222
|
+
const ordered = require_helpers.resolveServedBlockOutcome(response)?.ordered;
|
|
223
|
+
if (!ordered) require_helpers.assertCustomToolCalls(response);
|
|
224
|
+
events = require_responses.contentWithToolCallsStreamEvents(response.content ?? "", require_helpers.servedToolCalls(response), completionReq.model, chunkSize, require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, require_helpers.extractOverrides(response), ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
journalEntry.response.status = 500;
|
|
227
|
+
if (require_helpers.isFixtureToolCallError(error)) journalEntry.response.error = error.message;
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
135
230
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
136
231
|
if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
137
232
|
ws.destroy();
|
|
@@ -152,6 +247,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
152
247
|
fixture
|
|
153
248
|
}
|
|
154
249
|
});
|
|
250
|
+
recordOutcome(journalEntry);
|
|
155
251
|
const events = require_responses.buildTextStreamEvents(response.content, completionReq.model, chunkSize, require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, require_helpers.extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
156
252
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
157
253
|
if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
@@ -173,7 +269,16 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
173
269
|
fixture
|
|
174
270
|
}
|
|
175
271
|
});
|
|
176
|
-
|
|
272
|
+
recordOutcome(journalEntry);
|
|
273
|
+
let events;
|
|
274
|
+
try {
|
|
275
|
+
require_helpers.assertCustomToolCalls(response);
|
|
276
|
+
events = require_responses.toolCallStreamEvents(require_helpers.servedToolCalls(response), completionReq.model, chunkSize, require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, require_helpers.extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
journalEntry.response.status = 500;
|
|
279
|
+
if (require_helpers.isFixtureToolCallError(error)) journalEntry.response.error = error.message;
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
177
282
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
178
283
|
if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
179
284
|
ws.destroy();
|
|
@@ -183,7 +288,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
183
288
|
interruption?.cleanup();
|
|
184
289
|
return;
|
|
185
290
|
}
|
|
186
|
-
journal.add({
|
|
291
|
+
recordOutcome(journal.add({
|
|
187
292
|
method: "WS",
|
|
188
293
|
path: "/v1/responses",
|
|
189
294
|
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
@@ -192,7 +297,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
192
297
|
status: 500,
|
|
193
298
|
fixture
|
|
194
299
|
}
|
|
195
|
-
});
|
|
300
|
+
}));
|
|
196
301
|
ws.send(JSON.stringify(buildErrorEvent("Fixture response did not match any known type", "server_error")));
|
|
197
302
|
}
|
|
198
303
|
async function sendEvents(ws, events, latency, signal, onChunkSent, recordedTimings, replaySpeed) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-responses.cjs","names":["requestWantsEncryptedReasoning","requestIncludesEncryptedReasoning","responsesToCompletionRequest","DEFAULT_TEST_ID","matchFixtureDiagnostic","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","flattenHeaders","strictOverrideField","resolveResponse","isErrorResponse","isContentWithToolCallsResponse","buildContentWithToolCallsStreamEvents","resolveReasoningForModel","extractOverrides","createInterruptionSignal","isTextResponse","buildTextStreamEvents","isToolCallResponse","buildToolCallStreamEvents","calculateDelay","delay"],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { ChatCompletionRequest, Fixture } from \"./types.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n requestIncludesEncryptedReasoning,\n requestWantsEncryptedReasoning,\n buildTextStreamEvents,\n buildToolCallStreamEvents,\n buildContentWithToolCallsStreamEvents,\n type ResponsesSSEEvent,\n} from \"./responses.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n extractOverrides,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n strictOverrideField,\n flattenHeaders,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(() =>\n processMessage(raw, ws, fixtures, journal, defaults).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\")));\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }),\n );\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: {\n latency: number;\n chunkSize: number;\n replaySpeed?: number;\n model: string;\n logger: Logger;\n strict?: boolean;\n requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n },\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n ws.send(\n JSON.stringify(\n buildErrorEvent(`Malformed JSON: ${detail}`, \"invalid_request_error\", \"invalid_json\"),\n ),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n input: (parsed.input ?? []) as {\n role?: string;\n type?: string;\n content?: string | { type: string; text?: string }[];\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n id?: string;\n }[],\n instructions: parsed.instructions,\n tools: parsed.tools as\n | {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n strict?: boolean;\n }[]\n | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n include: (parsed as { include?: string[] }).include,\n store: (parsed as { store?: boolean }).store,\n };\n\n // Gate encrypted-reasoning emission identically to the HTTP transport so the\n // agent-framework#7233 stateless-replay path works over WebSocket too.\n const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);\n // Synthesizing a reasoning item the fixture never declared takes the NARROWER\n // explicit-`include` gate, transport-identically to HTTP — a `store: false`\n // request with a summary-less fixture still gets no reasoning item.\n const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);\n\n const completionReq = responsesToCompletionRequest(responsesReq);\n completionReq._endpointType = \"chat\";\n const contextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n completionReq._context =\n typeof contextHeader === \"string\"\n ? contextHeader\n : Array.isArray(contextHeader) && contextHeader.length > 0\n ? contextHeader[0]\n : undefined;\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/responses\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // The WS path has no per-request `req.headers`; strict is resolved from the\n // connection's upgrade headers (see the `!fixture` branch above). Used below\n // to gate the synthesized reasoning channel on the requested model's capability.\n const effectiveStrict = resolveStrictMode(defaults.strict, defaults.upgradeHeaders);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildContentWithToolCallsStreamEvents(\n response.content ?? \"\",\n response.toolCalls ?? [],\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n response.blocks,\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const events = buildToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n // Gate the synthesized reasoning channel on the requested model's\n // capability, matching the WS text / content+tool branches and the HTTP\n // tool-only path so reasoning emission is transport-independent.\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n recordedTimings?: import(\"./types.js\").RecordedTimings,\n replaySpeed?: number,\n): Promise<boolean> {\n let eventIndex = 0;\n for (const event of events) {\n if (ws.isClosed) return false;\n const chunkDelay = calculateDelay(eventIndex, undefined, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return false;\n ws.send(JSON.stringify(event));\n eventIndex++;\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ws-responses.cjs","names":["isFixtureToolCallError","fixtureToolCallErrorCode","requestWantsEncryptedReasoning","requestIncludesEncryptedReasoning","responsesToCompletionRequest","DEFAULT_TEST_ID","matchFixtureDiagnostic","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","flattenHeaders","strictOverrideField","resolveResponse","withoutLegacyNamespaces","planMisbehavior","recordMisbehaviorOutcome","prepareResponsesMisbehavior","buildResponsesMisbehavior","createInterruptionSignal","isErrorResponse","isCombinedFixtureResponse","resolveServedBlockOutcome","contentWithToolCallsStreamEvents","servedToolCalls","resolveReasoningForModel","extractOverrides","isTextResponse","buildTextStreamEvents","isToolCallResponse","toolCallStreamEvents","calculateDelay","delay"],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { Fixture, HandlerDefaults } from \"./types.js\";\nimport { planMisbehavior, recordMisbehaviorOutcome } from \"./misbehavior.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n requestIncludesEncryptedReasoning,\n requestWantsEncryptedReasoning,\n buildTextStreamEvents,\n toolCallStreamEvents,\n contentWithToolCallsStreamEvents,\n prepareResponsesMisbehavior,\n buildResponsesMisbehavior,\n type ResponsesSSEEvent,\n type ResponsesInputItem,\n type ResponsesToolDef,\n} from \"./responses.js\";\nimport {\n fixtureToolCallErrorCode,\n isFixtureToolCallError,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n extractOverrides,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n strictOverrideField,\n flattenHeaders,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n assertCustomToolCalls,\n resolveServedBlockOutcome,\n servedToolCalls,\n withoutLegacyNamespaces,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ntype ResponsesWebSocketDefaults = Omit<HandlerDefaults, \"replaySpeed\"> & {\n replaySpeed?: number;\n model: string;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n /** @internal Called before each message is processed (the server assigns fixture positions). */\n beforeProcessMessage?: () => void;\n};\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: ResponsesWebSocketDefaults,\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(async () => {\n try {\n defaults.beforeProcessMessage?.();\n await processMessage(raw, ws, fixtures, journal, defaults);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n const code = isFixtureToolCallError(err) ? fixtureToolCallErrorCode(err) : undefined;\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\", code)));\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }\n });\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: ResponsesWebSocketDefaults,\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n ws.send(\n JSON.stringify(\n buildErrorEvent(`Malformed JSON: ${detail}`, \"invalid_request_error\", \"invalid_json\"),\n ),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n // Same shapes as HTTP: namespace / custom tools, additional_tools items\n // and custom tool call items reach responsesToCompletionRequest unchanged.\n input: (parsed.input ?? []) as ResponsesInputItem[],\n instructions: parsed.instructions,\n tools: parsed.tools as ResponsesToolDef[] | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n include: (parsed as { include?: string[] }).include,\n store: (parsed as { store?: boolean }).store,\n };\n\n // Gate encrypted-reasoning emission identically to the HTTP transport so the\n // agent-framework#7233 stateless-replay path works over WebSocket too.\n const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);\n // Synthesizing a reasoning item the fixture never declared takes the NARROWER\n // explicit-`include` gate, transport-identically to HTTP — a `store: false`\n // request with a summary-less fixture still gets no reasoning item.\n const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);\n\n const extendedTools = defaults.responsesTools === \"extended\";\n const completionReq = responsesToCompletionRequest(responsesReq, {\n extended: extendedTools,\n logger: defaults.logger,\n });\n completionReq._endpointType = \"chat\";\n const contextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n completionReq._context =\n typeof contextHeader === \"string\"\n ? contextHeader\n : Array.isArray(contextHeader) && contextHeader.length > 0\n ? contextHeader[0]\n : undefined;\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/responses\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n // Without responsesTools \"extended\", a namespace on a toolCalls entry or a\n // toolCall block is not emitted, as in earlier releases.\n const resolvedResponse = await resolveResponse(fixture, completionReq);\n const response = extendedTools ? resolvedResponse : withoutLegacyNamespaces(resolvedResponse);\n const misbehaviorDefaults: HandlerDefaults = {\n ...defaults,\n replaySpeed: defaults.replaySpeed ?? 1,\n };\n const misbehavior = planMisbehavior({\n wire: \"openai-responses\",\n fixture,\n response,\n request: completionReq,\n // Responses WebSocket always emits streaming events, even with stream:false.\n stream: true,\n emitsToolCallIds: true,\n defaults: misbehaviorDefaults,\n // The server has already resolved the upgrade's header/query test ID.\n rawHeaders: { ...defaults.upgradeHeaders, \"x-test-id\": testId },\n url: \"/v1/responses\",\n });\n const recordOutcome = (entry: ReturnType<Journal[\"add\"]>) =>\n recordMisbehaviorOutcome({\n entry,\n summary: misbehavior.summary,\n defaults: misbehaviorDefaults,\n testId,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordOutcome(entry);\n if (!misbehavior.summary?.evaluations.length) {\n defaults.logger.error(\n `[misbehavior] ${misbehavior.code}: ${misbehavior.message} (testId=${testId})`,\n );\n }\n ws.send(\n JSON.stringify(\n buildErrorEvent(misbehavior.message, \"invalid_request_error\", misbehavior.code),\n ),\n );\n return;\n }\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n if (misbehavior.kind === \"applied\") {\n const prepared = prepareResponsesMisbehavior(misbehavior);\n const output = buildResponsesMisbehavior(\n prepared,\n completionReq,\n chunkSize,\n emitEncryptedReasoning,\n );\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry,\n summary: prepared.summary,\n defaults: misbehaviorDefaults,\n testId,\n });\n const timings = fixture.recordedTimings;\n const gaps = timings?.interChunkDelaysMs;\n // K4 can add events; extend only its replay using the last recorded gap.\n const appliedTimings =\n prepared.duplicateId && timings && gaps\n ? {\n ...timings,\n interChunkDelaysMs: Array.from(\n { length: Math.max(gaps.length, output.events.length - 1) },\n (_, index) => gaps[index] ?? gaps.at(-1) ?? 0,\n ),\n }\n : timings;\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n output.events,\n latency,\n interruption?.signal,\n interruption?.tick,\n appliedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n entry.response.interrupted = true;\n entry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // The WS path has no per-request `req.headers`; strict is resolved from the\n // connection's upgrade headers (see the `!fixture` branch above). Used below\n // to gate the synthesized reasoning channel on the requested model's capability.\n const effectiveStrict = resolveStrictMode(defaults.strict, defaults.upgradeHeaders);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n recordOutcome(entry);\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isCombinedFixtureResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n\n let events: ResponsesSSEEvent[];\n try {\n const ordered = resolveServedBlockOutcome(response)?.ordered;\n if (!ordered) assertCustomToolCalls(response);\n events = contentWithToolCallsStreamEvents(\n response.content ?? \"\",\n servedToolCalls(response),\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n ordered,\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n } catch (error) {\n journalEntry.response.status = 500;\n if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;\n throw error;\n }\n\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n let events: ResponsesSSEEvent[];\n try {\n assertCustomToolCalls(response);\n events = toolCallStreamEvents(\n servedToolCalls(response),\n completionReq.model,\n chunkSize,\n // Gate the synthesized reasoning channel on the requested model's\n // capability, matching the WS text / content+tool branches and the HTTP\n // tool-only path so reasoning emission is transport-independent.\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n } catch (error) {\n journalEntry.response.status = 500;\n if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;\n throw error;\n }\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordOutcome(entry);\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n recordedTimings?: import(\"./types.js\").RecordedTimings,\n replaySpeed?: number,\n): Promise<boolean> {\n let eventIndex = 0;\n for (const event of events) {\n if (ws.isClosed) return false;\n const chunkDelay = calculateDelay(eventIndex, undefined, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return false;\n ws.send(JSON.stringify(event));\n eventIndex++;\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":"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"}
|
package/dist/ws-responses.d.cts
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { Journal } from "./journal.cjs";
|
|
2
|
-
import {
|
|
3
|
-
import { ChatCompletionRequest, Fixture } from "./types.cjs";
|
|
2
|
+
import { Fixture, HandlerDefaults } from "./types.cjs";
|
|
4
3
|
import { WebSocketConnection } from "./ws-framing.cjs";
|
|
5
|
-
import * as
|
|
4
|
+
import * as node_http1 from "node:http";
|
|
6
5
|
|
|
7
6
|
//#region src/ws-responses.d.ts
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
latency: number;
|
|
11
|
-
chunkSize: number;
|
|
8
|
+
type ResponsesWebSocketDefaults = Omit<HandlerDefaults, "replaySpeed"> & {
|
|
12
9
|
replaySpeed?: number;
|
|
13
10
|
model: string;
|
|
14
|
-
logger: Logger;
|
|
15
|
-
strict?: boolean;
|
|
16
|
-
requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
|
|
17
11
|
testId?: string;
|
|
18
|
-
upgradeHeaders?:
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
upgradeHeaders?: node_http1.IncomingHttpHeaders;
|
|
13
|
+
/** @internal Called before each message is processed (the server assigns fixture positions). */
|
|
14
|
+
beforeProcessMessage?: () => void;
|
|
15
|
+
};
|
|
16
|
+
declare function handleWebSocketResponses(ws: WebSocketConnection, fixtures: Fixture[], journal: Journal, defaults: ResponsesWebSocketDefaults): void;
|
|
21
17
|
//#endregion
|
|
22
18
|
export { handleWebSocketResponses };
|
|
23
19
|
//# sourceMappingURL=ws-responses.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-responses.d.cts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ws-responses.d.cts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;KAiDK,0BAAA,GAA6B,IAIK,CAJA,eAIA,EAAA,aAAA,CAAA,GAAA;EAAmB,WAAA,CAAA,EAAA,MAAA;EAqC1C,KAAA,EAAA,MAAA;EAAwB,MAAA,CAAA,EAAA,MAAA;gBAClC,CAAA,EA1CgC,UAAA,CAIC,mBAsCjC;;sBAEK,CAAA,EAAA,GAAA,GAAA,IAAA;;AAC2B,iBAJtB,wBAAA,CAIsB,EAAA,EAHhC,mBAGgC,EAAA,QAAA,EAF1B,OAE0B,EAAA,EAAA,OAAA,EAD3B,OAC2B,EAAA,QAAA,EAA1B,0BAA0B,CAAA,EAAA,IAAA"}
|
package/dist/ws-responses.d.ts
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { Journal } from "./journal.js";
|
|
2
|
-
import {
|
|
3
|
-
import { ChatCompletionRequest, Fixture } from "./types.js";
|
|
2
|
+
import { Fixture, HandlerDefaults } from "./types.js";
|
|
4
3
|
import { WebSocketConnection } from "./ws-framing.js";
|
|
5
|
-
import * as
|
|
4
|
+
import * as node_http1 from "node:http";
|
|
6
5
|
|
|
7
6
|
//#region src/ws-responses.d.ts
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
latency: number;
|
|
11
|
-
chunkSize: number;
|
|
8
|
+
type ResponsesWebSocketDefaults = Omit<HandlerDefaults, "replaySpeed"> & {
|
|
12
9
|
replaySpeed?: number;
|
|
13
10
|
model: string;
|
|
14
|
-
logger: Logger;
|
|
15
|
-
strict?: boolean;
|
|
16
|
-
requestTransform?: (req: ChatCompletionRequest) => ChatCompletionRequest;
|
|
17
11
|
testId?: string;
|
|
18
|
-
upgradeHeaders?:
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
upgradeHeaders?: node_http1.IncomingHttpHeaders;
|
|
13
|
+
/** @internal Called before each message is processed (the server assigns fixture positions). */
|
|
14
|
+
beforeProcessMessage?: () => void;
|
|
15
|
+
};
|
|
16
|
+
declare function handleWebSocketResponses(ws: WebSocketConnection, fixtures: Fixture[], journal: Journal, defaults: ResponsesWebSocketDefaults): void;
|
|
21
17
|
//#endregion
|
|
22
18
|
export { handleWebSocketResponses };
|
|
23
19
|
//# sourceMappingURL=ws-responses.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-responses.d.ts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ws-responses.d.ts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;KAiDK,0BAAA,GAA6B,IAIK,CAJA,eAIA,EAAA,aAAA,CAAA,GAAA;EAAmB,WAAA,CAAA,EAAA,MAAA;EAqC1C,KAAA,EAAA,MAAA;EAAwB,MAAA,CAAA,EAAA,MAAA;gBAClC,CAAA,EA1CgC,UAAA,CAIC,mBAsCjC;;sBAEK,CAAA,EAAA,GAAA,GAAA,IAAA;;AAC2B,iBAJtB,wBAAA,CAIsB,EAAA,EAHhC,mBAGgC,EAAA,QAAA,EAF1B,OAE0B,EAAA,EAAA,OAAA,EAD3B,OAC2B,EAAA,QAAA,EAA1B,0BAA0B,CAAA,EAAA,IAAA"}
|
package/dist/ws-responses.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_TEST_ID } from "./constants.js";
|
|
2
|
-
import { extractOverrides, flattenHeaders,
|
|
2
|
+
import { assertCustomToolCalls, extractOverrides, fixtureToolCallErrorCode, flattenHeaders, isCombinedFixtureResponse, isErrorResponse, isFixtureToolCallError, isTextResponse, isToolCallResponse, resolveReasoningForModel, resolveResponse, resolveServedBlockOutcome, resolveStrictMode, servedToolCalls, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, withoutLegacyNamespaces } from "./helpers.js";
|
|
3
3
|
import { calculateDelay, delay } from "./sse-writer.js";
|
|
4
|
+
import { planMisbehavior, recordMisbehaviorOutcome } from "./misbehavior.js";
|
|
4
5
|
import { matchFixtureDiagnostic } from "./router.js";
|
|
5
6
|
import "./journal.js";
|
|
6
7
|
import { createInterruptionSignal } from "./interruption.js";
|
|
7
|
-
import {
|
|
8
|
+
import { buildResponsesMisbehavior, buildTextStreamEvents, contentWithToolCallsStreamEvents, prepareResponsesMisbehavior, requestIncludesEncryptedReasoning, requestWantsEncryptedReasoning, responsesToCompletionRequest, toolCallStreamEvents } from "./responses.js";
|
|
8
9
|
|
|
9
10
|
//#region src/ws-responses.ts
|
|
10
11
|
function isResponseCreateMessage(msg) {
|
|
@@ -24,15 +25,21 @@ function handleWebSocketResponses(ws, fixtures, journal, defaults) {
|
|
|
24
25
|
const { logger } = defaults;
|
|
25
26
|
let pending = Promise.resolve();
|
|
26
27
|
ws.on("message", (raw) => {
|
|
27
|
-
pending = pending.then(
|
|
28
|
-
const msg = err instanceof Error ? err.message : "Internal error";
|
|
29
|
-
logger.error(`WebSocket responses error: ${msg}`);
|
|
28
|
+
pending = pending.then(async () => {
|
|
30
29
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
defaults.beforeProcessMessage?.();
|
|
31
|
+
await processMessage(raw, ws, fixtures, journal, defaults);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
34
|
+
logger.error(`WebSocket responses error: ${msg}`);
|
|
35
|
+
try {
|
|
36
|
+
const code = isFixtureToolCallError(err) ? fixtureToolCallErrorCode(err) : void 0;
|
|
37
|
+
ws.send(JSON.stringify(buildErrorEvent(msg, "server_error", code)));
|
|
38
|
+
} catch (sendErr) {
|
|
39
|
+
defaults.logger.debug(`Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : "unknown"}`);
|
|
40
|
+
}
|
|
34
41
|
}
|
|
35
|
-
})
|
|
42
|
+
});
|
|
36
43
|
});
|
|
37
44
|
}
|
|
38
45
|
async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
@@ -62,7 +69,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
62
69
|
};
|
|
63
70
|
const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);
|
|
64
71
|
const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);
|
|
65
|
-
const
|
|
72
|
+
const extendedTools = defaults.responsesTools === "extended";
|
|
73
|
+
const completionReq = responsesToCompletionRequest(responsesReq, {
|
|
74
|
+
extended: extendedTools,
|
|
75
|
+
logger: defaults.logger
|
|
76
|
+
});
|
|
66
77
|
completionReq._endpointType = "chat";
|
|
67
78
|
const contextHeader = defaults.upgradeHeaders?.["x-aimock-context"];
|
|
68
79
|
completionReq._context = typeof contextHeader === "string" ? contextHeader : Array.isArray(contextHeader) && contextHeader.length > 0 ? contextHeader[0] : void 0;
|
|
@@ -101,13 +112,87 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
101
112
|
ws.send(JSON.stringify(buildErrorEvent("No fixture matched", "invalid_request_error", "no_fixture_match")));
|
|
102
113
|
return;
|
|
103
114
|
}
|
|
104
|
-
const
|
|
115
|
+
const resolvedResponse = await resolveResponse(fixture, completionReq);
|
|
116
|
+
const response = extendedTools ? resolvedResponse : withoutLegacyNamespaces(resolvedResponse);
|
|
117
|
+
const misbehaviorDefaults = {
|
|
118
|
+
...defaults,
|
|
119
|
+
replaySpeed: defaults.replaySpeed ?? 1
|
|
120
|
+
};
|
|
121
|
+
const misbehavior = planMisbehavior({
|
|
122
|
+
wire: "openai-responses",
|
|
123
|
+
fixture,
|
|
124
|
+
response,
|
|
125
|
+
request: completionReq,
|
|
126
|
+
stream: true,
|
|
127
|
+
emitsToolCallIds: true,
|
|
128
|
+
defaults: misbehaviorDefaults,
|
|
129
|
+
rawHeaders: {
|
|
130
|
+
...defaults.upgradeHeaders,
|
|
131
|
+
"x-test-id": testId
|
|
132
|
+
},
|
|
133
|
+
url: "/v1/responses"
|
|
134
|
+
});
|
|
135
|
+
const recordOutcome = (entry) => recordMisbehaviorOutcome({
|
|
136
|
+
entry,
|
|
137
|
+
summary: misbehavior.summary,
|
|
138
|
+
defaults: misbehaviorDefaults,
|
|
139
|
+
testId
|
|
140
|
+
});
|
|
141
|
+
if (misbehavior.kind === "error") {
|
|
142
|
+
recordOutcome(journal.add({
|
|
143
|
+
method: "WS",
|
|
144
|
+
path: "/v1/responses",
|
|
145
|
+
headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
146
|
+
body: completionReq,
|
|
147
|
+
response: {
|
|
148
|
+
status: misbehavior.status,
|
|
149
|
+
fixture
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
if (!misbehavior.summary?.evaluations.length) defaults.logger.error(`[misbehavior] ${misbehavior.code}: ${misbehavior.message} (testId=${testId})`);
|
|
153
|
+
ws.send(JSON.stringify(buildErrorEvent(misbehavior.message, "invalid_request_error", misbehavior.code)));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
105
156
|
const latency = fixture.latency ?? defaults.latency;
|
|
106
157
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
158
|
+
if (misbehavior.kind === "applied") {
|
|
159
|
+
const prepared = prepareResponsesMisbehavior(misbehavior);
|
|
160
|
+
const output = buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
|
|
161
|
+
const entry = journal.add({
|
|
162
|
+
method: "WS",
|
|
163
|
+
path: "/v1/responses",
|
|
164
|
+
headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
165
|
+
body: completionReq,
|
|
166
|
+
response: {
|
|
167
|
+
status: 200,
|
|
168
|
+
fixture
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
recordMisbehaviorOutcome({
|
|
172
|
+
entry,
|
|
173
|
+
summary: prepared.summary,
|
|
174
|
+
defaults: misbehaviorDefaults,
|
|
175
|
+
testId
|
|
176
|
+
});
|
|
177
|
+
const timings = fixture.recordedTimings;
|
|
178
|
+
const gaps = timings?.interChunkDelaysMs;
|
|
179
|
+
const appliedTimings = prepared.duplicateId && timings && gaps ? {
|
|
180
|
+
...timings,
|
|
181
|
+
interChunkDelaysMs: Array.from({ length: Math.max(gaps.length, output.events.length - 1) }, (_, index) => gaps[index] ?? gaps.at(-1) ?? 0)
|
|
182
|
+
} : timings;
|
|
183
|
+
const interruption = createInterruptionSignal(fixture);
|
|
184
|
+
if (!await sendEvents(ws, output.events, latency, interruption?.signal, interruption?.tick, appliedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
185
|
+
ws.destroy();
|
|
186
|
+
entry.response.interrupted = true;
|
|
187
|
+
entry.response.interruptReason = interruption?.reason();
|
|
188
|
+
}
|
|
189
|
+
interruption?.cleanup();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
107
192
|
const effectiveStrict = resolveStrictMode(defaults.strict, defaults.upgradeHeaders);
|
|
108
193
|
if (isErrorResponse(response)) {
|
|
109
194
|
const status = response.status ?? 500;
|
|
110
|
-
journal.add({
|
|
195
|
+
recordOutcome(journal.add({
|
|
111
196
|
method: "WS",
|
|
112
197
|
path: "/v1/responses",
|
|
113
198
|
headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
@@ -116,11 +201,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
116
201
|
status,
|
|
117
202
|
fixture
|
|
118
203
|
}
|
|
119
|
-
});
|
|
204
|
+
}));
|
|
120
205
|
ws.send(JSON.stringify(buildErrorEvent(response.error.message, response.error.type, response.error.code)));
|
|
121
206
|
return;
|
|
122
207
|
}
|
|
123
|
-
if (
|
|
208
|
+
if (isCombinedFixtureResponse(response)) {
|
|
124
209
|
const journalEntry = journal.add({
|
|
125
210
|
method: "WS",
|
|
126
211
|
path: "/v1/responses",
|
|
@@ -131,7 +216,17 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
131
216
|
fixture
|
|
132
217
|
}
|
|
133
218
|
});
|
|
134
|
-
|
|
219
|
+
recordOutcome(journalEntry);
|
|
220
|
+
let events;
|
|
221
|
+
try {
|
|
222
|
+
const ordered = resolveServedBlockOutcome(response)?.ordered;
|
|
223
|
+
if (!ordered) assertCustomToolCalls(response);
|
|
224
|
+
events = contentWithToolCallsStreamEvents(response.content ?? "", servedToolCalls(response), completionReq.model, chunkSize, resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, extractOverrides(response), ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
journalEntry.response.status = 500;
|
|
227
|
+
if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
135
230
|
const interruption = createInterruptionSignal(fixture);
|
|
136
231
|
if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
137
232
|
ws.destroy();
|
|
@@ -152,6 +247,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
152
247
|
fixture
|
|
153
248
|
}
|
|
154
249
|
});
|
|
250
|
+
recordOutcome(journalEntry);
|
|
155
251
|
const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
156
252
|
const interruption = createInterruptionSignal(fixture);
|
|
157
253
|
if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
@@ -173,7 +269,16 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
173
269
|
fixture
|
|
174
270
|
}
|
|
175
271
|
});
|
|
176
|
-
|
|
272
|
+
recordOutcome(journalEntry);
|
|
273
|
+
let events;
|
|
274
|
+
try {
|
|
275
|
+
assertCustomToolCalls(response);
|
|
276
|
+
events = toolCallStreamEvents(servedToolCalls(response), completionReq.model, chunkSize, resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
journalEntry.response.status = 500;
|
|
279
|
+
if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
177
282
|
const interruption = createInterruptionSignal(fixture);
|
|
178
283
|
if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
|
|
179
284
|
ws.destroy();
|
|
@@ -183,7 +288,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
183
288
|
interruption?.cleanup();
|
|
184
289
|
return;
|
|
185
290
|
}
|
|
186
|
-
journal.add({
|
|
291
|
+
recordOutcome(journal.add({
|
|
187
292
|
method: "WS",
|
|
188
293
|
path: "/v1/responses",
|
|
189
294
|
headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
@@ -192,7 +297,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
|
|
|
192
297
|
status: 500,
|
|
193
298
|
fixture
|
|
194
299
|
}
|
|
195
|
-
});
|
|
300
|
+
}));
|
|
196
301
|
ws.send(JSON.stringify(buildErrorEvent("Fixture response did not match any known type", "server_error")));
|
|
197
302
|
}
|
|
198
303
|
async function sendEvents(ws, events, latency, signal, onChunkSent, recordedTimings, replaySpeed) {
|