@d-id/client-sdk 2.0.5 → 2.0.6-staging.397
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 +10 -0
- package/dist/{index-C9-G_CI4.js → index-BT-_g68F.js} +2475 -2458
- package/dist/index.js +19 -17
- package/dist/index.umd.cjs +24 -24
- package/dist/livekit-manager-DlYh2os8.js +484 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/services/streaming-manager/advanced.test.d.ts +0 -4
- package/dist/src/services/streaming-manager/common.d.ts +8 -13
- package/dist/src/services/streaming-manager/edge-cases.test.d.ts +0 -4
- package/dist/src/services/streaming-manager/livekit-manager.d.ts +0 -5
- package/dist/src/services/streaming-manager/webrtc-core.test.d.ts +0 -4
- package/dist/src/types/entities/agents/manager.d.ts +20 -1
- package/dist/src/types/stream/data-channel.d.ts +16 -0
- package/dist/src/types/stream/stream.d.ts +29 -0
- package/dist/src/utils/tool-calls.d.ts +8 -0
- package/dist/src/utils/tool-calls.test.d.ts +1 -0
- package/package.json +1 -1
- package/dist/livekit-manager-BA2FEl8R.js +0 -482
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { c as Be, C as J, S as f, a as Je, b as Qe, A as m, d as We, l as pe, e as qe, t as Ge, f as A, g as U, h as He, D as ge, i as Xe, j as b, n as Ze } from "./index-BT-_g68F.js";
|
|
2
|
+
function Ye() {
|
|
3
|
+
try {
|
|
4
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone ?? "";
|
|
5
|
+
} catch {
|
|
6
|
+
return "";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function en() {
|
|
10
|
+
if (typeof navigator > "u")
|
|
11
|
+
return "";
|
|
12
|
+
const l = navigator.userAgent ?? "", C = (navigator.platform ?? "").toLowerCase();
|
|
13
|
+
let u = "Unknown";
|
|
14
|
+
/android/i.test(l) ? u = "Android" : /iphone|ipad|ipod/i.test(l) ? u = "iOS" : C.includes("win") ? u = "Windows" : C.includes("mac") ? u = "Mac OS X" : C.includes("linux") && (u = "Linux");
|
|
15
|
+
const i = u === "Android" || u === "iOS" || /Mobi/i.test(l);
|
|
16
|
+
return `${u}, ${i ? "Mobile" : "Desktop"}`;
|
|
17
|
+
}
|
|
18
|
+
function nn() {
|
|
19
|
+
const l = {}, C = Ye();
|
|
20
|
+
C && (l.timezone = C);
|
|
21
|
+
const u = en();
|
|
22
|
+
return u && (l.device = u), l;
|
|
23
|
+
}
|
|
24
|
+
function tn(l, C, u, i) {
|
|
25
|
+
const k = Be(l, `${C}/v2/agents/${u}`, i);
|
|
26
|
+
return {
|
|
27
|
+
async createStream(g) {
|
|
28
|
+
return k.post("/sessions", g);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const on = {
|
|
33
|
+
[f.ChatAnswer]: J.Answer,
|
|
34
|
+
[f.ChatPartial]: J.Partial
|
|
35
|
+
}, he = 2e4, rn = [];
|
|
36
|
+
async function an() {
|
|
37
|
+
try {
|
|
38
|
+
return await import("./index-BT-_g68F.js").then((l) => l.F);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"LiveKit client is required for this streaming manager. Please install it using: npm install livekit-client"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const cn = {
|
|
46
|
+
excellent: U.Strong,
|
|
47
|
+
good: U.Strong,
|
|
48
|
+
poor: U.Weak,
|
|
49
|
+
lost: U.Unknown,
|
|
50
|
+
unknown: U.Unknown
|
|
51
|
+
}, O = (l = "Stream Error") => new qe(l);
|
|
52
|
+
function me(l, C, u) {
|
|
53
|
+
var i, k;
|
|
54
|
+
throw C("Failed to connect to LiveKit room:", l), (i = u.onConnectionStateChange) == null || i.call(u, A.Fail, "internal:init-error"), (k = u.onError) == null || k.call(u, l, { sessionId: "" }), l;
|
|
55
|
+
}
|
|
56
|
+
async function un(l, C, u) {
|
|
57
|
+
var se;
|
|
58
|
+
const i = Je(u.debug || !1, "LiveKitStreamingManager"), { Room: k, RoomEvent: g, ConnectionState: I, Track: Y } = await an(), { callbacks: n, auth: Ce, baseURL: Se, analytics: ee } = u;
|
|
59
|
+
let a = null, E = !1;
|
|
60
|
+
const ne = Qe.Fluent;
|
|
61
|
+
let L = null;
|
|
62
|
+
const P = { isPublishing: !1, publication: null }, Q = { isPublishing: !1, publication: null };
|
|
63
|
+
let h = null, S = null, j = null, K = !1;
|
|
64
|
+
a = new k({
|
|
65
|
+
adaptiveStream: !1,
|
|
66
|
+
// Must be false to use mediaStreamTrack directly
|
|
67
|
+
dynacast: !0
|
|
68
|
+
});
|
|
69
|
+
let R = null, M = m.Idle, W = !0;
|
|
70
|
+
const y = /* @__PURE__ */ new Map();
|
|
71
|
+
let x = null;
|
|
72
|
+
const Te = tn(Ce, Se || We, l, n.onError);
|
|
73
|
+
let v, _, $, te = !0;
|
|
74
|
+
try {
|
|
75
|
+
const e = await Te.createStream({
|
|
76
|
+
transport: C.transport,
|
|
77
|
+
chat_persist: C.chat_persist ?? !0,
|
|
78
|
+
verbose: u.verbose ?? !1
|
|
79
|
+
}), { id: t, session_token: o, session_url: r, interrupt_enabled: c } = e;
|
|
80
|
+
(se = n.onStreamCreated) == null || se.call(n, { session_id: t, stream_id: t, agent_id: l }), v = t, _ = o, $ = r, te = c ?? !0, await a.prepareConnection($, _);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
me(e, i, n);
|
|
83
|
+
}
|
|
84
|
+
if (!$ || !_ || !v)
|
|
85
|
+
return Promise.reject(new Error("Failed to initialize LiveKit stream"));
|
|
86
|
+
a.on(g.ConnectionStateChanged, we).on(g.ConnectionQualityChanged, Ae).on(g.ParticipantConnected, Ee).on(g.ParticipantDisconnected, Re).on(g.TrackSubscribed, Le).on(g.TrackUnsubscribed, Pe).on(g.DataReceived, Ke).on(g.MediaDevicesError, xe).on(g.TranscriptionReceived, ye).on(g.EncryptionError, Ve).on(g.TrackSubscriptionFailed, Fe);
|
|
87
|
+
function ye(e, t) {
|
|
88
|
+
t != null && t.isLocal && pe.update();
|
|
89
|
+
}
|
|
90
|
+
async function ve() {
|
|
91
|
+
const e = nn();
|
|
92
|
+
if (!(!a || Object.keys(e).length === 0))
|
|
93
|
+
try {
|
|
94
|
+
await a.localParticipant.setAttributes(e);
|
|
95
|
+
} catch (t) {
|
|
96
|
+
i("Failed to set user context attributes", t);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await a.connect($, _), i("LiveKit room joined successfully"), ve(), R = setTimeout(() => {
|
|
101
|
+
var t;
|
|
102
|
+
i(
|
|
103
|
+
`Track subscription timeout - no track subscribed within ${he / 1e3} seconds after connect`
|
|
104
|
+
), R = null;
|
|
105
|
+
const e = O("Track subscription timeout");
|
|
106
|
+
ee.track("connectivity-error", {
|
|
107
|
+
error: Ge(e),
|
|
108
|
+
sessionId: v
|
|
109
|
+
}), (t = n.onError) == null || t.call(n, e, { sessionId: v }), Z("internal:track-subscription-timeout");
|
|
110
|
+
}, he);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
me(e, i, n);
|
|
113
|
+
}
|
|
114
|
+
ee.enrich({
|
|
115
|
+
"stream-type": ne
|
|
116
|
+
});
|
|
117
|
+
function we(e) {
|
|
118
|
+
var t, o, r, c;
|
|
119
|
+
switch (i("Connection state changed:", e), e) {
|
|
120
|
+
case I.Connecting:
|
|
121
|
+
i("CALLBACK: onConnectionStateChange(Connecting)"), (t = n.onConnectionStateChange) == null || t.call(n, A.Connecting, "livekit:connecting");
|
|
122
|
+
break;
|
|
123
|
+
case I.Connected:
|
|
124
|
+
i("LiveKit room connected successfully"), E = !0;
|
|
125
|
+
break;
|
|
126
|
+
case I.Disconnected:
|
|
127
|
+
i("LiveKit room disconnected"), E = !1, K = !1, P.publication = null, Q.publication = null, (o = n.onConnectionStateChange) == null || o.call(n, A.Disconnected, "livekit:disconnected");
|
|
128
|
+
break;
|
|
129
|
+
case I.Reconnecting:
|
|
130
|
+
i("LiveKit room reconnecting..."), (r = n.onConnectionStateChange) == null || r.call(n, A.Connecting, "livekit:reconnecting");
|
|
131
|
+
break;
|
|
132
|
+
case I.SignalReconnecting:
|
|
133
|
+
i("LiveKit room signal reconnecting..."), (c = n.onConnectionStateChange) == null || c.call(n, A.Connecting, "livekit:signal-reconnecting");
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function Ae(e, t) {
|
|
138
|
+
var o;
|
|
139
|
+
i("Connection quality:", e), t != null && t.isLocal && ((o = n.onConnectivityStateChange) == null || o.call(n, cn[e]));
|
|
140
|
+
}
|
|
141
|
+
function Ee(e) {
|
|
142
|
+
i("Participant connected:", e.identity);
|
|
143
|
+
}
|
|
144
|
+
function Re(e) {
|
|
145
|
+
i("Participant disconnected:", e.identity), Z("livekit:participant-disconnected");
|
|
146
|
+
}
|
|
147
|
+
function be() {
|
|
148
|
+
var e;
|
|
149
|
+
j !== b.Start && (i("CALLBACK: onVideoStateChange(Start)"), j = b.Start, (e = n.onVideoStateChange) == null || e.call(n, b.Start));
|
|
150
|
+
}
|
|
151
|
+
function ie(e) {
|
|
152
|
+
var t;
|
|
153
|
+
j !== b.Stop && (i("CALLBACK: onVideoStateChange(Stop)"), j = b.Stop, (t = n.onVideoStateChange) == null || t.call(n, b.Stop, e));
|
|
154
|
+
}
|
|
155
|
+
function Le(e, t, o) {
|
|
156
|
+
var c, d, s;
|
|
157
|
+
i(`Track subscribed: ${e.kind} from ${o.identity}`);
|
|
158
|
+
const r = e.mediaStreamTrack;
|
|
159
|
+
if (!r) {
|
|
160
|
+
i(`No mediaStreamTrack available for ${e.kind}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
L ? (L.addTrack(r), i(`Added ${e.kind} track to shared MediaStream`)) : (L = new MediaStream([r]), i(`Created shared MediaStream with ${e.kind} track`)), e.kind === "audio" && (S = Xe(
|
|
164
|
+
() => e.getRTCStatsReport(),
|
|
165
|
+
({ sttLatency: p, serviceLatency: T }) => {
|
|
166
|
+
var ue, de, le;
|
|
167
|
+
const V = pe.get(!0);
|
|
168
|
+
let F = 0;
|
|
169
|
+
if (p) {
|
|
170
|
+
const fe = ((de = (ue = h == null ? void 0 : h.getReport()) == null ? void 0 : ue.webRTCStats) == null ? void 0 : de.avgRtt) ?? 0;
|
|
171
|
+
F = fe > 0 ? Math.round(fe * 1e3) : 0;
|
|
172
|
+
}
|
|
173
|
+
const D = V > 0 ? V + (p ?? 0) + F : void 0, w = D !== void 0 && T !== void 0 ? D - T : void 0;
|
|
174
|
+
(le = n.onFirstAudioDetected) == null || le.call(n, { latency: D, networkLatency: w });
|
|
175
|
+
}
|
|
176
|
+
)), e.kind === "video" && ((c = n.onStreamReady) == null || c.call(n), i("CALLBACK: onSrcObjectReady"), (d = n.onSrcObjectReady) == null || d.call(n, L), K || (K = !0, i("CALLBACK: onConnectionStateChange(Connected)"), (s = n.onConnectionStateChange) == null || s.call(n, A.Connected, "livekit:track-subscribed")), h = He(
|
|
177
|
+
() => e.getRTCStatsReport(),
|
|
178
|
+
() => E,
|
|
179
|
+
Ze,
|
|
180
|
+
(p, T) => {
|
|
181
|
+
i(`Video state change: ${p}`), p === b.Start ? (R && (clearTimeout(R), R = null, i("Track subscription timeout cleared")), be()) : p === b.Stop && ie(T);
|
|
182
|
+
}
|
|
183
|
+
), h.start());
|
|
184
|
+
}
|
|
185
|
+
function Pe(e, t, o) {
|
|
186
|
+
i(`Track unsubscribed: ${e.kind} from ${o.identity}`), e.kind === "audio" && (S == null || S.destroy(), S = null), e.kind === "video" && (ie(h == null ? void 0 : h.getReport()), h == null || h.stop(), h = null);
|
|
187
|
+
}
|
|
188
|
+
function oe(e, t) {
|
|
189
|
+
var r;
|
|
190
|
+
const o = on[e];
|
|
191
|
+
o && ((r = n.onMessage) == null || r.call(n, o, { event: o, ...t }));
|
|
192
|
+
}
|
|
193
|
+
function q(e, t) {
|
|
194
|
+
var o, r, c, d;
|
|
195
|
+
if (e === f.ToolCallStarted) {
|
|
196
|
+
const s = t;
|
|
197
|
+
y.set(s.call_id, {
|
|
198
|
+
call: {
|
|
199
|
+
callId: s.call_id,
|
|
200
|
+
name: s.name,
|
|
201
|
+
executionMode: s.execution_mode === "async" ? "async" : "blocking"
|
|
202
|
+
},
|
|
203
|
+
interruptible: s.interruptible === !0,
|
|
204
|
+
turnId: s.turn_id ?? x
|
|
205
|
+
}), N(), z(), M = m.ToolActive, (o = n.onAgentActivityStateChange) == null || o.call(n, m.ToolActive), (r = n.onToolEvent) == null || r.call(n, f.ToolCallStarted, s);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (e === f.ToolCallDone) {
|
|
209
|
+
const s = t;
|
|
210
|
+
re(s.call_id), (c = n.onToolEvent) == null || c.call(n, f.ToolCallDone, s);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (e === f.ToolCallError) {
|
|
214
|
+
const s = t;
|
|
215
|
+
re(s.call_id), (d = n.onToolEvent) == null || d.call(n, f.ToolCallError, s);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function re(e) {
|
|
219
|
+
y.delete(e) && (N(), z());
|
|
220
|
+
}
|
|
221
|
+
function Me(e) {
|
|
222
|
+
let t = !1;
|
|
223
|
+
for (const [o, r] of y)
|
|
224
|
+
r.call.executionMode === "blocking" && (e !== null && r.turnId !== null && r.turnId !== e || (y.delete(o), t = !0));
|
|
225
|
+
t && (N(), z());
|
|
226
|
+
}
|
|
227
|
+
function N() {
|
|
228
|
+
var t;
|
|
229
|
+
const e = ![...y.values()].some(({ call: o }) => o.executionMode === "blocking");
|
|
230
|
+
e !== W && (W = e, (t = n.onInterruptibleChange) == null || t.call(n, e));
|
|
231
|
+
}
|
|
232
|
+
function z() {
|
|
233
|
+
var e;
|
|
234
|
+
(e = n.onRunningToolCallsChange) == null || e.call(
|
|
235
|
+
n,
|
|
236
|
+
y.size === 0 ? rn : [...y.values()].map(({ call: t }) => t)
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
function ke(e, t) {
|
|
240
|
+
var o, r, c;
|
|
241
|
+
if (e === f.StreamVideoCreated) {
|
|
242
|
+
M = m.Talking, (o = n.onAgentActivityStateChange) == null || o.call(n, m.Talking), S == null || S.arm({
|
|
243
|
+
sttLatency: (r = t == null ? void 0 : t.stt) == null ? void 0 : r.latency,
|
|
244
|
+
serviceLatency: t == null ? void 0 : t.serviceLatency
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (y.size > 0) {
|
|
249
|
+
M !== m.ToolActive && (M = m.ToolActive, (c = n.onAgentActivityStateChange) == null || c.call(n, m.ToolActive));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function B(e, t) {
|
|
254
|
+
var d, s, p, T;
|
|
255
|
+
const o = ((s = (d = h == null ? void 0 : h.getReport()) == null ? void 0 : d.webRTCStats) == null ? void 0 : s.avgRtt) ?? 0, r = o > 0 ? Math.round(o / 2 * 1e3) : 0, c = { ...t, downstreamNetworkLatency: r };
|
|
256
|
+
u.debug && ((p = t == null ? void 0 : t.metadata) != null && p.sentiment) && (c.sentiment = {
|
|
257
|
+
id: t.metadata.sentiment.id,
|
|
258
|
+
name: t.metadata.sentiment.sentiment
|
|
259
|
+
}), (T = n.onMessage) == null || T.call(n, e, c), ke(e, t);
|
|
260
|
+
}
|
|
261
|
+
function Ie(e, t) {
|
|
262
|
+
var o;
|
|
263
|
+
(o = n.onMessage) == null || o.call(n, J.Transcribe, { event: J.Transcribe, ...t }), queueMicrotask(() => {
|
|
264
|
+
var r;
|
|
265
|
+
(r = n.onAgentActivityStateChange) == null || r.call(n, m.Loading);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function _e(e, t) {
|
|
269
|
+
var o;
|
|
270
|
+
x = (t == null ? void 0 : t.turn_id) ?? null, M = m.Loading, (o = n.onAgentActivityStateChange) == null || o.call(n, m.Loading);
|
|
271
|
+
}
|
|
272
|
+
function $e(e, t) {
|
|
273
|
+
var r;
|
|
274
|
+
const o = (t == null ? void 0 : t.turn_id) ?? null;
|
|
275
|
+
x !== null && o !== null && o < x || (Me(o), M = m.Idle, (r = n.onAgentActivityStateChange) == null || r.call(n, m.Idle));
|
|
276
|
+
}
|
|
277
|
+
const De = {
|
|
278
|
+
[f.ChatAnswer]: oe,
|
|
279
|
+
[f.ChatPartial]: oe,
|
|
280
|
+
[f.ToolCallStarted]: q,
|
|
281
|
+
[f.ToolCallDone]: q,
|
|
282
|
+
[f.ToolCallError]: q,
|
|
283
|
+
[f.StreamVideoCreated]: B,
|
|
284
|
+
[f.StreamVideoDone]: B,
|
|
285
|
+
[f.StreamVideoError]: B,
|
|
286
|
+
[f.StreamVideoRejected]: B,
|
|
287
|
+
[f.ChatAudioTranscribed]: Ie,
|
|
288
|
+
[f.TurnStarted]: _e,
|
|
289
|
+
[f.TurnEnded]: $e
|
|
290
|
+
};
|
|
291
|
+
function Ke(e, t, o, r) {
|
|
292
|
+
const c = new TextDecoder().decode(e);
|
|
293
|
+
try {
|
|
294
|
+
const d = JSON.parse(c), s = r || d.subject;
|
|
295
|
+
if (i("Data received:", { subject: s, data: d }), !s) return;
|
|
296
|
+
const p = De[s];
|
|
297
|
+
p == null || p(s, d);
|
|
298
|
+
} catch (d) {
|
|
299
|
+
i("Failed to parse data channel message:", d);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function xe(e) {
|
|
303
|
+
var t;
|
|
304
|
+
i("Media devices error:", e), (t = n.onError) == null || t.call(n, O(), { sessionId: v });
|
|
305
|
+
}
|
|
306
|
+
function Ve(e) {
|
|
307
|
+
var t;
|
|
308
|
+
i("Encryption error:", e), (t = n.onError) == null || t.call(n, O(), { sessionId: v });
|
|
309
|
+
}
|
|
310
|
+
function Fe(e, t, o) {
|
|
311
|
+
i("Track subscription failed:", { trackSid: e, participant: t, reason: o });
|
|
312
|
+
}
|
|
313
|
+
function Ue(e, t, o) {
|
|
314
|
+
for (const [r, c] of o)
|
|
315
|
+
if (c.source === t && c.track) {
|
|
316
|
+
const d = c.track.mediaStreamTrack;
|
|
317
|
+
if (d === e || (d == null ? void 0 : d.id) === e.id)
|
|
318
|
+
return c;
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
async function ae(e, t, o, r, c, d) {
|
|
323
|
+
var V, F, D;
|
|
324
|
+
if (!E || !a)
|
|
325
|
+
throw i(`Room is not connected, cannot publish ${r} stream`), new Error("Room is not connected");
|
|
326
|
+
if (e.isPublishing) {
|
|
327
|
+
i(`${r} publish already in progress, skipping`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const s = o(t);
|
|
331
|
+
if (s.length === 0)
|
|
332
|
+
throw new Error(`No ${r} track found in the provided MediaStream`);
|
|
333
|
+
const p = s[0], T = Ue(p, r, c());
|
|
334
|
+
if (T) {
|
|
335
|
+
i(`${r} track is already published, skipping`, {
|
|
336
|
+
trackId: p.id,
|
|
337
|
+
publishedTrackId: (F = (V = T.track) == null ? void 0 : V.mediaStreamTrack) == null ? void 0 : F.id
|
|
338
|
+
}), e.publication = T;
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if ((D = e.publication) != null && D.track) {
|
|
342
|
+
const w = e.publication.track.mediaStreamTrack;
|
|
343
|
+
w !== p && (w == null ? void 0 : w.id) !== p.id && (i(`Unpublishing existing ${r} track before publishing new one`), await d());
|
|
344
|
+
}
|
|
345
|
+
i(`Publishing ${r} track from provided MediaStream`, { trackId: p.id }), e.isPublishing = !0;
|
|
346
|
+
try {
|
|
347
|
+
e.publication = await a.localParticipant.publishTrack(p, { source: r }), i(`${r} track published successfully`, { trackSid: e.publication.trackSid });
|
|
348
|
+
} catch (w) {
|
|
349
|
+
throw i(`Failed to publish ${r} track:`, w), w;
|
|
350
|
+
} finally {
|
|
351
|
+
e.isPublishing = !1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function ce(e, t) {
|
|
355
|
+
if (!(!e.publication || !e.publication.track))
|
|
356
|
+
try {
|
|
357
|
+
a && (await a.localParticipant.unpublishTrack(e.publication.track, !1), i(`${t} track unpublished`));
|
|
358
|
+
} catch (o) {
|
|
359
|
+
i(`Error unpublishing ${t} track:`, o);
|
|
360
|
+
} finally {
|
|
361
|
+
e.publication = null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function Oe(e) {
|
|
365
|
+
return ae(
|
|
366
|
+
P,
|
|
367
|
+
e,
|
|
368
|
+
(t) => t.getAudioTracks(),
|
|
369
|
+
Y.Source.Microphone,
|
|
370
|
+
() => a.localParticipant.audioTrackPublications,
|
|
371
|
+
G
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
async function G() {
|
|
375
|
+
return ce(P, "Microphone");
|
|
376
|
+
}
|
|
377
|
+
async function je(e) {
|
|
378
|
+
if (!E || !a)
|
|
379
|
+
throw i("Cannot replace microphone track: room is not connected"), new Error("Room is not connected");
|
|
380
|
+
if (e.kind !== "audio")
|
|
381
|
+
throw i("Cannot replace microphone track: not an audio track", { kind: e.kind }), new Error("Microphone track must be an audio track");
|
|
382
|
+
if (P.isPublishing)
|
|
383
|
+
throw i("Cannot replace microphone track: publish in progress"), new Error("Microphone publish in progress");
|
|
384
|
+
const t = P.publication;
|
|
385
|
+
if (!t || !t.track)
|
|
386
|
+
throw i("Cannot replace microphone track: no publication to replace"), new Error("No microphone publication to replace");
|
|
387
|
+
try {
|
|
388
|
+
P.isPublishing = !0, await t.track.replaceTrack(e), i("Microphone track replaced", { trackId: e.id, trackSid: t.trackSid });
|
|
389
|
+
} finally {
|
|
390
|
+
P.isPublishing = !1;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async function Ne(e) {
|
|
394
|
+
return ae(
|
|
395
|
+
Q,
|
|
396
|
+
e,
|
|
397
|
+
(t) => t.getVideoTracks(),
|
|
398
|
+
Y.Source.Camera,
|
|
399
|
+
() => a.localParticipant.videoTrackPublications,
|
|
400
|
+
H
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
async function H() {
|
|
404
|
+
return ce(Q, "Camera");
|
|
405
|
+
}
|
|
406
|
+
function ze() {
|
|
407
|
+
L && (L.getTracks().forEach((e) => e.stop()), L = null);
|
|
408
|
+
}
|
|
409
|
+
async function X(e, t) {
|
|
410
|
+
var o, r;
|
|
411
|
+
if (!E || !a) {
|
|
412
|
+
i("Room is not connected for sending messages"), (o = n.onError) == null || o.call(n, O(), {
|
|
413
|
+
sessionId: v
|
|
414
|
+
});
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
await a.localParticipant.sendText(t, { topic: e }), i("Message sent successfully:", t);
|
|
419
|
+
} catch (c) {
|
|
420
|
+
i("Failed to send message:", c), (r = n.onError) == null || r.call(n, O(), { sessionId: v });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async function Z(e) {
|
|
424
|
+
var t, o;
|
|
425
|
+
R && (clearTimeout(R), R = null), S == null || S.destroy(), S = null, a && ((t = n.onConnectionStateChange) == null || t.call(n, A.Disconnecting, e), await Promise.all([G(), H()]), await a.disconnect()), ze(), E = !1, K = !1, y.size > 0 && (y.clear(), N(), z()), x = null, (o = n.onAgentActivityStateChange) == null || o.call(n, m.Idle), M = m.Idle;
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
speak(e) {
|
|
429
|
+
const t = typeof e == "string" ? e : JSON.stringify(e);
|
|
430
|
+
return X(ge.Speak, t);
|
|
431
|
+
},
|
|
432
|
+
disconnect: () => Z("user:disconnect"),
|
|
433
|
+
async reconnect() {
|
|
434
|
+
var e, t;
|
|
435
|
+
if ((a == null ? void 0 : a.state) === I.Connected) {
|
|
436
|
+
i("Room is already connected");
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (!a || !$ || !_)
|
|
440
|
+
throw i("Cannot reconnect: missing room, URL or token"), new Error("Cannot reconnect: session not available");
|
|
441
|
+
i("Reconnecting to LiveKit room, state:", a.state), K = !1, (e = n.onConnectionStateChange) == null || e.call(n, A.Connecting, "user:reconnect");
|
|
442
|
+
try {
|
|
443
|
+
if (await a.connect($, _), i("Room reconnected"), E = !0, a.remoteParticipants.size === 0) {
|
|
444
|
+
if (i("Waiting for agent to join..."), !await new Promise((r) => {
|
|
445
|
+
const c = setTimeout(() => {
|
|
446
|
+
a == null || a.off(g.ParticipantConnected, d), r(!1);
|
|
447
|
+
}, 5e3), d = () => {
|
|
448
|
+
clearTimeout(c), a == null || a.off(g.ParticipantConnected, d), r(!0);
|
|
449
|
+
};
|
|
450
|
+
a == null || a.on(g.ParticipantConnected, d);
|
|
451
|
+
}))
|
|
452
|
+
throw i("Agent did not join within timeout"), await a.disconnect(), new Error("Agent did not rejoin the room");
|
|
453
|
+
i("Agent joined, reconnection successful");
|
|
454
|
+
}
|
|
455
|
+
} catch (o) {
|
|
456
|
+
throw i("Failed to reconnect:", o), (t = n.onConnectionStateChange) == null || t.call(n, A.Fail, "user:reconnect-failed"), o;
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
sendDataChannelMessage: X,
|
|
460
|
+
publishMicrophoneStream: Oe,
|
|
461
|
+
unpublishMicrophoneStream: G,
|
|
462
|
+
replaceMicrophoneTrack: je,
|
|
463
|
+
publishCameraStream: Ne,
|
|
464
|
+
unpublishCameraStream: H,
|
|
465
|
+
interrupt(e) {
|
|
466
|
+
e !== "text" && X(ge.Interrupt, "");
|
|
467
|
+
},
|
|
468
|
+
registerRpcMethod(e, t) {
|
|
469
|
+
a == null || a.registerRpcMethod(e, t);
|
|
470
|
+
},
|
|
471
|
+
unregisterRpcMethod(e) {
|
|
472
|
+
a == null || a.unregisterRpcMethod(e);
|
|
473
|
+
},
|
|
474
|
+
sessionId: v,
|
|
475
|
+
streamId: v,
|
|
476
|
+
streamType: ne,
|
|
477
|
+
interruptAvailable: te,
|
|
478
|
+
isInterruptible: W
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
export {
|
|
482
|
+
un as createLiveKitStreamingManager,
|
|
483
|
+
me as handleInitError
|
|
484
|
+
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CreateSessionV2Options, CreateStreamOptions, Interrupt, PayloadType, StreamType } from '../../types';
|
|
2
|
+
import { DataChannelTopic } from '../../types/stream/data-channel';
|
|
2
3
|
|
|
3
4
|
export declare const createStreamingLogger: (debug: boolean, prefix: string) => (message: string, extra?: any) => false | void;
|
|
4
5
|
/**
|
|
@@ -16,16 +17,13 @@ export type StreamingManager<T extends CreateStreamOptions | CreateSessionV2Opti
|
|
|
16
17
|
*/
|
|
17
18
|
disconnect(): Promise<void>;
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* The single send primitive: every message leaving the client goes out through here.
|
|
21
|
+
* V2 routes on the topic; V1 has no topic concept and ignores it.
|
|
22
|
+
* The promise tracks the send attempt and never rejects - failures go to `callbacks.onError`.
|
|
23
|
+
* @param topic Data-channel topic to send on
|
|
24
|
+
* @param payload The message payload to send, already serialized
|
|
21
25
|
*/
|
|
22
|
-
sendDataChannelMessage(payload: string): void
|
|
23
|
-
/**
|
|
24
|
-
* Method to send text messages to the server
|
|
25
|
-
* @param payload The message payload to send
|
|
26
|
-
* supported only for livekit manager
|
|
27
|
-
*/
|
|
28
|
-
sendTextMessage?(payload: string): Promise<void>;
|
|
26
|
+
sendDataChannelMessage(topic: DataChannelTopic, payload: string): Promise<void>;
|
|
29
27
|
/**
|
|
30
28
|
* Publish a microphone stream to the DataChannel
|
|
31
29
|
* Can be called after connection to add microphone input
|
|
@@ -81,10 +79,7 @@ export type StreamingManager<T extends CreateStreamOptions | CreateSessionV2Opti
|
|
|
81
79
|
*/
|
|
82
80
|
isInterruptible: boolean;
|
|
83
81
|
/**
|
|
84
|
-
* Send an interrupt for the current stream segment
|
|
85
|
-
* Each implementation owns the validation/transport details (e.g. V1
|
|
86
|
-
* sends a `stream/interrupt` payload over the data channel; V2 sends
|
|
87
|
-
* `did.interrupt` and ignores `text` interrupts to avoid races).
|
|
82
|
+
* Send an interrupt for the current stream segment
|
|
88
83
|
*/
|
|
89
84
|
interrupt(type: Interrupt['type']): void;
|
|
90
85
|
/**
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { CreateSessionV2Options, CreateStreamOptions, StreamingManagerOptions } from '../../types';
|
|
2
2
|
import { StreamingManager } from './common';
|
|
3
3
|
|
|
4
|
-
export declare enum DataChannelTopic {
|
|
5
|
-
Chat = "lk.chat",
|
|
6
|
-
Speak = "did.speak",
|
|
7
|
-
Interrupt = "did.interrupt"
|
|
8
|
-
}
|
|
9
4
|
export declare function handleInitError(error: unknown, log: (message?: any, ...optionalParams: any[]) => void, callbacks: StreamingManagerOptions['callbacks']): void;
|
|
10
5
|
export declare function createLiveKitStreamingManager<T extends CreateSessionV2Options>(agentId: string, sessionOptions: CreateSessionV2Options, options: StreamingManagerOptions): Promise<StreamingManager<T> & {
|
|
11
6
|
reconnect(): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { STTTokenResponse } from '../..';
|
|
2
2
|
import { Auth } from '../../auth';
|
|
3
|
-
import { AgentActivityState, ClientToolHandler, CompatibilityMode, ConnectionState, ConnectivityState, SendStreamPayloadResponse, StreamEvents, StreamType, StreamingState } from '../../stream';
|
|
3
|
+
import { AgentActivityState, ClientToolHandler, CompatibilityMode, ConnectionState, ConnectivityState, PublicDataChannelTopic, SendStreamPayloadResponse, StreamEvents, StreamType, StreamingState } from '../../stream';
|
|
4
4
|
import { SupportedStreamScript } from '../../stream-script';
|
|
5
5
|
import { ManagerCallbacks as StreamManagerCallbacks } from '../../stream/stream';
|
|
6
6
|
import { Agent } from './agent';
|
|
@@ -103,6 +103,12 @@ interface ManagerCallbacks {
|
|
|
103
103
|
* @param interruptible - Whether the agent can be interrupted by the user
|
|
104
104
|
*/
|
|
105
105
|
onInterruptibleChange?: StreamManagerCallbacks['onInterruptibleChange'];
|
|
106
|
+
/**
|
|
107
|
+
* Optional callback function that will be triggered when the set of running tool calls changes,
|
|
108
|
+
* including an empty array on disconnect.
|
|
109
|
+
* @param calls - The tool calls currently running
|
|
110
|
+
*/
|
|
111
|
+
onRunningToolCallsChange?: StreamManagerCallbacks['onRunningToolCallsChange'];
|
|
106
112
|
}
|
|
107
113
|
interface StreamOptions {
|
|
108
114
|
/**
|
|
@@ -280,6 +286,19 @@ export interface AgentManager {
|
|
|
280
286
|
* Only available for Fluent streams and when there's an active video to interrupt
|
|
281
287
|
*/
|
|
282
288
|
interrupt: (interrupt: Interrupt) => void;
|
|
289
|
+
/**
|
|
290
|
+
* Switch the STT language mid-session
|
|
291
|
+
* Only available for Expressive (V4) agents
|
|
292
|
+
* @param language - Language name or BCP-47 code (e.g. "English" or "en-US")
|
|
293
|
+
*/
|
|
294
|
+
setSttLanguage: (language: string) => Promise<void>;
|
|
295
|
+
/**
|
|
296
|
+
* Send a JSON payload to the agent over a data-channel topic
|
|
297
|
+
* Only available for Expressive (V4) agents
|
|
298
|
+
* @param topic - Data-channel topic to send on (see `PublicDataChannelTopic`)
|
|
299
|
+
* @param payload - Plain object, serialized as JSON
|
|
300
|
+
*/
|
|
301
|
+
sendDataChannelMessage: (topic: PublicDataChannelTopic, payload: Record<string, unknown>) => Promise<void>;
|
|
283
302
|
/**
|
|
284
303
|
* Register a handler for a client tool. When the agent's LLM calls this tool,
|
|
285
304
|
* the handler executes on the client and returns the result to the LLM.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-channel topics messages can be sent on.
|
|
3
|
+
* V2 (LiveKit) routes by topic; V1 (WebRTC) has no topic concept and ignores it.
|
|
4
|
+
*
|
|
5
|
+
* Internal on purpose: this module is deliberately left out of the `stream`
|
|
6
|
+
* barrel so the topics driven by dedicated methods (`chat`, `speak`,
|
|
7
|
+
* `interrupt`, `setSttLanguage`) never reach the package's public API.
|
|
8
|
+
* Customers get `PublicDataChannelTopic` instead.
|
|
9
|
+
*/
|
|
10
|
+
export declare enum DataChannelTopic {
|
|
11
|
+
Chat = "lk.chat",
|
|
12
|
+
Speak = "did.speak",
|
|
13
|
+
Interrupt = "did.interrupt",
|
|
14
|
+
SttLanguage = "did.stt-language",
|
|
15
|
+
Presentation = "did.presentation"
|
|
16
|
+
}
|
|
@@ -3,6 +3,7 @@ import { VideoRTCStatsReport } from '../../services/streaming-manager/stats/repo
|
|
|
3
3
|
import { Auth } from '../auth';
|
|
4
4
|
import { ChatProgressCallback } from '../entities/agents/manager';
|
|
5
5
|
import { CreateClipStreamRequest, CreateTalkStreamRequest, SendClipStreamPayload, SendTalkStreamPayload } from './api';
|
|
6
|
+
import { DataChannelTopic } from './data-channel';
|
|
6
7
|
import { ICreateStreamRequestResponse, IceCandidate, SendStreamPayloadResponse, Status } from './rtc';
|
|
7
8
|
|
|
8
9
|
export type CompatibilityMode = 'on' | 'off' | 'auto';
|
|
@@ -41,6 +42,20 @@ export declare enum StreamEvents {
|
|
|
41
42
|
TurnStarted = "turn/started",
|
|
42
43
|
TurnEnded = "turn/ended"
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Topics a customer can send on via `agentManager.sendDataChannelMessage`.
|
|
47
|
+
* The remaining `DataChannelTopic` members are driven by their own methods
|
|
48
|
+
* (`chat`, `speak`, `interrupt`, `setSttLanguage`), which own the payload shape
|
|
49
|
+
* and bookkeeping those topics expect, so they stay internal.
|
|
50
|
+
*
|
|
51
|
+
* A const object rather than a second enum: it borrows the value from
|
|
52
|
+
* `DataChannelTopic`, so there is one source of truth for the wire string and
|
|
53
|
+
* no cast is needed where the topic reaches the transport.
|
|
54
|
+
*/
|
|
55
|
+
export declare const PublicDataChannelTopic: {
|
|
56
|
+
readonly Presentation: DataChannelTopic.Presentation;
|
|
57
|
+
};
|
|
58
|
+
export type PublicDataChannelTopic = (typeof PublicDataChannelTopic)[keyof typeof PublicDataChannelTopic];
|
|
44
59
|
export declare enum ConnectionState {
|
|
45
60
|
New = "new",
|
|
46
61
|
Fail = "fail",
|
|
@@ -72,6 +87,7 @@ export interface ManagerCallbacks {
|
|
|
72
87
|
onStreamReady?: () => void;
|
|
73
88
|
onToolEvent?: ToolEventCallback;
|
|
74
89
|
onInterruptibleChange?: (interruptible: boolean) => void;
|
|
90
|
+
onRunningToolCallsChange?: (calls: readonly RunningToolCall[]) => void;
|
|
75
91
|
onFirstAudioDetected?: (metrics: AudioDetectionMetrics) => void;
|
|
76
92
|
}
|
|
77
93
|
export interface AudioDetectionMetrics {
|
|
@@ -181,12 +197,25 @@ export interface StreamInterruptPayload {
|
|
|
181
197
|
timestamp: number;
|
|
182
198
|
}
|
|
183
199
|
export type ClientToolHandler = (args: Record<string, unknown>) => Promise<string>;
|
|
200
|
+
export type ToolExecutionMode = 'blocking' | 'async';
|
|
201
|
+
/** A tool call currently running in the session. */
|
|
202
|
+
export interface RunningToolCall {
|
|
203
|
+
callId: string;
|
|
204
|
+
name: string;
|
|
205
|
+
/**
|
|
206
|
+
* 'blocking' - the agent waits for the result before it can continue.
|
|
207
|
+
* 'async' - the agent keeps talking while the call runs.
|
|
208
|
+
*/
|
|
209
|
+
executionMode: ToolExecutionMode;
|
|
210
|
+
}
|
|
184
211
|
export interface ToolCallStartedPayload {
|
|
185
212
|
call_id: string;
|
|
186
213
|
name: string;
|
|
187
214
|
input: Record<string, unknown>;
|
|
188
215
|
output: Record<string, unknown>;
|
|
189
216
|
interruptible: boolean;
|
|
217
|
+
execution_mode?: ToolExecutionMode;
|
|
218
|
+
turn_id?: number | null;
|
|
190
219
|
timestamp: string;
|
|
191
220
|
}
|
|
192
221
|
export interface ToolCallDonePayload {
|