@copilotkit/aimock 1.16.4 → 1.17.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.
Files changed (269) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +24 -0
  4. package/README.md +10 -10
  5. package/dist/a2a-mock.d.cts +2 -2
  6. package/dist/a2a-mock.d.cts.map +1 -1
  7. package/dist/a2a-mock.d.ts +2 -2
  8. package/dist/a2a-mock.d.ts.map +1 -1
  9. package/dist/a2a-mock.js +2 -2
  10. package/dist/a2a-mock.js.map +1 -1
  11. package/dist/agui-handler.cjs +120 -5
  12. package/dist/agui-handler.cjs.map +1 -1
  13. package/dist/agui-handler.d.cts +41 -5
  14. package/dist/agui-handler.d.cts.map +1 -1
  15. package/dist/agui-handler.d.ts +41 -5
  16. package/dist/agui-handler.d.ts.map +1 -1
  17. package/dist/agui-handler.js +114 -6
  18. package/dist/agui-handler.js.map +1 -1
  19. package/dist/agui-mock.cjs +18 -7
  20. package/dist/agui-mock.cjs.map +1 -1
  21. package/dist/agui-mock.d.cts +2 -2
  22. package/dist/agui-mock.d.cts.map +1 -1
  23. package/dist/agui-mock.d.ts +2 -2
  24. package/dist/agui-mock.d.ts.map +1 -1
  25. package/dist/agui-mock.js +20 -9
  26. package/dist/agui-mock.js.map +1 -1
  27. package/dist/agui-recorder.cjs +43 -22
  28. package/dist/agui-recorder.cjs.map +1 -1
  29. package/dist/agui-recorder.d.cts +4 -3
  30. package/dist/agui-recorder.d.cts.map +1 -1
  31. package/dist/agui-recorder.d.ts +4 -3
  32. package/dist/agui-recorder.d.ts.map +1 -1
  33. package/dist/agui-recorder.js +45 -24
  34. package/dist/agui-recorder.js.map +1 -1
  35. package/dist/agui-stub.cjs +28 -0
  36. package/dist/agui-stub.d.cts +5 -0
  37. package/dist/agui-stub.d.ts +5 -0
  38. package/dist/agui-stub.js +5 -0
  39. package/dist/agui-types.d.cts +33 -6
  40. package/dist/agui-types.d.cts.map +1 -1
  41. package/dist/agui-types.d.ts +33 -6
  42. package/dist/agui-types.d.ts.map +1 -1
  43. package/dist/aimock-cli.cjs +1 -1
  44. package/dist/aimock-cli.js +1 -1
  45. package/dist/aws-event-stream.d.cts +2 -2
  46. package/dist/aws-event-stream.d.cts.map +1 -1
  47. package/dist/aws-event-stream.d.ts +2 -2
  48. package/dist/aws-event-stream.d.ts.map +1 -1
  49. package/dist/bedrock-converse.d.cts +3 -3
  50. package/dist/bedrock-converse.d.cts.map +1 -1
  51. package/dist/bedrock-converse.d.ts +3 -3
  52. package/dist/bedrock-converse.d.ts.map +1 -1
  53. package/dist/bedrock.d.cts +3 -3
  54. package/dist/bedrock.d.cts.map +1 -1
  55. package/dist/bedrock.d.ts +3 -3
  56. package/dist/bedrock.d.ts.map +1 -1
  57. package/dist/chaos.d.cts +3 -3
  58. package/dist/chaos.d.cts.map +1 -1
  59. package/dist/chaos.d.ts +3 -3
  60. package/dist/chaos.d.ts.map +1 -1
  61. package/dist/cli.cjs +6 -5
  62. package/dist/cli.cjs.map +1 -1
  63. package/dist/cli.js +6 -5
  64. package/dist/cli.js.map +1 -1
  65. package/dist/cohere.d.cts +2 -2
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts +2 -2
  68. package/dist/cohere.d.ts.map +1 -1
  69. package/dist/config-loader.cjs +3 -3
  70. package/dist/config-loader.d.cts +1 -1
  71. package/dist/config-loader.d.cts.map +1 -1
  72. package/dist/config-loader.d.ts +1 -1
  73. package/dist/config-loader.d.ts.map +1 -1
  74. package/dist/config-loader.js +2 -2
  75. package/dist/convert-vidaimock.cjs +1 -1
  76. package/dist/convert-vidaimock.js +1 -1
  77. package/dist/convert.cjs +1 -1
  78. package/dist/convert.js +1 -1
  79. package/dist/elevenlabs-audio.cjs +209 -0
  80. package/dist/elevenlabs-audio.cjs.map +1 -0
  81. package/dist/elevenlabs-audio.d.cts +11 -0
  82. package/dist/elevenlabs-audio.d.cts.map +1 -0
  83. package/dist/elevenlabs-audio.d.ts +11 -0
  84. package/dist/elevenlabs-audio.d.ts.map +1 -0
  85. package/dist/elevenlabs-audio.js +209 -0
  86. package/dist/elevenlabs-audio.js.map +1 -0
  87. package/dist/embeddings.d.cts +2 -2
  88. package/dist/embeddings.d.cts.map +1 -1
  89. package/dist/embeddings.d.ts +2 -2
  90. package/dist/embeddings.d.ts.map +1 -1
  91. package/dist/fal-audio.cjs +477 -0
  92. package/dist/fal-audio.cjs.map +1 -0
  93. package/dist/fal-audio.d.cts +10 -0
  94. package/dist/fal-audio.d.cts.map +1 -0
  95. package/dist/fal-audio.d.ts +10 -0
  96. package/dist/fal-audio.d.ts.map +1 -0
  97. package/dist/fal-audio.js +474 -0
  98. package/dist/fal-audio.js.map +1 -0
  99. package/dist/fixture-loader.cjs +14 -1
  100. package/dist/fixture-loader.cjs.map +1 -1
  101. package/dist/fixture-loader.js +14 -1
  102. package/dist/fixture-loader.js.map +1 -1
  103. package/dist/fixtures-remote.cjs +1 -1
  104. package/dist/fixtures-remote.js +1 -1
  105. package/dist/gemini-interactions.cjs +617 -0
  106. package/dist/gemini-interactions.cjs.map +1 -0
  107. package/dist/gemini-interactions.d.cts +46 -0
  108. package/dist/gemini-interactions.d.cts.map +1 -0
  109. package/dist/gemini-interactions.d.ts +46 -0
  110. package/dist/gemini-interactions.d.ts.map +1 -0
  111. package/dist/gemini-interactions.js +616 -0
  112. package/dist/gemini-interactions.js.map +1 -0
  113. package/dist/gemini.cjs +76 -0
  114. package/dist/gemini.cjs.map +1 -1
  115. package/dist/gemini.d.cts +2 -2
  116. package/dist/gemini.d.cts.map +1 -1
  117. package/dist/gemini.d.ts +2 -2
  118. package/dist/gemini.d.ts.map +1 -1
  119. package/dist/gemini.js +77 -1
  120. package/dist/gemini.js.map +1 -1
  121. package/dist/helpers.cjs +24 -1
  122. package/dist/helpers.cjs.map +1 -1
  123. package/dist/helpers.d.cts +13 -3
  124. package/dist/helpers.d.cts.map +1 -1
  125. package/dist/helpers.d.ts +13 -3
  126. package/dist/helpers.d.ts.map +1 -1
  127. package/dist/helpers.js +23 -2
  128. package/dist/helpers.js.map +1 -1
  129. package/dist/images.d.cts +2 -2
  130. package/dist/images.d.cts.map +1 -1
  131. package/dist/images.d.ts +2 -2
  132. package/dist/images.d.ts.map +1 -1
  133. package/dist/index.cjs +21 -4
  134. package/dist/index.d.cts +10 -7
  135. package/dist/index.d.ts +10 -7
  136. package/dist/index.js +10 -7
  137. package/dist/jest.cjs +1 -1
  138. package/dist/jest.js +1 -1
  139. package/dist/jsonrpc.d.cts +3 -3
  140. package/dist/jsonrpc.d.cts.map +1 -1
  141. package/dist/jsonrpc.d.ts +3 -3
  142. package/dist/jsonrpc.d.ts.map +1 -1
  143. package/dist/llmock.cjs +38 -2
  144. package/dist/llmock.cjs.map +1 -1
  145. package/dist/llmock.d.cts +4 -0
  146. package/dist/llmock.d.cts.map +1 -1
  147. package/dist/llmock.d.ts +4 -0
  148. package/dist/llmock.d.ts.map +1 -1
  149. package/dist/llmock.js +38 -2
  150. package/dist/llmock.js.map +1 -1
  151. package/dist/logger.cjs +5 -4
  152. package/dist/logger.cjs.map +1 -1
  153. package/dist/logger.d.cts +1 -1
  154. package/dist/logger.d.cts.map +1 -1
  155. package/dist/logger.d.ts +1 -1
  156. package/dist/logger.d.ts.map +1 -1
  157. package/dist/logger.js +5 -4
  158. package/dist/logger.js.map +1 -1
  159. package/dist/mcp-mock.d.cts +2 -2
  160. package/dist/mcp-mock.d.cts.map +1 -1
  161. package/dist/mcp-mock.d.ts +2 -2
  162. package/dist/mcp-mock.d.ts.map +1 -1
  163. package/dist/mcp-mock.js +2 -2
  164. package/dist/mcp-mock.js.map +1 -1
  165. package/dist/messages.d.cts +2 -2
  166. package/dist/messages.d.cts.map +1 -1
  167. package/dist/messages.d.ts +2 -2
  168. package/dist/messages.d.ts.map +1 -1
  169. package/dist/moderation.d.cts +3 -3
  170. package/dist/moderation.d.cts.map +1 -1
  171. package/dist/moderation.d.ts +3 -3
  172. package/dist/moderation.d.ts.map +1 -1
  173. package/dist/ndjson-writer.d.cts +2 -2
  174. package/dist/ndjson-writer.d.cts.map +1 -1
  175. package/dist/ndjson-writer.d.ts +2 -2
  176. package/dist/ndjson-writer.d.ts.map +1 -1
  177. package/dist/ollama.d.cts +3 -3
  178. package/dist/ollama.d.cts.map +1 -1
  179. package/dist/ollama.d.ts +3 -3
  180. package/dist/ollama.d.ts.map +1 -1
  181. package/dist/recorder.cjs +64 -21
  182. package/dist/recorder.cjs.map +1 -1
  183. package/dist/recorder.d.cts +2 -2
  184. package/dist/recorder.d.cts.map +1 -1
  185. package/dist/recorder.d.ts +2 -2
  186. package/dist/recorder.d.ts.map +1 -1
  187. package/dist/recorder.js +66 -23
  188. package/dist/recorder.js.map +1 -1
  189. package/dist/rerank.d.cts +3 -3
  190. package/dist/rerank.d.cts.map +1 -1
  191. package/dist/rerank.d.ts +3 -3
  192. package/dist/rerank.d.ts.map +1 -1
  193. package/dist/responses.d.cts +2 -2
  194. package/dist/responses.d.cts.map +1 -1
  195. package/dist/responses.d.ts +2 -2
  196. package/dist/responses.d.ts.map +1 -1
  197. package/dist/router.cjs +1 -1
  198. package/dist/router.cjs.map +1 -1
  199. package/dist/router.js +1 -1
  200. package/dist/router.js.map +1 -1
  201. package/dist/search.d.cts +3 -3
  202. package/dist/search.d.cts.map +1 -1
  203. package/dist/search.d.ts +3 -3
  204. package/dist/search.d.ts.map +1 -1
  205. package/dist/server.cjs +170 -1
  206. package/dist/server.cjs.map +1 -1
  207. package/dist/server.d.cts +2 -2
  208. package/dist/server.d.cts.map +1 -1
  209. package/dist/server.d.ts +2 -2
  210. package/dist/server.d.ts.map +1 -1
  211. package/dist/server.js +173 -4
  212. package/dist/server.js.map +1 -1
  213. package/dist/speech.cjs +18 -9
  214. package/dist/speech.cjs.map +1 -1
  215. package/dist/speech.d.cts +2 -2
  216. package/dist/speech.d.cts.map +1 -1
  217. package/dist/speech.d.ts +2 -2
  218. package/dist/speech.d.ts.map +1 -1
  219. package/dist/speech.js +18 -9
  220. package/dist/speech.js.map +1 -1
  221. package/dist/sse-writer.d.cts +3 -3
  222. package/dist/sse-writer.d.cts.map +1 -1
  223. package/dist/sse-writer.d.ts +3 -3
  224. package/dist/sse-writer.d.ts.map +1 -1
  225. package/dist/stream-collapse.cjs +80 -9
  226. package/dist/stream-collapse.cjs.map +1 -1
  227. package/dist/stream-collapse.d.cts +11 -1
  228. package/dist/stream-collapse.d.cts.map +1 -1
  229. package/dist/stream-collapse.d.ts +11 -1
  230. package/dist/stream-collapse.d.ts.map +1 -1
  231. package/dist/stream-collapse.js +80 -10
  232. package/dist/stream-collapse.js.map +1 -1
  233. package/dist/suite.cjs +1 -1
  234. package/dist/suite.d.cts +2 -2
  235. package/dist/suite.d.ts +2 -2
  236. package/dist/suite.js +1 -1
  237. package/dist/transcription.d.cts +2 -2
  238. package/dist/transcription.d.cts.map +1 -1
  239. package/dist/transcription.d.ts +2 -2
  240. package/dist/transcription.d.ts.map +1 -1
  241. package/dist/types.d.cts +10 -7
  242. package/dist/types.d.cts.map +1 -1
  243. package/dist/types.d.ts +10 -7
  244. package/dist/types.d.ts.map +1 -1
  245. package/dist/vector-mock.d.cts +2 -2
  246. package/dist/vector-mock.d.cts.map +1 -1
  247. package/dist/vector-mock.d.ts +2 -2
  248. package/dist/vector-mock.d.ts.map +1 -1
  249. package/dist/vector-mock.js +2 -2
  250. package/dist/vector-mock.js.map +1 -1
  251. package/dist/vector-types.d.ts.map +1 -1
  252. package/dist/video.d.cts +3 -3
  253. package/dist/video.d.cts.map +1 -1
  254. package/dist/video.d.ts +3 -3
  255. package/dist/video.d.ts.map +1 -1
  256. package/dist/vitest.cjs +1 -1
  257. package/dist/vitest.js +1 -1
  258. package/dist/ws-framing.d.cts +2 -2
  259. package/dist/ws-framing.d.cts.map +1 -1
  260. package/dist/ws-framing.d.ts +2 -2
  261. package/dist/ws-framing.d.ts.map +1 -1
  262. package/dist/ws-gemini-live.cjs +145 -2
  263. package/dist/ws-gemini-live.cjs.map +1 -1
  264. package/dist/ws-gemini-live.d.cts.map +1 -1
  265. package/dist/ws-gemini-live.d.ts.map +1 -1
  266. package/dist/ws-gemini-live.js +146 -3
  267. package/dist/ws-gemini-live.js.map +1 -1
  268. package/package.json +16 -2
  269. package/skills/write-fixtures/SKILL.md +10 -10
