@exotel-npm-dev/exotel-ai-assist 1.2.5 → 1.2.6
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 +35 -32
- package/dist/controller/index.js +4 -4
- package/dist/index.js +5 -5
- package/dist/react/index.js +5 -5
- package/dist/types/index.d.ts +20 -3
- package/package.json +1 -1
- package/src/components/ExotelAIAssistApp.tsx +1 -1
- package/src/controller/index.ts +4 -4
- package/src/types/index.ts +25 -3
package/README.md
CHANGED
|
@@ -123,11 +123,12 @@ function MyCustomUI({ call_sid }: { call_sid: string }) {
|
|
|
123
123
|
|
|
124
124
|
if (streamState === "throttled") return <p>AI Assist is at capacity — available on your next call.</p>;
|
|
125
125
|
if (streamState === "disconnected") return <p>AI Assist disconnected.</p>;
|
|
126
|
+
if (streamState === "connection_timeout") return <p>Connection timed out.</p>;
|
|
126
127
|
|
|
127
128
|
return (
|
|
128
129
|
<div>
|
|
129
130
|
{suggestions.map((s) => (
|
|
130
|
-
<p key={s.id}>{s.
|
|
131
|
+
<p key={s.id}>{s.value}</p>
|
|
131
132
|
))}
|
|
132
133
|
</div>
|
|
133
134
|
);
|
|
@@ -153,7 +154,7 @@ function SuggestionsPanel() {
|
|
|
153
154
|
return (
|
|
154
155
|
<ul>
|
|
155
156
|
{suggestions.map((s) => (
|
|
156
|
-
<li key={s.id}>{s.
|
|
157
|
+
<li key={s.id}>{s.value}</li>
|
|
157
158
|
))}
|
|
158
159
|
</ul>
|
|
159
160
|
);
|
|
@@ -188,7 +189,7 @@ ctrl.on("suggestion", (s) => console.log("Suggestion:", s));
|
|
|
188
189
|
ctrl.on("transcript", (t) => console.log("Transcript:", t));
|
|
189
190
|
ctrl.on("streamState", (state) => {
|
|
190
191
|
console.log("Stream state:", state);
|
|
191
|
-
// state: "connected" | "throttled" | "disconnected"
|
|
192
|
+
// state: "connected" | "throttled" | "pending" | "disconnected" | "connection_timeout"
|
|
192
193
|
});
|
|
193
194
|
ctrl.on("error", (err) => console.error("Error:", err));
|
|
194
195
|
|
|
@@ -238,14 +239,14 @@ Extends `EventEmitter`.
|
|
|
238
239
|
|
|
239
240
|
#### Methods
|
|
240
241
|
|
|
241
|
-
| Method
|
|
242
|
-
|
|
|
243
|
-
| `connect()`
|
|
244
|
-
| `disconnect()`
|
|
245
|
-
| `setParams(patch)`
|
|
246
|
-
| `destroy()`
|
|
247
|
-
| `getStatus()`
|
|
248
|
-
| `getStreamState()`
|
|
242
|
+
| Method | Description |
|
|
243
|
+
| --------------------------------------- | ---------------------------------------------- |
|
|
244
|
+
| `connect()` | Open the WebSocket |
|
|
245
|
+
| `disconnect()` | Close cleanly |
|
|
246
|
+
| `setParams(patch)` | Merge params; reconnects if `call_sid` changes |
|
|
247
|
+
| `destroy()` | Dispose controller and remove all listeners |
|
|
248
|
+
| `getStatus()` | Returns current `ConnectionStatus` |
|
|
249
|
+
| `getStreamState()` | Returns current `StreamState \| null` |
|
|
249
250
|
|
|
250
251
|
#### Events
|
|
251
252
|
|
|
@@ -255,7 +256,7 @@ Extends `EventEmitter`.
|
|
|
255
256
|
| `suggestion` | `Suggestion` | New AI suggestion (capped at last 50) |
|
|
256
257
|
| `transcript` | `TranscriptLine[]` | Live transcript update |
|
|
257
258
|
| `sentiment` | `Sentiment` | Sentiment label update |
|
|
258
|
-
| `streamState` | `StreamState` | Server stream state change (`connected`, `throttled`, `disconnected`)
|
|
259
|
+
| `streamState` | `StreamState` | Server stream state change (`connected`, `throttled`, `pending`, `disconnected`, `connection_timeout`) |
|
|
259
260
|
| `onCallStart` | — | Connection opened |
|
|
260
261
|
| `onCallEnd` | — | Connection closed |
|
|
261
262
|
| `statusChange` | `ConnectionStatus` | WebSocket-level status transition (internal) |
|
|
@@ -266,36 +267,36 @@ Extends `EventEmitter`.
|
|
|
266
267
|
|
|
267
268
|
### `useExotelAIAssist(params)` — Hook return values
|
|
268
269
|
|
|
269
|
-
| Field
|
|
270
|
-
|
|
|
271
|
-
| `isReady`
|
|
272
|
-
| `streamState`
|
|
273
|
-
| `suggestions`
|
|
274
|
-
| `transcripts`
|
|
275
|
-
| `sentiment`
|
|
276
|
-
| `lastError`
|
|
277
|
-
| `connect()`
|
|
278
|
-
| `disconnect()`
|
|
279
|
-
| `setParams()`
|
|
270
|
+
| Field | Type | Description |
|
|
271
|
+
| -------------------------- | ----------------------- | ---------------------------------------------------------------------------- |
|
|
272
|
+
| `isReady` | `boolean` | `true` after connected + server ack. Multi-tab safe. `false` on disconnect |
|
|
273
|
+
| `streamState` | `StreamState \| null` | Server stream state: `"connected"`, `"throttled"`, `"pending"`, `"disconnected"`, or `"connection_timeout"`. `null` until the first `stream_status` message |
|
|
274
|
+
| `suggestions` | `Suggestion[]` | AI suggestions, oldest first, capped at 50 |
|
|
275
|
+
| `transcripts` | `TranscriptLine[]` | Live transcript lines, ordered by start time |
|
|
276
|
+
| `sentiment` | `Sentiment \| null` | Latest sentiment reading |
|
|
277
|
+
| `lastError` | `Error \| null` | Most recent error |
|
|
278
|
+
| `connect()` | `() => void` | Manually open the connection |
|
|
279
|
+
| `disconnect()` | `() => void` | Manually close the connection |
|
|
280
|
+
| `setParams()` | `(patch) => void` | Merge params; reconnects if connection params change |
|
|
280
281
|
|
|
281
282
|
---
|
|
282
283
|
|
|
283
284
|
### TypeScript Types
|
|
284
285
|
|
|
285
286
|
```ts
|
|
286
|
-
type StreamState = "connected" | "throttled" | "disconnected";
|
|
287
|
+
type StreamState = "connected" | "throttled" | "pending" | "disconnected" | "connection_timeout";
|
|
287
288
|
|
|
288
289
|
type ConnectionStatus = "idle" | "connecting" | "connected" | "disconnected" | "error";
|
|
289
290
|
|
|
290
291
|
interface Suggestion {
|
|
291
292
|
id: string;
|
|
292
|
-
|
|
293
|
+
value: string;
|
|
293
294
|
timestamp: number;
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
interface TranscriptLine {
|
|
297
298
|
id: string;
|
|
298
|
-
|
|
299
|
+
value: string;
|
|
299
300
|
startTime: number;
|
|
300
301
|
endTime: number;
|
|
301
302
|
isFinal: boolean;
|
|
@@ -330,15 +331,17 @@ wss://<wssBaseUrl>?[customParam1=value1&customParam2=value2&...]
|
|
|
330
331
|
"type": "stream_status",
|
|
331
332
|
"config": { /* bot config, populated on connected, null otherwise */ },
|
|
332
333
|
"events": [ /* accumulated events since last message, usually [] */ ],
|
|
333
|
-
"stream_state": "connected" | "throttled" | "disconnected"
|
|
334
|
+
"stream_state": "connected" | "throttled" | "pending" | "disconnected" | "connection_timeout"
|
|
334
335
|
}
|
|
335
336
|
```
|
|
336
337
|
|
|
337
|
-
| `stream_state`
|
|
338
|
-
|
|
|
339
|
-
| `connected`
|
|
340
|
-
| `throttled`
|
|
341
|
-
| `
|
|
338
|
+
| `stream_state` | Meaning |
|
|
339
|
+
| -------------------- | --------------------------------------------------------- |
|
|
340
|
+
| `connected` | Stream is active — suggestions, transcript, and sentiment flow normally |
|
|
341
|
+
| `throttled` | Capacity full — AI Assist cannot join this call; available on the next call |
|
|
342
|
+
| `pending` | Stream is pending — waiting for a definitive state |
|
|
343
|
+
| `disconnected` | Stream ended or dropped |
|
|
344
|
+
| `connection_timeout` | Connection timeout occurred |
|
|
342
345
|
|
|
343
346
|
### Reconnection
|
|
344
347
|
|
package/dist/controller/index.js
CHANGED
|
@@ -1774,14 +1774,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1774
1774
|
for (const event of events) {
|
|
1775
1775
|
if (!event) continue;
|
|
1776
1776
|
if (event.event_type === "transcript" && event.value) {
|
|
1777
|
-
const transcripts = Array.isArray(event
|
|
1777
|
+
const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
|
|
1778
1778
|
const lines = transcripts.map(msg => {
|
|
1779
|
-
var _a, _b, _c
|
|
1779
|
+
var _a, _b, _c;
|
|
1780
1780
|
const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
|
|
1781
1781
|
const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
|
|
1782
1782
|
return {
|
|
1783
1783
|
id: String(msg.sequence),
|
|
1784
|
-
value: (
|
|
1784
|
+
value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
|
|
1785
1785
|
startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
|
|
1786
1786
|
endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
|
|
1787
1787
|
isFinal: msg.transcript_segments.every(s => s.is_final)
|
|
@@ -1792,7 +1792,7 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1792
1792
|
if (event.event_type === "suggestion" && event.value) {
|
|
1793
1793
|
const suggestion = {
|
|
1794
1794
|
id: Utils.getUniqueId(),
|
|
1795
|
-
value: event.value,
|
|
1795
|
+
value: event.value.text,
|
|
1796
1796
|
timestamp: now
|
|
1797
1797
|
};
|
|
1798
1798
|
this.emit("suggestion", suggestion);
|
package/dist/index.js
CHANGED
|
@@ -21192,14 +21192,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21192
21192
|
for (const event of events) {
|
|
21193
21193
|
if (!event) continue;
|
|
21194
21194
|
if (event.event_type === "transcript" && event.value) {
|
|
21195
|
-
const transcripts = Array.isArray(event
|
|
21195
|
+
const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
|
|
21196
21196
|
const lines = transcripts.map(msg => {
|
|
21197
|
-
var _a, _b, _c
|
|
21197
|
+
var _a, _b, _c;
|
|
21198
21198
|
const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
|
|
21199
21199
|
const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
|
|
21200
21200
|
return {
|
|
21201
21201
|
id: String(msg.sequence),
|
|
21202
|
-
value: (
|
|
21202
|
+
value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
|
|
21203
21203
|
startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
|
|
21204
21204
|
endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
|
|
21205
21205
|
isFinal: msg.transcript_segments.every(s => s.is_final)
|
|
@@ -21210,7 +21210,7 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21210
21210
|
if (event.event_type === "suggestion" && event.value) {
|
|
21211
21211
|
const suggestion = {
|
|
21212
21212
|
id: Utils.getUniqueId(),
|
|
21213
|
-
value: event.value,
|
|
21213
|
+
value: event.value.text,
|
|
21214
21214
|
timestamp: now
|
|
21215
21215
|
};
|
|
21216
21216
|
this.emit("suggestion", suggestion);
|
|
@@ -21902,7 +21902,7 @@ function ExotelAIAssist(_a) {
|
|
|
21902
21902
|
if (streamState === "connection_timeout") {
|
|
21903
21903
|
return jsxRuntimeExports.jsx(EmptyState, {
|
|
21904
21904
|
title: "AI Assist is currently inactive",
|
|
21905
|
-
subtitle: "
|
|
21905
|
+
subtitle: "We were unable to establish a connection."
|
|
21906
21906
|
});
|
|
21907
21907
|
}
|
|
21908
21908
|
return null;
|
package/dist/react/index.js
CHANGED
|
@@ -6478,14 +6478,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6478
6478
|
for (const event of events) {
|
|
6479
6479
|
if (!event) continue;
|
|
6480
6480
|
if (event.event_type === "transcript" && event.value) {
|
|
6481
|
-
const transcripts = Array.isArray(event
|
|
6481
|
+
const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
|
|
6482
6482
|
const lines = transcripts.map(msg => {
|
|
6483
|
-
var _a, _b, _c
|
|
6483
|
+
var _a, _b, _c;
|
|
6484
6484
|
const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
|
|
6485
6485
|
const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
|
|
6486
6486
|
return {
|
|
6487
6487
|
id: String(msg.sequence),
|
|
6488
|
-
value: (
|
|
6488
|
+
value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
|
|
6489
6489
|
startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
|
|
6490
6490
|
endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
|
|
6491
6491
|
isFinal: msg.transcript_segments.every(s => s.is_final)
|
|
@@ -6496,7 +6496,7 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
6496
6496
|
if (event.event_type === "suggestion" && event.value) {
|
|
6497
6497
|
const suggestion = {
|
|
6498
6498
|
id: Utils.getUniqueId(),
|
|
6499
|
-
value: event.value,
|
|
6499
|
+
value: event.value.text,
|
|
6500
6500
|
timestamp: now
|
|
6501
6501
|
};
|
|
6502
6502
|
this.emit("suggestion", suggestion);
|
|
@@ -7188,7 +7188,7 @@ function ExotelAIAssist(_a) {
|
|
|
7188
7188
|
if (streamState === "connection_timeout") {
|
|
7189
7189
|
return jsxRuntime.jsx(EmptyState, {
|
|
7190
7190
|
title: "AI Assist is currently inactive",
|
|
7191
|
-
subtitle: "
|
|
7191
|
+
subtitle: "We were unable to establish a connection."
|
|
7192
7192
|
});
|
|
7193
7193
|
}
|
|
7194
7194
|
return null;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -50,17 +50,34 @@ interface TranscriptSegment {
|
|
|
50
50
|
is_final: boolean;
|
|
51
51
|
end_timestamp: string;
|
|
52
52
|
start_timestamp: string;
|
|
53
|
-
|
|
53
|
+
speaker: string;
|
|
54
|
+
text: string;
|
|
54
55
|
}
|
|
55
56
|
interface TranscriptMessage {
|
|
56
57
|
sequence: number;
|
|
57
58
|
transcript_segments: TranscriptSegment[];
|
|
58
59
|
}
|
|
59
|
-
export interface
|
|
60
|
-
|
|
60
|
+
export interface SuggestionValue {
|
|
61
|
+
sequence: number;
|
|
62
|
+
text: string;
|
|
63
|
+
}
|
|
64
|
+
interface WssEventBase {
|
|
61
65
|
transcript: TranscriptMessage[];
|
|
66
|
+
text: string;
|
|
67
|
+
}
|
|
68
|
+
interface WssTranscriptEvent extends WssEventBase {
|
|
69
|
+
event_type: "transcript";
|
|
70
|
+
value: TranscriptMessage[];
|
|
71
|
+
}
|
|
72
|
+
interface WssSentimentEvent extends WssEventBase {
|
|
73
|
+
event_type: "sentiment";
|
|
62
74
|
value: string;
|
|
63
75
|
}
|
|
76
|
+
interface WssSuggestionEvent extends WssEventBase {
|
|
77
|
+
event_type: "suggestion";
|
|
78
|
+
value: SuggestionValue;
|
|
79
|
+
}
|
|
80
|
+
export type WssEvent = WssTranscriptEvent | WssSentimentEvent | WssSuggestionEvent;
|
|
64
81
|
export interface InitialHandshakeResponse {
|
|
65
82
|
type: string;
|
|
66
83
|
config: BotConfig;
|
package/package.json
CHANGED
|
@@ -42,7 +42,7 @@ export function ExotelAIAssist({ className, onReady, ...params }: ExotelAIAssist
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
if (streamState === "connection_timeout") {
|
|
45
|
-
return <EmptyState title="AI Assist is currently inactive" subtitle="
|
|
45
|
+
return <EmptyState title="AI Assist is currently inactive" subtitle="We were unable to establish a connection." />;
|
|
46
46
|
}
|
|
47
47
|
return null;
|
|
48
48
|
}
|
package/src/controller/index.ts
CHANGED
|
@@ -225,16 +225,16 @@ export class ExotelAIAssistController extends EventEmitter<ControllerEvents> {
|
|
|
225
225
|
if (!event) continue;
|
|
226
226
|
|
|
227
227
|
if (event.event_type === "transcript" && event.value) {
|
|
228
|
-
const transcripts = Array.isArray(event
|
|
228
|
+
const transcripts = Array.isArray(event.value) ? event.value : (event.transcript ?? []);
|
|
229
229
|
const lines: TranscriptLine[] = transcripts.map((msg) => {
|
|
230
230
|
const first = msg.transcript_segments?.[0];
|
|
231
231
|
const last = msg.transcript_segments?.[msg.transcript_segments.length - 1];
|
|
232
232
|
return {
|
|
233
233
|
id: String(msg.sequence),
|
|
234
|
-
value:
|
|
234
|
+
value: first?.text ?? "",
|
|
235
235
|
startTime: first?.start_timestamp ? Date.parse(first.start_timestamp) : now,
|
|
236
236
|
endTime: last?.end_timestamp ? Date.parse(last.end_timestamp) : now,
|
|
237
|
-
isFinal: msg.transcript_segments.every((s
|
|
237
|
+
isFinal: msg.transcript_segments.every((s) => s.is_final),
|
|
238
238
|
};
|
|
239
239
|
});
|
|
240
240
|
this.emit("transcript", lines);
|
|
@@ -243,7 +243,7 @@ export class ExotelAIAssistController extends EventEmitter<ControllerEvents> {
|
|
|
243
243
|
if (event.event_type === "suggestion" && event.value) {
|
|
244
244
|
const suggestion: Suggestion = {
|
|
245
245
|
id: Utils.getUniqueId(),
|
|
246
|
-
value: event.value,
|
|
246
|
+
value: event.value.text,
|
|
247
247
|
timestamp: now,
|
|
248
248
|
};
|
|
249
249
|
this.emit("suggestion", suggestion);
|
package/src/types/index.ts
CHANGED
|
@@ -61,7 +61,8 @@ interface TranscriptSegment {
|
|
|
61
61
|
is_final: boolean;
|
|
62
62
|
end_timestamp: string;
|
|
63
63
|
start_timestamp: string;
|
|
64
|
-
|
|
64
|
+
speaker: string;
|
|
65
|
+
text: string;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
interface TranscriptMessage {
|
|
@@ -69,12 +70,33 @@ interface TranscriptMessage {
|
|
|
69
70
|
transcript_segments: TranscriptSegment[];
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
export interface
|
|
73
|
-
|
|
73
|
+
export interface SuggestionValue {
|
|
74
|
+
sequence: number;
|
|
75
|
+
text: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface WssEventBase {
|
|
74
79
|
transcript: TranscriptMessage[];
|
|
80
|
+
text: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface WssTranscriptEvent extends WssEventBase {
|
|
84
|
+
event_type: "transcript";
|
|
85
|
+
value: TranscriptMessage[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface WssSentimentEvent extends WssEventBase {
|
|
89
|
+
event_type: "sentiment";
|
|
75
90
|
value: string;
|
|
76
91
|
}
|
|
77
92
|
|
|
93
|
+
interface WssSuggestionEvent extends WssEventBase {
|
|
94
|
+
event_type: "suggestion";
|
|
95
|
+
value: SuggestionValue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type WssEvent = WssTranscriptEvent | WssSentimentEvent | WssSuggestionEvent;
|
|
99
|
+
|
|
78
100
|
export interface InitialHandshakeResponse {
|
|
79
101
|
type: string;
|
|
80
102
|
config: BotConfig;
|