@copilotkit/aimock 1.22.0 → 1.23.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 +37 -0
- package/README.md +10 -10
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/aimock-cli.cjs +0 -0
- package/dist/aimock-cli.js +0 -0
- package/dist/bedrock-converse.cjs +62 -22
- 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 +62 -22
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +59 -20
- 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 +59 -20
- package/dist/bedrock.js.map +1 -1
- package/dist/cli.cjs +8 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +8 -2
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +29 -9
- 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 +30 -10
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/constants.cjs +8 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.d.cts +8 -0
- package/dist/constants.d.cts.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -0
- package/dist/elevenlabs-audio.cjs +41 -18
- 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 +42 -19
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +19 -17
- 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 +20 -18
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +128 -39
- 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 +129 -40
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +25 -8
- 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 +26 -9
- package/dist/fal.js.map +1 -1
- package/dist/fixture-loader.cjs +11 -2
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +11 -2
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +29 -7
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.js +28 -8
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +45 -19
- 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 +45 -19
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +52 -8
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +6 -0
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +6 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +52 -9
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +26 -8
- 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 +27 -9
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/journal.cjs +17 -7
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +2 -3
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +2 -3
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +15 -4
- package/dist/journal.js.map +1 -1
- package/dist/messages.cjs +33 -12
- 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 +33 -12
- package/dist/messages.js.map +1 -1
- package/dist/model-utils.cjs +11 -0
- package/dist/model-utils.cjs.map +1 -0
- package/dist/model-utils.js +10 -0
- package/dist/model-utils.js.map +1 -0
- package/dist/ollama.cjs +59 -18
- 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 -19
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +30 -11
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +30 -11
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +61 -52
- 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 +62 -53
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +7 -3
- package/dist/router.cjs.map +1 -1
- package/dist/router.js +7 -3
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +64 -180
- 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 +40 -156
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +26 -8
- 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 +27 -9
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +57 -19
- 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 +58 -20
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +19 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +19 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/video.cjs +50 -14
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +8 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +8 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +51 -15
- package/dist/video.js.map +1 -1
- package/dist/ws-gemini-live.cjs +34 -27
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +2 -2
- 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 +34 -27
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +776 -175
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +2 -2
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +776 -175
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +48 -12
- 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 +49 -13
- package/dist/ws-responses.js.map +1 -1
- package/package.json +2 -2
package/dist/ws-realtime.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
|
+
const require_constants = require('./constants.cjs');
|
|
2
3
|
const require_helpers = require('./helpers.cjs');
|
|
3
|
-
|
|
4
|
+
require('./journal.cjs');
|
|
4
5
|
const require_router = require('./router.cjs');
|
|
5
6
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
6
7
|
const require_interruption = require('./interruption.cjs');
|
|
@@ -25,12 +26,34 @@ function realtimeItemsToMessages(items, instructions, logger) {
|
|
|
25
26
|
content: instructions
|
|
26
27
|
});
|
|
27
28
|
for (const item of items) if (item.type === "message") {
|
|
28
|
-
const text = item.content?.[0]?.text ?? "";
|
|
29
29
|
const role = item.role === "assistant" ? "assistant" : item.role === "system" ? "system" : "user";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if (item.content?.some((p) => p.type === "input_text" || p.type === "input_image" || p.type === "input_audio") && item.content) {
|
|
31
|
+
const mappedContent = item.content.map((part) => {
|
|
32
|
+
if (part.type === "input_text") return {
|
|
33
|
+
type: "text",
|
|
34
|
+
text: part.text ?? ""
|
|
35
|
+
};
|
|
36
|
+
if (part.type === "input_image") return {
|
|
37
|
+
type: "image_url",
|
|
38
|
+
image_url: { url: part.url ?? "" }
|
|
39
|
+
};
|
|
40
|
+
if (part.type === "input_audio") return {
|
|
41
|
+
type: "text",
|
|
42
|
+
text: "[audio input]"
|
|
43
|
+
};
|
|
44
|
+
return part;
|
|
45
|
+
});
|
|
46
|
+
messages.push({
|
|
47
|
+
role,
|
|
48
|
+
content: mappedContent
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
const text = item.content?.[0]?.text ?? "";
|
|
52
|
+
messages.push({
|
|
53
|
+
role,
|
|
54
|
+
content: text
|
|
55
|
+
});
|
|
56
|
+
}
|
|
34
57
|
} else if (item.type === "function_call") {
|
|
35
58
|
if (!item.name) logger?.warn("Realtime function_call item missing 'name'");
|
|
36
59
|
messages.push({
|
|
@@ -55,23 +78,112 @@ function realtimeItemsToMessages(items, instructions, logger) {
|
|
|
55
78
|
}
|
|
56
79
|
return messages;
|
|
57
80
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
81
|
+
/** GA -> Beta event name mapping */
|
|
82
|
+
const GA_TO_BETA_EVENT = {
|
|
83
|
+
"response.output_text.delta": "response.text.delta",
|
|
84
|
+
"response.output_text.done": "response.text.done",
|
|
85
|
+
"response.output_audio.delta": "response.audio.delta",
|
|
86
|
+
"response.output_audio.done": "response.audio.done",
|
|
87
|
+
"response.output_audio_transcript.delta": "response.audio_transcript.delta",
|
|
88
|
+
"response.output_audio_transcript.done": "response.audio_transcript.done",
|
|
89
|
+
"conversation.item.added": "conversation.item.created"
|
|
90
|
+
};
|
|
91
|
+
/** GA -> Beta content type mapping */
|
|
92
|
+
const GA_TO_BETA_CONTENT_TYPE = {
|
|
93
|
+
output_text: "text",
|
|
94
|
+
output_audio: "audio"
|
|
95
|
+
};
|
|
96
|
+
/** Events suppressed in Beta mode (GA-only events) */
|
|
97
|
+
const BETA_SUPPRESSED_EVENTS = new Set(["conversation.item.done"]);
|
|
98
|
+
function translateGAToBeta(event) {
|
|
99
|
+
const type = event.type;
|
|
100
|
+
if (BETA_SUPPRESSED_EVENTS.has(type)) return null;
|
|
101
|
+
const translated = { ...event };
|
|
102
|
+
if (GA_TO_BETA_EVENT[type]) translated.type = GA_TO_BETA_EVENT[type];
|
|
103
|
+
if (translated.part && typeof translated.part === "object") {
|
|
104
|
+
const part = { ...translated.part };
|
|
105
|
+
if (typeof part.type === "string" && GA_TO_BETA_CONTENT_TYPE[part.type]) part.type = GA_TO_BETA_CONTENT_TYPE[part.type];
|
|
106
|
+
translated.part = part;
|
|
107
|
+
}
|
|
108
|
+
if (translated.content_part && typeof translated.content_part === "object") {
|
|
109
|
+
const cp = { ...translated.content_part };
|
|
110
|
+
if (typeof cp.type === "string" && GA_TO_BETA_CONTENT_TYPE[cp.type]) cp.type = GA_TO_BETA_CONTENT_TYPE[cp.type];
|
|
111
|
+
translated.content_part = cp;
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(translated.content)) translated.content = translated.content.map((c) => {
|
|
114
|
+
if (typeof c.type === "string" && GA_TO_BETA_CONTENT_TYPE[c.type]) return {
|
|
115
|
+
...c,
|
|
116
|
+
type: GA_TO_BETA_CONTENT_TYPE[c.type]
|
|
117
|
+
};
|
|
118
|
+
return c;
|
|
63
119
|
});
|
|
120
|
+
if (translated.item && typeof translated.item === "object") {
|
|
121
|
+
const item = { ...translated.item };
|
|
122
|
+
delete item.phase;
|
|
123
|
+
if (Array.isArray(item.content)) item.content = item.content.map((c) => {
|
|
124
|
+
if (typeof c.type === "string" && GA_TO_BETA_CONTENT_TYPE[c.type]) return {
|
|
125
|
+
...c,
|
|
126
|
+
type: GA_TO_BETA_CONTENT_TYPE[c.type]
|
|
127
|
+
};
|
|
128
|
+
return c;
|
|
129
|
+
});
|
|
130
|
+
translated.item = item;
|
|
131
|
+
}
|
|
132
|
+
if (translated.response && typeof translated.response === "object") {
|
|
133
|
+
const resp = { ...translated.response };
|
|
134
|
+
if (Array.isArray(resp.output)) resp.output = resp.output.map((outItem) => {
|
|
135
|
+
const o = { ...outItem };
|
|
136
|
+
if (Array.isArray(o.content)) o.content = o.content.map((c) => typeof c.type === "string" && GA_TO_BETA_CONTENT_TYPE[c.type] ? {
|
|
137
|
+
...c,
|
|
138
|
+
type: GA_TO_BETA_CONTENT_TYPE[c.type]
|
|
139
|
+
} : c);
|
|
140
|
+
return o;
|
|
141
|
+
});
|
|
142
|
+
translated.response = resp;
|
|
143
|
+
}
|
|
144
|
+
if (type === "session.created" || type === "session.updated") {
|
|
145
|
+
if (translated.session && typeof translated.session === "object") {
|
|
146
|
+
const session = { ...translated.session };
|
|
147
|
+
if (session.audio && typeof session.audio === "object") {
|
|
148
|
+
const audio = session.audio;
|
|
149
|
+
session.voice = audio.voice;
|
|
150
|
+
session.input_audio_format = audio.input_audio_format;
|
|
151
|
+
session.output_audio_format = audio.output_audio_format;
|
|
152
|
+
session.input_audio_transcription = audio.input_audio_transcription;
|
|
153
|
+
delete session.audio;
|
|
154
|
+
}
|
|
155
|
+
delete session.type;
|
|
156
|
+
delete session.reasoning;
|
|
157
|
+
translated.session = session;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return translated;
|
|
161
|
+
}
|
|
162
|
+
function sendEvent(ws, event, isBeta) {
|
|
163
|
+
const out = {
|
|
164
|
+
...event,
|
|
165
|
+
event_id: event.event_id ?? realtimeId("event")
|
|
166
|
+
};
|
|
167
|
+
if (isBeta) {
|
|
168
|
+
const translated = translateGAToBeta(out);
|
|
169
|
+
if (translated === null) return;
|
|
170
|
+
ws.send(JSON.stringify(translated));
|
|
171
|
+
} else ws.send(JSON.stringify(out));
|
|
64
172
|
}
|
|
65
|
-
function buildErrorRealtimeEvent(message, type = "invalid_request_error", code) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
173
|
+
function buildErrorRealtimeEvent(ws, message, isBeta, type = "invalid_request_error", code) {
|
|
174
|
+
sendEvent(ws, {
|
|
175
|
+
type: "error",
|
|
176
|
+
error: {
|
|
177
|
+
message,
|
|
178
|
+
type,
|
|
179
|
+
code
|
|
180
|
+
}
|
|
181
|
+
}, isBeta);
|
|
71
182
|
}
|
|
72
183
|
function handleWebSocketRealtime(ws, fixtures, journal, defaults) {
|
|
73
184
|
const { logger } = defaults;
|
|
74
185
|
const sessionId = realtimeId("sess");
|
|
186
|
+
const isBeta = defaults.upgradeHeaders?.["openai-beta"] ? String(defaults.upgradeHeaders["openai-beta"]).includes("realtime=v1") : false;
|
|
75
187
|
const session = {
|
|
76
188
|
model: defaults.model,
|
|
77
189
|
modalities: ["text"],
|
|
@@ -80,98 +192,253 @@ function handleWebSocketRealtime(ws, fixtures, journal, defaults) {
|
|
|
80
192
|
voice: null,
|
|
81
193
|
input_audio_format: null,
|
|
82
194
|
output_audio_format: null,
|
|
195
|
+
input_audio_noise_reduction: null,
|
|
196
|
+
input_audio_transcription: null,
|
|
83
197
|
turn_detection: null,
|
|
84
|
-
temperature: .8
|
|
198
|
+
temperature: .8,
|
|
199
|
+
type: "conversation",
|
|
200
|
+
reasoning: null
|
|
85
201
|
};
|
|
86
202
|
const conversationItems = [];
|
|
87
|
-
ws
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
203
|
+
sendEvent(ws, {
|
|
204
|
+
type: "session.created",
|
|
205
|
+
session: {
|
|
206
|
+
id: sessionId,
|
|
207
|
+
object: "realtime.session",
|
|
208
|
+
model: session.model,
|
|
209
|
+
expires_at: Math.floor(Date.now() / 1e3) + 3600,
|
|
210
|
+
modalities: session.modalities,
|
|
211
|
+
instructions: session.instructions,
|
|
212
|
+
tools: session.tools,
|
|
213
|
+
tool_choice: "auto",
|
|
214
|
+
temperature: session.temperature,
|
|
215
|
+
max_response_output_tokens: "inf",
|
|
216
|
+
audio: {
|
|
217
|
+
voice: session.voice,
|
|
218
|
+
input_audio_format: session.input_audio_format,
|
|
219
|
+
output_audio_format: session.output_audio_format,
|
|
220
|
+
input_audio_noise_reduction: session.input_audio_noise_reduction,
|
|
221
|
+
input_audio_transcription: session.input_audio_transcription
|
|
222
|
+
},
|
|
223
|
+
turn_detection: session.turn_detection,
|
|
224
|
+
type: session.type,
|
|
225
|
+
reasoning: session.reasoning
|
|
226
|
+
}
|
|
227
|
+
}, isBeta);
|
|
96
228
|
let pending = Promise.resolve();
|
|
97
229
|
ws.on("message", (raw) => {
|
|
98
|
-
pending = pending.then(() => processMessage(raw, ws, fixtures, journal, defaults, session, conversationItems).catch((err) => {
|
|
230
|
+
pending = pending.then(() => processMessage(raw, ws, fixtures, journal, defaults, session, conversationItems, isBeta).catch((err) => {
|
|
99
231
|
const msg = err instanceof Error ? err.message : "Internal error";
|
|
100
232
|
logger.error(`WebSocket realtime error: ${msg}`);
|
|
101
233
|
try {
|
|
102
|
-
|
|
103
|
-
} catch {
|
|
234
|
+
buildErrorRealtimeEvent(ws, msg, isBeta, "server_error");
|
|
235
|
+
} catch (sendErr) {
|
|
236
|
+
defaults.logger.debug(`Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : "unknown"}`);
|
|
237
|
+
}
|
|
104
238
|
}));
|
|
105
239
|
});
|
|
106
240
|
}
|
|
107
|
-
async function processMessage(raw, ws, fixtures, journal, defaults, session, conversationItems) {
|
|
241
|
+
async function processMessage(raw, ws, fixtures, journal, defaults, session, conversationItems, isBeta) {
|
|
108
242
|
let parsed;
|
|
109
243
|
try {
|
|
110
244
|
parsed = JSON.parse(raw);
|
|
111
245
|
} catch (parseErr) {
|
|
112
|
-
|
|
113
|
-
ws.send(buildErrorRealtimeEvent(`Malformed JSON: ${detail}`, "invalid_request_error", "invalid_json"));
|
|
246
|
+
buildErrorRealtimeEvent(ws, `Malformed JSON: ${parseErr instanceof Error ? parseErr.message : "unknown"}`, isBeta, "invalid_request_error", "invalid_json");
|
|
114
247
|
return;
|
|
115
248
|
}
|
|
116
249
|
const msgType = parsed.type;
|
|
117
250
|
if (msgType === "session.update") {
|
|
118
251
|
if (parsed.session) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
252
|
+
const s = parsed.session;
|
|
253
|
+
const validTypes = new Set([
|
|
254
|
+
"conversation",
|
|
255
|
+
"transcription",
|
|
256
|
+
"translation"
|
|
257
|
+
]);
|
|
258
|
+
if (s.type !== void 0) {
|
|
259
|
+
if (!validTypes.has(s.type)) {
|
|
260
|
+
sendEvent(ws, {
|
|
261
|
+
type: "error",
|
|
262
|
+
error: {
|
|
263
|
+
message: `Invalid session type: ${s.type}`,
|
|
264
|
+
type: "invalid_request_error",
|
|
265
|
+
code: "invalid_session_config"
|
|
266
|
+
}
|
|
267
|
+
}, isBeta);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const prevModel = session.model;
|
|
272
|
+
const prevType = session.type;
|
|
273
|
+
if (s.instructions !== void 0) session.instructions = s.instructions;
|
|
274
|
+
if (s.tools !== void 0) session.tools = s.tools;
|
|
275
|
+
if (s.modalities !== void 0) session.modalities = s.modalities;
|
|
276
|
+
if (s.model !== void 0) session.model = s.model;
|
|
277
|
+
if (s.temperature !== void 0) session.temperature = s.temperature;
|
|
278
|
+
if (s.type !== void 0) session.type = s.type;
|
|
279
|
+
if (s.audio) {
|
|
280
|
+
const audio = s.audio;
|
|
281
|
+
if (audio.voice !== void 0) session.voice = audio.voice;
|
|
282
|
+
if (audio.input_audio_format !== void 0) session.input_audio_format = audio.input_audio_format;
|
|
283
|
+
if (audio.output_audio_format !== void 0) session.output_audio_format = audio.output_audio_format;
|
|
284
|
+
if (audio.input_audio_noise_reduction !== void 0) session.input_audio_noise_reduction = audio.input_audio_noise_reduction;
|
|
285
|
+
if (audio.input_audio_transcription !== void 0) session.input_audio_transcription = audio.input_audio_transcription;
|
|
286
|
+
}
|
|
287
|
+
if (s.voice !== void 0) session.voice = s.voice;
|
|
288
|
+
if (s.input_audio_format !== void 0) session.input_audio_format = s.input_audio_format;
|
|
289
|
+
if (s.output_audio_format !== void 0) session.output_audio_format = s.output_audio_format;
|
|
290
|
+
if (s.reasoning !== void 0) session.reasoning = s.reasoning;
|
|
291
|
+
const transcriptionModels = new Set([
|
|
292
|
+
"gpt-4o-transcribe",
|
|
293
|
+
"gpt-4o-mini-transcribe",
|
|
294
|
+
"gpt-realtime-whisper",
|
|
295
|
+
"whisper-1"
|
|
296
|
+
]);
|
|
297
|
+
const translationModels = new Set([
|
|
298
|
+
"gpt-4o-transcribe",
|
|
299
|
+
"gpt-4o-mini-transcribe",
|
|
300
|
+
"gpt-realtime-translate"
|
|
301
|
+
]);
|
|
302
|
+
if (session.type === "transcription" && !transcriptionModels.has(session.model)) {
|
|
303
|
+
session.model = prevModel;
|
|
304
|
+
session.type = prevType;
|
|
305
|
+
sendEvent(ws, {
|
|
306
|
+
type: "error",
|
|
307
|
+
error: {
|
|
308
|
+
message: `Model ${s.model ?? prevModel} does not support session type transcription`,
|
|
309
|
+
type: "invalid_request_error",
|
|
310
|
+
code: "invalid_session_config"
|
|
311
|
+
}
|
|
312
|
+
}, isBeta);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (session.type === "translation" && !translationModels.has(session.model)) {
|
|
316
|
+
session.model = prevModel;
|
|
317
|
+
session.type = prevType;
|
|
318
|
+
sendEvent(ws, {
|
|
319
|
+
type: "error",
|
|
320
|
+
error: {
|
|
321
|
+
message: `Model ${s.model ?? prevModel} does not support session type translation`,
|
|
322
|
+
type: "invalid_request_error",
|
|
323
|
+
code: "invalid_session_config"
|
|
324
|
+
}
|
|
325
|
+
}, isBeta);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
124
328
|
}
|
|
125
|
-
ws
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
329
|
+
sendEvent(ws, {
|
|
330
|
+
type: "session.updated",
|
|
331
|
+
session: {
|
|
332
|
+
object: "realtime.session",
|
|
333
|
+
model: session.model,
|
|
334
|
+
expires_at: Math.floor(Date.now() / 1e3) + 3600,
|
|
335
|
+
modalities: session.modalities,
|
|
336
|
+
instructions: session.instructions,
|
|
337
|
+
tools: session.tools,
|
|
338
|
+
tool_choice: "auto",
|
|
339
|
+
temperature: session.temperature,
|
|
340
|
+
max_response_output_tokens: "inf",
|
|
341
|
+
audio: {
|
|
342
|
+
voice: session.voice,
|
|
343
|
+
input_audio_format: session.input_audio_format,
|
|
344
|
+
output_audio_format: session.output_audio_format,
|
|
345
|
+
input_audio_noise_reduction: session.input_audio_noise_reduction,
|
|
346
|
+
input_audio_transcription: session.input_audio_transcription
|
|
347
|
+
},
|
|
348
|
+
turn_detection: session.turn_detection,
|
|
349
|
+
type: session.type,
|
|
350
|
+
reasoning: session.reasoning
|
|
351
|
+
}
|
|
352
|
+
}, isBeta);
|
|
133
353
|
return;
|
|
134
354
|
}
|
|
135
355
|
if (msgType === "conversation.item.create") {
|
|
136
356
|
if (!parsed.item) {
|
|
137
|
-
|
|
357
|
+
buildErrorRealtimeEvent(ws, "Missing 'item' in conversation.item.create", isBeta, "invalid_request_error");
|
|
138
358
|
return;
|
|
139
359
|
}
|
|
140
360
|
const item = parsed.item;
|
|
141
361
|
if (!item.id) item.id = realtimeId("item");
|
|
142
362
|
const previousId = conversationItems.length > 0 ? conversationItems[conversationItems.length - 1].id ?? null : null;
|
|
143
363
|
conversationItems.push(item);
|
|
144
|
-
ws
|
|
364
|
+
sendEvent(ws, {
|
|
365
|
+
type: "conversation.item.added",
|
|
145
366
|
previous_item_id: previousId,
|
|
146
367
|
item
|
|
147
|
-
})
|
|
368
|
+
}, isBeta);
|
|
148
369
|
return;
|
|
149
370
|
}
|
|
150
371
|
if (msgType === "response.create") {
|
|
151
|
-
await handleResponseCreate(ws, fixtures, journal, defaults, session, conversationItems);
|
|
372
|
+
await handleResponseCreate(ws, fixtures, journal, defaults, session, conversationItems, isBeta, parsed.response);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (msgType === "input_audio_buffer.append") return;
|
|
376
|
+
if (msgType === "input_audio_buffer.commit") {
|
|
377
|
+
sendEvent(ws, { type: "input_audio_buffer.committed" }, isBeta);
|
|
378
|
+
if (session.type === "transcription" || session.type === "translation") {
|
|
379
|
+
const audioItem = {
|
|
380
|
+
type: "message",
|
|
381
|
+
id: realtimeId("item"),
|
|
382
|
+
role: "user",
|
|
383
|
+
content: [{
|
|
384
|
+
type: "input_audio",
|
|
385
|
+
transcript: null
|
|
386
|
+
}]
|
|
387
|
+
};
|
|
388
|
+
conversationItems.push(audioItem);
|
|
389
|
+
sendEvent(ws, {
|
|
390
|
+
type: "conversation.item.added",
|
|
391
|
+
item: audioItem
|
|
392
|
+
}, isBeta);
|
|
393
|
+
}
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (msgType === "input_audio_buffer.clear") {
|
|
397
|
+
sendEvent(ws, { type: "input_audio_buffer.cleared" }, isBeta);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (msgType === "response.cancel") {
|
|
401
|
+
sendEvent(ws, { type: "response.cancelled" }, isBeta);
|
|
152
402
|
return;
|
|
153
403
|
}
|
|
154
404
|
}
|
|
155
|
-
async function handleResponseCreate(ws, fixtures, journal, defaults, session, conversationItems) {
|
|
156
|
-
const messages = realtimeItemsToMessages(conversationItems, session.instructions || void 0, defaults.logger);
|
|
405
|
+
async function handleResponseCreate(ws, fixtures, journal, defaults, session, conversationItems, isBeta, responseOverrides) {
|
|
406
|
+
const messages = realtimeItemsToMessages(conversationItems, (responseOverrides?.instructions ?? session.instructions) || void 0, defaults.logger);
|
|
407
|
+
const endpointType = {
|
|
408
|
+
conversation: "realtime",
|
|
409
|
+
transcription: "realtime-transcription",
|
|
410
|
+
translation: "realtime-translation"
|
|
411
|
+
}[session.type] ?? "realtime";
|
|
157
412
|
const completionReq = {
|
|
158
413
|
model: session.model,
|
|
159
|
-
messages
|
|
414
|
+
messages,
|
|
415
|
+
_endpointType: endpointType
|
|
160
416
|
};
|
|
161
|
-
const testId = defaults.testId ??
|
|
417
|
+
const testId = defaults.testId ?? require_constants.DEFAULT_TEST_ID;
|
|
162
418
|
const fixture = require_router.matchFixture(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
163
419
|
const responseId = realtimeId("resp");
|
|
164
420
|
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
165
421
|
if (!fixture) {
|
|
166
422
|
if (require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
|
|
167
423
|
defaults.logger.warn(`STRICT: No fixture matched for WebSocket message`);
|
|
424
|
+
journal.add({
|
|
425
|
+
method: "WS",
|
|
426
|
+
path: "/v1/realtime",
|
|
427
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
428
|
+
body: completionReq,
|
|
429
|
+
response: {
|
|
430
|
+
status: 503,
|
|
431
|
+
fixture: null,
|
|
432
|
+
...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
|
|
433
|
+
}
|
|
434
|
+
});
|
|
168
435
|
ws.close(1008, "Strict mode: no fixture matched");
|
|
169
436
|
return;
|
|
170
437
|
}
|
|
171
438
|
journal.add({
|
|
172
439
|
method: "WS",
|
|
173
440
|
path: "/v1/realtime",
|
|
174
|
-
headers: {},
|
|
441
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
175
442
|
body: completionReq,
|
|
176
443
|
response: {
|
|
177
444
|
status: 404,
|
|
@@ -179,33 +446,39 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
179
446
|
...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
|
|
180
447
|
}
|
|
181
448
|
});
|
|
182
|
-
ws
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
449
|
+
sendEvent(ws, {
|
|
450
|
+
type: "response.created",
|
|
451
|
+
response: {
|
|
452
|
+
id: responseId,
|
|
453
|
+
object: "realtime.response",
|
|
454
|
+
status: "failed",
|
|
455
|
+
status_details: null,
|
|
456
|
+
output: [],
|
|
457
|
+
usage: null
|
|
458
|
+
}
|
|
459
|
+
}, isBeta);
|
|
460
|
+
sendEvent(ws, {
|
|
461
|
+
type: "response.done",
|
|
462
|
+
response: {
|
|
463
|
+
id: responseId,
|
|
464
|
+
object: "realtime.response",
|
|
465
|
+
status: "failed",
|
|
466
|
+
output: [],
|
|
467
|
+
status_details: {
|
|
468
|
+
type: "error",
|
|
469
|
+
error: {
|
|
470
|
+
message: "No fixture matched",
|
|
471
|
+
type: "invalid_request_error",
|
|
472
|
+
code: "no_fixture_match"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
usage: {
|
|
476
|
+
total_tokens: 0,
|
|
477
|
+
input_tokens: 0,
|
|
478
|
+
output_tokens: 0
|
|
201
479
|
}
|
|
202
|
-
},
|
|
203
|
-
usage: {
|
|
204
|
-
total_tokens: 0,
|
|
205
|
-
input_tokens: 0,
|
|
206
|
-
output_tokens: 0
|
|
207
480
|
}
|
|
208
|
-
}
|
|
481
|
+
}, isBeta);
|
|
209
482
|
return;
|
|
210
483
|
}
|
|
211
484
|
const response = await require_helpers.resolveResponse(fixture, completionReq);
|
|
@@ -216,47 +489,322 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
216
489
|
journal.add({
|
|
217
490
|
method: "WS",
|
|
218
491
|
path: "/v1/realtime",
|
|
219
|
-
headers: {},
|
|
492
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
220
493
|
body: completionReq,
|
|
221
494
|
response: {
|
|
222
495
|
status,
|
|
223
496
|
fixture
|
|
224
497
|
}
|
|
225
498
|
});
|
|
226
|
-
ws
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
499
|
+
sendEvent(ws, {
|
|
500
|
+
type: "response.created",
|
|
501
|
+
response: {
|
|
502
|
+
id: responseId,
|
|
503
|
+
object: "realtime.response",
|
|
504
|
+
status: "failed",
|
|
505
|
+
status_details: null,
|
|
506
|
+
output: [],
|
|
507
|
+
usage: null
|
|
508
|
+
}
|
|
509
|
+
}, isBeta);
|
|
510
|
+
sendEvent(ws, {
|
|
511
|
+
type: "response.done",
|
|
512
|
+
response: {
|
|
513
|
+
id: responseId,
|
|
514
|
+
object: "realtime.response",
|
|
515
|
+
status: "failed",
|
|
516
|
+
output: [],
|
|
517
|
+
status_details: {
|
|
518
|
+
type: "error",
|
|
519
|
+
error: {
|
|
520
|
+
message: response.error.message,
|
|
521
|
+
type: response.error.type,
|
|
522
|
+
code: response.error.code
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
usage: {
|
|
526
|
+
total_tokens: 0,
|
|
527
|
+
input_tokens: 0,
|
|
528
|
+
output_tokens: 0
|
|
245
529
|
}
|
|
246
|
-
},
|
|
247
|
-
usage: {
|
|
248
|
-
total_tokens: 0,
|
|
249
|
-
input_tokens: 0,
|
|
250
|
-
output_tokens: 0
|
|
251
530
|
}
|
|
252
|
-
}
|
|
531
|
+
}, isBeta);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (require_helpers.isContentWithToolCallsResponse(response)) {
|
|
535
|
+
const journalEntry = journal.add({
|
|
536
|
+
method: "WS",
|
|
537
|
+
path: "/v1/realtime",
|
|
538
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
539
|
+
body: completionReq,
|
|
540
|
+
response: {
|
|
541
|
+
status: 200,
|
|
542
|
+
fixture
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
sendEvent(ws, {
|
|
546
|
+
type: "response.created",
|
|
547
|
+
response: {
|
|
548
|
+
id: responseId,
|
|
549
|
+
object: "realtime.response",
|
|
550
|
+
status: "in_progress",
|
|
551
|
+
status_details: null,
|
|
552
|
+
output: [],
|
|
553
|
+
usage: null
|
|
554
|
+
}
|
|
555
|
+
}, isBeta);
|
|
556
|
+
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
557
|
+
let interrupted = false;
|
|
558
|
+
const allOutputItems = [];
|
|
559
|
+
const textItemId = realtimeId("item");
|
|
560
|
+
const contentIndex = 0;
|
|
561
|
+
const textOutputIndex = 0;
|
|
562
|
+
const textOutputItem = {
|
|
563
|
+
id: textItemId,
|
|
564
|
+
type: "message",
|
|
565
|
+
role: "assistant",
|
|
566
|
+
status: "completed",
|
|
567
|
+
content: [{
|
|
568
|
+
type: "output_text",
|
|
569
|
+
text: response.content
|
|
570
|
+
}]
|
|
571
|
+
};
|
|
572
|
+
const textPhase = response.toolCalls && response.toolCalls.length > 0 ? "commentary" : "final_answer";
|
|
573
|
+
sendEvent(ws, {
|
|
574
|
+
type: "response.output_item.added",
|
|
575
|
+
response_id: responseId,
|
|
576
|
+
output_index: textOutputIndex,
|
|
577
|
+
item: {
|
|
578
|
+
id: textItemId,
|
|
579
|
+
type: "message",
|
|
580
|
+
role: "assistant",
|
|
581
|
+
status: "in_progress",
|
|
582
|
+
content: [],
|
|
583
|
+
phase: textPhase
|
|
584
|
+
}
|
|
585
|
+
}, isBeta);
|
|
586
|
+
sendEvent(ws, {
|
|
587
|
+
type: "response.content_part.added",
|
|
588
|
+
response_id: responseId,
|
|
589
|
+
item_id: textItemId,
|
|
590
|
+
output_index: textOutputIndex,
|
|
591
|
+
content_index: contentIndex,
|
|
592
|
+
part: {
|
|
593
|
+
type: "output_text",
|
|
594
|
+
text: ""
|
|
595
|
+
}
|
|
596
|
+
}, isBeta);
|
|
597
|
+
const content = response.content;
|
|
598
|
+
for (let i = 0; i < content.length; i += chunkSize) {
|
|
599
|
+
if (ws.isClosed) break;
|
|
600
|
+
if (latency > 0) await require_sse_writer.delay(latency, interruption?.signal);
|
|
601
|
+
if (interruption?.signal.aborted) {
|
|
602
|
+
interrupted = true;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
if (ws.isClosed) break;
|
|
606
|
+
sendEvent(ws, {
|
|
607
|
+
type: "response.output_text.delta",
|
|
608
|
+
response_id: responseId,
|
|
609
|
+
item_id: textItemId,
|
|
610
|
+
output_index: textOutputIndex,
|
|
611
|
+
content_index: contentIndex,
|
|
612
|
+
delta: content.slice(i, i + chunkSize)
|
|
613
|
+
}, isBeta);
|
|
614
|
+
interruption?.tick();
|
|
615
|
+
if (interruption?.signal.aborted) {
|
|
616
|
+
interrupted = true;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (interrupted) {
|
|
621
|
+
ws.destroy();
|
|
622
|
+
journalEntry.response.interrupted = true;
|
|
623
|
+
journalEntry.response.interruptReason = interruption?.reason();
|
|
624
|
+
interruption?.cleanup();
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (ws.isClosed) {
|
|
628
|
+
interruption?.cleanup();
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
sendEvent(ws, {
|
|
632
|
+
type: "response.output_text.done",
|
|
633
|
+
response_id: responseId,
|
|
634
|
+
item_id: textItemId,
|
|
635
|
+
output_index: textOutputIndex,
|
|
636
|
+
content_index: contentIndex,
|
|
637
|
+
text: content
|
|
638
|
+
}, isBeta);
|
|
639
|
+
if (ws.isClosed) {
|
|
640
|
+
interruption?.cleanup();
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
sendEvent(ws, {
|
|
644
|
+
type: "response.content_part.done",
|
|
645
|
+
response_id: responseId,
|
|
646
|
+
item_id: textItemId,
|
|
647
|
+
output_index: textOutputIndex,
|
|
648
|
+
content_index: contentIndex,
|
|
649
|
+
part: {
|
|
650
|
+
type: "output_text",
|
|
651
|
+
text: content
|
|
652
|
+
}
|
|
653
|
+
}, isBeta);
|
|
654
|
+
if (ws.isClosed) {
|
|
655
|
+
interruption?.cleanup();
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
sendEvent(ws, {
|
|
659
|
+
type: "response.output_item.done",
|
|
660
|
+
response_id: responseId,
|
|
661
|
+
output_index: textOutputIndex,
|
|
662
|
+
item: {
|
|
663
|
+
...textOutputItem,
|
|
664
|
+
phase: textPhase
|
|
665
|
+
}
|
|
666
|
+
}, isBeta);
|
|
667
|
+
sendEvent(ws, {
|
|
668
|
+
type: "conversation.item.done",
|
|
669
|
+
item: {
|
|
670
|
+
id: textItemId,
|
|
671
|
+
object: "realtime.item",
|
|
672
|
+
type: "message",
|
|
673
|
+
role: "assistant",
|
|
674
|
+
status: "completed",
|
|
675
|
+
content: textOutputItem.content
|
|
676
|
+
}
|
|
677
|
+
}, isBeta);
|
|
678
|
+
if (ws.isClosed) {
|
|
679
|
+
interruption?.cleanup();
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
allOutputItems.push(textOutputItem);
|
|
683
|
+
for (let tcIdx = 0; tcIdx < response.toolCalls.length; tcIdx++) {
|
|
684
|
+
const tc = response.toolCalls[tcIdx];
|
|
685
|
+
const callId = tc.id ?? require_helpers.generateToolCallId();
|
|
686
|
+
const itemId = realtimeId("item");
|
|
687
|
+
const outputIndex = tcIdx + 1;
|
|
688
|
+
const toolOutputItem = {
|
|
689
|
+
id: itemId,
|
|
690
|
+
type: "function_call",
|
|
691
|
+
status: "completed",
|
|
692
|
+
call_id: callId,
|
|
693
|
+
name: tc.name,
|
|
694
|
+
arguments: tc.arguments
|
|
695
|
+
};
|
|
696
|
+
sendEvent(ws, {
|
|
697
|
+
type: "response.output_item.added",
|
|
698
|
+
response_id: responseId,
|
|
699
|
+
output_index: outputIndex,
|
|
700
|
+
item: {
|
|
701
|
+
id: itemId,
|
|
702
|
+
type: "function_call",
|
|
703
|
+
status: "in_progress",
|
|
704
|
+
call_id: callId,
|
|
705
|
+
name: tc.name,
|
|
706
|
+
arguments: "",
|
|
707
|
+
phase: "final_answer"
|
|
708
|
+
}
|
|
709
|
+
}, isBeta);
|
|
710
|
+
const args = tc.arguments;
|
|
711
|
+
for (let i = 0; i < args.length; i += chunkSize) {
|
|
712
|
+
if (ws.isClosed) break;
|
|
713
|
+
if (latency > 0) await require_sse_writer.delay(latency, interruption?.signal);
|
|
714
|
+
if (interruption?.signal.aborted) {
|
|
715
|
+
interrupted = true;
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
if (ws.isClosed) break;
|
|
719
|
+
sendEvent(ws, {
|
|
720
|
+
type: "response.function_call_arguments.delta",
|
|
721
|
+
response_id: responseId,
|
|
722
|
+
item_id: itemId,
|
|
723
|
+
output_index: outputIndex,
|
|
724
|
+
call_id: callId,
|
|
725
|
+
delta: args.slice(i, i + chunkSize)
|
|
726
|
+
}, isBeta);
|
|
727
|
+
interruption?.tick();
|
|
728
|
+
if (interruption?.signal.aborted) {
|
|
729
|
+
interrupted = true;
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (interrupted) break;
|
|
734
|
+
if (ws.isClosed) break;
|
|
735
|
+
sendEvent(ws, {
|
|
736
|
+
type: "response.function_call_arguments.done",
|
|
737
|
+
response_id: responseId,
|
|
738
|
+
item_id: itemId,
|
|
739
|
+
output_index: outputIndex,
|
|
740
|
+
call_id: callId,
|
|
741
|
+
arguments: args
|
|
742
|
+
}, isBeta);
|
|
743
|
+
if (ws.isClosed) break;
|
|
744
|
+
sendEvent(ws, {
|
|
745
|
+
type: "response.output_item.done",
|
|
746
|
+
response_id: responseId,
|
|
747
|
+
output_index: outputIndex,
|
|
748
|
+
item: {
|
|
749
|
+
...toolOutputItem,
|
|
750
|
+
phase: "final_answer"
|
|
751
|
+
}
|
|
752
|
+
}, isBeta);
|
|
753
|
+
sendEvent(ws, {
|
|
754
|
+
type: "conversation.item.done",
|
|
755
|
+
item: {
|
|
756
|
+
id: itemId,
|
|
757
|
+
object: "realtime.item",
|
|
758
|
+
type: "function_call",
|
|
759
|
+
status: "completed",
|
|
760
|
+
call_id: callId,
|
|
761
|
+
name: tc.name,
|
|
762
|
+
arguments: args
|
|
763
|
+
}
|
|
764
|
+
}, isBeta);
|
|
765
|
+
if (ws.isClosed) break;
|
|
766
|
+
allOutputItems.push(toolOutputItem);
|
|
767
|
+
}
|
|
768
|
+
if (interrupted) {
|
|
769
|
+
ws.destroy();
|
|
770
|
+
journalEntry.response.interrupted = true;
|
|
771
|
+
journalEntry.response.interruptReason = interruption?.reason();
|
|
772
|
+
interruption?.cleanup();
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
interruption?.cleanup();
|
|
776
|
+
if (ws.isClosed) return;
|
|
777
|
+
sendEvent(ws, {
|
|
778
|
+
type: "response.done",
|
|
779
|
+
response: {
|
|
780
|
+
id: responseId,
|
|
781
|
+
object: "realtime.response",
|
|
782
|
+
status: "completed",
|
|
783
|
+
output: allOutputItems,
|
|
784
|
+
usage: {
|
|
785
|
+
total_tokens: 0,
|
|
786
|
+
input_tokens: 0,
|
|
787
|
+
output_tokens: 0
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}, isBeta);
|
|
791
|
+
conversationItems.push({
|
|
792
|
+
type: "message",
|
|
793
|
+
id: textItemId,
|
|
794
|
+
role: "assistant",
|
|
795
|
+
content: [{
|
|
796
|
+
type: "text",
|
|
797
|
+
text: content
|
|
798
|
+
}]
|
|
799
|
+
});
|
|
800
|
+
for (const item of allOutputItems.slice(1)) conversationItems.push(item);
|
|
253
801
|
return;
|
|
254
802
|
}
|
|
255
803
|
if (require_helpers.isTextResponse(response)) {
|
|
256
804
|
const journalEntry = journal.add({
|
|
257
805
|
method: "WS",
|
|
258
806
|
path: "/v1/realtime",
|
|
259
|
-
headers: {},
|
|
807
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
260
808
|
body: completionReq,
|
|
261
809
|
response: {
|
|
262
810
|
status: 200,
|
|
@@ -272,19 +820,23 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
272
820
|
role: "assistant",
|
|
273
821
|
status: "completed",
|
|
274
822
|
content: [{
|
|
275
|
-
type: "
|
|
823
|
+
type: "output_text",
|
|
276
824
|
text: response.content
|
|
277
825
|
}]
|
|
278
826
|
};
|
|
279
|
-
ws
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
827
|
+
sendEvent(ws, {
|
|
828
|
+
type: "response.created",
|
|
829
|
+
response: {
|
|
830
|
+
id: responseId,
|
|
831
|
+
object: "realtime.response",
|
|
832
|
+
status: "in_progress",
|
|
833
|
+
status_details: null,
|
|
834
|
+
output: [],
|
|
835
|
+
usage: null
|
|
836
|
+
}
|
|
837
|
+
}, isBeta);
|
|
838
|
+
sendEvent(ws, {
|
|
839
|
+
type: "response.output_item.added",
|
|
288
840
|
response_id: responseId,
|
|
289
841
|
output_index: outputIndex,
|
|
290
842
|
item: {
|
|
@@ -292,19 +844,21 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
292
844
|
type: "message",
|
|
293
845
|
role: "assistant",
|
|
294
846
|
status: "in_progress",
|
|
295
|
-
content: []
|
|
847
|
+
content: [],
|
|
848
|
+
phase: "final_answer"
|
|
296
849
|
}
|
|
297
|
-
})
|
|
298
|
-
ws
|
|
850
|
+
}, isBeta);
|
|
851
|
+
sendEvent(ws, {
|
|
852
|
+
type: "response.content_part.added",
|
|
299
853
|
response_id: responseId,
|
|
300
854
|
item_id: itemId,
|
|
301
855
|
output_index: outputIndex,
|
|
302
856
|
content_index: contentIndex,
|
|
303
857
|
part: {
|
|
304
|
-
type: "
|
|
858
|
+
type: "output_text",
|
|
305
859
|
text: ""
|
|
306
860
|
}
|
|
307
|
-
})
|
|
861
|
+
}, isBeta);
|
|
308
862
|
const content = response.content;
|
|
309
863
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
310
864
|
let interrupted = false;
|
|
@@ -316,14 +870,14 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
316
870
|
break;
|
|
317
871
|
}
|
|
318
872
|
if (ws.isClosed) break;
|
|
319
|
-
|
|
320
|
-
|
|
873
|
+
sendEvent(ws, {
|
|
874
|
+
type: "response.output_text.delta",
|
|
321
875
|
response_id: responseId,
|
|
322
876
|
item_id: itemId,
|
|
323
877
|
output_index: outputIndex,
|
|
324
878
|
content_index: contentIndex,
|
|
325
|
-
delta:
|
|
326
|
-
})
|
|
879
|
+
delta: content.slice(i, i + chunkSize)
|
|
880
|
+
}, isBeta);
|
|
327
881
|
interruption?.tick();
|
|
328
882
|
if (interruption?.signal.aborted) {
|
|
329
883
|
interrupted = true;
|
|
@@ -339,39 +893,59 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
339
893
|
}
|
|
340
894
|
interruption?.cleanup();
|
|
341
895
|
if (ws.isClosed) return;
|
|
342
|
-
ws
|
|
896
|
+
sendEvent(ws, {
|
|
897
|
+
type: "response.output_text.done",
|
|
343
898
|
response_id: responseId,
|
|
344
899
|
item_id: itemId,
|
|
345
900
|
output_index: outputIndex,
|
|
346
901
|
content_index: contentIndex,
|
|
347
902
|
text: content
|
|
348
|
-
})
|
|
349
|
-
ws
|
|
903
|
+
}, isBeta);
|
|
904
|
+
sendEvent(ws, {
|
|
905
|
+
type: "response.content_part.done",
|
|
350
906
|
response_id: responseId,
|
|
351
907
|
item_id: itemId,
|
|
352
908
|
output_index: outputIndex,
|
|
353
909
|
content_index: contentIndex,
|
|
354
910
|
part: {
|
|
355
|
-
type: "
|
|
911
|
+
type: "output_text",
|
|
356
912
|
text: content
|
|
357
913
|
}
|
|
358
|
-
})
|
|
359
|
-
ws
|
|
914
|
+
}, isBeta);
|
|
915
|
+
sendEvent(ws, {
|
|
916
|
+
type: "response.output_item.done",
|
|
360
917
|
response_id: responseId,
|
|
361
918
|
output_index: outputIndex,
|
|
362
|
-
item:
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
919
|
+
item: {
|
|
920
|
+
...outputItem,
|
|
921
|
+
phase: "final_answer"
|
|
922
|
+
}
|
|
923
|
+
}, isBeta);
|
|
924
|
+
sendEvent(ws, {
|
|
925
|
+
type: "conversation.item.done",
|
|
926
|
+
item: {
|
|
927
|
+
id: itemId,
|
|
928
|
+
object: "realtime.item",
|
|
929
|
+
type: "message",
|
|
930
|
+
role: "assistant",
|
|
931
|
+
status: "completed",
|
|
932
|
+
content: outputItem.content
|
|
933
|
+
}
|
|
934
|
+
}, isBeta);
|
|
935
|
+
sendEvent(ws, {
|
|
936
|
+
type: "response.done",
|
|
937
|
+
response: {
|
|
938
|
+
id: responseId,
|
|
939
|
+
object: "realtime.response",
|
|
940
|
+
status: "completed",
|
|
941
|
+
output: [outputItem],
|
|
942
|
+
usage: {
|
|
943
|
+
total_tokens: 0,
|
|
944
|
+
input_tokens: 0,
|
|
945
|
+
output_tokens: 0
|
|
946
|
+
}
|
|
373
947
|
}
|
|
374
|
-
}
|
|
948
|
+
}, isBeta);
|
|
375
949
|
conversationItems.push({
|
|
376
950
|
type: "message",
|
|
377
951
|
id: itemId,
|
|
@@ -387,21 +961,24 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
387
961
|
const journalEntry = journal.add({
|
|
388
962
|
method: "WS",
|
|
389
963
|
path: "/v1/realtime",
|
|
390
|
-
headers: {},
|
|
964
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
391
965
|
body: completionReq,
|
|
392
966
|
response: {
|
|
393
967
|
status: 200,
|
|
394
968
|
fixture
|
|
395
969
|
}
|
|
396
970
|
});
|
|
397
|
-
ws
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
971
|
+
sendEvent(ws, {
|
|
972
|
+
type: "response.created",
|
|
973
|
+
response: {
|
|
974
|
+
id: responseId,
|
|
975
|
+
object: "realtime.response",
|
|
976
|
+
status: "in_progress",
|
|
977
|
+
status_details: null,
|
|
978
|
+
output: [],
|
|
979
|
+
usage: null
|
|
980
|
+
}
|
|
981
|
+
}, isBeta);
|
|
405
982
|
const outputItems = [];
|
|
406
983
|
const interruption = require_interruption.createInterruptionSignal(fixture);
|
|
407
984
|
let interrupted = false;
|
|
@@ -417,7 +994,8 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
417
994
|
name: tc.name,
|
|
418
995
|
arguments: tc.arguments
|
|
419
996
|
};
|
|
420
|
-
ws
|
|
997
|
+
sendEvent(ws, {
|
|
998
|
+
type: "response.output_item.added",
|
|
421
999
|
response_id: responseId,
|
|
422
1000
|
output_index: tcIdx,
|
|
423
1001
|
item: {
|
|
@@ -426,9 +1004,10 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
426
1004
|
status: "in_progress",
|
|
427
1005
|
call_id: callId,
|
|
428
1006
|
name: tc.name,
|
|
429
|
-
arguments: ""
|
|
1007
|
+
arguments: "",
|
|
1008
|
+
phase: "final_answer"
|
|
430
1009
|
}
|
|
431
|
-
})
|
|
1010
|
+
}, isBeta);
|
|
432
1011
|
const args = tc.arguments;
|
|
433
1012
|
for (let i = 0; i < args.length; i += chunkSize) {
|
|
434
1013
|
if (ws.isClosed) break;
|
|
@@ -439,13 +1018,14 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
439
1018
|
}
|
|
440
1019
|
if (ws.isClosed) break;
|
|
441
1020
|
const chunk = args.slice(i, i + chunkSize);
|
|
442
|
-
ws
|
|
1021
|
+
sendEvent(ws, {
|
|
1022
|
+
type: "response.function_call_arguments.delta",
|
|
443
1023
|
response_id: responseId,
|
|
444
1024
|
item_id: itemId,
|
|
445
1025
|
output_index: tcIdx,
|
|
446
1026
|
call_id: callId,
|
|
447
1027
|
delta: chunk
|
|
448
|
-
})
|
|
1028
|
+
}, isBeta);
|
|
449
1029
|
interruption?.tick();
|
|
450
1030
|
if (interruption?.signal.aborted) {
|
|
451
1031
|
interrupted = true;
|
|
@@ -453,18 +1033,36 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
453
1033
|
}
|
|
454
1034
|
}
|
|
455
1035
|
if (interrupted) break;
|
|
456
|
-
ws.
|
|
1036
|
+
if (ws.isClosed) break;
|
|
1037
|
+
sendEvent(ws, {
|
|
1038
|
+
type: "response.function_call_arguments.done",
|
|
457
1039
|
response_id: responseId,
|
|
458
1040
|
item_id: itemId,
|
|
459
1041
|
output_index: tcIdx,
|
|
460
1042
|
call_id: callId,
|
|
461
1043
|
arguments: args
|
|
462
|
-
})
|
|
463
|
-
ws
|
|
1044
|
+
}, isBeta);
|
|
1045
|
+
sendEvent(ws, {
|
|
1046
|
+
type: "response.output_item.done",
|
|
464
1047
|
response_id: responseId,
|
|
465
1048
|
output_index: tcIdx,
|
|
466
|
-
item:
|
|
467
|
-
|
|
1049
|
+
item: {
|
|
1050
|
+
...outputItem,
|
|
1051
|
+
phase: "final_answer"
|
|
1052
|
+
}
|
|
1053
|
+
}, isBeta);
|
|
1054
|
+
sendEvent(ws, {
|
|
1055
|
+
type: "conversation.item.done",
|
|
1056
|
+
item: {
|
|
1057
|
+
id: itemId,
|
|
1058
|
+
object: "realtime.item",
|
|
1059
|
+
type: "function_call",
|
|
1060
|
+
status: "completed",
|
|
1061
|
+
call_id: callId,
|
|
1062
|
+
name: tc.name,
|
|
1063
|
+
arguments: args
|
|
1064
|
+
}
|
|
1065
|
+
}, isBeta);
|
|
468
1066
|
outputItems.push(outputItem);
|
|
469
1067
|
}
|
|
470
1068
|
if (interrupted) {
|
|
@@ -476,31 +1074,34 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
|
|
|
476
1074
|
}
|
|
477
1075
|
interruption?.cleanup();
|
|
478
1076
|
if (ws.isClosed) return;
|
|
479
|
-
ws
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
1077
|
+
sendEvent(ws, {
|
|
1078
|
+
type: "response.done",
|
|
1079
|
+
response: {
|
|
1080
|
+
id: responseId,
|
|
1081
|
+
object: "realtime.response",
|
|
1082
|
+
status: "completed",
|
|
1083
|
+
output: outputItems,
|
|
1084
|
+
usage: {
|
|
1085
|
+
total_tokens: 0,
|
|
1086
|
+
input_tokens: 0,
|
|
1087
|
+
output_tokens: 0
|
|
1088
|
+
}
|
|
488
1089
|
}
|
|
489
|
-
}
|
|
1090
|
+
}, isBeta);
|
|
490
1091
|
for (const item of outputItems) conversationItems.push(item);
|
|
491
1092
|
return;
|
|
492
1093
|
}
|
|
493
1094
|
journal.add({
|
|
494
1095
|
method: "WS",
|
|
495
1096
|
path: "/v1/realtime",
|
|
496
|
-
headers: {},
|
|
1097
|
+
headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
|
|
497
1098
|
body: completionReq,
|
|
498
1099
|
response: {
|
|
499
1100
|
status: 500,
|
|
500
1101
|
fixture
|
|
501
1102
|
}
|
|
502
1103
|
});
|
|
503
|
-
|
|
1104
|
+
buildErrorRealtimeEvent(ws, "Fixture response did not match any known type", isBeta, "server_error");
|
|
504
1105
|
}
|
|
505
1106
|
|
|
506
1107
|
//#endregion
|