@@ -0,0 +1,617 @@
1
+ const require_helpers = require('./helpers.cjs');
2
+ const require_router = require('./router.cjs');
3
+ const require_sse_writer = require('./sse-writer.cjs');
4
+ const require_interruption = require('./interruption.cjs');
5
+ const require_chaos = require('./chaos.cjs');
6
+ const require_recorder = require('./recorder.cjs');
7
+
8
+ //#region src/gemini-interactions.ts
9
+ function geminiInteractionsToCompletionRequest(req) {
10
+ const messages = [];
11
+ const model = req.model ?? "gemini-2.5-flash";
12
+ if (req.system_instruction) messages.push({
13
+ role: "system",
14
+ content: req.system_instruction
15
+ });
16
+ if (req.input !== void 0) {
17
+ if (typeof req.input === "string") messages.push({
18
+ role: "user",
19
+ content: req.input
20
+ });
21
+ else if (Array.isArray(req.input)) {
22
+ const firstItem = req.input[0];
23
+ if (firstItem && "role" in firstItem) for (const turn of req.input) {
24
+ const role = turn.role === "model" ? "assistant" : turn.role;
25
+ const blocks = turn.content ?? turn.parts;
26
+ if (!blocks || blocks.length === 0) {
27
+ if (role === "user" || role === "assistant") messages.push({
28
+ role,
29
+ content: ""
30
+ });
31
+ continue;
32
+ }
33
+ const funcCallParts = blocks.filter((p) => p.type === "function_call");
34
+ const funcResultParts = blocks.filter((p) => p.type === "function_result");
35
+ const textParts = blocks.filter((p) => p.type === "text");
36
+ if (funcCallParts.length > 0) {
37
+ const textContent = textParts.map((p) => p.text ?? "").join("");
38
+ messages.push({
39
+ role: "assistant",
40
+ content: textContent || null,
41
+ tool_calls: funcCallParts.map((p) => ({
42
+ id: p.id ?? p.call_id ?? require_helpers.generateToolCallId(),
43
+ type: "function",
44
+ function: {
45
+ name: p.name ?? "",
46
+ arguments: JSON.stringify(p.arguments ?? {})
47
+ }
48
+ }))
49
+ });
50
+ } else if (funcResultParts.length > 0) {
51
+ for (const part of funcResultParts) {
52
+ const resultValue = part.result ?? part.output;
53
+ messages.push({
54
+ role: "tool",
55
+ content: typeof resultValue === "string" ? resultValue : JSON.stringify(resultValue ?? ""),
56
+ tool_call_id: part.call_id ?? part.id ?? ""
57
+ });
58
+ }
59
+ if (textParts.length > 0) {
60
+ const text = textParts.map((p) => p.text ?? "").join("");
61
+ if (text) messages.push({
62
+ role: "user",
63
+ content: text
64
+ });
65
+ }
66
+ } else {
67
+ const text = textParts.map((p) => p.text ?? "").join("");
68
+ if (role === "user" || role === "assistant" || role === "system") messages.push({
69
+ role,
70
+ content: text
71
+ });
72
+ }
73
+ }
74
+ else {
75
+ const text = req.input.filter((p) => p.type === "text").map((p) => p.text ?? "").join("");
76
+ messages.push({
77
+ role: "user",
78
+ content: text || ""
79
+ });
80
+ }
81
+ }
82
+ }
83
+ let tools;
84
+ if (req.tools && req.tools.length > 0) {
85
+ const funcTools = req.tools.filter((t) => t.type === "function");
86
+ if (funcTools.length > 0) tools = funcTools.map((t) => ({
87
+ type: "function",
88
+ function: {
89
+ name: t.name,
90
+ description: t.description,
91
+ parameters: t.parameters
92
+ }
93
+ }));
94
+ }
95
+ return {
96
+ model,
97
+ messages,
98
+ stream: req.stream !== false,
99
+ temperature: req.generation_config?.temperature,
100
+ max_tokens: req.generation_config?.max_output_tokens,
101
+ tools
102
+ };
103
+ }
104
+ let interactionCounter = 0;
105
+ function nextInteractionId() {
106
+ return `aimock-int-${interactionCounter++}`;
107
+ }
108
+ function interactionsUsage(overrides) {
109
+ if (!overrides?.usage) return {
110
+ total_input_tokens: 0,
111
+ total_output_tokens: 0,
112
+ total_tokens: 0
113
+ };
114
+ const input = overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0;
115
+ const output = overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0;
116
+ return {
117
+ total_input_tokens: input,
118
+ total_output_tokens: output,
119
+ total_tokens: input + output
120
+ };
121
+ }
122
+ function buildInteractionsTextResponse(content, model, interactionId, overrides) {
123
+ return {
124
+ id: interactionId,
125
+ status: "completed",
126
+ model: overrides?.model ?? model,
127
+ role: "model",
128
+ outputs: [{
129
+ type: "text",
130
+ text: content
131
+ }],
132
+ usage: interactionsUsage(overrides)
133
+ };
134
+ }
135
+ function buildInteractionsToolCallResponse(toolCalls, model, interactionId, logger, overrides) {
136
+ return {
137
+ id: interactionId,
138
+ status: "requires_action",
139
+ model: overrides?.model ?? model,
140
+ role: "model",
141
+ outputs: toolCalls.map((tc) => {
142
+ let argsObj;
143
+ try {
144
+ argsObj = JSON.parse(tc.arguments || "{}");
145
+ } catch {
146
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
147
+ argsObj = {};
148
+ }
149
+ return {
150
+ type: "function_call",
151
+ id: tc.id || require_helpers.generateToolCallId(),
152
+ name: tc.name,
153
+ arguments: argsObj
154
+ };
155
+ }),
156
+ usage: interactionsUsage(overrides)
157
+ };
158
+ }
159
+ function buildInteractionsContentWithToolCallsResponse(content, toolCalls, model, interactionId, logger, overrides) {
160
+ const outputs = [{
161
+ type: "text",
162
+ text: content
163
+ }];
164
+ for (const tc of toolCalls) {
165
+ let argsObj;
166
+ try {
167
+ argsObj = JSON.parse(tc.arguments || "{}");
168
+ } catch {
169
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
170
+ argsObj = {};
171
+ }
172
+ outputs.push({
173
+ type: "function_call",
174
+ id: tc.id || require_helpers.generateToolCallId(),
175
+ name: tc.name,
176
+ arguments: argsObj
177
+ });
178
+ }
179
+ return {
180
+ id: interactionId,
181
+ status: "requires_action",
182
+ model: overrides?.model ?? model,
183
+ role: "model",
184
+ outputs,
185
+ usage: interactionsUsage(overrides)
186
+ };
187
+ }
188
+ function buildInteractionsErrorResponse(message, code) {
189
+ return { error: {
190
+ code: code ?? "INVALID_ARGUMENT",
191
+ message
192
+ } };
193
+ }
194
+ let eventIdCounter = 0;
195
+ function nextEventId() {
196
+ return `evt_${++eventIdCounter}`;
197
+ }
198
+ function buildInteractionsTextSSEEvents(content, interactionId, chunkSize, overrides) {
199
+ const events = [];
200
+ events.push({
201
+ event_type: "interaction.start",
202
+ interaction: {
203
+ id: interactionId,
204
+ status: "in_progress"
205
+ },
206
+ event_id: nextEventId()
207
+ });
208
+ events.push({
209
+ event_type: "content.start",
210
+ index: 0,
211
+ content: { type: "text" },
212
+ event_id: nextEventId()
213
+ });
214
+ if (content.length === 0) events.push({
215
+ event_type: "content.delta",
216
+ index: 0,
217
+ delta: {
218
+ type: "text",
219
+ text: ""
220
+ },
221
+ event_id: nextEventId()
222
+ });
223
+ else for (let i = 0; i < content.length; i += chunkSize) {
224
+ const slice = content.slice(i, i + chunkSize);
225
+ events.push({
226
+ event_type: "content.delta",
227
+ index: 0,
228
+ delta: {
229
+ type: "text",
230
+ text: slice
231
+ },
232
+ event_id: nextEventId()
233
+ });
234
+ }
235
+ events.push({
236
+ event_type: "content.stop",
237
+ index: 0,
238
+ event_id: nextEventId()
239
+ });
240
+ events.push({
241
+ event_type: "interaction.complete",
242
+ interaction: {
243
+ id: interactionId,
244
+ status: "completed",
245
+ usage: interactionsUsage(overrides)
246
+ },
247
+ event_id: nextEventId()
248
+ });
249
+ return events;
250
+ }
251
+ function buildInteractionsToolCallSSEEvents(toolCalls, interactionId, logger, overrides) {
252
+ const events = [];
253
+ events.push({
254
+ event_type: "interaction.start",
255
+ interaction: {
256
+ id: interactionId,
257
+ status: "in_progress"
258
+ },
259
+ event_id: nextEventId()
260
+ });
261
+ for (let idx = 0; idx < toolCalls.length; idx++) {
262
+ const tc = toolCalls[idx];
263
+ let argsObj;
264
+ try {
265
+ argsObj = JSON.parse(tc.arguments || "{}");
266
+ } catch {
267
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
268
+ argsObj = {};
269
+ }
270
+ events.push({
271
+ event_type: "content.start",
272
+ index: idx,
273
+ content: { type: "function_call" },
274
+ event_id: nextEventId()
275
+ });
276
+ events.push({
277
+ event_type: "content.delta",
278
+ index: idx,
279
+ delta: {
280
+ type: "function_call",
281
+ id: tc.id || require_helpers.generateToolCallId(),
282
+ name: tc.name,
283
+ arguments: argsObj
284
+ },
285
+ event_id: nextEventId()
286
+ });
287
+ events.push({
288
+ event_type: "content.stop",
289
+ index: idx,
290
+ event_id: nextEventId()
291
+ });
292
+ }
293
+ events.push({
294
+ event_type: "interaction.complete",
295
+ interaction: {
296
+ id: interactionId,
297
+ status: "requires_action",
298
+ usage: interactionsUsage(overrides)
299
+ },
300
+ event_id: nextEventId()
301
+ });
302
+ return events;
303
+ }
304
+ function buildInteractionsContentWithToolCallsSSEEvents(content, toolCalls, interactionId, chunkSize, logger, overrides) {
305
+ const events = [];
306
+ events.push({
307
+ event_type: "interaction.start",
308
+ interaction: {
309
+ id: interactionId,
310
+ status: "in_progress"
311
+ },
312
+ event_id: nextEventId()
313
+ });
314
+ events.push({
315
+ event_type: "content.start",
316
+ index: 0,
317
+ content: { type: "text" },
318
+ event_id: nextEventId()
319
+ });
320
+ if (content.length === 0) events.push({
321
+ event_type: "content.delta",
322
+ index: 0,
323
+ delta: {
324
+ type: "text",
325
+ text: ""
326
+ },
327
+ event_id: nextEventId()
328
+ });
329
+ else for (let i = 0; i < content.length; i += chunkSize) {
330
+ const slice = content.slice(i, i + chunkSize);
331
+ events.push({
332
+ event_type: "content.delta",
333
+ index: 0,
334
+ delta: {
335
+ type: "text",
336
+ text: slice
337
+ },
338
+ event_id: nextEventId()
339
+ });
340
+ }
341
+ events.push({
342
+ event_type: "content.stop",
343
+ index: 0,
344
+ event_id: nextEventId()
345
+ });
346
+ for (let i = 0; i < toolCalls.length; i++) {
347
+ const tc = toolCalls[i];
348
+ const idx = i + 1;
349
+ let argsObj;
350
+ try {
351
+ argsObj = JSON.parse(tc.arguments || "{}");
352
+ } catch {
353
+ logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
354
+ argsObj = {};
355
+ }
356
+ events.push({
357
+ event_type: "content.start",
358
+ index: idx,
359
+ content: { type: "function_call" },
360
+ event_id: nextEventId()
361
+ });
362
+ events.push({
363
+ event_type: "content.delta",
364
+ index: idx,
365
+ delta: {
366
+ type: "function_call",
367
+ id: tc.id || require_helpers.generateToolCallId(),
368
+ name: tc.name,
369
+ arguments: argsObj
370
+ },
371
+ event_id: nextEventId()
372
+ });
373
+ events.push({
374
+ event_type: "content.stop",
375
+ index: idx,
376
+ event_id: nextEventId()
377
+ });
378
+ }
379
+ events.push({
380
+ event_type: "interaction.complete",
381
+ interaction: {
382
+ id: interactionId,
383
+ status: "requires_action",
384
+ usage: interactionsUsage(overrides)
385
+ },
386
+ event_id: nextEventId()
387
+ });
388
+ return events;
389
+ }
390
+ async function writeGeminiInteractionsSSEStream(res, events, optionsOrLatency) {
391
+ const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
392
+ const latency = opts.latency ?? 0;
393
+ const profile = opts.streamingProfile;
394
+ const signal = opts.signal;
395
+ const onChunkSent = opts.onChunkSent;
396
+ if (res.writableEnded) return true;
397
+ res.setHeader("Content-Type", "text/event-stream");
398
+ res.setHeader("Cache-Control", "no-cache");
399
+ res.setHeader("Connection", "keep-alive");
400
+ let chunkIndex = 0;
401
+ for (const event of events) {
402
+ const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency);
403
+ if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
404
+ if (signal?.aborted) return false;
405
+ if (res.writableEnded) return true;
406
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
407
+ onChunkSent?.();
408
+ if (signal?.aborted) return false;
409
+ chunkIndex++;
410
+ }
411
+ if (!res.writableEnded) res.end();
412
+ return true;
413
+ }
414
+ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
415
+ const { logger } = defaults;
416
+ setCorsHeaders(res);
417
+ const urlPath = req.url ?? "/v1beta/interactions";
418
+ let interactionsReq;
419
+ try {
420
+ interactionsReq = JSON.parse(raw);
421
+ } catch {
422
+ journal.add({
423
+ method: req.method ?? "POST",
424
+ path: urlPath,
425
+ headers: require_helpers.flattenHeaders(req.headers),
426
+ body: null,
427
+ response: {
428
+ status: 400,
429
+ fixture: null
430
+ }
431
+ });
432
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify(buildInteractionsErrorResponse("Malformed JSON", "INVALID_ARGUMENT")));
433
+ return;
434
+ }
435
+ const completionReq = geminiInteractionsToCompletionRequest(interactionsReq);
436
+ completionReq._endpointType = "chat";
437
+ const streaming = interactionsReq.stream !== false;
438
+ const model = completionReq.model;
439
+ const testId = require_helpers.getTestId(req);
440
+ const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
441
+ if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
442
+ if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
443
+ method: req.method ?? "POST",
444
+ path: urlPath,
445
+ headers: require_helpers.flattenHeaders(req.headers),
446
+ body: completionReq
447
+ }, defaults.registry, defaults.logger)) return;
448
+ if (!fixture) {
449
+ if (defaults.record) {
450
+ if (await require_recorder.proxyAndRecord(req, res, completionReq, "gemini-interactions", urlPath, fixtures, defaults, raw)) {
451
+ journal.add({
452
+ method: req.method ?? "POST",
453
+ path: urlPath,
454
+ headers: require_helpers.flattenHeaders(req.headers),
455
+ body: completionReq,
456
+ response: {
457
+ status: res.statusCode ?? 200,
458
+ fixture: null,
459
+ source: "proxy"
460
+ }
461
+ });
462
+ return;
463
+ }
464
+ }
465
+ const strictStatus = defaults.strict ? 503 : 404;
466
+ const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
467
+ if (defaults.strict) logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
468
+ journal.add({
469
+ method: req.method ?? "POST",
470
+ path: urlPath,
471
+ headers: require_helpers.flattenHeaders(req.headers),
472
+ body: completionReq,
473
+ response: {
474
+ status: strictStatus,
475
+ fixture: null
476
+ }
477
+ });
478
+ require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify(buildInteractionsErrorResponse(strictMessage, defaults.strict ? "UNAVAILABLE" : "NOT_FOUND")));
479
+ return;
480
+ }
481
+ const response = fixture.response;
482
+ const latency = fixture.latency ?? defaults.latency;
483
+ const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
484
+ if (require_helpers.isErrorResponse(response)) {
485
+ const status = response.status ?? 500;
486
+ journal.add({
487
+ method: req.method ?? "POST",
488
+ path: urlPath,
489
+ headers: require_helpers.flattenHeaders(req.headers),
490
+ body: completionReq,
491
+ response: {
492
+ status,
493
+ fixture
494
+ }
495
+ });
496
+ require_sse_writer.writeErrorResponse(res, status, JSON.stringify(buildInteractionsErrorResponse(response.error.message, response.error.type ?? "ERROR")));
497
+ return;
498
+ }
499
+ const interactionId = nextInteractionId();
500
+ if (require_helpers.isContentWithToolCallsResponse(response)) {
501
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Gemini Interactions API — ignoring");
502
+ const overrides = require_helpers.extractOverrides(response);
503
+ const journalEntry = journal.add({
504
+ method: req.method ?? "POST",
505
+ path: urlPath,
506
+ headers: require_helpers.flattenHeaders(req.headers),
507
+ body: completionReq,
508
+ response: {
509
+ status: 200,
510
+ fixture
511
+ }
512
+ });
513
+ if (!streaming) {
514
+ const body = buildInteractionsContentWithToolCallsResponse(response.content, response.toolCalls, model, interactionId, logger, overrides);
515
+ res.writeHead(200, { "Content-Type": "application/json" });
516
+ res.end(JSON.stringify(body));
517
+ } else {
518
+ const events = buildInteractionsContentWithToolCallsSSEEvents(response.content, response.toolCalls, interactionId, chunkSize, logger, overrides);
519
+ const interruption = require_interruption.createInterruptionSignal(fixture);
520
+ if (!await writeGeminiInteractionsSSEStream(res, events, {
521
+ latency,
522
+ streamingProfile: fixture.streamingProfile,
523
+ signal: interruption?.signal,
524
+ onChunkSent: interruption?.tick
525
+ })) {
526
+ if (!res.writableEnded) res.destroy();
527
+ journalEntry.response.interrupted = true;
528
+ journalEntry.response.interruptReason = interruption?.reason();
529
+ }
530
+ interruption?.cleanup();
531
+ }
532
+ return;
533
+ }
534
+ if (require_helpers.isTextResponse(response)) {
535
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Gemini Interactions API — ignoring");
536
+ const overrides = require_helpers.extractOverrides(response);
537
+ const journalEntry = journal.add({
538
+ method: req.method ?? "POST",
539
+ path: urlPath,
540
+ headers: require_helpers.flattenHeaders(req.headers),
541
+ body: completionReq,
542
+ response: {
543
+ status: 200,
544
+ fixture
545
+ }
546
+ });
547
+ if (!streaming) {
548
+ const body = buildInteractionsTextResponse(response.content, model, interactionId, overrides);
549
+ res.writeHead(200, { "Content-Type": "application/json" });
550
+ res.end(JSON.stringify(body));
551
+ } else {
552
+ const events = buildInteractionsTextSSEEvents(response.content, interactionId, chunkSize, overrides);
553
+ const interruption = require_interruption.createInterruptionSignal(fixture);
554
+ if (!await writeGeminiInteractionsSSEStream(res, events, {
555
+ latency,
556
+ streamingProfile: fixture.streamingProfile,
557
+ signal: interruption?.signal,
558
+ onChunkSent: interruption?.tick
559
+ })) {
560
+ if (!res.writableEnded) res.destroy();
561
+ journalEntry.response.interrupted = true;
562
+ journalEntry.response.interruptReason = interruption?.reason();
563
+ }
564
+ interruption?.cleanup();
565
+ }
566
+ return;
567
+ }
568
+ if (require_helpers.isToolCallResponse(response)) {
569
+ const overrides = require_helpers.extractOverrides(response);
570
+ const journalEntry = journal.add({
571
+ method: req.method ?? "POST",
572
+ path: urlPath,
573
+ headers: require_helpers.flattenHeaders(req.headers),
574
+ body: completionReq,
575
+ response: {
576
+ status: 200,
577
+ fixture
578
+ }
579
+ });
580
+ if (!streaming) {
581
+ const body = buildInteractionsToolCallResponse(response.toolCalls, model, interactionId, logger, overrides);
582
+ res.writeHead(200, { "Content-Type": "application/json" });
583
+ res.end(JSON.stringify(body));
584
+ } else {
585
+ const events = buildInteractionsToolCallSSEEvents(response.toolCalls, interactionId, logger, overrides);
586
+ const interruption = require_interruption.createInterruptionSignal(fixture);
587
+ if (!await writeGeminiInteractionsSSEStream(res, events, {
588
+ latency,
589
+ streamingProfile: fixture.streamingProfile,
590
+ signal: interruption?.signal,
591
+ onChunkSent: interruption?.tick
592
+ })) {
593
+ if (!res.writableEnded) res.destroy();
594
+ journalEntry.response.interrupted = true;
595
+ journalEntry.response.interruptReason = interruption?.reason();
596
+ }
597
+ interruption?.cleanup();
598
+ }
599
+ return;
600
+ }
601
+ journal.add({
602
+ method: req.method ?? "POST",
603
+ path: urlPath,
604
+ headers: require_helpers.flattenHeaders(req.headers),
605
+ body: completionReq,
606
+ response: {
607
+ status: 500,
608
+ fixture
609
+ }
610
+ });
611
+ require_sse_writer.writeErrorResponse(res, 500, JSON.stringify(buildInteractionsErrorResponse("Fixture response did not match any known type", "INTERNAL")));
612
+ }
613
+
614
+ //#endregion
615
+ exports.geminiInteractionsToCompletionRequest = geminiInteractionsToCompletionRequest;
616
+ exports.handleGeminiInteractions = handleGeminiInteractions;
617
+ //# sourceMappingURL=gemini-interactions.cjs.map