@opengeni/sdk 0.48.0 → 0.57.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/README.md +147 -1
- package/dist/artifact-client.d.ts +20 -2
- package/dist/artifacts.d.ts +4 -0
- package/dist/artifacts.js +11 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/{chunk-V5F253OG.js → chunk-GU6JF75T.js} +6 -3
- package/dist/chunk-GU6JF75T.js.map +1 -0
- package/dist/chunk-HJ3HHUT5.js +222 -0
- package/dist/chunk-HJ3HHUT5.js.map +1 -0
- package/dist/{chunk-KIHGPM7H.js → chunk-JDLDDRNE.js} +20 -14
- package/dist/chunk-JDLDDRNE.js.map +1 -0
- package/dist/chunk-JRPFWI6T.js +128 -0
- package/dist/chunk-JRPFWI6T.js.map +1 -0
- package/dist/{chunk-MBGBLVUV.js → chunk-PKQ377ED.js} +1725 -555
- package/dist/chunk-PKQ377ED.js.map +1 -0
- package/dist/chunk-ST5DDKJP.js +987 -0
- package/dist/chunk-ST5DDKJP.js.map +1 -0
- package/dist/chunk-VWE2QIVO.js +1162 -0
- package/dist/chunk-VWE2QIVO.js.map +1 -0
- package/dist/chunk-VYCTL62C.js +230 -0
- package/dist/chunk-VYCTL62C.js.map +1 -0
- package/dist/{chunk-DXDL7EEW.js → chunk-YKZME56C.js} +197 -113
- package/dist/chunk-YKZME56C.js.map +1 -0
- package/dist/client.d.ts +240 -16
- package/dist/codex-realtime-controller.d.ts +9 -6
- package/dist/codex-realtime-controller.js +2 -2
- package/dist/codex-realtime-lifecycle.d.ts +1 -1
- package/dist/codex-realtime-v3.d.ts +3 -3
- package/dist/codex-realtime.d.ts +1 -1
- package/dist/company-profile.d.ts +100 -0
- package/dist/core.d.ts +5 -5
- package/dist/core.js +10 -5
- package/dist/desktop.d.ts +9 -1
- package/dist/editable-artifact-resources.d.ts +96 -0
- package/dist/editable-artifacts/browser-session.d.ts +35 -0
- package/dist/editable-artifacts/controller.d.ts +60 -0
- package/dist/editable-artifacts/errors.d.ts +23 -0
- package/dist/editable-artifacts/http-live-transport.d.ts +46 -0
- package/dist/editable-artifacts/index.d.ts +19 -0
- package/dist/editable-artifacts/pool.d.ts +15 -0
- package/dist/editable-artifacts/session.d.ts +96 -0
- package/dist/editable-artifacts/storage.d.ts +72 -0
- package/dist/editable-artifacts/types.d.ts +416 -0
- package/dist/editable-artifacts/worker/browser-client.d.ts +80 -0
- package/dist/editable-artifacts/worker/browser-entry.d.ts +6 -0
- package/dist/editable-artifacts/worker/kernel-adapter.d.ts +45 -0
- package/dist/editable-artifacts/worker/rpc-protocol.d.ts +92 -0
- package/dist/editable-artifacts/worker/runtime.d.ts +66 -0
- package/dist/editable-artifacts/worker/wire-codec.d.ts +52 -0
- package/dist/editable-artifacts-worker.d.ts +8 -0
- package/dist/editable-artifacts-worker.js +1708 -0
- package/dist/editable-artifacts-worker.js.map +1 -0
- package/dist/editable-artifacts.d.ts +6 -0
- package/dist/editable-artifacts.js +5954 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/gateway-realtime-transport.d.ts +2 -1
- package/dist/gateway-realtime-transport.js +5 -3
- package/dist/index.d.ts +45 -35
- package/dist/index.js +134 -64
- package/dist/index.js.map +1 -1
- package/dist/interaction-revision-stream.d.ts +11 -0
- package/dist/interaction.d.ts +1484 -0
- package/dist/interaction.js +81 -0
- package/dist/interaction.js.map +1 -0
- package/dist/memory-slack-client.d.ts +13 -0
- package/dist/memory-slack-delivery.d.ts +99 -0
- package/dist/memory-slack.d.ts +4 -0
- package/dist/memory-slack.js +44 -0
- package/dist/memory-slack.js.map +1 -0
- package/dist/proxy.d.ts +3 -3
- package/dist/realtime.d.ts +14 -14
- package/dist/realtime.js +11 -7
- package/dist/realtime.js.map +1 -1
- package/dist/retained-artifacts.d.ts +12 -0
- package/dist/stream.d.ts +1 -1
- package/dist/terminal.d.ts +1 -1
- package/dist/types.d.ts +1347 -34
- package/dist/workspace-control-stream.d.ts +2 -2
- package/dist/workspace-live-stream.d.ts +14 -0
- package/dist/workspace-state.d.ts +1 -1
- package/package.json +29 -3
- package/src/artifact-client.ts +129 -0
- package/src/artifacts.ts +17 -0
- package/src/client.ts +3022 -910
- package/src/codex-realtime-controller.ts +25 -7
- package/src/codex-realtime-lifecycle.ts +1 -0
- package/src/company-profile.ts +92 -0
- package/src/desktop.ts +11 -1
- package/src/editable-artifact-resources.ts +119 -0
- package/src/editable-artifacts/browser-session.ts +176 -0
- package/src/editable-artifacts/controller.ts +2632 -0
- package/src/editable-artifacts/errors.ts +52 -0
- package/src/editable-artifacts/http-live-transport.ts +1449 -0
- package/src/editable-artifacts/index.ts +175 -0
- package/src/editable-artifacts/pool.ts +81 -0
- package/src/editable-artifacts/session.ts +778 -0
- package/src/editable-artifacts/storage.ts +1718 -0
- package/src/editable-artifacts/types.ts +499 -0
- package/src/editable-artifacts/worker/browser-client.ts +1167 -0
- package/src/editable-artifacts/worker/browser-entry.ts +57 -0
- package/src/editable-artifacts/worker/kernel-adapter.ts +1039 -0
- package/src/editable-artifacts/worker/rpc-protocol.ts +622 -0
- package/src/editable-artifacts/worker/runtime.ts +1204 -0
- package/src/editable-artifacts/worker/wire-codec.ts +852 -0
- package/src/editable-artifacts-worker.ts +8 -0
- package/src/editable-artifacts.ts +6 -0
- package/src/errors.ts +16 -2
- package/src/gateway-realtime-transport.ts +252 -109
- package/src/index.ts +215 -1
- package/src/interaction-revision-stream.ts +117 -0
- package/src/interaction.ts +2874 -0
- package/src/memory-slack-client.ts +71 -0
- package/src/memory-slack-delivery.ts +128 -0
- package/src/memory-slack.ts +19 -0
- package/src/realtime.ts +14 -4
- package/src/retained-artifacts.ts +326 -0
- package/src/types.ts +1636 -52
- package/src/workspace-live-stream.ts +137 -0
- package/dist/chunk-DXDL7EEW.js.map +0 -1
- package/dist/chunk-KIHGPM7H.js.map +0 -1
- package/dist/chunk-MBGBLVUV.js.map +0 -1
- package/dist/chunk-V5F253OG.js.map +0 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedicated browser module-Worker entry for editable artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Bundle this subpath as a Worker URL; importing it in the Worker installs the
|
|
5
|
+
* bounded RPC runtime. The ordinary SDK and editable-artifacts client entries
|
|
6
|
+
* do not load this code on the main thread.
|
|
7
|
+
*/
|
|
8
|
+
export { installBrowserArtifactWorkerEntry } from "./editable-artifacts/worker/browser-entry";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable editable-artifact synchronization. Import from
|
|
3
|
+
* `@opengeni/sdk/editable-artifacts` so ordinary SDK/session/realtime consumers
|
|
4
|
+
* do not load the collaboration controller or its offline storage adapters.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./editable-artifacts/index";
|
package/src/errors.ts
CHANGED
|
@@ -41,7 +41,9 @@ export class OpenGeniApiError extends Error {
|
|
|
41
41
|
this.retryable = options.retryable ?? decoded?.retryable ?? retryableApiStatus(status);
|
|
42
42
|
this.correlationId = correlationId;
|
|
43
43
|
this.outcomeUnknown =
|
|
44
|
-
options.outcomeUnknown ??
|
|
44
|
+
options.outcomeUnknown ??
|
|
45
|
+
decoded?.outcomeUnknown ??
|
|
46
|
+
(gatewayFailure && !!options.mutation && !decoded);
|
|
45
47
|
this.body = !fromResponse || decoded ? body : "";
|
|
46
48
|
this.details = decoded?.details;
|
|
47
49
|
}
|
|
@@ -52,6 +54,7 @@ function decodeApiErrorBody(body: string): {
|
|
|
52
54
|
message: string | undefined;
|
|
53
55
|
requestId: string | undefined;
|
|
54
56
|
retryable: boolean | undefined;
|
|
57
|
+
outcomeUnknown: boolean | undefined;
|
|
55
58
|
details: Record<string, unknown> | undefined;
|
|
56
59
|
} | null {
|
|
57
60
|
if (!body) return null;
|
|
@@ -67,13 +70,24 @@ function decodeApiErrorBody(body: string): {
|
|
|
67
70
|
const message = boundedApiField(nested.message);
|
|
68
71
|
const requestId = boundedCorrelationId(nested.requestId);
|
|
69
72
|
const retryable = typeof nested.retryable === "boolean" ? nested.retryable : undefined;
|
|
73
|
+
const outcomeUnknown =
|
|
74
|
+
typeof nested.outcomeUnknown === "boolean" ? nested.outcomeUnknown : undefined;
|
|
70
75
|
const details = boundedApiDetails(nested.details);
|
|
71
|
-
if (
|
|
76
|
+
if (
|
|
77
|
+
!code &&
|
|
78
|
+
!message &&
|
|
79
|
+
!requestId &&
|
|
80
|
+
retryable === undefined &&
|
|
81
|
+
outcomeUnknown === undefined &&
|
|
82
|
+
!details
|
|
83
|
+
)
|
|
84
|
+
return null;
|
|
72
85
|
return {
|
|
73
86
|
code,
|
|
74
87
|
message,
|
|
75
88
|
requestId,
|
|
76
89
|
retryable,
|
|
90
|
+
outcomeUnknown,
|
|
77
91
|
details,
|
|
78
92
|
};
|
|
79
93
|
} catch {
|
|
@@ -9,14 +9,33 @@ type GatewayServerEvent = Record<string, unknown> & { type: string };
|
|
|
9
9
|
|
|
10
10
|
const AUDIO_SAMPLE_RATE = 24_000;
|
|
11
11
|
const DELEGATION_TOOL = "delegate_to_session";
|
|
12
|
+
type RealtimeDialect = "gateway" | "xai";
|
|
12
13
|
|
|
13
14
|
export function createGatewayRealtimeTransportStarter(): RealtimeControllerTransportStarter {
|
|
15
|
+
return createWebsocketRealtimeTransportStarter("gateway");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createXaiSubscriptionRealtimeTransportStarter(): RealtimeControllerTransportStarter {
|
|
19
|
+
return createWebsocketRealtimeTransportStarter("xai");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createWebsocketRealtimeTransportStarter(
|
|
23
|
+
dialect: RealtimeDialect,
|
|
24
|
+
): RealtimeControllerTransportStarter {
|
|
14
25
|
return async (input) => {
|
|
15
26
|
const client = input.client as CodexRealtimeControllerClient;
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
const negotiate =
|
|
28
|
+
dialect === "xai"
|
|
29
|
+
? client.negotiateXaiSubscriptionRealtime?.bind(client)
|
|
30
|
+
: client.negotiateGatewayRealtime?.bind(client);
|
|
31
|
+
if (!negotiate) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
dialect === "xai"
|
|
34
|
+
? "The OpenGeni client does not support connected SuperGrok realtime"
|
|
35
|
+
: "The OpenGeni client does not support AI Gateway realtime",
|
|
36
|
+
);
|
|
18
37
|
}
|
|
19
|
-
const answer = await
|
|
38
|
+
const answer = await negotiate(
|
|
20
39
|
input.workspaceId,
|
|
21
40
|
input.sessionId,
|
|
22
41
|
{
|
|
@@ -32,16 +51,22 @@ export function createGatewayRealtimeTransportStarter(): RealtimeControllerTrans
|
|
|
32
51
|
);
|
|
33
52
|
throwIfAborted(input.signal);
|
|
34
53
|
|
|
35
|
-
const websocket = new WebSocket(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
const websocket = new WebSocket(
|
|
55
|
+
answer.url,
|
|
56
|
+
dialect === "xai"
|
|
57
|
+
? [`xai-client-secret.${answer.token}`]
|
|
58
|
+
: ["ai-gateway-realtime.v1", `ai-gateway-auth.${answer.token}`],
|
|
59
|
+
);
|
|
39
60
|
const channel = new GatewayRealtimeDataChannel((payload) =>
|
|
40
|
-
handleBridgeOutbound(websocket, payload),
|
|
61
|
+
handleBridgeOutbound(websocket, payload, dialect),
|
|
41
62
|
);
|
|
42
63
|
input.onEventsCreated(channel.asRtcDataChannel());
|
|
43
64
|
const audio = new GatewayRealtimeAudio({
|
|
44
|
-
onAudio: (encoded) =>
|
|
65
|
+
onAudio: (encoded) =>
|
|
66
|
+
send(websocket, {
|
|
67
|
+
type: dialect === "xai" ? "input_audio_buffer.append" : "input-audio-append",
|
|
68
|
+
audio: encoded,
|
|
69
|
+
}),
|
|
45
70
|
onAudibleOutputState: input.onAudibleOutputState,
|
|
46
71
|
});
|
|
47
72
|
let stopped = false;
|
|
@@ -62,8 +87,9 @@ export function createGatewayRealtimeTransportStarter(): RealtimeControllerTrans
|
|
|
62
87
|
return;
|
|
63
88
|
}
|
|
64
89
|
if (!isRecord(parsed) || typeof parsed.type !== "string") return;
|
|
65
|
-
|
|
90
|
+
handleRealtimeEvent({
|
|
66
91
|
event: parsed as GatewayServerEvent,
|
|
92
|
+
dialect,
|
|
67
93
|
channel,
|
|
68
94
|
websocket,
|
|
69
95
|
audio,
|
|
@@ -84,56 +110,6 @@ export function createGatewayRealtimeTransportStarter(): RealtimeControllerTrans
|
|
|
84
110
|
websocket.addEventListener("close", onClose);
|
|
85
111
|
websocket.addEventListener("error", onError);
|
|
86
112
|
|
|
87
|
-
await waitForWebSocketOpen(websocket, input.signal);
|
|
88
|
-
channel.open();
|
|
89
|
-
send(websocket, {
|
|
90
|
-
type: "session-update",
|
|
91
|
-
config: {
|
|
92
|
-
instructions: answer.instructions,
|
|
93
|
-
outputModalities: ["audio"],
|
|
94
|
-
inputAudioFormat: { type: "audio/pcm", rate: AUDIO_SAMPLE_RATE },
|
|
95
|
-
outputAudioFormat: { type: "audio/pcm", rate: AUDIO_SAMPLE_RATE },
|
|
96
|
-
inputAudioTranscription: {},
|
|
97
|
-
outputAudioTranscription: {},
|
|
98
|
-
turnDetection: {
|
|
99
|
-
type: "server-vad",
|
|
100
|
-
prefixPaddingMs: 300,
|
|
101
|
-
silenceDurationMs: 500,
|
|
102
|
-
},
|
|
103
|
-
tools: [
|
|
104
|
-
{
|
|
105
|
-
type: "function",
|
|
106
|
-
name: DELEGATION_TOOL,
|
|
107
|
-
description:
|
|
108
|
-
"Pass execution work, actions, and session tasks to the underlying session agent. Include the complete standalone request and relevant conversational context.",
|
|
109
|
-
parameters: {
|
|
110
|
-
type: "object",
|
|
111
|
-
properties: {
|
|
112
|
-
request: {
|
|
113
|
-
type: "string",
|
|
114
|
-
description: "Complete standalone task for the session agent",
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
required: ["request"],
|
|
118
|
-
additionalProperties: false,
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
for (const item of answer.initialItems) {
|
|
125
|
-
send(websocket, {
|
|
126
|
-
type: "conversation-item-create",
|
|
127
|
-
item: {
|
|
128
|
-
type: "text-message",
|
|
129
|
-
role: "user",
|
|
130
|
-
text: `<session_initial_item role="${item.role}">\n${item.text}\n</session_initial_item>`,
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
await audio.startCapture(input.media);
|
|
135
|
-
input.onConnectionHealth("connected");
|
|
136
|
-
|
|
137
113
|
const stop = (): void => {
|
|
138
114
|
if (stopped) return;
|
|
139
115
|
stopped = true;
|
|
@@ -153,6 +129,41 @@ export function createGatewayRealtimeTransportStarter(): RealtimeControllerTrans
|
|
|
153
129
|
};
|
|
154
130
|
input.signal.addEventListener("abort", stop, { once: true });
|
|
155
131
|
|
|
132
|
+
try {
|
|
133
|
+
await waitForWebSocketOpen(websocket, input.signal);
|
|
134
|
+
channel.open();
|
|
135
|
+
send(
|
|
136
|
+
websocket,
|
|
137
|
+
dialect === "xai"
|
|
138
|
+
? xaiSessionUpdate(answer.instructions)
|
|
139
|
+
: gatewaySessionUpdate(answer.instructions),
|
|
140
|
+
);
|
|
141
|
+
for (const item of answer.initialItems) {
|
|
142
|
+
const text = `<session_initial_item role="${item.role}">\n${item.text}\n</session_initial_item>`;
|
|
143
|
+
send(
|
|
144
|
+
websocket,
|
|
145
|
+
dialect === "xai"
|
|
146
|
+
? {
|
|
147
|
+
type: "conversation.item.create",
|
|
148
|
+
item: {
|
|
149
|
+
type: "message",
|
|
150
|
+
role: "user",
|
|
151
|
+
content: [{ type: "input_text", text }],
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
: {
|
|
155
|
+
type: "conversation-item-create",
|
|
156
|
+
item: { type: "text-message", role: "user", text },
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
await audio.startCapture(input.media);
|
|
161
|
+
input.onConnectionHealth("connected");
|
|
162
|
+
} catch (error) {
|
|
163
|
+
stop();
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
return {
|
|
157
168
|
peerConnection: null as unknown as RTCPeerConnection,
|
|
158
169
|
events: channel.asRtcDataChannel(),
|
|
@@ -174,8 +185,73 @@ export function createGatewayRealtimeTransportStarter(): RealtimeControllerTrans
|
|
|
174
185
|
};
|
|
175
186
|
}
|
|
176
187
|
|
|
177
|
-
function
|
|
188
|
+
function delegationTool(): Record<string, unknown> {
|
|
189
|
+
return {
|
|
190
|
+
type: "function",
|
|
191
|
+
name: DELEGATION_TOOL,
|
|
192
|
+
description:
|
|
193
|
+
"Pass execution work, actions, and session tasks to the underlying session agent. Include the complete standalone request and relevant conversational context.",
|
|
194
|
+
parameters: {
|
|
195
|
+
type: "object",
|
|
196
|
+
properties: {
|
|
197
|
+
request: {
|
|
198
|
+
type: "string",
|
|
199
|
+
description: "Complete standalone task for the session agent",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
required: ["request"],
|
|
203
|
+
additionalProperties: false,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function gatewaySessionUpdate(instructions: string): GatewayClientEvent {
|
|
209
|
+
return {
|
|
210
|
+
type: "session-update",
|
|
211
|
+
config: {
|
|
212
|
+
instructions,
|
|
213
|
+
outputModalities: ["audio"],
|
|
214
|
+
inputAudioFormat: { type: "audio/pcm", rate: AUDIO_SAMPLE_RATE },
|
|
215
|
+
outputAudioFormat: { type: "audio/pcm", rate: AUDIO_SAMPLE_RATE },
|
|
216
|
+
inputAudioTranscription: {},
|
|
217
|
+
outputAudioTranscription: {},
|
|
218
|
+
turnDetection: {
|
|
219
|
+
type: "server-vad",
|
|
220
|
+
prefixPaddingMs: 300,
|
|
221
|
+
silenceDurationMs: 500,
|
|
222
|
+
},
|
|
223
|
+
tools: [delegationTool()],
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function xaiSessionUpdate(instructions: string): GatewayClientEvent {
|
|
229
|
+
return {
|
|
230
|
+
type: "session.update",
|
|
231
|
+
session: {
|
|
232
|
+
instructions,
|
|
233
|
+
voice: "eve",
|
|
234
|
+
reasoning: { effort: "high" },
|
|
235
|
+
turn_detection: {
|
|
236
|
+
type: "server_vad",
|
|
237
|
+
prefix_padding_ms: 300,
|
|
238
|
+
silence_duration_ms: 500,
|
|
239
|
+
},
|
|
240
|
+
audio: {
|
|
241
|
+
input: {
|
|
242
|
+
format: { type: "audio/pcm", rate: AUDIO_SAMPLE_RATE },
|
|
243
|
+
transcription: { model: "grok-transcribe" },
|
|
244
|
+
},
|
|
245
|
+
output: { format: { type: "audio/pcm", rate: AUDIO_SAMPLE_RATE } },
|
|
246
|
+
},
|
|
247
|
+
tools: [delegationTool()],
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function handleRealtimeEvent(input: {
|
|
178
253
|
event: GatewayServerEvent;
|
|
254
|
+
dialect: RealtimeDialect;
|
|
179
255
|
channel: GatewayRealtimeDataChannel;
|
|
180
256
|
websocket: WebSocket;
|
|
181
257
|
audio: GatewayRealtimeAudio;
|
|
@@ -185,8 +261,10 @@ function handleGatewayEvent(input: {
|
|
|
185
261
|
}): void {
|
|
186
262
|
const event = input.event;
|
|
187
263
|
const eventId = providerEventId(event);
|
|
188
|
-
if (event.type === "session-created") {
|
|
189
|
-
const
|
|
264
|
+
if (event.type === "session-created" || event.type === "session.created") {
|
|
265
|
+
const session = isRecord(event.session) ? event.session : null;
|
|
266
|
+
const sessionId =
|
|
267
|
+
stringValue(event.sessionId) ?? stringValue(session?.id) ?? `realtime-${crypto.randomUUID()}`;
|
|
190
268
|
input.channel.providerEvent({
|
|
191
269
|
type: "session.started",
|
|
192
270
|
event_id: eventId,
|
|
@@ -194,23 +272,36 @@ function handleGatewayEvent(input: {
|
|
|
194
272
|
});
|
|
195
273
|
return;
|
|
196
274
|
}
|
|
197
|
-
if (event.type === "speech-started") {
|
|
275
|
+
if (event.type === "speech-started" || event.type === "input_audio_buffer.speech_started") {
|
|
198
276
|
const itemId = input.getCurrentOutputItemId();
|
|
199
277
|
if (itemId && input.audio.isPlaying()) {
|
|
200
278
|
const audioEndMs = input.audio.playbackOffsetMs();
|
|
201
279
|
input.audio.stopPlayback();
|
|
202
|
-
send(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
280
|
+
send(
|
|
281
|
+
input.websocket,
|
|
282
|
+
input.dialect === "xai"
|
|
283
|
+
? {
|
|
284
|
+
type: "conversation.item.truncate",
|
|
285
|
+
item_id: itemId,
|
|
286
|
+
content_index: 0,
|
|
287
|
+
audio_end_ms: Math.max(0, Math.round(audioEndMs)),
|
|
288
|
+
}
|
|
289
|
+
: {
|
|
290
|
+
type: "conversation-item-truncate",
|
|
291
|
+
itemId,
|
|
292
|
+
contentIndex: 0,
|
|
293
|
+
audioEndMs: Math.max(0, Math.round(audioEndMs)),
|
|
294
|
+
},
|
|
295
|
+
);
|
|
208
296
|
}
|
|
209
297
|
return;
|
|
210
298
|
}
|
|
211
|
-
if (
|
|
299
|
+
if (
|
|
300
|
+
event.type === "input-transcription-completed" ||
|
|
301
|
+
event.type === "conversation.item.input_audio_transcription.completed"
|
|
302
|
+
) {
|
|
212
303
|
const transcript = stringValue(event.transcript) ?? "";
|
|
213
|
-
const itemId = stringValue(event.itemId) ?? crypto.randomUUID();
|
|
304
|
+
const itemId = stringValue(event.itemId) ?? stringValue(event.item_id) ?? crypto.randomUUID();
|
|
214
305
|
if (transcript.trim()) {
|
|
215
306
|
input.channel.providerEvent({
|
|
216
307
|
type: "turn.done",
|
|
@@ -220,17 +311,27 @@ function handleGatewayEvent(input: {
|
|
|
220
311
|
}
|
|
221
312
|
return;
|
|
222
313
|
}
|
|
223
|
-
if (
|
|
314
|
+
if (
|
|
315
|
+
event.type === "audio-delta" ||
|
|
316
|
+
event.type === "response.output_audio.delta" ||
|
|
317
|
+
event.type === "response.audio.delta"
|
|
318
|
+
) {
|
|
224
319
|
const delta = stringValue(event.delta);
|
|
225
320
|
if (!delta) return;
|
|
226
|
-
const itemId = stringValue(event.itemId);
|
|
321
|
+
const itemId = stringValue(event.itemId) ?? stringValue(event.item_id);
|
|
227
322
|
if (itemId) input.setCurrentOutputItemId(itemId);
|
|
228
323
|
input.audio.play(delta);
|
|
229
324
|
input.channel.providerEvent({ type: "output_audio.delta", event_id: eventId, audio: delta });
|
|
230
325
|
return;
|
|
231
326
|
}
|
|
232
|
-
if (
|
|
233
|
-
|
|
327
|
+
if (
|
|
328
|
+
event.type === "audio-transcript-done" ||
|
|
329
|
+
event.type === "text-done" ||
|
|
330
|
+
event.type === "response.output_audio_transcript.done" ||
|
|
331
|
+
event.type === "response.audio_transcript.done" ||
|
|
332
|
+
event.type === "response.output_text.done"
|
|
333
|
+
) {
|
|
334
|
+
const itemId = stringValue(event.itemId) ?? stringValue(event.item_id) ?? crypto.randomUUID();
|
|
234
335
|
const transcript = stringValue(event.transcript) ?? stringValue(event.text) ?? "";
|
|
235
336
|
if (transcript.trim() && !input.finalizedAssistantItems.has(itemId)) {
|
|
236
337
|
input.finalizedAssistantItems.add(itemId);
|
|
@@ -242,21 +343,27 @@ function handleGatewayEvent(input: {
|
|
|
242
343
|
}
|
|
243
344
|
return;
|
|
244
345
|
}
|
|
245
|
-
if (event.type === "audio-done") return;
|
|
246
|
-
if (
|
|
247
|
-
|
|
346
|
+
if (event.type === "audio-done" || event.type === "response.output_audio.done") return;
|
|
347
|
+
if (
|
|
348
|
+
event.type === "function-call-arguments-done" ||
|
|
349
|
+
event.type === "response.function_call_arguments.done"
|
|
350
|
+
) {
|
|
351
|
+
const callId =
|
|
352
|
+
stringValue(event.callId) ??
|
|
353
|
+
stringValue(event.call_id) ??
|
|
354
|
+
stringValue(event.itemId) ??
|
|
355
|
+
stringValue(event.item_id) ??
|
|
356
|
+
crypto.randomUUID();
|
|
248
357
|
const name = stringValue(event.name);
|
|
249
358
|
if (name !== DELEGATION_TOOL) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
});
|
|
259
|
-
send(input.websocket, { type: "response-create" });
|
|
359
|
+
sendFunctionOutput(
|
|
360
|
+
input.websocket,
|
|
361
|
+
input.dialect,
|
|
362
|
+
callId,
|
|
363
|
+
name,
|
|
364
|
+
JSON.stringify({ error: "Unsupported realtime tool" }),
|
|
365
|
+
);
|
|
366
|
+
sendResponseCreate(input.websocket, input.dialect);
|
|
260
367
|
return;
|
|
261
368
|
}
|
|
262
369
|
const request = delegationRequest(stringValue(event.arguments) ?? "");
|
|
@@ -273,10 +380,12 @@ function handleGatewayEvent(input: {
|
|
|
273
380
|
return;
|
|
274
381
|
}
|
|
275
382
|
if (event.type === "error") {
|
|
383
|
+
const nested = isRecord(event.error) ? event.error : null;
|
|
276
384
|
input.channel.providerEvent({
|
|
277
385
|
type: "error",
|
|
278
386
|
event_id: eventId,
|
|
279
|
-
message:
|
|
387
|
+
message:
|
|
388
|
+
stringValue(event.message) ?? stringValue(nested?.message) ?? "Realtime provider error",
|
|
280
389
|
});
|
|
281
390
|
}
|
|
282
391
|
}
|
|
@@ -335,6 +444,7 @@ class GatewayRealtimeDataChannel extends EventTarget {
|
|
|
335
444
|
function handleBridgeOutbound(
|
|
336
445
|
websocket: WebSocket,
|
|
337
446
|
messages: Array<Record<string, unknown>>,
|
|
447
|
+
dialect: RealtimeDialect,
|
|
338
448
|
): void {
|
|
339
449
|
const groups = new Map<
|
|
340
450
|
string,
|
|
@@ -361,16 +471,14 @@ function handleBridgeOutbound(
|
|
|
361
471
|
}
|
|
362
472
|
for (const group of groups.values()) {
|
|
363
473
|
if (group.type === "delegation.context.append" && group.id && group.channel === "speakable") {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
});
|
|
373
|
-
send(websocket, { type: "response-create" });
|
|
474
|
+
sendFunctionOutput(
|
|
475
|
+
websocket,
|
|
476
|
+
dialect,
|
|
477
|
+
group.id,
|
|
478
|
+
DELEGATION_TOOL,
|
|
479
|
+
JSON.stringify({ result: group.text }),
|
|
480
|
+
);
|
|
481
|
+
sendResponseCreate(websocket, dialect);
|
|
374
482
|
continue;
|
|
375
483
|
}
|
|
376
484
|
const wrapper =
|
|
@@ -379,18 +487,52 @@ function handleBridgeOutbound(
|
|
|
379
487
|
? "execution_progress"
|
|
380
488
|
: "execution_result"
|
|
381
489
|
: "session_update";
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
490
|
+
const text = `<${wrapper}>\n${group.text}\n</${wrapper}>`;
|
|
491
|
+
send(
|
|
492
|
+
websocket,
|
|
493
|
+
dialect === "xai"
|
|
494
|
+
? {
|
|
495
|
+
type: "conversation.item.create",
|
|
496
|
+
item: {
|
|
497
|
+
type: "message",
|
|
498
|
+
role: "user",
|
|
499
|
+
content: [{ type: "input_text", text }],
|
|
500
|
+
},
|
|
501
|
+
}
|
|
502
|
+
: {
|
|
503
|
+
type: "conversation-item-create",
|
|
504
|
+
item: { type: "text-message", role: "user", text },
|
|
505
|
+
},
|
|
506
|
+
);
|
|
507
|
+
if (group.channel === "speakable") sendResponseCreate(websocket, dialect);
|
|
391
508
|
}
|
|
392
509
|
}
|
|
393
510
|
|
|
511
|
+
function sendFunctionOutput(
|
|
512
|
+
websocket: WebSocket,
|
|
513
|
+
dialect: RealtimeDialect,
|
|
514
|
+
callId: string,
|
|
515
|
+
name: string | null,
|
|
516
|
+
output: string,
|
|
517
|
+
): void {
|
|
518
|
+
send(
|
|
519
|
+
websocket,
|
|
520
|
+
dialect === "xai"
|
|
521
|
+
? {
|
|
522
|
+
type: "conversation.item.create",
|
|
523
|
+
item: { type: "function_call_output", call_id: callId, output },
|
|
524
|
+
}
|
|
525
|
+
: {
|
|
526
|
+
type: "conversation-item-create",
|
|
527
|
+
item: { type: "function-call-output", callId, name, output },
|
|
528
|
+
},
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function sendResponseCreate(websocket: WebSocket, dialect: RealtimeDialect): void {
|
|
533
|
+
send(websocket, { type: dialect === "xai" ? "response.create" : "response-create" });
|
|
534
|
+
}
|
|
535
|
+
|
|
394
536
|
class GatewayRealtimeAudio {
|
|
395
537
|
private captureContext: AudioContext | null = null;
|
|
396
538
|
private captureSource: MediaStreamAudioSourceNode | null = null;
|
|
@@ -560,6 +702,7 @@ function providerEventId(event: Record<string, unknown>): string {
|
|
|
560
702
|
const raw = isRecord(event.raw) ? event.raw : null;
|
|
561
703
|
return (
|
|
562
704
|
stringValue(event.eventId) ??
|
|
705
|
+
stringValue(event.event_id) ??
|
|
563
706
|
stringValue(raw?.event_id) ??
|
|
564
707
|
stringValue(raw?.id) ??
|
|
565
708
|
crypto.randomUUID()
|