@exotel-npm-dev/exotel-ai-assist 1.2.4 → 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/components/key-components/SuggestionTab.d.ts +1 -2
- package/dist/controller/index.d.ts +0 -1
- package/dist/controller/index.js +12 -41
- package/dist/hooks/useExotelAIAssist.d.ts +0 -1
- package/dist/index.js +53 -193
- package/dist/react/index.js +53 -193
- package/dist/types/index.d.ts +17 -19
- package/package.json +1 -1
- package/src/components/ExotelAIAssistApp.tsx +5 -5
- package/src/components/key-components/SuggestionTab.tsx +15 -101
- package/src/controller/index.ts +24 -45
- package/src/hooks/useExotelAIAssist.ts +1 -17
- package/src/styles/index.css +3 -62
- package/src/types/index.ts +20 -19
- package/src/utils/index.ts +1 -1
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
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Suggestion, BotConfig } from "../../types";
|
|
2
2
|
import "../../styles/index.css";
|
|
3
|
-
export declare function SuggestionsTab({ suggestions, connected, botConfig,
|
|
3
|
+
export declare function SuggestionsTab({ suggestions, connected, botConfig, }: {
|
|
4
4
|
suggestions: Suggestion[];
|
|
5
5
|
connected: boolean;
|
|
6
6
|
botConfig: BotConfig | null;
|
|
7
|
-
sendSuggestionFeedback: (sequence: number, feedbackType: "good" | "bad" | null, badFeedbackReason?: string | null) => boolean;
|
|
8
7
|
}): JSX.Element;
|
|
@@ -19,7 +19,6 @@ export declare class ExotelAIAssistController extends EventEmitter<ControllerEve
|
|
|
19
19
|
destroy(): void;
|
|
20
20
|
getStatus(): ConnectionStatus;
|
|
21
21
|
getStreamState(): StreamState | null;
|
|
22
|
-
sendSuggestionFeedback(sequence: number, feedbackType: "good" | "bad" | null, badFeedbackReason?: string | null): boolean;
|
|
23
22
|
private _ensureTransport;
|
|
24
23
|
private _handleTransportMessage;
|
|
25
24
|
private _scheduleReconnect;
|
package/dist/controller/index.js
CHANGED
|
@@ -1538,7 +1538,7 @@ class Utils {
|
|
|
1538
1538
|
* @returns The base WebSocket URL.
|
|
1539
1539
|
*/
|
|
1540
1540
|
static getWssBaseUrl(accountId) {
|
|
1541
|
-
return `wss://
|
|
1541
|
+
return `wss://ai-assist.in.exotel.com/ai-assist/ws/v1/accounts/${accountId}/ai-assistants/conversation-events`;
|
|
1542
1542
|
}
|
|
1543
1543
|
/**
|
|
1544
1544
|
* Builds a WebSocket URL from the given parameters.
|
|
@@ -1667,25 +1667,6 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1667
1667
|
getStreamState() {
|
|
1668
1668
|
return this._streamState;
|
|
1669
1669
|
}
|
|
1670
|
-
sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason = null) {
|
|
1671
|
-
if (!this.transport || this.status !== "connected") {
|
|
1672
|
-
console.warn("[ExotelAIAssist] Cannot send feedback: not connected");
|
|
1673
|
-
return false;
|
|
1674
|
-
}
|
|
1675
|
-
const message = {
|
|
1676
|
-
type: "suggestion_feedback",
|
|
1677
|
-
sequence,
|
|
1678
|
-
feedback_type: feedbackType,
|
|
1679
|
-
bad_feedback_reason: badFeedbackReason
|
|
1680
|
-
};
|
|
1681
|
-
try {
|
|
1682
|
-
this.transport.send(JSON.stringify(message));
|
|
1683
|
-
return true;
|
|
1684
|
-
} catch (error) {
|
|
1685
|
-
console.error("[ExotelAIAssist] Failed to send feedback:", error);
|
|
1686
|
-
return false;
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
1670
|
_ensureTransport() {
|
|
1690
1671
|
if (this.transport) return;
|
|
1691
1672
|
this.transport = createTransport(Utils.hash(this.params));
|
|
@@ -1793,14 +1774,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1793
1774
|
for (const event of events) {
|
|
1794
1775
|
if (!event) continue;
|
|
1795
1776
|
if (event.event_type === "transcript" && event.value) {
|
|
1796
|
-
const transcripts = Array.isArray(event
|
|
1777
|
+
const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
|
|
1797
1778
|
const lines = transcripts.map(msg => {
|
|
1798
|
-
var _a, _b, _c
|
|
1779
|
+
var _a, _b, _c;
|
|
1799
1780
|
const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
|
|
1800
1781
|
const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
|
|
1801
1782
|
return {
|
|
1802
1783
|
id: String(msg.sequence),
|
|
1803
|
-
value: (
|
|
1784
|
+
value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
|
|
1804
1785
|
startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
|
|
1805
1786
|
endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
|
|
1806
1787
|
isFinal: msg.transcript_segments.every(s => s.is_final)
|
|
@@ -1808,27 +1789,17 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
1808
1789
|
});
|
|
1809
1790
|
this.emit("transcript", lines);
|
|
1810
1791
|
}
|
|
1811
|
-
if (event.event_type === "suggestion") {
|
|
1812
|
-
const
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
text,
|
|
1819
|
-
value: text,
|
|
1820
|
-
timestamp: now,
|
|
1821
|
-
sequence,
|
|
1822
|
-
feedbackType: null,
|
|
1823
|
-
badFeedbackReason: null
|
|
1824
|
-
};
|
|
1825
|
-
this.emit("suggestion", suggestion);
|
|
1826
|
-
}
|
|
1792
|
+
if (event.event_type === "suggestion" && event.value) {
|
|
1793
|
+
const suggestion = {
|
|
1794
|
+
id: Utils.getUniqueId(),
|
|
1795
|
+
value: event.value.text,
|
|
1796
|
+
timestamp: now
|
|
1797
|
+
};
|
|
1798
|
+
this.emit("suggestion", suggestion);
|
|
1827
1799
|
}
|
|
1828
1800
|
if (event.event_type === "sentiment" && event.value) {
|
|
1829
|
-
const sentimentValue = typeof event.value === "string" ? event.value : event.value.text;
|
|
1830
1801
|
const sentiment = {
|
|
1831
|
-
label:
|
|
1802
|
+
label: event.value.toLowerCase(),
|
|
1832
1803
|
timestamp: now
|
|
1833
1804
|
};
|
|
1834
1805
|
this.emit("sentiment", sentiment);
|
|
@@ -25,7 +25,6 @@ export interface UseExotelAIAssistReturn {
|
|
|
25
25
|
connect: () => void;
|
|
26
26
|
disconnect: () => void;
|
|
27
27
|
setParams: (patch: Partial<ExotelAIAssistParams>) => void;
|
|
28
|
-
sendSuggestionFeedback: (sequence: number, feedbackType: "good" | "bad" | null, badFeedbackReason?: string | null) => boolean;
|
|
29
28
|
}
|
|
30
29
|
/** Internal extended return — adds botConfig for the UI component only. */
|
|
31
30
|
export interface UseExotelAIAssistInternalReturn extends UseExotelAIAssistReturn {
|
package/dist/index.js
CHANGED
|
@@ -19294,30 +19294,6 @@ const Smile = createLucideIcon$1("Smile", [["circle", {
|
|
|
19294
19294
|
key: "1p4y9e"
|
|
19295
19295
|
}]]);
|
|
19296
19296
|
|
|
19297
|
-
/**
|
|
19298
|
-
* lucide-react v0.0.1 - ISC
|
|
19299
|
-
*/
|
|
19300
|
-
|
|
19301
|
-
const ThumbsDown = createLucideIcon$1("ThumbsDown", [["path", {
|
|
19302
|
-
d: "M17 14V2",
|
|
19303
|
-
key: "8ymqnk"
|
|
19304
|
-
}], ["path", {
|
|
19305
|
-
d: "M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z",
|
|
19306
|
-
key: "s6e0r"
|
|
19307
|
-
}]]);
|
|
19308
|
-
|
|
19309
|
-
/**
|
|
19310
|
-
* lucide-react v0.0.1 - ISC
|
|
19311
|
-
*/
|
|
19312
|
-
|
|
19313
|
-
const ThumbsUp = createLucideIcon$1("ThumbsUp", [["path", {
|
|
19314
|
-
d: "M7 10v12",
|
|
19315
|
-
key: "1qc93n"
|
|
19316
|
-
}], ["path", {
|
|
19317
|
-
d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z",
|
|
19318
|
-
key: "y3tblf"
|
|
19319
|
-
}]]);
|
|
19320
|
-
|
|
19321
19297
|
const MAX_VISIBLE_TOASTS = 5;
|
|
19322
19298
|
const AUTO_DISMISS_MS = 3000;
|
|
19323
19299
|
const ToastContext = reactExports.createContext(null);
|
|
@@ -20980,7 +20956,7 @@ class Utils {
|
|
|
20980
20956
|
* @returns The base WebSocket URL.
|
|
20981
20957
|
*/
|
|
20982
20958
|
static getWssBaseUrl(accountId) {
|
|
20983
|
-
return `wss://
|
|
20959
|
+
return `wss://ai-assist.in.exotel.com/ai-assist/ws/v1/accounts/${accountId}/ai-assistants/conversation-events`;
|
|
20984
20960
|
}
|
|
20985
20961
|
/**
|
|
20986
20962
|
* Builds a WebSocket URL from the given parameters.
|
|
@@ -21109,25 +21085,6 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21109
21085
|
getStreamState() {
|
|
21110
21086
|
return this._streamState;
|
|
21111
21087
|
}
|
|
21112
|
-
sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason = null) {
|
|
21113
|
-
if (!this.transport || this.status !== "connected") {
|
|
21114
|
-
console.warn("[ExotelAIAssist] Cannot send feedback: not connected");
|
|
21115
|
-
return false;
|
|
21116
|
-
}
|
|
21117
|
-
const message = {
|
|
21118
|
-
type: "suggestion_feedback",
|
|
21119
|
-
sequence,
|
|
21120
|
-
feedback_type: feedbackType,
|
|
21121
|
-
bad_feedback_reason: badFeedbackReason
|
|
21122
|
-
};
|
|
21123
|
-
try {
|
|
21124
|
-
this.transport.send(JSON.stringify(message));
|
|
21125
|
-
return true;
|
|
21126
|
-
} catch (error) {
|
|
21127
|
-
console.error("[ExotelAIAssist] Failed to send feedback:", error);
|
|
21128
|
-
return false;
|
|
21129
|
-
}
|
|
21130
|
-
}
|
|
21131
21088
|
_ensureTransport() {
|
|
21132
21089
|
if (this.transport) return;
|
|
21133
21090
|
this.transport = createTransport(Utils.hash(this.params));
|
|
@@ -21235,14 +21192,14 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21235
21192
|
for (const event of events) {
|
|
21236
21193
|
if (!event) continue;
|
|
21237
21194
|
if (event.event_type === "transcript" && event.value) {
|
|
21238
|
-
const transcripts = Array.isArray(event
|
|
21195
|
+
const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
|
|
21239
21196
|
const lines = transcripts.map(msg => {
|
|
21240
|
-
var _a, _b, _c
|
|
21197
|
+
var _a, _b, _c;
|
|
21241
21198
|
const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
|
|
21242
21199
|
const last = (_b = msg.transcript_segments) === null || _b === void 0 ? void 0 : _b[msg.transcript_segments.length - 1];
|
|
21243
21200
|
return {
|
|
21244
21201
|
id: String(msg.sequence),
|
|
21245
|
-
value: (
|
|
21202
|
+
value: (_c = first === null || first === void 0 ? void 0 : first.text) !== null && _c !== void 0 ? _c : "",
|
|
21246
21203
|
startTime: (first === null || first === void 0 ? void 0 : first.start_timestamp) ? Date.parse(first.start_timestamp) : now,
|
|
21247
21204
|
endTime: (last === null || last === void 0 ? void 0 : last.end_timestamp) ? Date.parse(last.end_timestamp) : now,
|
|
21248
21205
|
isFinal: msg.transcript_segments.every(s => s.is_final)
|
|
@@ -21250,27 +21207,17 @@ class ExotelAIAssistController extends EventEmitter {
|
|
|
21250
21207
|
});
|
|
21251
21208
|
this.emit("transcript", lines);
|
|
21252
21209
|
}
|
|
21253
|
-
if (event.event_type === "suggestion") {
|
|
21254
|
-
const
|
|
21255
|
-
|
|
21256
|
-
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
|
|
21260
|
-
text,
|
|
21261
|
-
value: text,
|
|
21262
|
-
timestamp: now,
|
|
21263
|
-
sequence,
|
|
21264
|
-
feedbackType: null,
|
|
21265
|
-
badFeedbackReason: null
|
|
21266
|
-
};
|
|
21267
|
-
this.emit("suggestion", suggestion);
|
|
21268
|
-
}
|
|
21210
|
+
if (event.event_type === "suggestion" && event.value) {
|
|
21211
|
+
const suggestion = {
|
|
21212
|
+
id: Utils.getUniqueId(),
|
|
21213
|
+
value: event.value.text,
|
|
21214
|
+
timestamp: now
|
|
21215
|
+
};
|
|
21216
|
+
this.emit("suggestion", suggestion);
|
|
21269
21217
|
}
|
|
21270
21218
|
if (event.event_type === "sentiment" && event.value) {
|
|
21271
|
-
const sentimentValue = typeof event.value === "string" ? event.value : event.value.text;
|
|
21272
21219
|
const sentiment = {
|
|
21273
|
-
label:
|
|
21220
|
+
label: event.value.toLowerCase(),
|
|
21274
21221
|
timestamp: now
|
|
21275
21222
|
};
|
|
21276
21223
|
this.emit("sentiment", sentiment);
|
|
@@ -21346,20 +21293,9 @@ function useExotelAIAssist(params) {
|
|
|
21346
21293
|
var _a;
|
|
21347
21294
|
return (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.setParams(patch);
|
|
21348
21295
|
}, []);
|
|
21349
|
-
const sendSuggestionFeedback = reactExports.useCallback((sequence, feedbackType, badFeedbackReason = null) => {
|
|
21350
|
-
var _a, _b;
|
|
21351
|
-
const sent = (_b = (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason)) !== null && _b !== void 0 ? _b : false;
|
|
21352
|
-
if (sent) {
|
|
21353
|
-
setSuggestions(prev => prev.map(s => s.sequence === sequence ? Object.assign(Object.assign({}, s), {
|
|
21354
|
-
feedbackType,
|
|
21355
|
-
badFeedbackReason
|
|
21356
|
-
}) : s));
|
|
21357
|
-
}
|
|
21358
|
-
return sent;
|
|
21359
|
-
}, []);
|
|
21360
21296
|
return {
|
|
21361
|
-
streamState,
|
|
21362
21297
|
isReady,
|
|
21298
|
+
streamState,
|
|
21363
21299
|
suggestions,
|
|
21364
21300
|
transcripts,
|
|
21365
21301
|
sentiment,
|
|
@@ -21367,8 +21303,7 @@ function useExotelAIAssist(params) {
|
|
|
21367
21303
|
lastError,
|
|
21368
21304
|
connect,
|
|
21369
21305
|
disconnect,
|
|
21370
|
-
setParams
|
|
21371
|
-
sendSuggestionFeedback
|
|
21306
|
+
setParams
|
|
21372
21307
|
};
|
|
21373
21308
|
}
|
|
21374
21309
|
|
|
@@ -21397,7 +21332,7 @@ function styleInject(css, ref) {
|
|
|
21397
21332
|
}
|
|
21398
21333
|
}
|
|
21399
21334
|
|
|
21400
|
-
var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon
|
|
21335
|
+
var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover{background:#f9fafb;color:#374151}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-muted{color:#6b7280}";
|
|
21401
21336
|
styleInject(css_248z);
|
|
21402
21337
|
|
|
21403
21338
|
function Sparkle() {
|
|
@@ -21745,17 +21680,9 @@ const EmptyState = ({
|
|
|
21745
21680
|
function SuggestionsTab({
|
|
21746
21681
|
suggestions,
|
|
21747
21682
|
connected,
|
|
21748
|
-
botConfig
|
|
21749
|
-
sendSuggestionFeedback
|
|
21683
|
+
botConfig
|
|
21750
21684
|
}) {
|
|
21751
|
-
var _a, _b, _c;
|
|
21752
21685
|
const toast = useToast();
|
|
21753
|
-
const [expandedSequence, setExpandedSequence] = reactExports.useState(null);
|
|
21754
|
-
const feedbackEnabled = ((_a = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _a === void 0 ? void 0 : _a.feedback_enabled) === true;
|
|
21755
|
-
const badFeedbackOptions = reactExports.useMemo(() => {
|
|
21756
|
-
var _a, _b;
|
|
21757
|
-
return (_b = (_a = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _a === void 0 ? void 0 : _a.bad_feedback_options) !== null && _b !== void 0 ? _b : [];
|
|
21758
|
-
}, [(_b = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _b === void 0 ? void 0 : _b.bad_feedback_options]);
|
|
21759
21686
|
const handleCopy = async text => {
|
|
21760
21687
|
try {
|
|
21761
21688
|
await navigator.clipboard.writeText(text);
|
|
@@ -21764,36 +21691,13 @@ function SuggestionsTab({
|
|
|
21764
21691
|
toast.error("Failed to copy");
|
|
21765
21692
|
}
|
|
21766
21693
|
};
|
|
21767
|
-
const handleThumbsUp = reactExports.useCallback((sequence, currentFeedback) => {
|
|
21768
|
-
if (!feedbackEnabled) return;
|
|
21769
|
-
const newType = currentFeedback === "good" ? null : "good";
|
|
21770
|
-
sendSuggestionFeedback(sequence, newType, null);
|
|
21771
|
-
setExpandedSequence(null);
|
|
21772
|
-
}, [sendSuggestionFeedback, feedbackEnabled]);
|
|
21773
|
-
const handleThumbsDown = reactExports.useCallback((sequence, currentFeedback) => {
|
|
21774
|
-
if (!feedbackEnabled) return;
|
|
21775
|
-
if (currentFeedback === "bad") {
|
|
21776
|
-
sendSuggestionFeedback(sequence, null, null);
|
|
21777
|
-
setExpandedSequence(null);
|
|
21778
|
-
return;
|
|
21779
|
-
}
|
|
21780
|
-
sendSuggestionFeedback(sequence, "bad", null);
|
|
21781
|
-
if (badFeedbackOptions.length > 0) {
|
|
21782
|
-
setExpandedSequence(sequence);
|
|
21783
|
-
}
|
|
21784
|
-
}, [sendSuggestionFeedback, badFeedbackOptions, feedbackEnabled]);
|
|
21785
|
-
const handleBadFeedbackReason = reactExports.useCallback((sequence, reason) => {
|
|
21786
|
-
if (!feedbackEnabled) return;
|
|
21787
|
-
sendSuggestionFeedback(sequence, "bad", reason);
|
|
21788
|
-
setExpandedSequence(null);
|
|
21789
|
-
}, [sendSuggestionFeedback, feedbackEnabled]);
|
|
21790
21694
|
if (!connected) {
|
|
21791
21695
|
return jsxRuntimeExports.jsx(EmptyState, {
|
|
21792
21696
|
title: "Assistant is inactive",
|
|
21793
21697
|
subtitle: "Start a call to receive real-time suggestions."
|
|
21794
21698
|
});
|
|
21795
21699
|
}
|
|
21796
|
-
if ((
|
|
21700
|
+
if ((botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestions) === false) {
|
|
21797
21701
|
return jsxRuntimeExports.jsx(EmptyState, {
|
|
21798
21702
|
title: "Suggestions are disabled",
|
|
21799
21703
|
subtitle: "Suggestions are disabled in the bot configuration. Please contact the administrator to enable it."
|
|
@@ -21827,37 +21731,21 @@ function SuggestionsTab({
|
|
|
21827
21731
|
children: displayed.map((suggestion, index) => {
|
|
21828
21732
|
const isRecent = index === 0;
|
|
21829
21733
|
const cardClass = isRecent ? "oa-suggestion-card oa-suggestion-card--recent" : "oa-suggestion-card oa-suggestion-card--older";
|
|
21830
|
-
const showReasons = suggestion.feedbackType === "bad" || expandedSequence === suggestion.sequence;
|
|
21831
21734
|
return jsxRuntimeExports.jsxs("div", {
|
|
21832
|
-
|
|
21735
|
+
style: {
|
|
21736
|
+
display: "flex",
|
|
21737
|
+
flexDirection: "row",
|
|
21738
|
+
alignItems: "flex-start",
|
|
21739
|
+
gap: "8px"
|
|
21740
|
+
},
|
|
21741
|
+
children: [isRecent ? jsxRuntimeExports.jsx("div", {
|
|
21742
|
+
className: "oa-suggestion-card--recent-wrapper",
|
|
21833
21743
|
style: {
|
|
21834
|
-
|
|
21835
|
-
|
|
21836
|
-
alignItems: "flex-start",
|
|
21837
|
-
gap: "8px"
|
|
21744
|
+
flex: "1 1 auto",
|
|
21745
|
+
maxWidth: "80%"
|
|
21838
21746
|
},
|
|
21839
|
-
children:
|
|
21840
|
-
className: "oa-suggestion-card--recent-wrapper",
|
|
21841
|
-
style: {
|
|
21842
|
-
flex: "1 1 auto",
|
|
21843
|
-
maxWidth: "72%"
|
|
21844
|
-
},
|
|
21845
|
-
children: jsxRuntimeExports.jsx("div", {
|
|
21846
|
-
className: cardClass,
|
|
21847
|
-
children: jsxRuntimeExports.jsx("span", {
|
|
21848
|
-
className: "oa-suggestion-text",
|
|
21849
|
-
style: {
|
|
21850
|
-
fontSize: "15px"
|
|
21851
|
-
},
|
|
21852
|
-
children: suggestion.text
|
|
21853
|
-
})
|
|
21854
|
-
})
|
|
21855
|
-
}) : jsxRuntimeExports.jsx("div", {
|
|
21747
|
+
children: jsxRuntimeExports.jsx("div", {
|
|
21856
21748
|
className: cardClass,
|
|
21857
|
-
style: {
|
|
21858
|
-
flex: "1 1 auto",
|
|
21859
|
-
maxWidth: "72%"
|
|
21860
|
-
},
|
|
21861
21749
|
children: jsxRuntimeExports.jsx("span", {
|
|
21862
21750
|
className: "oa-suggestion-text",
|
|
21863
21751
|
style: {
|
|
@@ -21865,57 +21753,31 @@ function SuggestionsTab({
|
|
|
21865
21753
|
},
|
|
21866
21754
|
children: suggestion.value
|
|
21867
21755
|
})
|
|
21868
|
-
})
|
|
21756
|
+
})
|
|
21757
|
+
}) : jsxRuntimeExports.jsx("div", {
|
|
21758
|
+
className: cardClass,
|
|
21759
|
+
style: {
|
|
21760
|
+
flex: "1 1 auto",
|
|
21761
|
+
maxWidth: "80%"
|
|
21762
|
+
},
|
|
21763
|
+
children: jsxRuntimeExports.jsx("span", {
|
|
21764
|
+
className: "oa-suggestion-text",
|
|
21869
21765
|
style: {
|
|
21870
|
-
|
|
21871
|
-
flexDirection: "column",
|
|
21872
|
-
gap: "4px",
|
|
21873
|
-
flexShrink: 0,
|
|
21874
|
-
marginTop: "10px"
|
|
21766
|
+
fontSize: "15px"
|
|
21875
21767
|
},
|
|
21876
|
-
children:
|
|
21877
|
-
|
|
21878
|
-
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
|
|
21882
|
-
})
|
|
21883
|
-
}), feedbackEnabled && jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
|
|
21884
|
-
children: [jsxRuntimeExports.jsx("button", {
|
|
21885
|
-
className: `oa-feedback-icon ${suggestion.feedbackType === "good" ? "oa-feedback-icon--positive" : ""}`,
|
|
21886
|
-
"aria-label": "Mark as helpful",
|
|
21887
|
-
onClick: () => handleThumbsUp(suggestion.sequence, suggestion.feedbackType),
|
|
21888
|
-
style: {
|
|
21889
|
-
color: suggestion.feedbackType === "good" ? "#16a34a" : undefined
|
|
21890
|
-
},
|
|
21891
|
-
children: jsxRuntimeExports.jsx(ThumbsUp, {
|
|
21892
|
-
size: 14
|
|
21893
|
-
})
|
|
21894
|
-
}), jsxRuntimeExports.jsx("button", {
|
|
21895
|
-
className: `oa-feedback-icon ${suggestion.feedbackType === "bad" ? "oa-feedback-icon--negative" : ""}`,
|
|
21896
|
-
"aria-label": "Mark as not helpful",
|
|
21897
|
-
onClick: () => handleThumbsDown(suggestion.sequence, suggestion.feedbackType),
|
|
21898
|
-
style: {
|
|
21899
|
-
color: suggestion.feedbackType === "bad" ? "#dc2626" : undefined
|
|
21900
|
-
},
|
|
21901
|
-
children: jsxRuntimeExports.jsx(ThumbsDown, {
|
|
21902
|
-
size: 14
|
|
21903
|
-
})
|
|
21904
|
-
})]
|
|
21905
|
-
})]
|
|
21906
|
-
})]
|
|
21907
|
-
}), showReasons && badFeedbackOptions.length > 0 && jsxRuntimeExports.jsx("div", {
|
|
21768
|
+
children: suggestion.value
|
|
21769
|
+
})
|
|
21770
|
+
}), jsxRuntimeExports.jsx("button", {
|
|
21771
|
+
className: "oa-copy-icon",
|
|
21772
|
+
"aria-label": "Copy suggestion",
|
|
21773
|
+
onClick: () => handleCopy(suggestion.value),
|
|
21908
21774
|
style: {
|
|
21909
|
-
|
|
21910
|
-
|
|
21911
|
-
flexWrap: "wrap",
|
|
21912
|
-
marginTop: "8px"
|
|
21775
|
+
flexShrink: 0,
|
|
21776
|
+
marginTop: "10px"
|
|
21913
21777
|
},
|
|
21914
|
-
children:
|
|
21915
|
-
|
|
21916
|
-
|
|
21917
|
-
children: reason
|
|
21918
|
-
}, reason))
|
|
21778
|
+
children: jsxRuntimeExports.jsx(Copy, {
|
|
21779
|
+
size: 14
|
|
21780
|
+
})
|
|
21919
21781
|
})]
|
|
21920
21782
|
}, suggestion.id);
|
|
21921
21783
|
})
|
|
@@ -22014,13 +21876,12 @@ function ExotelAIAssist(_a) {
|
|
|
22014
21876
|
} = _a,
|
|
22015
21877
|
params = __rest(_a, ["className", "onReady"]);
|
|
22016
21878
|
const {
|
|
22017
|
-
streamState,
|
|
22018
21879
|
isReady,
|
|
21880
|
+
streamState,
|
|
22019
21881
|
suggestions,
|
|
22020
21882
|
transcripts,
|
|
22021
21883
|
sentiment,
|
|
22022
|
-
botConfig
|
|
22023
|
-
sendSuggestionFeedback
|
|
21884
|
+
botConfig
|
|
22024
21885
|
} = useExotelAIAssist(params);
|
|
22025
21886
|
const onReadyRef = reactExports.useRef(onReady);
|
|
22026
21887
|
onReadyRef.current = onReady;
|
|
@@ -22041,7 +21902,7 @@ function ExotelAIAssist(_a) {
|
|
|
22041
21902
|
if (streamState === "connection_timeout") {
|
|
22042
21903
|
return jsxRuntimeExports.jsx(EmptyState, {
|
|
22043
21904
|
title: "AI Assist is currently inactive",
|
|
22044
|
-
subtitle: "
|
|
21905
|
+
subtitle: "We were unable to establish a connection."
|
|
22045
21906
|
});
|
|
22046
21907
|
}
|
|
22047
21908
|
return null;
|
|
@@ -22098,8 +21959,7 @@ function ExotelAIAssist(_a) {
|
|
|
22098
21959
|
children: jsxRuntimeExports.jsx(SuggestionsTab, {
|
|
22099
21960
|
suggestions: suggestions,
|
|
22100
21961
|
connected: connected,
|
|
22101
|
-
botConfig: botConfig
|
|
22102
|
-
sendSuggestionFeedback: sendSuggestionFeedback
|
|
21962
|
+
botConfig: botConfig
|
|
22103
21963
|
})
|
|
22104
21964
|
}), jsxRuntimeExports.jsx(Content$1, {
|
|
22105
21965
|
value: "transcript",
|