@cognidesk/voice-openai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +34 -0
- package/dist/index.js +553 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { RealtimeClientEvent, RealtimeServerEvent, RealtimeSessionCreateRequest } from 'openai/resources/realtime/realtime';
|
|
3
|
+
import { VoiceProvider } from '@cognidesk/voice-websocket';
|
|
4
|
+
|
|
5
|
+
declare const OPENAI_REALTIME_V1_MODEL = "gpt-realtime-2";
|
|
6
|
+
interface OpenAIVoiceProviderOptions {
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
client?: OpenAI;
|
|
9
|
+
model?: typeof OPENAI_REALTIME_V1_MODEL;
|
|
10
|
+
voice?: string;
|
|
11
|
+
transcriptionLanguage?: string;
|
|
12
|
+
baseURL?: string;
|
|
13
|
+
realtime?: OpenAIRealtimeFactory;
|
|
14
|
+
sessionDefaults?: Partial<RealtimeSessionCreateRequest>;
|
|
15
|
+
}
|
|
16
|
+
interface OpenAIRealtimeFactoryInput {
|
|
17
|
+
client: OpenAI;
|
|
18
|
+
model: typeof OPENAI_REALTIME_V1_MODEL;
|
|
19
|
+
}
|
|
20
|
+
interface OpenAIRealtimeSocket {
|
|
21
|
+
send(event: RealtimeClientEvent): void;
|
|
22
|
+
close(props?: {
|
|
23
|
+
code: number;
|
|
24
|
+
reason: string;
|
|
25
|
+
}): void;
|
|
26
|
+
on(event: "event", listener: (event: RealtimeServerEvent) => void): void;
|
|
27
|
+
on(event: "error", listener: (error: unknown) => void): void;
|
|
28
|
+
}
|
|
29
|
+
type OpenAIRealtimeFactory = (input: OpenAIRealtimeFactoryInput) => Promise<OpenAIRealtimeSocket>;
|
|
30
|
+
declare function createOpenAIVoiceProvider(options?: OpenAIVoiceProviderOptions): VoiceProvider;
|
|
31
|
+
declare const createOpenAIVoiceAdapter: typeof createOpenAIVoiceProvider;
|
|
32
|
+
declare function createOpenAIRealtimeSocket(input: OpenAIRealtimeFactoryInput): Promise<OpenAIRealtimeSocket>;
|
|
33
|
+
|
|
34
|
+
export { OPENAI_REALTIME_V1_MODEL, type OpenAIRealtimeFactory, type OpenAIRealtimeFactoryInput, type OpenAIRealtimeSocket, type OpenAIVoiceProviderOptions, createOpenAIRealtimeSocket, createOpenAIVoiceAdapter, createOpenAIVoiceProvider };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import { OpenAIRealtimeWS } from "openai/realtime/ws";
|
|
4
|
+
var OPENAI_REALTIME_V1_MODEL = "gpt-realtime-2";
|
|
5
|
+
function createOpenAIVoiceProvider(options = {}) {
|
|
6
|
+
const model = options.model ?? OPENAI_REALTIME_V1_MODEL;
|
|
7
|
+
if (model !== OPENAI_REALTIME_V1_MODEL) {
|
|
8
|
+
throw new Error(`@cognidesk/voice-openai v1 supports only ${OPENAI_REALTIME_V1_MODEL}.`);
|
|
9
|
+
}
|
|
10
|
+
const client = options.client ?? new OpenAI({
|
|
11
|
+
apiKey: options.apiKey,
|
|
12
|
+
...options.baseURL ? { baseURL: options.baseURL } : {}
|
|
13
|
+
});
|
|
14
|
+
const realtime = options.realtime ?? createOpenAIRealtimeSocket;
|
|
15
|
+
return {
|
|
16
|
+
id: "openai-realtime-ws",
|
|
17
|
+
async connect(input) {
|
|
18
|
+
const profileModel = input.profile?.modelSet;
|
|
19
|
+
assertSupportedModel(profileModel);
|
|
20
|
+
const socket = await realtime({ client, model });
|
|
21
|
+
let speechQueue = Promise.resolve();
|
|
22
|
+
let pendingSpeech = null;
|
|
23
|
+
const sendProviderEvent = async (event) => {
|
|
24
|
+
await input.onEvent(event);
|
|
25
|
+
};
|
|
26
|
+
socket.on("event", (event) => {
|
|
27
|
+
resolvePendingSpeech(event);
|
|
28
|
+
if (input.control && event.type === "response.function_call_arguments.done") {
|
|
29
|
+
void handleControlToolCall({
|
|
30
|
+
event,
|
|
31
|
+
socket,
|
|
32
|
+
control: input.control,
|
|
33
|
+
session: input.session,
|
|
34
|
+
signal: input.signal,
|
|
35
|
+
sendProviderEvent
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
void handleRealtimeEvent(event, sendProviderEvent);
|
|
40
|
+
});
|
|
41
|
+
socket.on("error", (error) => {
|
|
42
|
+
rejectPendingSpeech(error);
|
|
43
|
+
void sendProviderEvent({
|
|
44
|
+
kind: "error",
|
|
45
|
+
code: "openai_realtime_error",
|
|
46
|
+
message: error instanceof Error ? error.message : "OpenAI Realtime socket error.",
|
|
47
|
+
details: error
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
socket.send({
|
|
51
|
+
type: "session.update",
|
|
52
|
+
session: buildRealtimeSession({
|
|
53
|
+
...options.sessionDefaults ? { defaults: options.sessionDefaults } : {},
|
|
54
|
+
model,
|
|
55
|
+
...profileModel?.voice ?? options.voice ? { voice: profileModel?.voice ?? options.voice } : {},
|
|
56
|
+
...input.profile?.instructions ? { instructions: input.profile.instructions } : {},
|
|
57
|
+
...options.transcriptionLanguage ? { transcriptionLanguage: options.transcriptionLanguage } : {},
|
|
58
|
+
...input.control ? { control: input.control } : {}
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
send(event) {
|
|
63
|
+
const translated = translateBrowserEvent(event);
|
|
64
|
+
if (translated) socket.send(translated);
|
|
65
|
+
},
|
|
66
|
+
speak({ text }) {
|
|
67
|
+
return enqueueSpeech((id) => createSpeechResponse({ id, text }));
|
|
68
|
+
},
|
|
69
|
+
preamble({ text }) {
|
|
70
|
+
return enqueueSpeech((id) => createPreambleResponse({ id, text }));
|
|
71
|
+
},
|
|
72
|
+
close() {
|
|
73
|
+
resolvePendingSpeech();
|
|
74
|
+
socket.close({ code: 1e3, reason: "Cognidesk voice session closed" });
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
function enqueueSpeech(createEvent) {
|
|
78
|
+
const id = createId("cognidesk_voice_speech");
|
|
79
|
+
const queued = speechQueue.catch(() => void 0).then(
|
|
80
|
+
() => new Promise((resolve, reject) => {
|
|
81
|
+
const timeout = setTimeout(() => {
|
|
82
|
+
if (pendingSpeech?.id === id) {
|
|
83
|
+
pendingSpeech = null;
|
|
84
|
+
resolve();
|
|
85
|
+
}
|
|
86
|
+
}, 9e4);
|
|
87
|
+
pendingSpeech = { id, resolve, reject, timeout };
|
|
88
|
+
try {
|
|
89
|
+
socket.send(createEvent(id));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
clearTimeout(timeout);
|
|
92
|
+
pendingSpeech = null;
|
|
93
|
+
reject(error);
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
speechQueue = queued;
|
|
98
|
+
return queued;
|
|
99
|
+
}
|
|
100
|
+
function resolvePendingSpeech(event) {
|
|
101
|
+
if (!pendingSpeech) return;
|
|
102
|
+
if (!event) {
|
|
103
|
+
const pending2 = pendingSpeech;
|
|
104
|
+
pendingSpeech = null;
|
|
105
|
+
clearTimeout(pending2.timeout);
|
|
106
|
+
pending2.resolve();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (event.type === "response.created") {
|
|
110
|
+
if (responseHasSpeechId(event.response, pendingSpeech.id) || !pendingSpeech.responseId) {
|
|
111
|
+
if (event.response.id) pendingSpeech.responseId = event.response.id;
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (event.type !== "response.done") return;
|
|
116
|
+
const matchesResponseId = Boolean(pendingSpeech.responseId && event.response.id === pendingSpeech.responseId);
|
|
117
|
+
const matchesSpeechId = responseHasSpeechId(event.response, pendingSpeech.id);
|
|
118
|
+
if (!matchesResponseId && !matchesSpeechId && pendingSpeech.responseId) return;
|
|
119
|
+
const pending = pendingSpeech;
|
|
120
|
+
pendingSpeech = null;
|
|
121
|
+
clearTimeout(pending.timeout);
|
|
122
|
+
pending.resolve();
|
|
123
|
+
}
|
|
124
|
+
function rejectPendingSpeech(error) {
|
|
125
|
+
if (!pendingSpeech) return;
|
|
126
|
+
const pending = pendingSpeech;
|
|
127
|
+
pendingSpeech = null;
|
|
128
|
+
clearTimeout(pending.timeout);
|
|
129
|
+
pending.reject(error instanceof Error ? error : new Error("OpenAI Realtime speech failed."));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
var createOpenAIVoiceAdapter = createOpenAIVoiceProvider;
|
|
135
|
+
async function createOpenAIRealtimeSocket(input) {
|
|
136
|
+
const socket = await OpenAIRealtimeWS.create(input.client, { model: input.model });
|
|
137
|
+
await waitForRealtimeOpen(socket);
|
|
138
|
+
return socket;
|
|
139
|
+
}
|
|
140
|
+
async function handleControlToolCall(input) {
|
|
141
|
+
const parsedArguments = parseToolArguments(input.event.arguments);
|
|
142
|
+
try {
|
|
143
|
+
const result = await input.control.handleToolCall({
|
|
144
|
+
session: input.session,
|
|
145
|
+
name: input.event.name,
|
|
146
|
+
arguments: parsedArguments,
|
|
147
|
+
callId: input.event.call_id,
|
|
148
|
+
itemId: input.event.item_id,
|
|
149
|
+
responseId: input.event.response_id,
|
|
150
|
+
signal: input.signal,
|
|
151
|
+
notify: (notification) => sendControlNotification({
|
|
152
|
+
socket: input.socket,
|
|
153
|
+
notification,
|
|
154
|
+
sendProviderEvent: input.sendProviderEvent
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
if (result.events?.length) {
|
|
158
|
+
await input.sendProviderEvent({
|
|
159
|
+
kind: "runtime_events",
|
|
160
|
+
events: result.events
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
input.socket.send(createFunctionCallOutput(input.event.call_id, result.output));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
input.socket.send(createFunctionCallOutput(input.event.call_id, {
|
|
166
|
+
ok: false,
|
|
167
|
+
error: error instanceof Error ? error.message : "Voice control tool failed."
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
input.socket.send({
|
|
171
|
+
type: "response.create",
|
|
172
|
+
response: {
|
|
173
|
+
conversation: "auto",
|
|
174
|
+
output_modalities: ["audio"],
|
|
175
|
+
instructions: [
|
|
176
|
+
"Continue the conversation using the Cognidesk tool result.",
|
|
177
|
+
"Follow the active session instructions for language, brevity, tone, confirmations, and follow-up policy.",
|
|
178
|
+
"If the tool result contains voiceGuidance, obey it before deciding what to say.",
|
|
179
|
+
"Speak only customer-facing prose."
|
|
180
|
+
].join(" ")
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async function sendControlNotification(input) {
|
|
185
|
+
if (input.notification.events?.length) {
|
|
186
|
+
await input.sendProviderEvent({
|
|
187
|
+
kind: "runtime_events",
|
|
188
|
+
events: input.notification.events
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
input.socket.send(createSystemMessage(input.notification.message));
|
|
192
|
+
if (input.notification.createResponse === false) return;
|
|
193
|
+
input.socket.send({
|
|
194
|
+
type: "response.create",
|
|
195
|
+
response: {
|
|
196
|
+
conversation: "auto",
|
|
197
|
+
output_modalities: ["audio"],
|
|
198
|
+
instructions: input.notification.responseInstructions ?? "A Cognidesk background update arrived. Follow the active session instructions and any guidance in the update; if it is still relevant, briefly tell the customer the result in natural spoken language."
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function createFunctionCallOutput(callId, output) {
|
|
203
|
+
return {
|
|
204
|
+
type: "conversation.item.create",
|
|
205
|
+
item: {
|
|
206
|
+
type: "function_call_output",
|
|
207
|
+
call_id: callId,
|
|
208
|
+
output: JSON.stringify(output)
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function createSystemMessage(text) {
|
|
213
|
+
return {
|
|
214
|
+
type: "conversation.item.create",
|
|
215
|
+
item: {
|
|
216
|
+
type: "message",
|
|
217
|
+
role: "system",
|
|
218
|
+
content: [{
|
|
219
|
+
type: "input_text",
|
|
220
|
+
text
|
|
221
|
+
}]
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function parseToolArguments(value) {
|
|
226
|
+
try {
|
|
227
|
+
return JSON.parse(value);
|
|
228
|
+
} catch {
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function waitForRealtimeOpen(socket) {
|
|
233
|
+
if (socket.socket.readyState === socket.socket.OPEN) return;
|
|
234
|
+
await new Promise((resolve, reject) => {
|
|
235
|
+
const cleanup = () => {
|
|
236
|
+
socket.socket.off("open", onOpen);
|
|
237
|
+
socket.socket.off("error", onError);
|
|
238
|
+
};
|
|
239
|
+
const onOpen = () => {
|
|
240
|
+
cleanup();
|
|
241
|
+
resolve();
|
|
242
|
+
};
|
|
243
|
+
const onError = (error) => {
|
|
244
|
+
cleanup();
|
|
245
|
+
reject(error);
|
|
246
|
+
};
|
|
247
|
+
socket.socket.once("open", onOpen);
|
|
248
|
+
socket.socket.once("error", onError);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function buildRealtimeSession(input) {
|
|
252
|
+
const realtimeControlEnabled = Boolean(input.control);
|
|
253
|
+
const realtimeTools = input.control ? input.control.tools.map(toRealtimeFunctionTool) : void 0;
|
|
254
|
+
const sessionInstructions = buildSessionInstructions({
|
|
255
|
+
...input.instructions ? { profileInstructions: input.instructions } : {},
|
|
256
|
+
...input.control?.instructions ? { controlInstructions: input.control.instructions } : {},
|
|
257
|
+
realtimeControlEnabled
|
|
258
|
+
});
|
|
259
|
+
const base = {
|
|
260
|
+
type: "realtime",
|
|
261
|
+
model: input.model,
|
|
262
|
+
output_modalities: ["audio"],
|
|
263
|
+
...realtimeTools ? {
|
|
264
|
+
tools: realtimeTools,
|
|
265
|
+
tool_choice: "auto",
|
|
266
|
+
parallel_tool_calls: false
|
|
267
|
+
} : {},
|
|
268
|
+
reasoning: { effort: "low" },
|
|
269
|
+
audio: {
|
|
270
|
+
input: {
|
|
271
|
+
format: { type: "audio/pcm", rate: 24e3 },
|
|
272
|
+
noise_reduction: { type: "near_field" },
|
|
273
|
+
transcription: {
|
|
274
|
+
model: "gpt-realtime-whisper",
|
|
275
|
+
...input.transcriptionLanguage ? { language: input.transcriptionLanguage } : {}
|
|
276
|
+
},
|
|
277
|
+
turn_detection: {
|
|
278
|
+
type: "server_vad",
|
|
279
|
+
create_response: realtimeControlEnabled,
|
|
280
|
+
interrupt_response: true,
|
|
281
|
+
prefix_padding_ms: 250,
|
|
282
|
+
silence_duration_ms: 420
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
output: {
|
|
286
|
+
format: { type: "audio/pcm", rate: 24e3 },
|
|
287
|
+
...input.voice ? { voice: input.voice } : {}
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
instructions: sessionInstructions
|
|
291
|
+
};
|
|
292
|
+
return mergeSession(base, input.defaults);
|
|
293
|
+
}
|
|
294
|
+
function buildSessionInstructions(input) {
|
|
295
|
+
if (!input.realtimeControlEnabled) {
|
|
296
|
+
return input.profileInstructions ?? [
|
|
297
|
+
"You are the voice transport for a Cognidesk agent.",
|
|
298
|
+
"Transcribe user speech accurately.",
|
|
299
|
+
"# Preambles",
|
|
300
|
+
"When asked for a preamble, generate exactly one brief, natural wait-time sentence. Do not claim a result or repeat the same wording.",
|
|
301
|
+
"# Language",
|
|
302
|
+
"Prefer German and English. If a short greeting sounds like 'Hallo', treat it as German unless the user clearly continues in another language.",
|
|
303
|
+
"When asked to speak supplied text, preserve the wording and keep the delivery natural and concise."
|
|
304
|
+
].join(" ");
|
|
305
|
+
}
|
|
306
|
+
return [
|
|
307
|
+
"You are the live realtime voice layer for a Cognidesk customer-support agent.",
|
|
308
|
+
"Speak naturally and quickly, but keep Cognidesk authoritative for facts, customer-specific state, workflow progress, and side effects.",
|
|
309
|
+
"Use lightweight conversation and the supplied history for greetings, small talk, clarifications, and interruption recovery.",
|
|
310
|
+
"Use the available Cognidesk voice control tools for policy knowledge, flight/customer state, tool work, confirmations, or workflow changes.",
|
|
311
|
+
"Do not claim a booking, policy answer, flight status, eligibility, handoff, or other substantive result until it comes from a control tool result or validated runtime context.",
|
|
312
|
+
"Ask at most one missing detail at a time.",
|
|
313
|
+
"Do not mention widgets, buttons, Markdown, JSON, tool names, internal state, Runtime Events, or provider events to the customer.",
|
|
314
|
+
input.profileInstructions,
|
|
315
|
+
input.controlInstructions
|
|
316
|
+
].filter(Boolean).join("\n\n");
|
|
317
|
+
}
|
|
318
|
+
function toRealtimeFunctionTool(tool) {
|
|
319
|
+
return {
|
|
320
|
+
type: "function",
|
|
321
|
+
name: tool.name,
|
|
322
|
+
...tool.description ? { description: tool.description } : {},
|
|
323
|
+
...tool.parameters ? { parameters: tool.parameters } : {}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function createSpeechResponse(input) {
|
|
327
|
+
return {
|
|
328
|
+
type: "response.create",
|
|
329
|
+
response: {
|
|
330
|
+
conversation: "none",
|
|
331
|
+
output_modalities: ["audio"],
|
|
332
|
+
metadata: {
|
|
333
|
+
cognidesk_voice_kind: "speech",
|
|
334
|
+
cognidesk_voice_id: input.id
|
|
335
|
+
},
|
|
336
|
+
input: [{
|
|
337
|
+
type: "message",
|
|
338
|
+
role: "user",
|
|
339
|
+
content: [{
|
|
340
|
+
type: "input_text",
|
|
341
|
+
text: `Read the following text aloud exactly, with a natural service-agent tone and no extra words:
|
|
342
|
+
|
|
343
|
+
${input.text}`
|
|
344
|
+
}]
|
|
345
|
+
}],
|
|
346
|
+
instructions: "Read the supplied text exactly. Do not add a greeting, explanation, or closing."
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function createPreambleResponse(input) {
|
|
351
|
+
return {
|
|
352
|
+
type: "response.create",
|
|
353
|
+
response: {
|
|
354
|
+
conversation: "none",
|
|
355
|
+
output_modalities: ["audio"],
|
|
356
|
+
metadata: {
|
|
357
|
+
cognidesk_voice_kind: "preamble",
|
|
358
|
+
cognidesk_voice_id: input.id
|
|
359
|
+
},
|
|
360
|
+
input: [{
|
|
361
|
+
type: "message",
|
|
362
|
+
role: "user",
|
|
363
|
+
content: [{
|
|
364
|
+
type: "input_text",
|
|
365
|
+
text: [
|
|
366
|
+
"The customer just said:",
|
|
367
|
+
input.text,
|
|
368
|
+
"",
|
|
369
|
+
"Generate and speak one short wait-time preamble for a flight-service voice agent while Cognidesk continues checking the request."
|
|
370
|
+
].join("\n")
|
|
371
|
+
}]
|
|
372
|
+
}],
|
|
373
|
+
instructions: [
|
|
374
|
+
"Generate exactly one natural spoken sentence.",
|
|
375
|
+
"Do not use a fixed phrase. Vary the wording.",
|
|
376
|
+
"Do not claim a booking, policy result, tool result, queue state, or completion.",
|
|
377
|
+
"Use German if the customer appears to be speaking German; use English if they appear to be speaking English.",
|
|
378
|
+
"For the greeting 'Hallo', prefer German."
|
|
379
|
+
].join(" ")
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function translateBrowserEvent(event) {
|
|
384
|
+
switch (event.type) {
|
|
385
|
+
case "input_audio_buffer.append":
|
|
386
|
+
return {
|
|
387
|
+
type: "input_audio_buffer.append",
|
|
388
|
+
audio: event.audio,
|
|
389
|
+
...optionalStringField("event_id", event.event_id)
|
|
390
|
+
};
|
|
391
|
+
case "input_audio_buffer.commit":
|
|
392
|
+
return { type: "input_audio_buffer.commit", ...optionalStringField("event_id", event.event_id) };
|
|
393
|
+
case "input_audio_buffer.clear":
|
|
394
|
+
return { type: "input_audio_buffer.clear", ...optionalStringField("event_id", event.event_id) };
|
|
395
|
+
case "response.cancel":
|
|
396
|
+
return {
|
|
397
|
+
type: "response.cancel",
|
|
398
|
+
...optionalStringField("event_id", event.event_id),
|
|
399
|
+
...optionalStringField("response_id", event.response_id)
|
|
400
|
+
};
|
|
401
|
+
case "conversation.item.truncate":
|
|
402
|
+
if (!event.item_id || event.content_index === void 0 || event.audio_end_ms === void 0) return null;
|
|
403
|
+
return {
|
|
404
|
+
type: "conversation.item.truncate",
|
|
405
|
+
item_id: event.item_id,
|
|
406
|
+
content_index: event.content_index,
|
|
407
|
+
audio_end_ms: event.audio_end_ms,
|
|
408
|
+
...optionalStringField("event_id", event.event_id)
|
|
409
|
+
};
|
|
410
|
+
case "session.update":
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async function handleRealtimeEvent(event, sendProviderEvent) {
|
|
415
|
+
if (event.type === "conversation.item.input_audio_transcription.completed") {
|
|
416
|
+
await sendProviderEvent({
|
|
417
|
+
kind: "input_transcript.completed",
|
|
418
|
+
text: event.transcript,
|
|
419
|
+
itemId: event.item_id,
|
|
420
|
+
transcriptionSource: "openai-realtime"
|
|
421
|
+
});
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const forwarded = translateServerEvent(event);
|
|
425
|
+
if (forwarded) {
|
|
426
|
+
await sendProviderEvent({
|
|
427
|
+
kind: "server_event",
|
|
428
|
+
event: forwarded
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function translateServerEvent(event) {
|
|
433
|
+
switch (event.type) {
|
|
434
|
+
case "input_audio_buffer.speech_started":
|
|
435
|
+
return {
|
|
436
|
+
type: "input_audio_buffer.speech_started",
|
|
437
|
+
event_id: event.event_id,
|
|
438
|
+
audio_start_ms: event.audio_start_ms,
|
|
439
|
+
item_id: event.item_id
|
|
440
|
+
};
|
|
441
|
+
case "input_audio_buffer.speech_stopped":
|
|
442
|
+
return {
|
|
443
|
+
type: "input_audio_buffer.speech_stopped",
|
|
444
|
+
event_id: event.event_id,
|
|
445
|
+
audio_end_ms: event.audio_end_ms,
|
|
446
|
+
item_id: event.item_id
|
|
447
|
+
};
|
|
448
|
+
case "response.output_audio.delta":
|
|
449
|
+
return {
|
|
450
|
+
type: "response.output_audio.delta",
|
|
451
|
+
event_id: event.event_id,
|
|
452
|
+
response_id: event.response_id,
|
|
453
|
+
item_id: event.item_id,
|
|
454
|
+
output_index: event.output_index,
|
|
455
|
+
content_index: event.content_index,
|
|
456
|
+
delta: event.delta
|
|
457
|
+
};
|
|
458
|
+
case "response.output_audio.done":
|
|
459
|
+
return {
|
|
460
|
+
type: "response.output_audio.done",
|
|
461
|
+
event_id: event.event_id,
|
|
462
|
+
response_id: event.response_id,
|
|
463
|
+
item_id: event.item_id,
|
|
464
|
+
output_index: event.output_index,
|
|
465
|
+
content_index: event.content_index
|
|
466
|
+
};
|
|
467
|
+
case "response.output_audio_transcript.delta":
|
|
468
|
+
return {
|
|
469
|
+
type: "response.output_audio_transcript.delta",
|
|
470
|
+
event_id: event.event_id,
|
|
471
|
+
response_id: event.response_id,
|
|
472
|
+
item_id: event.item_id,
|
|
473
|
+
output_index: event.output_index,
|
|
474
|
+
content_index: event.content_index,
|
|
475
|
+
delta: event.delta
|
|
476
|
+
};
|
|
477
|
+
case "response.output_audio_transcript.done":
|
|
478
|
+
return {
|
|
479
|
+
type: "response.output_audio_transcript.done",
|
|
480
|
+
event_id: event.event_id,
|
|
481
|
+
response_id: event.response_id,
|
|
482
|
+
item_id: event.item_id,
|
|
483
|
+
output_index: event.output_index,
|
|
484
|
+
content_index: event.content_index,
|
|
485
|
+
transcript: event.transcript
|
|
486
|
+
};
|
|
487
|
+
case "response.done":
|
|
488
|
+
return {
|
|
489
|
+
type: "response.done",
|
|
490
|
+
event_id: event.event_id,
|
|
491
|
+
response: event.response
|
|
492
|
+
};
|
|
493
|
+
case "error":
|
|
494
|
+
return {
|
|
495
|
+
type: "error",
|
|
496
|
+
event_id: event.event_id,
|
|
497
|
+
error: {
|
|
498
|
+
code: event.error.code ?? event.error.type,
|
|
499
|
+
message: event.error.message,
|
|
500
|
+
details: event.error
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
default:
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function assertSupportedModel(modelSet) {
|
|
508
|
+
if (!modelSet) return;
|
|
509
|
+
if (modelSet.provider !== "openai") {
|
|
510
|
+
throw new Error(`@cognidesk/voice-openai cannot run voice model provider '${modelSet.provider}'.`);
|
|
511
|
+
}
|
|
512
|
+
if (modelSet.model !== OPENAI_REALTIME_V1_MODEL) {
|
|
513
|
+
throw new Error(`@cognidesk/voice-openai v1 supports only ${OPENAI_REALTIME_V1_MODEL}.`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function mergeSession(base, defaults) {
|
|
517
|
+
if (!defaults) return base;
|
|
518
|
+
return {
|
|
519
|
+
...base,
|
|
520
|
+
...defaults,
|
|
521
|
+
audio: {
|
|
522
|
+
...base.audio,
|
|
523
|
+
...defaults.audio,
|
|
524
|
+
input: {
|
|
525
|
+
...base.audio?.input,
|
|
526
|
+
...defaults.audio?.input
|
|
527
|
+
},
|
|
528
|
+
output: {
|
|
529
|
+
...base.audio?.output,
|
|
530
|
+
...defaults.audio?.output
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function responseHasSpeechId(response, id) {
|
|
536
|
+
const metadata = response.metadata;
|
|
537
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return false;
|
|
538
|
+
return metadata.cognidesk_voice_id === id;
|
|
539
|
+
}
|
|
540
|
+
function optionalStringField(key, value) {
|
|
541
|
+
return value ? { [key]: value } : {};
|
|
542
|
+
}
|
|
543
|
+
function createId(prefix) {
|
|
544
|
+
const random = globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);
|
|
545
|
+
return `${prefix}_${random}`;
|
|
546
|
+
}
|
|
547
|
+
export {
|
|
548
|
+
OPENAI_REALTIME_V1_MODEL,
|
|
549
|
+
createOpenAIRealtimeSocket,
|
|
550
|
+
createOpenAIVoiceAdapter,
|
|
551
|
+
createOpenAIVoiceProvider
|
|
552
|
+
};
|
|
553
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport { OpenAIRealtimeWS } from \"openai/realtime/ws\";\nimport type {\n RealtimeClientEvent,\n RealtimeServerEvent,\n RealtimeSessionCreateRequest,\n} from \"openai/resources/realtime/realtime\";\nimport type { VoiceModelSet } from \"@cognidesk/core\";\nimport type {\n VoiceControlNotification,\n VoiceControlTool,\n VoiceBrowserClientEvent,\n VoiceBrowserServerEvent,\n VoiceProvider,\n VoiceProviderConnectInput,\n VoiceProviderEvent,\n VoiceProviderSession,\n VoiceSocketSession,\n} from \"@cognidesk/voice-websocket\";\n\nexport const OPENAI_REALTIME_V1_MODEL = \"gpt-realtime-2\";\n\nexport interface OpenAIVoiceProviderOptions {\n apiKey?: string;\n client?: OpenAI;\n model?: typeof OPENAI_REALTIME_V1_MODEL;\n voice?: string;\n transcriptionLanguage?: string;\n baseURL?: string;\n realtime?: OpenAIRealtimeFactory;\n sessionDefaults?: Partial<RealtimeSessionCreateRequest>;\n}\n\nexport interface OpenAIRealtimeFactoryInput {\n client: OpenAI;\n model: typeof OPENAI_REALTIME_V1_MODEL;\n}\n\nexport interface OpenAIRealtimeSocket {\n send(event: RealtimeClientEvent): void;\n close(props?: { code: number; reason: string }): void;\n on(event: \"event\", listener: (event: RealtimeServerEvent) => void): void;\n on(event: \"error\", listener: (error: unknown) => void): void;\n}\n\ninterface PendingSpeech {\n id: string;\n responseId?: string;\n resolve(): void;\n reject(error: Error): void;\n timeout: ReturnType<typeof setTimeout>;\n}\n\nexport type OpenAIRealtimeFactory = (\n input: OpenAIRealtimeFactoryInput,\n) => Promise<OpenAIRealtimeSocket>;\n\nexport function createOpenAIVoiceProvider(options: OpenAIVoiceProviderOptions = {}): VoiceProvider {\n const model = options.model ?? OPENAI_REALTIME_V1_MODEL;\n if (model !== OPENAI_REALTIME_V1_MODEL) {\n throw new Error(`@cognidesk/voice-openai v1 supports only ${OPENAI_REALTIME_V1_MODEL}.`);\n }\n const client = options.client ?? new OpenAI({\n apiKey: options.apiKey,\n ...(options.baseURL ? { baseURL: options.baseURL } : {}),\n });\n const realtime = options.realtime ?? createOpenAIRealtimeSocket;\n\n return {\n id: \"openai-realtime-ws\",\n async connect(input): Promise<VoiceProviderSession> {\n const profileModel = input.profile?.modelSet;\n assertSupportedModel(profileModel);\n const socket = await realtime({ client, model });\n let speechQueue = Promise.resolve();\n let pendingSpeech: PendingSpeech | null = null;\n const sendProviderEvent = async (event: VoiceProviderEvent) => {\n await input.onEvent(event);\n };\n\n socket.on(\"event\", (event) => {\n resolvePendingSpeech(event);\n if (input.control && event.type === \"response.function_call_arguments.done\") {\n void handleControlToolCall({\n event,\n socket,\n control: input.control,\n session: input.session,\n signal: input.signal,\n sendProviderEvent,\n });\n return;\n }\n void handleRealtimeEvent(event, sendProviderEvent);\n });\n socket.on(\"error\", (error) => {\n rejectPendingSpeech(error);\n void sendProviderEvent({\n kind: \"error\",\n code: \"openai_realtime_error\",\n message: error instanceof Error ? error.message : \"OpenAI Realtime socket error.\",\n details: error,\n });\n });\n\n socket.send({\n type: \"session.update\",\n session: buildRealtimeSession({\n ...(options.sessionDefaults ? { defaults: options.sessionDefaults } : {}),\n model,\n ...(profileModel?.voice ?? options.voice ? { voice: profileModel?.voice ?? options.voice } : {}),\n ...(input.profile?.instructions ? { instructions: input.profile.instructions } : {}),\n ...(options.transcriptionLanguage ? { transcriptionLanguage: options.transcriptionLanguage } : {}),\n ...(input.control ? { control: input.control } : {}),\n }),\n });\n\n return {\n send(event) {\n const translated = translateBrowserEvent(event);\n if (translated) socket.send(translated);\n },\n speak({ text }) {\n return enqueueSpeech((id) => createSpeechResponse({ id, text }));\n },\n preamble({ text }) {\n return enqueueSpeech((id) => createPreambleResponse({ id, text }));\n },\n close() {\n resolvePendingSpeech();\n socket.close({ code: 1000, reason: \"Cognidesk voice session closed\" });\n },\n };\n\n function enqueueSpeech(createEvent: (id: string) => RealtimeClientEvent) {\n const id = createId(\"cognidesk_voice_speech\");\n const queued = speechQueue.catch(() => undefined).then(() =>\n new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n if (pendingSpeech?.id === id) {\n pendingSpeech = null;\n resolve();\n }\n }, 90_000);\n pendingSpeech = { id, resolve, reject, timeout };\n try {\n socket.send(createEvent(id));\n } catch (error) {\n clearTimeout(timeout);\n pendingSpeech = null;\n reject(error);\n }\n })\n );\n speechQueue = queued;\n return queued;\n }\n\n function resolvePendingSpeech(event?: RealtimeServerEvent) {\n if (!pendingSpeech) return;\n if (!event) {\n const pending = pendingSpeech;\n pendingSpeech = null;\n clearTimeout(pending.timeout);\n pending.resolve();\n return;\n }\n if (event.type === \"response.created\") {\n if (responseHasSpeechId(event.response, pendingSpeech.id) || !pendingSpeech.responseId) {\n if (event.response.id) pendingSpeech.responseId = event.response.id;\n }\n return;\n }\n if (event.type !== \"response.done\") return;\n const matchesResponseId = Boolean(pendingSpeech.responseId && event.response.id === pendingSpeech.responseId);\n const matchesSpeechId = responseHasSpeechId(event.response, pendingSpeech.id);\n if (!matchesResponseId && !matchesSpeechId && pendingSpeech.responseId) return;\n const pending = pendingSpeech;\n pendingSpeech = null;\n clearTimeout(pending.timeout);\n pending.resolve();\n }\n\n function rejectPendingSpeech(error: unknown) {\n if (!pendingSpeech) return;\n const pending = pendingSpeech;\n pendingSpeech = null;\n clearTimeout(pending.timeout);\n pending.reject(error instanceof Error ? error : new Error(\"OpenAI Realtime speech failed.\"));\n }\n },\n };\n}\n\nexport const createOpenAIVoiceAdapter = createOpenAIVoiceProvider;\n\nexport async function createOpenAIRealtimeSocket(\n input: OpenAIRealtimeFactoryInput,\n): Promise<OpenAIRealtimeSocket> {\n const socket = await OpenAIRealtimeWS.create(input.client, { model: input.model });\n await waitForRealtimeOpen(socket);\n return socket;\n}\n\nasync function handleControlToolCall(input: {\n event: Extract<RealtimeServerEvent, { type: \"response.function_call_arguments.done\" }>;\n socket: OpenAIRealtimeSocket;\n control: NonNullable<VoiceProviderConnectInput[\"control\"]>;\n session: VoiceSocketSession;\n signal: AbortSignal;\n sendProviderEvent(event: VoiceProviderEvent): Promise<void>;\n}) {\n const parsedArguments = parseToolArguments(input.event.arguments);\n try {\n const result = await input.control.handleToolCall({\n session: input.session,\n name: input.event.name,\n arguments: parsedArguments,\n callId: input.event.call_id,\n itemId: input.event.item_id,\n responseId: input.event.response_id,\n signal: input.signal,\n notify: (notification) => sendControlNotification({\n socket: input.socket,\n notification,\n sendProviderEvent: input.sendProviderEvent,\n }),\n });\n if (result.events?.length) {\n await input.sendProviderEvent({\n kind: \"runtime_events\",\n events: result.events,\n });\n }\n input.socket.send(createFunctionCallOutput(input.event.call_id, result.output));\n } catch (error) {\n input.socket.send(createFunctionCallOutput(input.event.call_id, {\n ok: false,\n error: error instanceof Error ? error.message : \"Voice control tool failed.\",\n }));\n }\n input.socket.send({\n type: \"response.create\",\n response: {\n conversation: \"auto\",\n output_modalities: [\"audio\"],\n instructions: [\n \"Continue the conversation using the Cognidesk tool result.\",\n \"Follow the active session instructions for language, brevity, tone, confirmations, and follow-up policy.\",\n \"If the tool result contains voiceGuidance, obey it before deciding what to say.\",\n \"Speak only customer-facing prose.\",\n ].join(\" \"),\n },\n });\n}\n\nasync function sendControlNotification(input: {\n socket: OpenAIRealtimeSocket;\n notification: VoiceControlNotification;\n sendProviderEvent(event: VoiceProviderEvent): Promise<void>;\n}) {\n if (input.notification.events?.length) {\n await input.sendProviderEvent({\n kind: \"runtime_events\",\n events: input.notification.events,\n });\n }\n input.socket.send(createSystemMessage(input.notification.message));\n if (input.notification.createResponse === false) return;\n input.socket.send({\n type: \"response.create\",\n response: {\n conversation: \"auto\",\n output_modalities: [\"audio\"],\n instructions: input.notification.responseInstructions\n ?? \"A Cognidesk background update arrived. Follow the active session instructions and any guidance in the update; if it is still relevant, briefly tell the customer the result in natural spoken language.\",\n },\n });\n}\n\nfunction createFunctionCallOutput(callId: string, output: unknown): RealtimeClientEvent {\n return {\n type: \"conversation.item.create\",\n item: {\n type: \"function_call_output\",\n call_id: callId,\n output: JSON.stringify(output),\n },\n };\n}\n\nfunction createSystemMessage(text: string): RealtimeClientEvent {\n return {\n type: \"conversation.item.create\",\n item: {\n type: \"message\",\n role: \"system\",\n content: [{\n type: \"input_text\",\n text,\n }],\n },\n };\n}\n\nfunction parseToolArguments(value: string) {\n try {\n return JSON.parse(value) as unknown;\n } catch {\n return {};\n }\n}\n\nasync function waitForRealtimeOpen(socket: OpenAIRealtimeWS) {\n if (socket.socket.readyState === socket.socket.OPEN) return;\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n socket.socket.off(\"open\", onOpen);\n socket.socket.off(\"error\", onError);\n };\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n socket.socket.once(\"open\", onOpen);\n socket.socket.once(\"error\", onError);\n });\n}\n\nfunction buildRealtimeSession(input: {\n defaults?: Partial<RealtimeSessionCreateRequest>;\n model: typeof OPENAI_REALTIME_V1_MODEL;\n voice?: string;\n instructions?: string;\n transcriptionLanguage?: string;\n control?: {\n tools: VoiceControlTool[];\n instructions?: string;\n };\n}): RealtimeSessionCreateRequest {\n const realtimeControlEnabled = Boolean(input.control);\n const realtimeTools = input.control ? input.control.tools.map(toRealtimeFunctionTool) : undefined;\n const sessionInstructions = buildSessionInstructions({\n ...(input.instructions ? { profileInstructions: input.instructions } : {}),\n ...(input.control?.instructions ? { controlInstructions: input.control.instructions } : {}),\n realtimeControlEnabled,\n });\n const base: RealtimeSessionCreateRequest = {\n type: \"realtime\",\n model: input.model,\n output_modalities: [\"audio\"],\n ...(realtimeTools ? {\n tools: realtimeTools,\n tool_choice: \"auto\",\n parallel_tool_calls: false,\n } : {}),\n reasoning: { effort: \"low\" },\n audio: {\n input: {\n format: { type: \"audio/pcm\", rate: 24000 },\n noise_reduction: { type: \"near_field\" },\n transcription: {\n model: \"gpt-realtime-whisper\",\n ...(input.transcriptionLanguage ? { language: input.transcriptionLanguage } : {}),\n },\n turn_detection: {\n type: \"server_vad\",\n create_response: realtimeControlEnabled,\n interrupt_response: true,\n prefix_padding_ms: 250,\n silence_duration_ms: 420,\n },\n },\n output: {\n format: { type: \"audio/pcm\", rate: 24000 },\n ...(input.voice ? { voice: input.voice } : {}),\n },\n },\n instructions: sessionInstructions,\n };\n return mergeSession(base, input.defaults);\n}\n\nfunction buildSessionInstructions(input: {\n profileInstructions?: string;\n controlInstructions?: string;\n realtimeControlEnabled: boolean;\n}) {\n if (!input.realtimeControlEnabled) {\n return input.profileInstructions ?? [\n \"You are the voice transport for a Cognidesk agent.\",\n \"Transcribe user speech accurately.\",\n \"# Preambles\",\n \"When asked for a preamble, generate exactly one brief, natural wait-time sentence. Do not claim a result or repeat the same wording.\",\n \"# Language\",\n \"Prefer German and English. If a short greeting sounds like 'Hallo', treat it as German unless the user clearly continues in another language.\",\n \"When asked to speak supplied text, preserve the wording and keep the delivery natural and concise.\",\n ].join(\" \");\n }\n return [\n \"You are the live realtime voice layer for a Cognidesk customer-support agent.\",\n \"Speak naturally and quickly, but keep Cognidesk authoritative for facts, customer-specific state, workflow progress, and side effects.\",\n \"Use lightweight conversation and the supplied history for greetings, small talk, clarifications, and interruption recovery.\",\n \"Use the available Cognidesk voice control tools for policy knowledge, flight/customer state, tool work, confirmations, or workflow changes.\",\n \"Do not claim a booking, policy answer, flight status, eligibility, handoff, or other substantive result until it comes from a control tool result or validated runtime context.\",\n \"Ask at most one missing detail at a time.\",\n \"Do not mention widgets, buttons, Markdown, JSON, tool names, internal state, Runtime Events, or provider events to the customer.\",\n input.profileInstructions,\n input.controlInstructions,\n ].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction toRealtimeFunctionTool(tool: VoiceControlTool): NonNullable<RealtimeSessionCreateRequest[\"tools\"]>[number] {\n return {\n type: \"function\",\n name: tool.name,\n ...(tool.description ? { description: tool.description } : {}),\n ...(tool.parameters ? { parameters: tool.parameters } : {}),\n };\n}\n\nfunction createSpeechResponse(input: { id: string; text: string }): RealtimeClientEvent {\n return {\n type: \"response.create\",\n response: {\n conversation: \"none\",\n output_modalities: [\"audio\"],\n metadata: {\n cognidesk_voice_kind: \"speech\",\n cognidesk_voice_id: input.id,\n },\n input: [{\n type: \"message\",\n role: \"user\",\n content: [{\n type: \"input_text\",\n text: `Read the following text aloud exactly, with a natural service-agent tone and no extra words:\\n\\n${input.text}`,\n }],\n }],\n instructions: \"Read the supplied text exactly. Do not add a greeting, explanation, or closing.\",\n },\n };\n}\n\nfunction createPreambleResponse(input: { id: string; text: string }): RealtimeClientEvent {\n return {\n type: \"response.create\",\n response: {\n conversation: \"none\",\n output_modalities: [\"audio\"],\n metadata: {\n cognidesk_voice_kind: \"preamble\",\n cognidesk_voice_id: input.id,\n },\n input: [{\n type: \"message\",\n role: \"user\",\n content: [{\n type: \"input_text\",\n text: [\n \"The customer just said:\",\n input.text,\n \"\",\n \"Generate and speak one short wait-time preamble for a flight-service voice agent while Cognidesk continues checking the request.\",\n ].join(\"\\n\"),\n }],\n }],\n instructions: [\n \"Generate exactly one natural spoken sentence.\",\n \"Do not use a fixed phrase. Vary the wording.\",\n \"Do not claim a booking, policy result, tool result, queue state, or completion.\",\n \"Use German if the customer appears to be speaking German; use English if they appear to be speaking English.\",\n \"For the greeting 'Hallo', prefer German.\",\n ].join(\" \"),\n },\n };\n}\n\nfunction translateBrowserEvent(event: VoiceBrowserClientEvent): RealtimeClientEvent | null {\n switch (event.type) {\n case \"input_audio_buffer.append\":\n return {\n type: \"input_audio_buffer.append\",\n audio: event.audio,\n ...optionalStringField(\"event_id\", event.event_id),\n };\n case \"input_audio_buffer.commit\":\n return { type: \"input_audio_buffer.commit\", ...optionalStringField(\"event_id\", event.event_id) };\n case \"input_audio_buffer.clear\":\n return { type: \"input_audio_buffer.clear\", ...optionalStringField(\"event_id\", event.event_id) };\n case \"response.cancel\":\n return {\n type: \"response.cancel\",\n ...optionalStringField(\"event_id\", event.event_id),\n ...optionalStringField(\"response_id\", event.response_id),\n };\n case \"conversation.item.truncate\":\n if (!event.item_id || event.content_index === undefined || event.audio_end_ms === undefined) return null;\n return {\n type: \"conversation.item.truncate\",\n item_id: event.item_id,\n content_index: event.content_index,\n audio_end_ms: event.audio_end_ms,\n ...optionalStringField(\"event_id\", event.event_id),\n };\n case \"session.update\":\n return null;\n }\n}\n\nasync function handleRealtimeEvent(\n event: RealtimeServerEvent,\n sendProviderEvent: (event: VoiceProviderEvent) => Promise<void>,\n) {\n if (event.type === \"conversation.item.input_audio_transcription.completed\") {\n await sendProviderEvent({\n kind: \"input_transcript.completed\",\n text: event.transcript,\n itemId: event.item_id,\n transcriptionSource: \"openai-realtime\",\n });\n return;\n }\n const forwarded = translateServerEvent(event);\n if (forwarded) {\n await sendProviderEvent({\n kind: \"server_event\",\n event: forwarded,\n });\n }\n}\n\nfunction translateServerEvent(event: RealtimeServerEvent): VoiceBrowserServerEvent | null {\n switch (event.type) {\n case \"input_audio_buffer.speech_started\":\n return {\n type: \"input_audio_buffer.speech_started\",\n event_id: event.event_id,\n audio_start_ms: event.audio_start_ms,\n item_id: event.item_id,\n };\n case \"input_audio_buffer.speech_stopped\":\n return {\n type: \"input_audio_buffer.speech_stopped\",\n event_id: event.event_id,\n audio_end_ms: event.audio_end_ms,\n item_id: event.item_id,\n };\n case \"response.output_audio.delta\":\n return {\n type: \"response.output_audio.delta\",\n event_id: event.event_id,\n response_id: event.response_id,\n item_id: event.item_id,\n output_index: event.output_index,\n content_index: event.content_index,\n delta: event.delta,\n };\n case \"response.output_audio.done\":\n return {\n type: \"response.output_audio.done\",\n event_id: event.event_id,\n response_id: event.response_id,\n item_id: event.item_id,\n output_index: event.output_index,\n content_index: event.content_index,\n };\n case \"response.output_audio_transcript.delta\":\n return {\n type: \"response.output_audio_transcript.delta\",\n event_id: event.event_id,\n response_id: event.response_id,\n item_id: event.item_id,\n output_index: event.output_index,\n content_index: event.content_index,\n delta: event.delta,\n };\n case \"response.output_audio_transcript.done\":\n return {\n type: \"response.output_audio_transcript.done\",\n event_id: event.event_id,\n response_id: event.response_id,\n item_id: event.item_id,\n output_index: event.output_index,\n content_index: event.content_index,\n transcript: event.transcript,\n };\n case \"response.done\":\n return {\n type: \"response.done\",\n event_id: event.event_id,\n response: event.response,\n };\n case \"error\":\n return {\n type: \"error\",\n event_id: event.event_id,\n error: {\n code: event.error.code ?? event.error.type,\n message: event.error.message,\n details: event.error,\n },\n };\n default:\n return null;\n }\n}\n\nfunction assertSupportedModel(modelSet: VoiceModelSet | undefined) {\n if (!modelSet) return;\n if (modelSet.provider !== \"openai\") {\n throw new Error(`@cognidesk/voice-openai cannot run voice model provider '${modelSet.provider}'.`);\n }\n if (modelSet.model !== OPENAI_REALTIME_V1_MODEL) {\n throw new Error(`@cognidesk/voice-openai v1 supports only ${OPENAI_REALTIME_V1_MODEL}.`);\n }\n}\n\nfunction mergeSession(\n base: RealtimeSessionCreateRequest,\n defaults: Partial<RealtimeSessionCreateRequest> | undefined,\n): RealtimeSessionCreateRequest {\n if (!defaults) return base;\n return {\n ...base,\n ...defaults,\n audio: {\n ...base.audio,\n ...defaults.audio,\n input: {\n ...base.audio?.input,\n ...defaults.audio?.input,\n },\n output: {\n ...base.audio?.output,\n ...defaults.audio?.output,\n },\n },\n };\n}\n\nfunction responseHasSpeechId(response: { metadata?: unknown }, id: string) {\n const metadata = response.metadata;\n if (!metadata || typeof metadata !== \"object\" || Array.isArray(metadata)) return false;\n return (metadata as Record<string, unknown>).cognidesk_voice_id === id;\n}\n\nfunction optionalStringField<TKey extends string>(key: TKey, value: string | undefined) {\n return value ? { [key]: value } as Record<TKey, string> : {};\n}\n\nfunction createId(prefix: string) {\n const random = globalThis.crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2);\n return `${prefix}_${random}`;\n}\n"],"mappings":"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","names":["pending"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cognidesk/voice-openai",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/index.ts --format esm --dts --sourcemap",
|
|
16
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
17
|
+
"test": "vitest run --passWithNoTests",
|
|
18
|
+
"lint": "tsc -p tsconfig.json --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@cognidesk/core": "0.0.1",
|
|
22
|
+
"@cognidesk/voice-websocket": "0.0.1",
|
|
23
|
+
"openai": "^6.1.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"tsup": "^8.5.0",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^4.0.14"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"registry": "https://registry.npmjs.org/"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
]
|
|
37
|
+
}
|