@alexkroman1/aai 0.10.1 → 0.10.3
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/_embeddings.d.ts +31 -0
- package/dist/_internal-types-IfPcaJd5.js +61 -0
- package/dist/_internal-types.js +1 -60
- package/dist/_ssrf-DCp_27V4.js +123 -0
- package/dist/_ssrf.js +1 -122
- package/dist/_utils-DgzpOMSV.js +61 -0
- package/dist/_utils.js +1 -60
- package/dist/{direct-executor-Ca0wt5H0.js → direct-executor-B-5mq3cu.js} +15 -17
- package/dist/index.js +1 -1
- package/dist/kv-iXtikQmR.js +32 -0
- package/dist/kv.js +1 -31
- package/dist/matchers.js +1 -1
- package/dist/middleware-core-BwyBIPed.js +107 -0
- package/dist/middleware-core.js +1 -106
- package/dist/protocol-B-H2Q4ox.js +162 -0
- package/dist/protocol.js +1 -161
- package/dist/runtime-CxcwaK68.js +58 -0
- package/dist/runtime.js +1 -52
- package/dist/s2s-M7JqtgFw.js +272 -0
- package/dist/s2s.js +1 -271
- package/dist/server.d.ts +6 -6
- package/dist/server.js +47 -43
- package/dist/{session-BkN9u0ni.js → session-BYlwcrya.js} +6 -6
- package/dist/session.js +1 -1
- package/dist/telemetry-CJlaDFNc.js +95 -0
- package/dist/telemetry.js +1 -94
- package/dist/{testing-MRl3SXsI.js → testing-BbitshLb.js} +7 -9
- package/dist/testing.js +1 -1
- package/dist/types-D8ZBxTL_.js +192 -0
- package/dist/types.js +1 -191
- package/dist/unstorage-kv-CDgP-frt.js +64 -0
- package/dist/unstorage-kv.d.ts +33 -0
- package/dist/unstorage-kv.js +2 -0
- package/dist/unstorage-vector-Cj5llNhg.js +172 -0
- package/dist/unstorage-vector.d.ts +47 -0
- package/dist/unstorage-vector.js +2 -0
- package/dist/vector.d.ts +3 -2
- package/dist/worker-entry-2jaiqIj0.js +70 -0
- package/dist/worker-entry.js +1 -69
- package/dist/ws-handler-C0Q6eSay.js +207 -0
- package/dist/ws-handler.js +1 -206
- package/package.json +14 -9
- package/dist/sqlite-kv.d.ts +0 -34
- package/dist/sqlite-kv.js +0 -133
- package/dist/sqlite-vector.d.ts +0 -58
- package/dist/sqlite-vector.js +0 -149
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { n as consoleLogger } from "./runtime-CxcwaK68.js";
|
|
2
|
+
import { c as s2sConnectionDuration, h as tracer, l as s2sErrorCounter } from "./telemetry-CJlaDFNc.js";
|
|
3
|
+
import { createNanoEvents } from "nanoevents";
|
|
4
|
+
import { WebSocket } from "ws";
|
|
5
|
+
//#region s2s.ts
|
|
6
|
+
const uint8ToBase64 = (bytes) => Buffer.from(bytes).toString("base64");
|
|
7
|
+
const base64ToUint8 = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
|
|
8
|
+
const WS_OPEN = 1;
|
|
9
|
+
const defaultCreateS2sWebSocket = (url, opts) => new WebSocket(url, { headers: opts.headers });
|
|
10
|
+
function hasStringFields(obj, ...keys) {
|
|
11
|
+
for (const k of keys) if (typeof obj[k] !== "string") return false;
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
function parseAgentTranscript(obj) {
|
|
15
|
+
if (typeof obj.text !== "string") return;
|
|
16
|
+
return {
|
|
17
|
+
type: "transcript.agent",
|
|
18
|
+
text: obj.text,
|
|
19
|
+
reply_id: typeof obj.reply_id === "string" ? obj.reply_id : "",
|
|
20
|
+
item_id: typeof obj.item_id === "string" ? obj.item_id : "",
|
|
21
|
+
interrupted: obj.interrupted === true
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function parseToolCall(obj) {
|
|
25
|
+
if (typeof obj.call_id !== "string" || typeof obj.name !== "string") return;
|
|
26
|
+
const args = obj.args != null && typeof obj.args === "object" && !Array.isArray(obj.args) ? obj.args : {};
|
|
27
|
+
return {
|
|
28
|
+
type: "tool.call",
|
|
29
|
+
call_id: obj.call_id,
|
|
30
|
+
name: obj.name,
|
|
31
|
+
args
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function passthrough(obj) {
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
function requireFields(...keys) {
|
|
38
|
+
return (obj) => hasStringFields(obj, ...keys) ? obj : void 0;
|
|
39
|
+
}
|
|
40
|
+
const MESSAGE_VALIDATORS = new Map([
|
|
41
|
+
["session.ready", requireFields("session_id")],
|
|
42
|
+
["session.updated", passthrough],
|
|
43
|
+
["input.speech.started", passthrough],
|
|
44
|
+
["input.speech.stopped", passthrough],
|
|
45
|
+
["reply.content_part.started", passthrough],
|
|
46
|
+
["reply.content_part.done", passthrough],
|
|
47
|
+
["transcript.user.delta", requireFields("text")],
|
|
48
|
+
["transcript.user", requireFields("item_id", "text")],
|
|
49
|
+
["reply.started", requireFields("reply_id")],
|
|
50
|
+
["transcript.agent.delta", requireFields("delta")],
|
|
51
|
+
["transcript.agent", parseAgentTranscript],
|
|
52
|
+
["tool.call", parseToolCall],
|
|
53
|
+
["reply.done", (obj) => ({
|
|
54
|
+
type: "reply.done",
|
|
55
|
+
...typeof obj.status === "string" ? { status: obj.status } : {}
|
|
56
|
+
})],
|
|
57
|
+
["session.error", requireFields("code", "message")],
|
|
58
|
+
["error", requireFields("message")]
|
|
59
|
+
]);
|
|
60
|
+
function parseS2sMessage(obj) {
|
|
61
|
+
const type = obj.type;
|
|
62
|
+
if (typeof type !== "string") return;
|
|
63
|
+
return MESSAGE_VALIDATORS.get(type)?.(obj);
|
|
64
|
+
}
|
|
65
|
+
function dispatchS2sMessage(emitter, msg) {
|
|
66
|
+
switch (msg.type) {
|
|
67
|
+
case "session.ready":
|
|
68
|
+
emitter.emit("ready", { sessionId: msg.session_id });
|
|
69
|
+
break;
|
|
70
|
+
case "session.updated":
|
|
71
|
+
emitter.emit("sessionUpdated", msg);
|
|
72
|
+
break;
|
|
73
|
+
case "input.speech.started":
|
|
74
|
+
emitter.emit("speechStarted");
|
|
75
|
+
break;
|
|
76
|
+
case "input.speech.stopped":
|
|
77
|
+
emitter.emit("speechStopped");
|
|
78
|
+
break;
|
|
79
|
+
case "transcript.user.delta":
|
|
80
|
+
emitter.emit("userTranscriptDelta", { text: msg.text });
|
|
81
|
+
break;
|
|
82
|
+
case "transcript.user":
|
|
83
|
+
emitter.emit("userTranscript", {
|
|
84
|
+
itemId: msg.item_id,
|
|
85
|
+
text: msg.text
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
case "reply.started":
|
|
89
|
+
emitter.emit("replyStarted", { replyId: msg.reply_id });
|
|
90
|
+
break;
|
|
91
|
+
case "transcript.agent.delta":
|
|
92
|
+
emitter.emit("agentTranscriptDelta", { text: msg.delta });
|
|
93
|
+
break;
|
|
94
|
+
case "transcript.agent":
|
|
95
|
+
emitter.emit("agentTranscript", {
|
|
96
|
+
text: msg.text,
|
|
97
|
+
replyId: msg.reply_id,
|
|
98
|
+
itemId: msg.item_id,
|
|
99
|
+
interrupted: msg.interrupted
|
|
100
|
+
});
|
|
101
|
+
break;
|
|
102
|
+
case "tool.call":
|
|
103
|
+
emitter.emit("toolCall", {
|
|
104
|
+
callId: msg.call_id,
|
|
105
|
+
name: msg.name,
|
|
106
|
+
args: msg.args
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
case "reply.done":
|
|
110
|
+
emitter.emit("replyDone", msg.status ? { status: msg.status } : {});
|
|
111
|
+
break;
|
|
112
|
+
case "session.error":
|
|
113
|
+
if (msg.code === "session_not_found" || msg.code === "session_forbidden") emitter.emit("sessionExpired", {
|
|
114
|
+
code: msg.code,
|
|
115
|
+
message: msg.message
|
|
116
|
+
});
|
|
117
|
+
else emitter.emit("error", {
|
|
118
|
+
code: msg.code,
|
|
119
|
+
message: msg.message
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
case "error":
|
|
123
|
+
emitter.emit("error", {
|
|
124
|
+
code: "connection",
|
|
125
|
+
message: msg.message
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
case "reply.content_part.started":
|
|
129
|
+
case "reply.content_part.done": break;
|
|
130
|
+
default: break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function connectS2s(opts) {
|
|
134
|
+
const { apiKey, config, createWebSocket, logger: log = consoleLogger } = opts;
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
log.info("S2S connecting", { url: config.wssUrl });
|
|
137
|
+
const connectionSpan = tracer.startSpan("s2s.connection", { attributes: { "aai.s2s.url": config.wssUrl } });
|
|
138
|
+
const connectStart = performance.now();
|
|
139
|
+
const ws = createWebSocket(config.wssUrl, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
140
|
+
const emitter = createNanoEvents();
|
|
141
|
+
let opened = false;
|
|
142
|
+
function send(msg) {
|
|
143
|
+
if (ws.readyState !== WS_OPEN) {
|
|
144
|
+
log.debug("S2S send dropped: socket not open", { type: msg.type });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const json = JSON.stringify(msg);
|
|
148
|
+
if (msg.type !== "input.audio") log.info(`S2S >> ${msg.type}`, msg.type === "session.update" ? { payload: json } : void 0);
|
|
149
|
+
ws.send(json);
|
|
150
|
+
}
|
|
151
|
+
const handle = {
|
|
152
|
+
on: emitter.on.bind(emitter),
|
|
153
|
+
sendAudio(audio) {
|
|
154
|
+
if (ws.readyState !== WS_OPEN) {
|
|
155
|
+
log.debug("S2S sendAudio dropped: socket not open");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
ws.send(`{"type":"input.audio","audio":"${uint8ToBase64(audio)}"}`);
|
|
159
|
+
},
|
|
160
|
+
sendToolResult(callId, result) {
|
|
161
|
+
const msg = {
|
|
162
|
+
type: "tool.result",
|
|
163
|
+
call_id: callId,
|
|
164
|
+
result
|
|
165
|
+
};
|
|
166
|
+
log.info("S2S >> tool.result", {
|
|
167
|
+
call_id: callId,
|
|
168
|
+
resultLength: result.length
|
|
169
|
+
});
|
|
170
|
+
send(msg);
|
|
171
|
+
},
|
|
172
|
+
updateSession(sessionConfig) {
|
|
173
|
+
const { systemPrompt, ...rest } = sessionConfig;
|
|
174
|
+
send({
|
|
175
|
+
type: "session.update",
|
|
176
|
+
session: {
|
|
177
|
+
system_prompt: systemPrompt,
|
|
178
|
+
...rest
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
resumeSession(sessionId) {
|
|
183
|
+
send({
|
|
184
|
+
type: "session.resume",
|
|
185
|
+
session_id: sessionId
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
close() {
|
|
189
|
+
log.info("S2S closing");
|
|
190
|
+
ws.close();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
ws.addEventListener("open", () => {
|
|
194
|
+
opened = true;
|
|
195
|
+
log.info("S2S WebSocket open");
|
|
196
|
+
connectionSpan.addEvent("ws.open");
|
|
197
|
+
resolve(handle);
|
|
198
|
+
});
|
|
199
|
+
function tryParseJson(data) {
|
|
200
|
+
try {
|
|
201
|
+
return JSON.parse(String(data));
|
|
202
|
+
} catch {
|
|
203
|
+
log.warn("S2S << invalid JSON", { data: String(data).slice(0, 200) });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function handleAudioFastPath(obj) {
|
|
207
|
+
if (obj.type === "reply.audio" && typeof obj.data === "string") {
|
|
208
|
+
const audioBytes = base64ToUint8(obj.data);
|
|
209
|
+
emitter.emit("audio", { audio: audioBytes });
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
function logIncoming(obj) {
|
|
215
|
+
if (obj.type === "reply.audio" || obj.type === "input.audio") return;
|
|
216
|
+
log.info(`S2S << ${obj.type}`, obj.type === "transcript.agent.delta" ? { delta: obj.delta } : void 0);
|
|
217
|
+
}
|
|
218
|
+
function handleS2sMessage(ev) {
|
|
219
|
+
const raw = tryParseJson(ev.data);
|
|
220
|
+
if (raw === void 0) return;
|
|
221
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
222
|
+
log.warn("S2S << non-object JSON message", { type: typeof raw });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const obj = raw;
|
|
226
|
+
logIncoming(obj);
|
|
227
|
+
if (handleAudioFastPath(obj)) return;
|
|
228
|
+
const parsed = parseS2sMessage(obj);
|
|
229
|
+
if (!parsed) {
|
|
230
|
+
log.warn(`S2S << unrecognised message type: ${obj.type ?? JSON.stringify(raw).slice(0, 200)}`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
dispatchS2sMessage(emitter, parsed);
|
|
234
|
+
}
|
|
235
|
+
ws.addEventListener("message", handleS2sMessage);
|
|
236
|
+
ws.addEventListener("close", (ev) => {
|
|
237
|
+
log.info("S2S WebSocket closed", {
|
|
238
|
+
code: ev.code ?? 0,
|
|
239
|
+
reason: ev.reason ?? ""
|
|
240
|
+
});
|
|
241
|
+
const elapsed = (performance.now() - connectStart) / 1e3;
|
|
242
|
+
s2sConnectionDuration.record(elapsed);
|
|
243
|
+
connectionSpan.addEvent("ws.closed", {
|
|
244
|
+
"ws.close.code": ev.code ?? 0,
|
|
245
|
+
"ws.close.reason": ev.reason ?? ""
|
|
246
|
+
});
|
|
247
|
+
connectionSpan.end();
|
|
248
|
+
if (!opened) reject(/* @__PURE__ */ new Error(`WebSocket closed before open (code: ${ev.code ?? 0})`));
|
|
249
|
+
emitter.emit("close");
|
|
250
|
+
});
|
|
251
|
+
ws.addEventListener("error", (ev) => {
|
|
252
|
+
const message = typeof ev.message === "string" ? ev.message : "WebSocket error";
|
|
253
|
+
const errObj = new Error(message);
|
|
254
|
+
log.error("S2S WebSocket error", { error: errObj.message });
|
|
255
|
+
s2sErrorCounter.add(1);
|
|
256
|
+
connectionSpan.setStatus({
|
|
257
|
+
code: 2,
|
|
258
|
+
message: errObj.message
|
|
259
|
+
});
|
|
260
|
+
connectionSpan.recordException(errObj);
|
|
261
|
+
if (!opened) {
|
|
262
|
+
connectionSpan.end();
|
|
263
|
+
reject(errObj);
|
|
264
|
+
} else emitter.emit("error", {
|
|
265
|
+
code: "ws_error",
|
|
266
|
+
message: errObj.message
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
272
|
+
export { defaultCreateS2sWebSocket as n, connectS2s as t };
|
package/dist/s2s.js
CHANGED
|
@@ -1,272 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { s2sConnectionDuration, s2sErrorCounter, tracer } from "./telemetry.js";
|
|
3
|
-
import { createNanoEvents } from "nanoevents";
|
|
4
|
-
import { WebSocket } from "ws";
|
|
5
|
-
//#region s2s.ts
|
|
6
|
-
const uint8ToBase64 = (bytes) => Buffer.from(bytes).toString("base64");
|
|
7
|
-
const base64ToUint8 = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
|
|
8
|
-
const WS_OPEN = 1;
|
|
9
|
-
const defaultCreateS2sWebSocket = (url, opts) => new WebSocket(url, { headers: opts.headers });
|
|
10
|
-
function hasStringFields(obj, ...keys) {
|
|
11
|
-
for (const k of keys) if (typeof obj[k] !== "string") return false;
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
function parseAgentTranscript(obj) {
|
|
15
|
-
if (typeof obj.text !== "string") return;
|
|
16
|
-
return {
|
|
17
|
-
type: "transcript.agent",
|
|
18
|
-
text: obj.text,
|
|
19
|
-
reply_id: typeof obj.reply_id === "string" ? obj.reply_id : "",
|
|
20
|
-
item_id: typeof obj.item_id === "string" ? obj.item_id : "",
|
|
21
|
-
interrupted: obj.interrupted === true
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function parseToolCall(obj) {
|
|
25
|
-
if (typeof obj.call_id !== "string" || typeof obj.name !== "string") return;
|
|
26
|
-
const args = obj.args != null && typeof obj.args === "object" && !Array.isArray(obj.args) ? obj.args : {};
|
|
27
|
-
return {
|
|
28
|
-
type: "tool.call",
|
|
29
|
-
call_id: obj.call_id,
|
|
30
|
-
name: obj.name,
|
|
31
|
-
args
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function passthrough(obj) {
|
|
35
|
-
return obj;
|
|
36
|
-
}
|
|
37
|
-
function requireFields(...keys) {
|
|
38
|
-
return (obj) => hasStringFields(obj, ...keys) ? obj : void 0;
|
|
39
|
-
}
|
|
40
|
-
const MESSAGE_VALIDATORS = new Map([
|
|
41
|
-
["session.ready", requireFields("session_id")],
|
|
42
|
-
["session.updated", passthrough],
|
|
43
|
-
["input.speech.started", passthrough],
|
|
44
|
-
["input.speech.stopped", passthrough],
|
|
45
|
-
["reply.content_part.started", passthrough],
|
|
46
|
-
["reply.content_part.done", passthrough],
|
|
47
|
-
["transcript.user.delta", requireFields("text")],
|
|
48
|
-
["transcript.user", requireFields("item_id", "text")],
|
|
49
|
-
["reply.started", requireFields("reply_id")],
|
|
50
|
-
["transcript.agent.delta", requireFields("delta")],
|
|
51
|
-
["transcript.agent", parseAgentTranscript],
|
|
52
|
-
["tool.call", parseToolCall],
|
|
53
|
-
["reply.done", (obj) => ({
|
|
54
|
-
type: "reply.done",
|
|
55
|
-
...typeof obj.status === "string" ? { status: obj.status } : {}
|
|
56
|
-
})],
|
|
57
|
-
["session.error", requireFields("code", "message")],
|
|
58
|
-
["error", requireFields("message")]
|
|
59
|
-
]);
|
|
60
|
-
function parseS2sMessage(obj) {
|
|
61
|
-
const type = obj.type;
|
|
62
|
-
if (typeof type !== "string") return;
|
|
63
|
-
return MESSAGE_VALIDATORS.get(type)?.(obj);
|
|
64
|
-
}
|
|
65
|
-
function dispatchS2sMessage(emitter, msg) {
|
|
66
|
-
switch (msg.type) {
|
|
67
|
-
case "session.ready":
|
|
68
|
-
emitter.emit("ready", { sessionId: msg.session_id });
|
|
69
|
-
break;
|
|
70
|
-
case "session.updated":
|
|
71
|
-
emitter.emit("sessionUpdated", msg);
|
|
72
|
-
break;
|
|
73
|
-
case "input.speech.started":
|
|
74
|
-
emitter.emit("speechStarted");
|
|
75
|
-
break;
|
|
76
|
-
case "input.speech.stopped":
|
|
77
|
-
emitter.emit("speechStopped");
|
|
78
|
-
break;
|
|
79
|
-
case "transcript.user.delta":
|
|
80
|
-
emitter.emit("userTranscriptDelta", { text: msg.text });
|
|
81
|
-
break;
|
|
82
|
-
case "transcript.user":
|
|
83
|
-
emitter.emit("userTranscript", {
|
|
84
|
-
itemId: msg.item_id,
|
|
85
|
-
text: msg.text
|
|
86
|
-
});
|
|
87
|
-
break;
|
|
88
|
-
case "reply.started":
|
|
89
|
-
emitter.emit("replyStarted", { replyId: msg.reply_id });
|
|
90
|
-
break;
|
|
91
|
-
case "transcript.agent.delta":
|
|
92
|
-
emitter.emit("agentTranscriptDelta", { text: msg.delta });
|
|
93
|
-
break;
|
|
94
|
-
case "transcript.agent":
|
|
95
|
-
emitter.emit("agentTranscript", {
|
|
96
|
-
text: msg.text,
|
|
97
|
-
replyId: msg.reply_id,
|
|
98
|
-
itemId: msg.item_id,
|
|
99
|
-
interrupted: msg.interrupted
|
|
100
|
-
});
|
|
101
|
-
break;
|
|
102
|
-
case "tool.call":
|
|
103
|
-
emitter.emit("toolCall", {
|
|
104
|
-
callId: msg.call_id,
|
|
105
|
-
name: msg.name,
|
|
106
|
-
args: msg.args
|
|
107
|
-
});
|
|
108
|
-
break;
|
|
109
|
-
case "reply.done":
|
|
110
|
-
emitter.emit("replyDone", msg.status ? { status: msg.status } : {});
|
|
111
|
-
break;
|
|
112
|
-
case "session.error":
|
|
113
|
-
if (msg.code === "session_not_found" || msg.code === "session_forbidden") emitter.emit("sessionExpired", {
|
|
114
|
-
code: msg.code,
|
|
115
|
-
message: msg.message
|
|
116
|
-
});
|
|
117
|
-
else emitter.emit("error", {
|
|
118
|
-
code: msg.code,
|
|
119
|
-
message: msg.message
|
|
120
|
-
});
|
|
121
|
-
break;
|
|
122
|
-
case "error":
|
|
123
|
-
emitter.emit("error", {
|
|
124
|
-
code: "connection",
|
|
125
|
-
message: msg.message
|
|
126
|
-
});
|
|
127
|
-
break;
|
|
128
|
-
case "reply.content_part.started":
|
|
129
|
-
case "reply.content_part.done": break;
|
|
130
|
-
default: break;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
function connectS2s(opts) {
|
|
134
|
-
const { apiKey, config, createWebSocket, logger: log = consoleLogger } = opts;
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
log.info("S2S connecting", { url: config.wssUrl });
|
|
137
|
-
const connectionSpan = tracer.startSpan("s2s.connection", { attributes: { "aai.s2s.url": config.wssUrl } });
|
|
138
|
-
const connectStart = performance.now();
|
|
139
|
-
const ws = createWebSocket(config.wssUrl, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
140
|
-
const emitter = createNanoEvents();
|
|
141
|
-
let opened = false;
|
|
142
|
-
function send(msg) {
|
|
143
|
-
if (ws.readyState !== WS_OPEN) {
|
|
144
|
-
log.debug("S2S send dropped: socket not open", { type: msg.type });
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const json = JSON.stringify(msg);
|
|
148
|
-
if (msg.type !== "input.audio") log.info(`S2S >> ${msg.type}`, msg.type === "session.update" ? { payload: json } : void 0);
|
|
149
|
-
ws.send(json);
|
|
150
|
-
}
|
|
151
|
-
const handle = {
|
|
152
|
-
on: emitter.on.bind(emitter),
|
|
153
|
-
sendAudio(audio) {
|
|
154
|
-
if (ws.readyState !== WS_OPEN) {
|
|
155
|
-
log.debug("S2S sendAudio dropped: socket not open");
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
ws.send(`{"type":"input.audio","audio":"${uint8ToBase64(audio)}"}`);
|
|
159
|
-
},
|
|
160
|
-
sendToolResult(callId, result) {
|
|
161
|
-
const msg = {
|
|
162
|
-
type: "tool.result",
|
|
163
|
-
call_id: callId,
|
|
164
|
-
result
|
|
165
|
-
};
|
|
166
|
-
log.info("S2S >> tool.result", {
|
|
167
|
-
call_id: callId,
|
|
168
|
-
resultLength: result.length
|
|
169
|
-
});
|
|
170
|
-
send(msg);
|
|
171
|
-
},
|
|
172
|
-
updateSession(sessionConfig) {
|
|
173
|
-
const { systemPrompt, ...rest } = sessionConfig;
|
|
174
|
-
send({
|
|
175
|
-
type: "session.update",
|
|
176
|
-
session: {
|
|
177
|
-
system_prompt: systemPrompt,
|
|
178
|
-
...rest
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
},
|
|
182
|
-
resumeSession(sessionId) {
|
|
183
|
-
send({
|
|
184
|
-
type: "session.resume",
|
|
185
|
-
session_id: sessionId
|
|
186
|
-
});
|
|
187
|
-
},
|
|
188
|
-
close() {
|
|
189
|
-
log.info("S2S closing");
|
|
190
|
-
ws.close();
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
ws.addEventListener("open", () => {
|
|
194
|
-
opened = true;
|
|
195
|
-
log.info("S2S WebSocket open");
|
|
196
|
-
connectionSpan.addEvent("ws.open");
|
|
197
|
-
resolve(handle);
|
|
198
|
-
});
|
|
199
|
-
function tryParseJson(data) {
|
|
200
|
-
try {
|
|
201
|
-
return JSON.parse(String(data));
|
|
202
|
-
} catch {
|
|
203
|
-
log.warn("S2S << invalid JSON", { data: String(data).slice(0, 200) });
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function handleAudioFastPath(obj) {
|
|
207
|
-
if (obj.type === "reply.audio" && typeof obj.data === "string") {
|
|
208
|
-
const audioBytes = base64ToUint8(obj.data);
|
|
209
|
-
emitter.emit("audio", { audio: audioBytes });
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
function logIncoming(obj) {
|
|
215
|
-
if (obj.type === "reply.audio" || obj.type === "input.audio") return;
|
|
216
|
-
log.info(`S2S << ${obj.type}`, obj.type === "transcript.agent.delta" ? { delta: obj.delta } : void 0);
|
|
217
|
-
}
|
|
218
|
-
function handleS2sMessage(ev) {
|
|
219
|
-
const raw = tryParseJson(ev.data);
|
|
220
|
-
if (raw === void 0) return;
|
|
221
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
222
|
-
log.warn("S2S << non-object JSON message", { type: typeof raw });
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const obj = raw;
|
|
226
|
-
logIncoming(obj);
|
|
227
|
-
if (handleAudioFastPath(obj)) return;
|
|
228
|
-
const parsed = parseS2sMessage(obj);
|
|
229
|
-
if (!parsed) {
|
|
230
|
-
log.warn(`S2S << unrecognised message type: ${obj.type ?? JSON.stringify(raw).slice(0, 200)}`);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
dispatchS2sMessage(emitter, parsed);
|
|
234
|
-
}
|
|
235
|
-
ws.addEventListener("message", handleS2sMessage);
|
|
236
|
-
ws.addEventListener("close", (ev) => {
|
|
237
|
-
log.info("S2S WebSocket closed", {
|
|
238
|
-
code: ev.code ?? 0,
|
|
239
|
-
reason: ev.reason ?? ""
|
|
240
|
-
});
|
|
241
|
-
const elapsed = (performance.now() - connectStart) / 1e3;
|
|
242
|
-
s2sConnectionDuration.record(elapsed);
|
|
243
|
-
connectionSpan.addEvent("ws.closed", {
|
|
244
|
-
"ws.close.code": ev.code ?? 0,
|
|
245
|
-
"ws.close.reason": ev.reason ?? ""
|
|
246
|
-
});
|
|
247
|
-
connectionSpan.end();
|
|
248
|
-
if (!opened) reject(/* @__PURE__ */ new Error(`WebSocket closed before open (code: ${ev.code ?? 0})`));
|
|
249
|
-
emitter.emit("close");
|
|
250
|
-
});
|
|
251
|
-
ws.addEventListener("error", (ev) => {
|
|
252
|
-
const message = typeof ev.message === "string" ? ev.message : "WebSocket error";
|
|
253
|
-
const errObj = new Error(message);
|
|
254
|
-
log.error("S2S WebSocket error", { error: errObj.message });
|
|
255
|
-
s2sErrorCounter.add(1);
|
|
256
|
-
connectionSpan.setStatus({
|
|
257
|
-
code: 2,
|
|
258
|
-
message: errObj.message
|
|
259
|
-
});
|
|
260
|
-
connectionSpan.recordException(errObj);
|
|
261
|
-
if (!opened) {
|
|
262
|
-
connectionSpan.end();
|
|
263
|
-
reject(errObj);
|
|
264
|
-
} else emitter.emit("error", {
|
|
265
|
-
code: "ws_error",
|
|
266
|
-
message: errObj.message
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
//#endregion
|
|
1
|
+
import { n as defaultCreateS2sWebSocket, t as connectS2s } from "./s2s-M7JqtgFw.js";
|
|
272
2
|
export { connectS2s, defaultCreateS2sWebSocket };
|
package/dist/server.d.ts
CHANGED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* Calls `createDirectExecutor` + `wireSessionSocket` directly — no
|
|
6
6
|
* intermediary needed.
|
|
7
7
|
*/
|
|
8
|
-
import type
|
|
8
|
+
import { type Storage } from "unstorage";
|
|
9
9
|
import type { Logger, S2SConfig } from "./runtime.ts";
|
|
10
10
|
import type { AgentDef } from "./types.ts";
|
|
11
|
-
import type { VectorStore } from "./vector.ts";
|
|
12
11
|
/**
|
|
13
12
|
* Configuration for a self-hosted agent server created by {@link createServer}.
|
|
14
13
|
*
|
|
@@ -19,10 +18,11 @@ export type ServerOptions = {
|
|
|
19
18
|
agent: AgentDef<any>;
|
|
20
19
|
/** Environment variables. Defaults to `process.env`. */
|
|
21
20
|
env?: Record<string, string>;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Unstorage instance for KV and vector storage. Defaults to in-memory.
|
|
23
|
+
* Configure with an S3/R2/filesystem driver for persistence.
|
|
24
|
+
*/
|
|
25
|
+
storage?: Storage;
|
|
26
26
|
/** HTML to serve at `GET /`. */
|
|
27
27
|
clientHtml?: string;
|
|
28
28
|
/** Directory containing built client files (index.html + assets/). */
|