@copilotkit/aimock 1.29.0 → 1.30.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/CHANGELOG.md +18 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/bedrock-converse.cjs +63 -31
- 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 +65 -33
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +95 -33
- 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 +97 -35
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +49 -15
- 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 +51 -17
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/elevenlabs-audio.cjs +8 -4
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +10 -6
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +4 -3
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +6 -5
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +8 -4
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +10 -6
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +4 -2
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +6 -4
- package/dist/fal.js.map +1 -1
- package/dist/gemini-embeddings.cjs +4 -3
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +6 -5
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +3 -3
- 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 +5 -5
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +55 -24
- 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 +57 -26
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +120 -2
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +43 -3
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +43 -3
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +117 -3
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +12 -6
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +14 -8
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/messages.cjs +325 -85
- 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 +327 -87
- package/dist/messages.js.map +1 -1
- package/dist/model-utils.cjs +68 -0
- package/dist/model-utils.cjs.map +1 -1
- package/dist/model-utils.js +68 -1
- package/dist/model-utils.js.map +1 -1
- package/dist/ollama.cjs +58 -21
- 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 +60 -23
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +49 -8
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.js +50 -9
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +26 -12
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +28 -14
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +37 -8
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +30 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +30 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +37 -9
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +15 -9
- 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 +17 -11
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +4 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +6 -4
- package/dist/speech.js.map +1 -1
- package/dist/stream-collapse.cjs +44 -1
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +28 -0
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +28 -0
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +44 -2
- package/dist/stream-collapse.js.map +1 -1
- package/dist/transcription.cjs +4 -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 +6 -4
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +42 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/video.cjs +4 -2
- 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 +6 -4
- package/dist/video.js.map +1 -1
- package/dist/ws-gemini-live.cjs +4 -3
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +6 -5
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +4 -3
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +6 -5
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +8 -6
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +10 -8
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
package/dist/messages.js
CHANGED
|
@@ -1,75 +1,92 @@
|
|
|
1
|
-
import { extractOverrides, flattenHeaders, generateMessageId, generateToolUseId, getContext, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse, resolveStrictMode, strictOverrideField } from "./helpers.js";
|
|
2
|
-
import {
|
|
1
|
+
import { extractOverrides, flattenHeaders, generateMessageId, generateToolUseId, getContext, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveReasoningArtifactsForModel, resolveReasoningForModel, resolveResponse, resolveStrictMode, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField } from "./helpers.js";
|
|
2
|
+
import { matchFixtureDiagnostic } from "./router.js";
|
|
3
3
|
import { calculateDelay, delay, writeErrorResponse } from "./sse-writer.js";
|
|
4
4
|
import { createInterruptionSignal } from "./interruption.js";
|
|
5
5
|
import { applyChaos } from "./chaos.js";
|
|
6
6
|
import { proxyAndRecord } from "./recorder.js";
|
|
7
7
|
|
|
8
8
|
//#region src/messages.ts
|
|
9
|
+
/**
|
|
10
|
+
* Non-empty placeholder signature written into emitted `thinking` blocks.
|
|
11
|
+
*
|
|
12
|
+
* The real Anthropic signature is a cryptographic value aimock cannot
|
|
13
|
+
* reproduce, but extended-thinking invariant (b) requires a non-empty
|
|
14
|
+
* `signature` on the leading thinking block of a tool-loop continuation turn.
|
|
15
|
+
* Emitting "" would make a record→replay round-trip of an aimock thinking turn
|
|
16
|
+
* self-trip that invariant under strict mode. A non-empty placeholder keeps
|
|
17
|
+
* round-trips green; the invariant only checks for non-emptiness, not value.
|
|
18
|
+
*/
|
|
19
|
+
const PLACEHOLDER_SIGNATURE = "aimock-placeholder-signature";
|
|
9
20
|
function extractClaudeTextContent(content) {
|
|
10
21
|
if (typeof content === "string") return content;
|
|
11
|
-
|
|
22
|
+
if (!Array.isArray(content)) return "";
|
|
23
|
+
return content.filter((b) => b != null && typeof b === "object" && b.type === "text").map((b) => b.text ?? "").join("");
|
|
12
24
|
}
|
|
13
25
|
function claudeToCompletionRequest(req) {
|
|
14
26
|
const messages = [];
|
|
15
27
|
if (req.system) {
|
|
16
|
-
const systemText = typeof req.system === "string" ? req.system : req.system.filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
|
|
28
|
+
const systemText = typeof req.system === "string" ? req.system : Array.isArray(req.system) ? req.system.filter((b) => b != null && typeof b === "object" && b.type === "text").map((b) => b.text ?? "").join("") : "";
|
|
17
29
|
if (systemText) messages.push({
|
|
18
30
|
role: "system",
|
|
19
31
|
content: systemText
|
|
20
32
|
});
|
|
21
33
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
const reqMessages = Array.isArray(req.messages) ? req.messages : [];
|
|
35
|
+
for (const msg of reqMessages) {
|
|
36
|
+
if (!msg || typeof msg !== "object") continue;
|
|
37
|
+
if (msg.role === "user") {
|
|
38
|
+
if (typeof msg.content !== "string" && Array.isArray(msg.content)) {
|
|
39
|
+
const blocks = msg.content.filter((b) => b != null && typeof b === "object");
|
|
40
|
+
const toolResults = blocks.filter((b) => b.type === "tool_result");
|
|
41
|
+
const textBlocks = blocks.filter((b) => b.type === "text");
|
|
42
|
+
if (toolResults.length > 0) {
|
|
43
|
+
for (const tr of toolResults) {
|
|
44
|
+
const resultContent = typeof tr.content === "string" ? tr.content : Array.isArray(tr.content) ? tr.content.filter((b) => b != null && typeof b === "object" && b.type === "text").map((b) => b.text ?? "").join("") : "";
|
|
45
|
+
messages.push({
|
|
46
|
+
role: "tool",
|
|
47
|
+
content: resultContent,
|
|
48
|
+
tool_call_id: tr.tool_use_id
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (textBlocks.length > 0) messages.push({
|
|
52
|
+
role: "user",
|
|
53
|
+
content: textBlocks.map((b) => b.text ?? "").join("")
|
|
33
54
|
});
|
|
55
|
+
continue;
|
|
34
56
|
}
|
|
35
|
-
if (textBlocks.length > 0) messages.push({
|
|
36
|
-
role: "user",
|
|
37
|
-
content: textBlocks.map((b) => b.text ?? "").join("")
|
|
38
|
-
});
|
|
39
|
-
continue;
|
|
40
57
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
})
|
|
46
|
-
} else if (msg.role === "assistant") if (typeof msg.content === "string") messages.push({
|
|
47
|
-
role: "assistant",
|
|
48
|
-
content: msg.content
|
|
49
|
-
});
|
|
50
|
-
else if (Array.isArray(msg.content)) {
|
|
51
|
-
const toolUseBlocks = msg.content.filter((b) => b.type === "tool_use");
|
|
52
|
-
const textContent = extractClaudeTextContent(msg.content);
|
|
53
|
-
if (toolUseBlocks.length > 0) messages.push({
|
|
58
|
+
messages.push({
|
|
59
|
+
role: "user",
|
|
60
|
+
content: extractClaudeTextContent(msg.content)
|
|
61
|
+
});
|
|
62
|
+
} else if (msg.role === "assistant") if (typeof msg.content === "string") messages.push({
|
|
54
63
|
role: "assistant",
|
|
55
|
-
content:
|
|
56
|
-
tool_calls: toolUseBlocks.map((b) => ({
|
|
57
|
-
id: b.id ?? generateToolUseId(),
|
|
58
|
-
type: "function",
|
|
59
|
-
function: {
|
|
60
|
-
name: b.name ?? "",
|
|
61
|
-
arguments: typeof b.input === "string" ? b.input : JSON.stringify(b.input ?? {})
|
|
62
|
-
}
|
|
63
|
-
}))
|
|
64
|
+
content: msg.content
|
|
64
65
|
});
|
|
65
|
-
else
|
|
66
|
+
else if (Array.isArray(msg.content)) {
|
|
67
|
+
const toolUseBlocks = msg.content.filter((b) => b != null && typeof b === "object" && b.type === "tool_use");
|
|
68
|
+
const textContent = extractClaudeTextContent(msg.content);
|
|
69
|
+
if (toolUseBlocks.length > 0) messages.push({
|
|
70
|
+
role: "assistant",
|
|
71
|
+
content: textContent || null,
|
|
72
|
+
tool_calls: toolUseBlocks.map((b) => ({
|
|
73
|
+
id: b.id ?? generateToolUseId(),
|
|
74
|
+
type: "function",
|
|
75
|
+
function: {
|
|
76
|
+
name: b.name ?? "",
|
|
77
|
+
arguments: typeof b.input === "string" ? b.input : JSON.stringify(b.input ?? {})
|
|
78
|
+
}
|
|
79
|
+
}))
|
|
80
|
+
});
|
|
81
|
+
else messages.push({
|
|
82
|
+
role: "assistant",
|
|
83
|
+
content: textContent || null
|
|
84
|
+
});
|
|
85
|
+
} else messages.push({
|
|
66
86
|
role: "assistant",
|
|
67
|
-
content:
|
|
87
|
+
content: null
|
|
68
88
|
});
|
|
69
|
-
}
|
|
70
|
-
role: "assistant",
|
|
71
|
-
content: null
|
|
72
|
-
});
|
|
89
|
+
}
|
|
73
90
|
let tools;
|
|
74
91
|
if (req.tools && req.tools.length > 0) tools = req.tools.map((t) => ({
|
|
75
92
|
type: "function",
|
|
@@ -89,6 +106,88 @@ function claudeToCompletionRequest(req) {
|
|
|
89
106
|
_endpointType: "chat"
|
|
90
107
|
};
|
|
91
108
|
}
|
|
109
|
+
/** True iff `req.thinking` is an object with `type === "enabled"`. */
|
|
110
|
+
function isThinkingEnabled(req) {
|
|
111
|
+
const t = req.thinking;
|
|
112
|
+
return typeof t === "object" && t !== null && t.type === "enabled";
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* True iff the user turn at `req.messages[userIndex]` carries a `tool_result`
|
|
116
|
+
* referencing one of `toolUseIds` — i.e. it answers the preceding assistant
|
|
117
|
+
* turn's `tool_use` block(s), making that assistant turn a tool-loop
|
|
118
|
+
* continuation point.
|
|
119
|
+
*/
|
|
120
|
+
function userTurnAnswersToolUse(msg, toolUseIds) {
|
|
121
|
+
if (!msg || msg.role !== "user" || !Array.isArray(msg.content)) return false;
|
|
122
|
+
return msg.content.some((b) => b != null && typeof b === "object" && b.type === "tool_result" && typeof b.tool_use_id === "string" && toolUseIds.has(b.tool_use_id));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Validate the Anthropic extended-thinking request invariants on tool-loop
|
|
126
|
+
* continuation turns. Pure: returns the first detected `ThinkingViolation` or
|
|
127
|
+
* `null` when thinking is disabled or every in-scope turn is well-formed.
|
|
128
|
+
*
|
|
129
|
+
* Scope: an assistant turn is in-scope only when it (a) is array content
|
|
130
|
+
* carrying at least one `tool_use` block and (b) is followed by a matching
|
|
131
|
+
* `tool_result` on the next user turn. Text-only / `end_turn` turns, string or
|
|
132
|
+
* empty turns, and trailing unanswered `tool_use` turns are all exempt.
|
|
133
|
+
*
|
|
134
|
+
* Scope detection assumes well-formed Anthropic transcripts: `tool_use` ids are
|
|
135
|
+
* unique, and a tool_use turn is answered by the immediately-following user
|
|
136
|
+
* turn's `tool_result` (adjacency). Malformed shapes — non-adjacent answers,
|
|
137
|
+
* idless `tool_use` blocks, or a `tool_result` separated from its `tool_use` by
|
|
138
|
+
* intervening turns — are intentionally treated as out-of-scope (and therefore
|
|
139
|
+
* not 400'd) rather than validated, since the real Anthropic API would never
|
|
140
|
+
* have produced them.
|
|
141
|
+
*/
|
|
142
|
+
function validateThinkingInvariants(req) {
|
|
143
|
+
if (!isThinkingEnabled(req)) return null;
|
|
144
|
+
const messages = Array.isArray(req.messages) ? req.messages : [];
|
|
145
|
+
for (let i = 0; i < messages.length; i++) {
|
|
146
|
+
const msg = messages[i];
|
|
147
|
+
if (!msg || typeof msg !== "object") continue;
|
|
148
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content) || msg.content.length === 0) continue;
|
|
149
|
+
const toolUseIds = /* @__PURE__ */ new Set();
|
|
150
|
+
for (const b of msg.content) {
|
|
151
|
+
if (!b || typeof b !== "object") continue;
|
|
152
|
+
if (b.type === "tool_use" && typeof b.id === "string") toolUseIds.add(b.id);
|
|
153
|
+
}
|
|
154
|
+
if (toolUseIds.size === 0) continue;
|
|
155
|
+
if (!userTurnAnswersToolUse(messages[i + 1], toolUseIds)) continue;
|
|
156
|
+
const first = msg.content[0];
|
|
157
|
+
if (!first || typeof first !== "object") return {
|
|
158
|
+
kind: "missing_thinking_first",
|
|
159
|
+
messageIndex: i,
|
|
160
|
+
observedFirstBlockType: void 0
|
|
161
|
+
};
|
|
162
|
+
if (first.type !== "thinking" && first.type !== "redacted_thinking") return {
|
|
163
|
+
kind: "missing_thinking_first",
|
|
164
|
+
messageIndex: i,
|
|
165
|
+
observedFirstBlockType: first.type
|
|
166
|
+
};
|
|
167
|
+
if (first.type === "thinking") {
|
|
168
|
+
if (typeof first.signature !== "string" || first.signature.length === 0) return {
|
|
169
|
+
kind: "missing_signature",
|
|
170
|
+
messageIndex: i
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (first.type === "redacted_thinking") {
|
|
174
|
+
if (typeof first.data !== "string" || first.data.length === 0) return {
|
|
175
|
+
kind: "dropped_redacted_thinking",
|
|
176
|
+
messageIndex: i
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/** Render the Anthropic-shaped 400 error message for a thinking violation. */
|
|
183
|
+
function thinkingViolationMessage(v) {
|
|
184
|
+
const prefix = `messages.${v.messageIndex}.content.0`;
|
|
185
|
+
switch (v.kind) {
|
|
186
|
+
case "missing_thinking_first": return `${prefix}: when \`thinking\` is enabled, a tool-loop continuation assistant turn must begin with a \`thinking\` block; got \`${v.observedFirstBlockType ?? "unknown"}\`.`;
|
|
187
|
+
case "missing_signature": return `${prefix}: the leading \`thinking\` block is missing a non-empty \`signature\`.`;
|
|
188
|
+
case "dropped_redacted_thinking": return `${prefix}: the leading \`redacted_thinking\` block must preserve its \`data\`.`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
92
191
|
function claudeStopReason(finishReason, defaultReason) {
|
|
93
192
|
if (!finishReason) return defaultReason;
|
|
94
193
|
if (finishReason === "stop") return "end_turn";
|
|
@@ -106,9 +205,44 @@ function claudeUsage(overrides) {
|
|
|
106
205
|
output_tokens: overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0
|
|
107
206
|
};
|
|
108
207
|
}
|
|
109
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Emit faithful Anthropic `redacted_thinking` content-block events at the head
|
|
210
|
+
* of a streamed turn — one `content_block_start` (carrying the opaque `data`)
|
|
211
|
+
* and `content_block_stop` per recorded block, with NO deltas (real Anthropic
|
|
212
|
+
* delivers the encrypted reasoning entirely on the start event). Returns the
|
|
213
|
+
* next free block index. When `redactedThinking` is empty/absent this is a
|
|
214
|
+
* no-op, so no events are added.
|
|
215
|
+
*
|
|
216
|
+
* Leading placement keeps the turn round-trip-safe for the extended-thinking
|
|
217
|
+
* leading-block invariant (a thinking / redacted_thinking block must come
|
|
218
|
+
* first). It does NOT reproduce real Anthropic's stream order: aimock joins all
|
|
219
|
+
* plaintext reasoning into one block and emits all redacted blocks ahead of it,
|
|
220
|
+
* whereas real Anthropic interleaves `thinking` and `redacted_thinking` in the
|
|
221
|
+
* order they occurred. The interleaving between the two is not preserved.
|
|
222
|
+
*/
|
|
223
|
+
function pushRedactedThinkingStreamEvents(events, startIndex, redactedThinking) {
|
|
224
|
+
let blockIndex = startIndex;
|
|
225
|
+
for (const data of redactedThinking ?? []) {
|
|
226
|
+
events.push({
|
|
227
|
+
type: "content_block_start",
|
|
228
|
+
index: blockIndex,
|
|
229
|
+
content_block: {
|
|
230
|
+
type: "redacted_thinking",
|
|
231
|
+
data
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
events.push({
|
|
235
|
+
type: "content_block_stop",
|
|
236
|
+
index: blockIndex
|
|
237
|
+
});
|
|
238
|
+
blockIndex++;
|
|
239
|
+
}
|
|
240
|
+
return blockIndex;
|
|
241
|
+
}
|
|
242
|
+
function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
110
243
|
const msgId = overrides?.id ?? generateMessageId();
|
|
111
244
|
const effectiveModel = overrides?.model ?? model;
|
|
245
|
+
const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
|
|
112
246
|
const events = [];
|
|
113
247
|
events.push({
|
|
114
248
|
type: "message_start",
|
|
@@ -124,6 +258,7 @@ function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overr
|
|
|
124
258
|
}
|
|
125
259
|
});
|
|
126
260
|
let blockIndex = 0;
|
|
261
|
+
blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);
|
|
127
262
|
if (reasoning) {
|
|
128
263
|
events.push({
|
|
129
264
|
type: "content_block_start",
|
|
@@ -150,7 +285,7 @@ function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overr
|
|
|
150
285
|
index: blockIndex,
|
|
151
286
|
delta: {
|
|
152
287
|
type: "signature_delta",
|
|
153
|
-
signature
|
|
288
|
+
signature
|
|
154
289
|
}
|
|
155
290
|
});
|
|
156
291
|
events.push({
|
|
@@ -193,9 +328,10 @@ function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overr
|
|
|
193
328
|
events.push({ type: "message_stop" });
|
|
194
329
|
return events;
|
|
195
330
|
}
|
|
196
|
-
function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, overrides) {
|
|
331
|
+
function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
197
332
|
const msgId = overrides?.id ?? generateMessageId();
|
|
198
333
|
const effectiveModel = overrides?.model ?? model;
|
|
334
|
+
const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
|
|
199
335
|
const events = [];
|
|
200
336
|
events.push({
|
|
201
337
|
type: "message_start",
|
|
@@ -210,8 +346,44 @@ function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, ov
|
|
|
210
346
|
usage: claudeUsage(overrides)
|
|
211
347
|
}
|
|
212
348
|
});
|
|
213
|
-
|
|
214
|
-
|
|
349
|
+
let blockIndex = 0;
|
|
350
|
+
blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);
|
|
351
|
+
if (reasoning) {
|
|
352
|
+
events.push({
|
|
353
|
+
type: "content_block_start",
|
|
354
|
+
index: blockIndex,
|
|
355
|
+
content_block: {
|
|
356
|
+
type: "thinking",
|
|
357
|
+
thinking: "",
|
|
358
|
+
signature: ""
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
for (let i = 0; i < reasoning.length; i += chunkSize) {
|
|
362
|
+
const slice = reasoning.slice(i, i + chunkSize);
|
|
363
|
+
events.push({
|
|
364
|
+
type: "content_block_delta",
|
|
365
|
+
index: blockIndex,
|
|
366
|
+
delta: {
|
|
367
|
+
type: "thinking_delta",
|
|
368
|
+
thinking: slice
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
events.push({
|
|
373
|
+
type: "content_block_delta",
|
|
374
|
+
index: blockIndex,
|
|
375
|
+
delta: {
|
|
376
|
+
type: "signature_delta",
|
|
377
|
+
signature
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
events.push({
|
|
381
|
+
type: "content_block_stop",
|
|
382
|
+
index: blockIndex
|
|
383
|
+
});
|
|
384
|
+
blockIndex++;
|
|
385
|
+
}
|
|
386
|
+
for (const tc of toolCalls) {
|
|
215
387
|
const toolUseId = tc.id || generateToolUseId();
|
|
216
388
|
let argsObj;
|
|
217
389
|
try {
|
|
@@ -223,7 +395,7 @@ function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, ov
|
|
|
223
395
|
const argsJson = JSON.stringify(argsObj);
|
|
224
396
|
events.push({
|
|
225
397
|
type: "content_block_start",
|
|
226
|
-
index:
|
|
398
|
+
index: blockIndex,
|
|
227
399
|
content_block: {
|
|
228
400
|
type: "tool_use",
|
|
229
401
|
id: toolUseId,
|
|
@@ -235,7 +407,7 @@ function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, ov
|
|
|
235
407
|
const slice = argsJson.slice(i, i + chunkSize);
|
|
236
408
|
events.push({
|
|
237
409
|
type: "content_block_delta",
|
|
238
|
-
index:
|
|
410
|
+
index: blockIndex,
|
|
239
411
|
delta: {
|
|
240
412
|
type: "input_json_delta",
|
|
241
413
|
partial_json: slice
|
|
@@ -244,8 +416,9 @@ function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, ov
|
|
|
244
416
|
}
|
|
245
417
|
events.push({
|
|
246
418
|
type: "content_block_stop",
|
|
247
|
-
index:
|
|
419
|
+
index: blockIndex
|
|
248
420
|
});
|
|
421
|
+
blockIndex++;
|
|
249
422
|
}
|
|
250
423
|
events.push({
|
|
251
424
|
type: "message_delta",
|
|
@@ -258,12 +431,31 @@ function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, ov
|
|
|
258
431
|
events.push({ type: "message_stop" });
|
|
259
432
|
return events;
|
|
260
433
|
}
|
|
261
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Push faithful Anthropic `redacted_thinking` content blocks (each a
|
|
436
|
+
* `{ type: "redacted_thinking", data }`) onto a non-streaming content array, in
|
|
437
|
+
* recorded order. No-op when `redactedThinking` is empty/absent, so no blocks
|
|
438
|
+
* are added.
|
|
439
|
+
*
|
|
440
|
+
* Leading placement keeps the turn round-trip-safe for the extended-thinking
|
|
441
|
+
* leading-block invariant. It does NOT reproduce real Anthropic's block order:
|
|
442
|
+
* aimock joins all plaintext reasoning into one block and emits all redacted
|
|
443
|
+
* blocks ahead of it, whereas real Anthropic interleaves `thinking` and
|
|
444
|
+
* `redacted_thinking` in occurrence order. The interleaving is not preserved.
|
|
445
|
+
*/
|
|
446
|
+
function pushRedactedThinkingBlocks(contentBlocks, redactedThinking) {
|
|
447
|
+
for (const data of redactedThinking ?? []) contentBlocks.push({
|
|
448
|
+
type: "redacted_thinking",
|
|
449
|
+
data
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
function buildClaudeTextResponse(content, model, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
262
453
|
const contentBlocks = [];
|
|
454
|
+
pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
|
|
263
455
|
if (reasoning) contentBlocks.push({
|
|
264
456
|
type: "thinking",
|
|
265
457
|
thinking: reasoning,
|
|
266
|
-
signature:
|
|
458
|
+
signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
|
|
267
459
|
});
|
|
268
460
|
contentBlocks.push({
|
|
269
461
|
type: "text",
|
|
@@ -280,35 +472,44 @@ function buildClaudeTextResponse(content, model, reasoning, overrides) {
|
|
|
280
472
|
usage: claudeUsage(overrides)
|
|
281
473
|
};
|
|
282
474
|
}
|
|
283
|
-
function buildClaudeToolCallResponse(toolCalls, model, logger, overrides) {
|
|
475
|
+
function buildClaudeToolCallResponse(toolCalls, model, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
476
|
+
const contentBlocks = [];
|
|
477
|
+
pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
|
|
478
|
+
if (reasoning) contentBlocks.push({
|
|
479
|
+
type: "thinking",
|
|
480
|
+
thinking: reasoning,
|
|
481
|
+
signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
|
|
482
|
+
});
|
|
483
|
+
for (const tc of toolCalls) {
|
|
484
|
+
let argsObj;
|
|
485
|
+
try {
|
|
486
|
+
argsObj = JSON.parse(tc.arguments || "{}");
|
|
487
|
+
} catch {
|
|
488
|
+
logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
|
|
489
|
+
argsObj = {};
|
|
490
|
+
}
|
|
491
|
+
contentBlocks.push({
|
|
492
|
+
type: "tool_use",
|
|
493
|
+
id: tc.id || generateToolUseId(),
|
|
494
|
+
name: tc.name,
|
|
495
|
+
input: argsObj
|
|
496
|
+
});
|
|
497
|
+
}
|
|
284
498
|
return {
|
|
285
499
|
id: overrides?.id ?? generateMessageId(),
|
|
286
500
|
type: "message",
|
|
287
501
|
role: overrides?.role ?? "assistant",
|
|
288
|
-
content:
|
|
289
|
-
let argsObj;
|
|
290
|
-
try {
|
|
291
|
-
argsObj = JSON.parse(tc.arguments || "{}");
|
|
292
|
-
} catch {
|
|
293
|
-
logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
|
|
294
|
-
argsObj = {};
|
|
295
|
-
}
|
|
296
|
-
return {
|
|
297
|
-
type: "tool_use",
|
|
298
|
-
id: tc.id || generateToolUseId(),
|
|
299
|
-
name: tc.name,
|
|
300
|
-
input: argsObj
|
|
301
|
-
};
|
|
302
|
-
}),
|
|
502
|
+
content: contentBlocks,
|
|
303
503
|
model: overrides?.model ?? model,
|
|
304
504
|
stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
|
|
305
505
|
stop_sequence: null,
|
|
306
506
|
usage: claudeUsage(overrides)
|
|
307
507
|
};
|
|
308
508
|
}
|
|
309
|
-
function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize, logger, reasoning, overrides) {
|
|
509
|
+
function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
310
510
|
const msgId = overrides?.id ?? generateMessageId();
|
|
311
511
|
const effectiveModel = overrides?.model ?? model;
|
|
512
|
+
const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
|
|
312
513
|
const events = [];
|
|
313
514
|
events.push({
|
|
314
515
|
type: "message_start",
|
|
@@ -324,6 +525,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
324
525
|
}
|
|
325
526
|
});
|
|
326
527
|
let blockIndex = 0;
|
|
528
|
+
blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);
|
|
327
529
|
if (reasoning) {
|
|
328
530
|
events.push({
|
|
329
531
|
type: "content_block_start",
|
|
@@ -350,7 +552,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
350
552
|
index: blockIndex,
|
|
351
553
|
delta: {
|
|
352
554
|
type: "signature_delta",
|
|
353
|
-
signature
|
|
555
|
+
signature
|
|
354
556
|
}
|
|
355
557
|
});
|
|
356
558
|
events.push({
|
|
@@ -431,12 +633,13 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
431
633
|
events.push({ type: "message_stop" });
|
|
432
634
|
return events;
|
|
433
635
|
}
|
|
434
|
-
function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logger, reasoning, overrides) {
|
|
636
|
+
function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
435
637
|
const contentBlocks = [];
|
|
638
|
+
pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
|
|
436
639
|
if (reasoning) contentBlocks.push({
|
|
437
640
|
type: "thinking",
|
|
438
641
|
thinking: reasoning,
|
|
439
|
-
signature:
|
|
642
|
+
signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
|
|
440
643
|
});
|
|
441
644
|
contentBlocks.push({
|
|
442
645
|
type: "text",
|
|
@@ -517,10 +720,38 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
517
720
|
} }));
|
|
518
721
|
return;
|
|
519
722
|
}
|
|
723
|
+
const thinkingViolation = validateThinkingInvariants(claudeReq);
|
|
724
|
+
if (thinkingViolation) {
|
|
725
|
+
const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
|
|
726
|
+
const violationMessage = thinkingViolationMessage(thinkingViolation);
|
|
727
|
+
if (effectiveStrict) {
|
|
728
|
+
logger.error(`THINKING: ${violationMessage}`);
|
|
729
|
+
journal.add({
|
|
730
|
+
method: req.method ?? "POST",
|
|
731
|
+
path: req.url ?? "/v1/messages",
|
|
732
|
+
headers: flattenHeaders(req.headers),
|
|
733
|
+
body: null,
|
|
734
|
+
response: {
|
|
735
|
+
status: 400,
|
|
736
|
+
fixture: null,
|
|
737
|
+
...strictOverrideField(defaults.strict, req.headers)
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
writeErrorResponse(res, 400, JSON.stringify({
|
|
741
|
+
type: "error",
|
|
742
|
+
error: {
|
|
743
|
+
type: "invalid_request_error",
|
|
744
|
+
message: violationMessage
|
|
745
|
+
}
|
|
746
|
+
}));
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
logger.warn(`THINKING: ${violationMessage} (strict off — replaying anyway)`);
|
|
750
|
+
}
|
|
520
751
|
const completionReq = claudeToCompletionRequest(claudeReq);
|
|
521
752
|
completionReq._context = getContext(req);
|
|
522
753
|
const testId = getTestId(req);
|
|
523
|
-
const fixture =
|
|
754
|
+
const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
524
755
|
if (fixture) {
|
|
525
756
|
journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
526
757
|
logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
|
|
@@ -538,8 +769,8 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
538
769
|
if (!fixture) {
|
|
539
770
|
if (resolveStrictMode(defaults.strict, req.headers)) {
|
|
540
771
|
const strictStatus = 503;
|
|
541
|
-
const strictMessage =
|
|
542
|
-
logger.error(
|
|
772
|
+
const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);
|
|
773
|
+
logger.error(strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v1/messages", skippedBySequenceOrTurn));
|
|
543
774
|
journal.add({
|
|
544
775
|
method: req.method ?? "POST",
|
|
545
776
|
path: req.url ?? "/v1/messages",
|
|
@@ -620,6 +851,9 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
620
851
|
if (isContentWithToolCallsResponse(response)) {
|
|
621
852
|
if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
|
|
622
853
|
const overrides = extractOverrides(response);
|
|
854
|
+
const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
|
|
855
|
+
const effReasoning = resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
|
|
856
|
+
const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } = resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, completionReq.model, effectiveStrict, defaults.logger);
|
|
623
857
|
const journalEntry = journal.add({
|
|
624
858
|
method: req.method ?? "POST",
|
|
625
859
|
path: req.url ?? "/v1/messages",
|
|
@@ -631,11 +865,11 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
631
865
|
}
|
|
632
866
|
});
|
|
633
867
|
if (claudeReq.stream !== true) {
|
|
634
|
-
const body = buildClaudeContentWithToolCallsResponse(response.content, response.toolCalls, completionReq.model, logger,
|
|
868
|
+
const body = buildClaudeContentWithToolCallsResponse(response.content, response.toolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
635
869
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
636
870
|
res.end(JSON.stringify(body));
|
|
637
871
|
} else {
|
|
638
|
-
const events = buildClaudeContentWithToolCallsStreamEvents(response.content, response.toolCalls, completionReq.model, chunkSize, logger,
|
|
872
|
+
const events = buildClaudeContentWithToolCallsStreamEvents(response.content, response.toolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
639
873
|
const interruption = createInterruptionSignal(fixture);
|
|
640
874
|
if (!await writeClaudeSSEStream(res, events, {
|
|
641
875
|
latency,
|
|
@@ -656,6 +890,9 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
656
890
|
if (isTextResponse(response)) {
|
|
657
891
|
if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
|
|
658
892
|
const overrides = extractOverrides(response);
|
|
893
|
+
const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
|
|
894
|
+
const effReasoning = resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
|
|
895
|
+
const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } = resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, completionReq.model, effectiveStrict, defaults.logger);
|
|
659
896
|
const journalEntry = journal.add({
|
|
660
897
|
method: req.method ?? "POST",
|
|
661
898
|
path: req.url ?? "/v1/messages",
|
|
@@ -667,11 +904,11 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
667
904
|
}
|
|
668
905
|
});
|
|
669
906
|
if (claudeReq.stream !== true) {
|
|
670
|
-
const body = buildClaudeTextResponse(response.content, completionReq.model,
|
|
907
|
+
const body = buildClaudeTextResponse(response.content, completionReq.model, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
671
908
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
672
909
|
res.end(JSON.stringify(body));
|
|
673
910
|
} else {
|
|
674
|
-
const events = buildClaudeTextStreamEvents(response.content, completionReq.model, chunkSize,
|
|
911
|
+
const events = buildClaudeTextStreamEvents(response.content, completionReq.model, chunkSize, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
675
912
|
const interruption = createInterruptionSignal(fixture);
|
|
676
913
|
if (!await writeClaudeSSEStream(res, events, {
|
|
677
914
|
latency,
|
|
@@ -692,6 +929,9 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
692
929
|
if (isToolCallResponse(response)) {
|
|
693
930
|
if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
|
|
694
931
|
const overrides = extractOverrides(response);
|
|
932
|
+
const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
|
|
933
|
+
const effReasoning = resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
|
|
934
|
+
const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } = resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, completionReq.model, effectiveStrict, defaults.logger);
|
|
695
935
|
const journalEntry = journal.add({
|
|
696
936
|
method: req.method ?? "POST",
|
|
697
937
|
path: req.url ?? "/v1/messages",
|
|
@@ -703,11 +943,11 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
703
943
|
}
|
|
704
944
|
});
|
|
705
945
|
if (claudeReq.stream !== true) {
|
|
706
|
-
const body = buildClaudeToolCallResponse(response.toolCalls, completionReq.model, logger, overrides);
|
|
946
|
+
const body = buildClaudeToolCallResponse(response.toolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
707
947
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
708
948
|
res.end(JSON.stringify(body));
|
|
709
949
|
} else {
|
|
710
|
-
const events = buildClaudeToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize, logger, overrides);
|
|
950
|
+
const events = buildClaudeToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
711
951
|
const interruption = createInterruptionSignal(fixture);
|
|
712
952
|
if (!await writeClaudeSSEStream(res, events, {
|
|
713
953
|
latency,
|