@aginies/webuikit 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.cjs +589 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +130 -5
- package/dist/index.d.ts +130 -5
- package/dist/index.js +586 -84
- package/dist/index.js.map +1 -1
- package/dist/styles.css +118 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -53,6 +53,7 @@ __export(src_exports, {
|
|
|
53
53
|
getPausedExecution: () => getPausedExecution,
|
|
54
54
|
init: () => init,
|
|
55
55
|
initialValues: () => initialValues,
|
|
56
|
+
isVoiceSupported: () => isVoiceSupported,
|
|
56
57
|
listPausedExecutions: () => listPausedExecutions,
|
|
57
58
|
outputOf: () => outputOf,
|
|
58
59
|
parseFieldValue: () => parseFieldValue,
|
|
@@ -66,7 +67,8 @@ __export(src_exports, {
|
|
|
66
67
|
useAginies: () => useAginies,
|
|
67
68
|
useApproval: () => useApproval,
|
|
68
69
|
useChat: () => useChat,
|
|
69
|
-
useModule: () => useModule
|
|
70
|
+
useModule: () => useModule,
|
|
71
|
+
useVoice: () => useVoice
|
|
70
72
|
});
|
|
71
73
|
module.exports = __toCommonJS(src_exports);
|
|
72
74
|
|
|
@@ -85,6 +87,7 @@ var trimSlash = (s) => s.replace(/\/+$/, "");
|
|
|
85
87
|
var AginiesClient = class {
|
|
86
88
|
baseUrl;
|
|
87
89
|
token;
|
|
90
|
+
hosted;
|
|
88
91
|
locale;
|
|
89
92
|
fetchImpl;
|
|
90
93
|
state = { status: "idle" };
|
|
@@ -93,9 +96,12 @@ var AginiesClient = class {
|
|
|
93
96
|
session = null;
|
|
94
97
|
constructor(config) {
|
|
95
98
|
if (!config?.baseUrl) throw new AginiesError("baseUrl is required", 0, "MISSING_BASE_URL");
|
|
96
|
-
if (!config?.token
|
|
99
|
+
if (!config?.token && !config?.hosted) {
|
|
100
|
+
throw new AginiesError("token is required", 0, "MISSING_TOKEN");
|
|
101
|
+
}
|
|
97
102
|
this.baseUrl = trimSlash(config.baseUrl);
|
|
98
|
-
this.token = config.token;
|
|
103
|
+
this.token = config.token ?? null;
|
|
104
|
+
this.hosted = config.hosted === true;
|
|
99
105
|
this.locale = config.locale ?? detectLocale();
|
|
100
106
|
this.fetchImpl = config.fetch ?? ((...args) => fetch(...args));
|
|
101
107
|
}
|
|
@@ -122,7 +128,7 @@ var AginiesClient = class {
|
|
|
122
128
|
const res = await this.fetchImpl(`${this.baseUrl}/api/ui/activate`, {
|
|
123
129
|
method: "POST",
|
|
124
130
|
headers: { "Content-Type": "application/json" },
|
|
125
|
-
body: JSON.stringify({ token: this.token })
|
|
131
|
+
body: JSON.stringify(this.hosted ? { hosted: true } : { token: this.token })
|
|
126
132
|
});
|
|
127
133
|
if (!res.ok) {
|
|
128
134
|
const body2 = await safeJson(res);
|
|
@@ -197,6 +203,30 @@ var AginiesClient = class {
|
|
|
197
203
|
this.assertActive();
|
|
198
204
|
return this.request(path2, init2);
|
|
199
205
|
}
|
|
206
|
+
/** Sends a recording to the platform's transcription model. */
|
|
207
|
+
async transcribe(identifier, audio, filename = "turn.webm", language) {
|
|
208
|
+
this.assertActive();
|
|
209
|
+
const form = new FormData();
|
|
210
|
+
form.append("file", audio, filename);
|
|
211
|
+
if (language) form.append("language", language);
|
|
212
|
+
const res = await this.request(`/api/chat/${encodeURIComponent(identifier)}/voice/transcribe`, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
body: form
|
|
215
|
+
});
|
|
216
|
+
if (!res.ok) throw await voiceError(res);
|
|
217
|
+
return await res.json();
|
|
218
|
+
}
|
|
219
|
+
/** Audio for a reply from the platform's synthesis endpoint. */
|
|
220
|
+
async speak(identifier, text) {
|
|
221
|
+
this.assertActive();
|
|
222
|
+
const res = await this.request(`/api/chat/${encodeURIComponent(identifier)}/voice/speak`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: { "Content-Type": "application/json" },
|
|
225
|
+
body: JSON.stringify({ text })
|
|
226
|
+
});
|
|
227
|
+
if (!res.ok) throw await voiceError(res);
|
|
228
|
+
return res.blob();
|
|
229
|
+
}
|
|
200
230
|
/** Hosted-chat configuration. Resolves to an auth requirement instead of throwing on 401. */
|
|
201
231
|
async getChat(identifier) {
|
|
202
232
|
this.assertActive();
|
|
@@ -286,6 +316,15 @@ function decodeFrame(frame) {
|
|
|
286
316
|
}
|
|
287
317
|
return null;
|
|
288
318
|
}
|
|
319
|
+
async function voiceError(res) {
|
|
320
|
+
const body = await safeJson(res);
|
|
321
|
+
if (res.status === 401 && body && typeof body.authRequired === "string") {
|
|
322
|
+
return new AginiesError("Authentication required", 401, "AUTH_REQUIRED");
|
|
323
|
+
}
|
|
324
|
+
if (res.status === 503)
|
|
325
|
+
return new AginiesError("Voice is not available", 503, "VOICE_UNAVAILABLE");
|
|
326
|
+
return new AginiesError(body?.error || `HTTP ${res.status}`, res.status);
|
|
327
|
+
}
|
|
289
328
|
async function safeJson(res) {
|
|
290
329
|
try {
|
|
291
330
|
return await res.json();
|
|
@@ -584,6 +623,35 @@ var STRINGS = {
|
|
|
584
623
|
},
|
|
585
624
|
ssoButton: { tr: "Kurumsal giri\u015F", en: "Sign in" }
|
|
586
625
|
},
|
|
626
|
+
voice: {
|
|
627
|
+
talk: { tr: "Konu\u015Fmak i\xE7in dokunun", en: "Tap to talk" },
|
|
628
|
+
stopTalking: { tr: "Bitirmek i\xE7in dokunun", en: "Tap when done" },
|
|
629
|
+
dictate: { tr: "Sesle yaz", en: "Dictate" },
|
|
630
|
+
handsFree: { tr: "Sesli sohbet", en: "Voice conversation" },
|
|
631
|
+
exit: { tr: "Sesli sohbetten \xE7\u0131k", en: "Leave voice conversation" },
|
|
632
|
+
listening: { tr: "Dinliyor", en: "Listening" },
|
|
633
|
+
transcribing: { tr: "Yaz\u0131ya d\xF6k\xFCl\xFCyor", en: "Transcribing" },
|
|
634
|
+
thinking: { tr: "Yan\u0131t haz\u0131rlan\u0131yor", en: "Working on it" },
|
|
635
|
+
speaking: { tr: "Konu\u015Fuyor", en: "Speaking" },
|
|
636
|
+
idle: { tr: "Haz\u0131r", en: "Ready" },
|
|
637
|
+
interrupt: { tr: "S\xF6z\xFCn\xFC kesmek i\xE7in dokunun", en: "Tap to interrupt" },
|
|
638
|
+
micDenied: {
|
|
639
|
+
tr: "Mikrofon izni verilmedi. Taray\u0131c\u0131 ayarlar\u0131ndan izin verin.",
|
|
640
|
+
en: "Microphone access was denied. Allow it in your browser settings."
|
|
641
|
+
},
|
|
642
|
+
unsupported: {
|
|
643
|
+
tr: "Bu taray\u0131c\u0131 ses kayd\u0131n\u0131 desteklemiyor.",
|
|
644
|
+
en: "This browser cannot record audio."
|
|
645
|
+
},
|
|
646
|
+
unavailable: {
|
|
647
|
+
tr: "Ses \xF6zelli\u011Fi bu sohbet i\xE7in a\xE7\u0131k de\u011Fil.",
|
|
648
|
+
en: "Voice is not enabled for this chat."
|
|
649
|
+
},
|
|
650
|
+
error: {
|
|
651
|
+
tr: "Ses i\u015Flenemedi. L\xFCtfen tekrar deneyin.",
|
|
652
|
+
en: "Voice could not be processed. Please try again."
|
|
653
|
+
}
|
|
654
|
+
},
|
|
587
655
|
run: {
|
|
588
656
|
submit: { tr: "\xC7al\u0131\u015Ft\u0131r", en: "Run" },
|
|
589
657
|
cancel: { tr: "\u0130ptal", en: "Cancel" },
|
|
@@ -1034,7 +1102,7 @@ function renderField(f, value, set, disabled) {
|
|
|
1034
1102
|
}
|
|
1035
1103
|
|
|
1036
1104
|
// src/chat/chat-widget.tsx
|
|
1037
|
-
var
|
|
1105
|
+
var import_react6 = require("react");
|
|
1038
1106
|
|
|
1039
1107
|
// src/chat/markdown.tsx
|
|
1040
1108
|
var import_react4 = require("react");
|
|
@@ -1344,20 +1412,258 @@ function Pie({ labels, data }) {
|
|
|
1344
1412
|
] });
|
|
1345
1413
|
}
|
|
1346
1414
|
|
|
1415
|
+
// src/chat/voice.ts
|
|
1416
|
+
var import_react5 = require("react");
|
|
1417
|
+
var MIME_CANDIDATES = [
|
|
1418
|
+
"audio/webm;codecs=opus",
|
|
1419
|
+
"audio/webm",
|
|
1420
|
+
"audio/mp4",
|
|
1421
|
+
"audio/ogg;codecs=opus"
|
|
1422
|
+
];
|
|
1423
|
+
var MIN_CLIP_BYTES = 800;
|
|
1424
|
+
var SPEECH_RMS = 0.02;
|
|
1425
|
+
function isVoiceSupported() {
|
|
1426
|
+
return typeof window !== "undefined" && typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia && typeof MediaRecorder !== "undefined";
|
|
1427
|
+
}
|
|
1428
|
+
function useVoice({
|
|
1429
|
+
identifier,
|
|
1430
|
+
onTranscript,
|
|
1431
|
+
language,
|
|
1432
|
+
silenceMs = 1400,
|
|
1433
|
+
maxMs = 6e4
|
|
1434
|
+
}) {
|
|
1435
|
+
const { client, t } = useAginies();
|
|
1436
|
+
const supported = isVoiceSupported();
|
|
1437
|
+
const [state, setState] = (0, import_react5.useState)(supported ? "idle" : "unsupported");
|
|
1438
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
1439
|
+
const [level, setLevel] = (0, import_react5.useState)(0);
|
|
1440
|
+
const recorderRef = (0, import_react5.useRef)(null);
|
|
1441
|
+
const streamRef = (0, import_react5.useRef)(null);
|
|
1442
|
+
const chunksRef = (0, import_react5.useRef)([]);
|
|
1443
|
+
const discardRef = (0, import_react5.useRef)(false);
|
|
1444
|
+
const timersRef = (0, import_react5.useRef)([]);
|
|
1445
|
+
const meterRef = (0, import_react5.useRef)(null);
|
|
1446
|
+
const audioRef = (0, import_react5.useRef)(null);
|
|
1447
|
+
const audioUrlRef = (0, import_react5.useRef)(null);
|
|
1448
|
+
const startingRef = (0, import_react5.useRef)(false);
|
|
1449
|
+
const onTranscriptRef = (0, import_react5.useRef)(onTranscript);
|
|
1450
|
+
onTranscriptRef.current = onTranscript;
|
|
1451
|
+
const clearTimers = (0, import_react5.useCallback)(() => {
|
|
1452
|
+
for (const id of timersRef.current) window.clearTimeout(id);
|
|
1453
|
+
timersRef.current = [];
|
|
1454
|
+
if (meterRef.current) {
|
|
1455
|
+
window.clearInterval(meterRef.current.interval);
|
|
1456
|
+
void meterRef.current.ctx.close().catch(() => {
|
|
1457
|
+
});
|
|
1458
|
+
meterRef.current = null;
|
|
1459
|
+
}
|
|
1460
|
+
setLevel(0);
|
|
1461
|
+
}, []);
|
|
1462
|
+
const releaseStream = (0, import_react5.useCallback)(() => {
|
|
1463
|
+
for (const track of streamRef.current?.getTracks() ?? []) track.stop();
|
|
1464
|
+
streamRef.current = null;
|
|
1465
|
+
}, []);
|
|
1466
|
+
const stopSpeaking = (0, import_react5.useCallback)(() => {
|
|
1467
|
+
const audio = audioRef.current;
|
|
1468
|
+
if (audio) {
|
|
1469
|
+
audio.pause();
|
|
1470
|
+
audio.src = "";
|
|
1471
|
+
audioRef.current = null;
|
|
1472
|
+
}
|
|
1473
|
+
if (audioUrlRef.current) {
|
|
1474
|
+
URL.revokeObjectURL(audioUrlRef.current);
|
|
1475
|
+
audioUrlRef.current = null;
|
|
1476
|
+
}
|
|
1477
|
+
setState((s) => s === "speaking" ? "idle" : s);
|
|
1478
|
+
}, []);
|
|
1479
|
+
const finish = (0, import_react5.useCallback)((recorder) => {
|
|
1480
|
+
recorder.stop();
|
|
1481
|
+
}, []);
|
|
1482
|
+
const stopListening = (0, import_react5.useCallback)(() => {
|
|
1483
|
+
const recorder = recorderRef.current;
|
|
1484
|
+
if (!recorder || recorder.state === "inactive") return;
|
|
1485
|
+
discardRef.current = false;
|
|
1486
|
+
finish(recorder);
|
|
1487
|
+
}, [finish]);
|
|
1488
|
+
const cancelListening = (0, import_react5.useCallback)(() => {
|
|
1489
|
+
const recorder = recorderRef.current;
|
|
1490
|
+
if (!recorder || recorder.state === "inactive") return;
|
|
1491
|
+
discardRef.current = true;
|
|
1492
|
+
finish(recorder);
|
|
1493
|
+
}, [finish]);
|
|
1494
|
+
const startMeter = (0, import_react5.useCallback)(
|
|
1495
|
+
(stream, onSilence) => {
|
|
1496
|
+
const Ctx = typeof AudioContext !== "undefined" ? AudioContext : window.webkitAudioContext;
|
|
1497
|
+
if (!Ctx) return;
|
|
1498
|
+
try {
|
|
1499
|
+
const ctx = new Ctx();
|
|
1500
|
+
const analyser = ctx.createAnalyser();
|
|
1501
|
+
analyser.fftSize = 2048;
|
|
1502
|
+
ctx.createMediaStreamSource(stream).connect(analyser);
|
|
1503
|
+
const data = new Uint8Array(analyser.fftSize);
|
|
1504
|
+
let heard = false;
|
|
1505
|
+
let lastVoice = Date.now();
|
|
1506
|
+
const interval = window.setInterval(() => {
|
|
1507
|
+
analyser.getByteTimeDomainData(data);
|
|
1508
|
+
let sum = 0;
|
|
1509
|
+
for (const v of data) {
|
|
1510
|
+
const n = (v - 128) / 128;
|
|
1511
|
+
sum += n * n;
|
|
1512
|
+
}
|
|
1513
|
+
const rms = Math.sqrt(sum / data.length);
|
|
1514
|
+
setLevel(Math.min(1, rms * 6));
|
|
1515
|
+
if (rms > SPEECH_RMS) {
|
|
1516
|
+
heard = true;
|
|
1517
|
+
lastVoice = Date.now();
|
|
1518
|
+
} else if (heard && Date.now() - lastVoice > silenceMs) {
|
|
1519
|
+
onSilence();
|
|
1520
|
+
}
|
|
1521
|
+
}, 100);
|
|
1522
|
+
meterRef.current = { ctx, interval };
|
|
1523
|
+
} catch {
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
[silenceMs]
|
|
1527
|
+
);
|
|
1528
|
+
const startListening = (0, import_react5.useCallback)(async () => {
|
|
1529
|
+
if (!supported) {
|
|
1530
|
+
setState("unsupported");
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
if (startingRef.current) return;
|
|
1534
|
+
if (recorderRef.current && recorderRef.current.state !== "inactive") return;
|
|
1535
|
+
startingRef.current = true;
|
|
1536
|
+
stopSpeaking();
|
|
1537
|
+
setError(null);
|
|
1538
|
+
let stream;
|
|
1539
|
+
try {
|
|
1540
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
1541
|
+
} catch {
|
|
1542
|
+
startingRef.current = false;
|
|
1543
|
+
setState("denied");
|
|
1544
|
+
setError(t("voice", "micDenied"));
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
startingRef.current = false;
|
|
1548
|
+
streamRef.current = stream;
|
|
1549
|
+
const mimeType = MIME_CANDIDATES.find((m) => MediaRecorder.isTypeSupported?.(m));
|
|
1550
|
+
const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
|
|
1551
|
+
recorderRef.current = recorder;
|
|
1552
|
+
chunksRef.current = [];
|
|
1553
|
+
discardRef.current = false;
|
|
1554
|
+
recorder.ondataavailable = (e) => {
|
|
1555
|
+
if (e.data && e.data.size > 0) chunksRef.current.push(e.data);
|
|
1556
|
+
};
|
|
1557
|
+
recorder.onstop = () => {
|
|
1558
|
+
clearTimers();
|
|
1559
|
+
releaseStream();
|
|
1560
|
+
recorderRef.current = null;
|
|
1561
|
+
const type = recorder.mimeType || mimeType || "audio/webm";
|
|
1562
|
+
const clip = new Blob(chunksRef.current, { type });
|
|
1563
|
+
chunksRef.current = [];
|
|
1564
|
+
if (discardRef.current || clip.size < MIN_CLIP_BYTES) {
|
|
1565
|
+
setState("idle");
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
setState("transcribing");
|
|
1569
|
+
const ext = type.includes("mp4") ? "m4a" : type.includes("ogg") ? "ogg" : "webm";
|
|
1570
|
+
client.transcribe(identifier, clip, `turn.${ext}`, language).then((result) => {
|
|
1571
|
+
setState("idle");
|
|
1572
|
+
if (result.transcript) onTranscriptRef.current(result.transcript);
|
|
1573
|
+
}).catch((err) => {
|
|
1574
|
+
setState("idle");
|
|
1575
|
+
setError(
|
|
1576
|
+
err instanceof AginiesError && err.code === "VOICE_UNAVAILABLE" ? t("voice", "unavailable") : t("voice", "error")
|
|
1577
|
+
);
|
|
1578
|
+
});
|
|
1579
|
+
};
|
|
1580
|
+
recorder.start(250);
|
|
1581
|
+
setState("listening");
|
|
1582
|
+
startMeter(stream, () => stopListening());
|
|
1583
|
+
timersRef.current.push(window.setTimeout(() => stopListening(), maxMs));
|
|
1584
|
+
}, [
|
|
1585
|
+
supported,
|
|
1586
|
+
stopSpeaking,
|
|
1587
|
+
t,
|
|
1588
|
+
client,
|
|
1589
|
+
identifier,
|
|
1590
|
+
language,
|
|
1591
|
+
maxMs,
|
|
1592
|
+
stopListening,
|
|
1593
|
+
clearTimers,
|
|
1594
|
+
releaseStream,
|
|
1595
|
+
startMeter
|
|
1596
|
+
]);
|
|
1597
|
+
const speak = (0, import_react5.useCallback)(
|
|
1598
|
+
async (text) => {
|
|
1599
|
+
stopSpeaking();
|
|
1600
|
+
setError(null);
|
|
1601
|
+
setState("speaking");
|
|
1602
|
+
try {
|
|
1603
|
+
const blob = await client.speak(identifier, text);
|
|
1604
|
+
const url = URL.createObjectURL(blob);
|
|
1605
|
+
audioUrlRef.current = url;
|
|
1606
|
+
const audio = new Audio(url);
|
|
1607
|
+
audioRef.current = audio;
|
|
1608
|
+
await new Promise((resolve) => {
|
|
1609
|
+
audio.onended = () => resolve();
|
|
1610
|
+
audio.onerror = () => resolve();
|
|
1611
|
+
audio.onpause = () => resolve();
|
|
1612
|
+
audio.play().catch(() => resolve());
|
|
1613
|
+
});
|
|
1614
|
+
} catch (err) {
|
|
1615
|
+
setError(
|
|
1616
|
+
err instanceof AginiesError && err.code === "VOICE_UNAVAILABLE" ? t("voice", "unavailable") : t("voice", "error")
|
|
1617
|
+
);
|
|
1618
|
+
} finally {
|
|
1619
|
+
if (audioUrlRef.current) URL.revokeObjectURL(audioUrlRef.current);
|
|
1620
|
+
audioUrlRef.current = null;
|
|
1621
|
+
audioRef.current = null;
|
|
1622
|
+
setState((s) => s === "speaking" ? "idle" : s);
|
|
1623
|
+
}
|
|
1624
|
+
},
|
|
1625
|
+
[client, identifier, stopSpeaking, t]
|
|
1626
|
+
);
|
|
1627
|
+
(0, import_react5.useEffect)(
|
|
1628
|
+
() => () => {
|
|
1629
|
+
discardRef.current = true;
|
|
1630
|
+
if (recorderRef.current && recorderRef.current.state !== "inactive")
|
|
1631
|
+
recorderRef.current.stop();
|
|
1632
|
+
clearTimers();
|
|
1633
|
+
releaseStream();
|
|
1634
|
+
const audio = audioRef.current;
|
|
1635
|
+
if (audio) audio.pause();
|
|
1636
|
+
if (audioUrlRef.current) URL.revokeObjectURL(audioUrlRef.current);
|
|
1637
|
+
},
|
|
1638
|
+
[clearTimers, releaseStream]
|
|
1639
|
+
);
|
|
1640
|
+
return {
|
|
1641
|
+
state,
|
|
1642
|
+
error,
|
|
1643
|
+
supported,
|
|
1644
|
+
level,
|
|
1645
|
+
startListening,
|
|
1646
|
+
stopListening,
|
|
1647
|
+
cancelListening,
|
|
1648
|
+
speak,
|
|
1649
|
+
stopSpeaking
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1347
1653
|
// src/chat/chat-widget.tsx
|
|
1348
1654
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1349
1655
|
var ATTACHMENT_LIMITS = { maxFiles: 5, maxBytes: 10 * 1024 * 1024 };
|
|
1350
1656
|
function useChat(identifier, enabled = true) {
|
|
1351
1657
|
const { client, t } = useAginies();
|
|
1352
|
-
const [config, setConfig] = (0,
|
|
1353
|
-
const [authNeed, setAuthNeed] = (0,
|
|
1354
|
-
const [authTitle, setAuthTitle] = (0,
|
|
1355
|
-
const [loadError, setLoadError] = (0,
|
|
1356
|
-
const [messages, setMessages] = (0,
|
|
1357
|
-
const [busy, setBusy] = (0,
|
|
1358
|
-
const [conversationId] = (0,
|
|
1359
|
-
const abortRef = (0,
|
|
1360
|
-
const load = (0,
|
|
1658
|
+
const [config, setConfig] = (0, import_react6.useState)(null);
|
|
1659
|
+
const [authNeed, setAuthNeed] = (0, import_react6.useState)(null);
|
|
1660
|
+
const [authTitle, setAuthTitle] = (0, import_react6.useState)();
|
|
1661
|
+
const [loadError, setLoadError] = (0, import_react6.useState)(null);
|
|
1662
|
+
const [messages, setMessages] = (0, import_react6.useState)([]);
|
|
1663
|
+
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
1664
|
+
const [conversationId] = (0, import_react6.useState)(() => randomId());
|
|
1665
|
+
const abortRef = (0, import_react6.useRef)(null);
|
|
1666
|
+
const load = (0, import_react6.useCallback)(async () => {
|
|
1361
1667
|
setLoadError(null);
|
|
1362
1668
|
try {
|
|
1363
1669
|
const res = await client.getChat(identifier);
|
|
@@ -1374,11 +1680,11 @@ function useChat(identifier, enabled = true) {
|
|
|
1374
1680
|
);
|
|
1375
1681
|
}
|
|
1376
1682
|
}, [client, identifier, t]);
|
|
1377
|
-
(0,
|
|
1683
|
+
(0, import_react6.useEffect)(() => {
|
|
1378
1684
|
if (enabled) void load();
|
|
1379
1685
|
}, [load, enabled]);
|
|
1380
1686
|
const chatPath = `/api/chat/${encodeURIComponent(identifier)}`;
|
|
1381
|
-
const authenticate = (0,
|
|
1687
|
+
const authenticate = (0, import_react6.useCallback)(
|
|
1382
1688
|
async ({ password }) => {
|
|
1383
1689
|
const res = await client.fetchRaw(chatPath, {
|
|
1384
1690
|
method: "POST",
|
|
@@ -1393,7 +1699,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1393
1699
|
},
|
|
1394
1700
|
[client, chatPath, conversationId, load]
|
|
1395
1701
|
);
|
|
1396
|
-
const requestCode = (0,
|
|
1702
|
+
const requestCode = (0, import_react6.useCallback)(
|
|
1397
1703
|
async (email) => {
|
|
1398
1704
|
const res = await client.fetchRaw(`${chatPath}/otp`, {
|
|
1399
1705
|
method: "POST",
|
|
@@ -1405,7 +1711,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1405
1711
|
},
|
|
1406
1712
|
[client, chatPath]
|
|
1407
1713
|
);
|
|
1408
|
-
const verifyCode = (0,
|
|
1714
|
+
const verifyCode = (0, import_react6.useCallback)(
|
|
1409
1715
|
async (email, otp) => {
|
|
1410
1716
|
const res = await client.fetchRaw(`${chatPath}/otp`, {
|
|
1411
1717
|
method: "PUT",
|
|
@@ -1420,7 +1726,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1420
1726
|
},
|
|
1421
1727
|
[client, chatPath, load]
|
|
1422
1728
|
);
|
|
1423
|
-
const stop = (0,
|
|
1729
|
+
const stop = (0, import_react6.useCallback)(() => {
|
|
1424
1730
|
abortRef.current?.abort();
|
|
1425
1731
|
abortRef.current = null;
|
|
1426
1732
|
setBusy(false);
|
|
@@ -1433,7 +1739,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1433
1739
|
];
|
|
1434
1740
|
});
|
|
1435
1741
|
}, [t]);
|
|
1436
|
-
const send = (0,
|
|
1742
|
+
const send = (0, import_react6.useCallback)(
|
|
1437
1743
|
async (text, files = []) => {
|
|
1438
1744
|
const input = text.trim();
|
|
1439
1745
|
if (!input && files.length === 0 || busy) return;
|
|
@@ -1525,15 +1831,36 @@ function ChatWidget({
|
|
|
1525
1831
|
launcherLabel,
|
|
1526
1832
|
defaultOpen = false,
|
|
1527
1833
|
theme = "auto",
|
|
1834
|
+
voice = true,
|
|
1528
1835
|
className
|
|
1529
1836
|
}) {
|
|
1530
|
-
const [open, setOpen] = (0,
|
|
1837
|
+
const [open, setOpen] = (0, import_react6.useState)(defaultOpen || mode !== "bubble");
|
|
1531
1838
|
const { t } = useAginies();
|
|
1532
1839
|
const enabled = useModule("chat");
|
|
1533
1840
|
const chat = useChat(identifier, enabled);
|
|
1841
|
+
const [voiceMode, setVoiceMode] = (0, import_react6.useState)(false);
|
|
1842
|
+
const [dictation, setDictation] = (0, import_react6.useState)(null);
|
|
1843
|
+
const voiceModeRef = (0, import_react6.useRef)(voiceMode);
|
|
1844
|
+
voiceModeRef.current = voiceMode;
|
|
1845
|
+
const sendRef = (0, import_react6.useRef)(chat.send);
|
|
1846
|
+
sendRef.current = chat.send;
|
|
1847
|
+
const voiceControls = useVoice({
|
|
1848
|
+
identifier,
|
|
1849
|
+
onTranscript: (text) => {
|
|
1850
|
+
if (voiceModeRef.current) void sendRef.current(text);
|
|
1851
|
+
else setDictation({ id: Date.now(), text });
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
1854
|
+
const capabilities = chat.config?.voice;
|
|
1855
|
+
const voiceAvailable = voice && voiceControls.supported && capabilities?.stt === true;
|
|
1534
1856
|
const title = chat.config?.title ?? chat.authTitle ?? launcherLabel ?? "Aginies";
|
|
1535
1857
|
const themeClass = theme === "auto" ? void 0 : theme === "dark" ? "dark" : "light";
|
|
1536
1858
|
if (!enabled) return null;
|
|
1859
|
+
const leaveVoiceMode = () => {
|
|
1860
|
+
voiceControls.cancelListening();
|
|
1861
|
+
voiceControls.stopSpeaking();
|
|
1862
|
+
setVoiceMode(false);
|
|
1863
|
+
};
|
|
1537
1864
|
const panel = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1538
1865
|
"section",
|
|
1539
1866
|
{
|
|
@@ -1571,7 +1898,16 @@ function ChatWidget({
|
|
|
1571
1898
|
onRequestCode: chat.requestCode,
|
|
1572
1899
|
onVerifyCode: chat.verifyCode
|
|
1573
1900
|
}
|
|
1574
|
-
) : !chat.config ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-chat__state", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner, { label: t("common", "loading") }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1901
|
+
) : !chat.config ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-chat__state", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner, { label: t("common", "loading") }) }) : voiceMode ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1902
|
+
VoiceConversation,
|
|
1903
|
+
{
|
|
1904
|
+
messages: chat.messages,
|
|
1905
|
+
busy: chat.busy,
|
|
1906
|
+
voice: voiceControls,
|
|
1907
|
+
speakReplies: capabilities?.tts === true,
|
|
1908
|
+
onExit: leaveVoiceMode
|
|
1909
|
+
}
|
|
1910
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1575
1911
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1576
1912
|
MessageList,
|
|
1577
1913
|
{
|
|
@@ -1580,7 +1916,22 @@ function ChatWidget({
|
|
|
1580
1916
|
onAction: (action) => void chat.send(action)
|
|
1581
1917
|
}
|
|
1582
1918
|
),
|
|
1583
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1920
|
+
Composer,
|
|
1921
|
+
{
|
|
1922
|
+
busy: chat.busy,
|
|
1923
|
+
onSend: chat.send,
|
|
1924
|
+
onStop: chat.stop,
|
|
1925
|
+
dictation,
|
|
1926
|
+
voice: voiceAvailable ? {
|
|
1927
|
+
state: voiceControls.state,
|
|
1928
|
+
error: voiceControls.error,
|
|
1929
|
+
start: voiceControls.startListening,
|
|
1930
|
+
stop: voiceControls.stopListening,
|
|
1931
|
+
onHandsFree: () => setVoiceMode(true)
|
|
1932
|
+
} : void 0
|
|
1933
|
+
}
|
|
1934
|
+
)
|
|
1584
1935
|
] }),
|
|
1585
1936
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("footer", { className: "agi-chat__foot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: "https://www.aginies.com", target: "_blank", rel: "noreferrer", children: t("chat", "poweredBy") }) })
|
|
1586
1937
|
]
|
|
@@ -1635,53 +1986,57 @@ function MessageList({
|
|
|
1635
1986
|
onAction
|
|
1636
1987
|
}) {
|
|
1637
1988
|
const { t } = useAginies();
|
|
1638
|
-
const endRef = (0,
|
|
1639
|
-
(0,
|
|
1989
|
+
const endRef = (0, import_react6.useRef)(null);
|
|
1990
|
+
(0, import_react6.useEffect)(() => {
|
|
1640
1991
|
endRef.current?.scrollIntoView?.({ block: "end" });
|
|
1641
1992
|
}, []);
|
|
1642
|
-
return (
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
"
|
|
1651
|
-
|
|
1652
|
-
className:
|
|
1653
|
-
children: [
|
|
1654
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: endRef })
|
|
1672
|
-
] })
|
|
1673
|
-
);
|
|
1993
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agi-chat__messages", role: "log", "aria-live": "polite", children: [
|
|
1994
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agi-msg agi-msg--assistant", children: [
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agi-msg__who", children: t("chat", "assistant") }),
|
|
1996
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-msg__body", children: welcome })
|
|
1997
|
+
] }),
|
|
1998
|
+
messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1999
|
+
"div",
|
|
2000
|
+
{
|
|
2001
|
+
className: cx("agi-msg", `agi-msg--${m.role}`, m.error && "agi-msg--error"),
|
|
2002
|
+
children: [
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agi-msg__who", children: m.role === "user" ? t("chat", "you") : t("chat", "assistant") }),
|
|
2004
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agi-msg__body", children: [
|
|
2005
|
+
m.structured ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StructuredUI, { response: m.structured, onAction }) : m.content ? m.role === "assistant" && !m.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Markdown, { className: "agi-md", text: m.content }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: m.content }) : m.streaming ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agi-msg__thinking", children: [
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner, { label: t("chat", "thinking") }),
|
|
2007
|
+
" ",
|
|
2008
|
+
t("chat", "thinking")
|
|
2009
|
+
] }) : null,
|
|
2010
|
+
m.attachments && m.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "agi-msg__files", "aria-label": t("chat", "attachments"), children: m.attachments.map((file, n) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { className: "agi-file", children: [
|
|
2011
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FileGlyph, {}),
|
|
2012
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agi-file__name", children: file.name }),
|
|
2013
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agi-file__size", children: formatBytes(file.size) })
|
|
2014
|
+
] }, n)) })
|
|
2015
|
+
] })
|
|
2016
|
+
]
|
|
2017
|
+
},
|
|
2018
|
+
m.id
|
|
2019
|
+
)),
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: endRef })
|
|
2021
|
+
] });
|
|
1674
2022
|
}
|
|
1675
2023
|
function Composer({
|
|
1676
2024
|
busy,
|
|
1677
2025
|
onSend,
|
|
1678
|
-
onStop
|
|
2026
|
+
onStop,
|
|
2027
|
+
voice,
|
|
2028
|
+
dictation
|
|
1679
2029
|
}) {
|
|
1680
2030
|
const { t } = useAginies();
|
|
1681
|
-
const [value, setValue] = (0,
|
|
1682
|
-
const [files, setFiles] = (0,
|
|
1683
|
-
const [fileError, setFileError] = (0,
|
|
1684
|
-
const fileInput = (0,
|
|
2031
|
+
const [value, setValue] = (0, import_react6.useState)("");
|
|
2032
|
+
const [files, setFiles] = (0, import_react6.useState)([]);
|
|
2033
|
+
const [fileError, setFileError] = (0, import_react6.useState)(null);
|
|
2034
|
+
const fileInput = (0, import_react6.useRef)(null);
|
|
2035
|
+
(0, import_react6.useEffect)(() => {
|
|
2036
|
+
if (dictation) setValue((v) => v.trim() ? `${v.trimEnd()} ${dictation.text}` : dictation.text);
|
|
2037
|
+
}, [dictation]);
|
|
2038
|
+
const listening = voice?.state === "listening";
|
|
2039
|
+
const transcribing = voice?.state === "transcribing";
|
|
1685
2040
|
const submit = (e) => {
|
|
1686
2041
|
e.preventDefault();
|
|
1687
2042
|
if (busy) return;
|
|
@@ -1727,6 +2082,7 @@ function Composer({
|
|
|
1727
2082
|
] }, n)),
|
|
1728
2083
|
fileError && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agi-chat__file-error", children: fileError })
|
|
1729
2084
|
] }),
|
|
2085
|
+
voice?.error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-chat__file-error", children: voice.error }),
|
|
1730
2086
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agi-chat__row", children: [
|
|
1731
2087
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1732
2088
|
"input",
|
|
@@ -1770,10 +2126,156 @@ function Composer({
|
|
|
1770
2126
|
autoComplete: "off"
|
|
1771
2127
|
}
|
|
1772
2128
|
),
|
|
2129
|
+
voice && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2130
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2131
|
+
Button,
|
|
2132
|
+
{
|
|
2133
|
+
variant: "ghost",
|
|
2134
|
+
type: "button",
|
|
2135
|
+
className: cx("agi-chat__mic", listening && "agi-chat__mic--on"),
|
|
2136
|
+
"aria-label": listening ? t("voice", "stopTalking") : t("voice", "dictate"),
|
|
2137
|
+
"aria-pressed": listening,
|
|
2138
|
+
title: listening ? t("voice", "stopTalking") : t("voice", "dictate"),
|
|
2139
|
+
disabled: busy || transcribing,
|
|
2140
|
+
onClick: () => listening ? voice.stop() : void voice.start(),
|
|
2141
|
+
children: transcribing ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner, { label: t("voice", "transcribing") }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicGlyph, {})
|
|
2142
|
+
}
|
|
2143
|
+
),
|
|
2144
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2145
|
+
Button,
|
|
2146
|
+
{
|
|
2147
|
+
variant: "ghost",
|
|
2148
|
+
type: "button",
|
|
2149
|
+
"aria-label": t("voice", "handsFree"),
|
|
2150
|
+
title: t("voice", "handsFree"),
|
|
2151
|
+
disabled: busy || listening || transcribing,
|
|
2152
|
+
onClick: voice.onHandsFree,
|
|
2153
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HeadsetGlyph, {})
|
|
2154
|
+
}
|
|
2155
|
+
)
|
|
2156
|
+
] }),
|
|
1773
2157
|
busy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { variant: "outline", onClick: onStop, children: t("chat", "stop") }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { variant: "signal", type: "submit", disabled: !canSend, children: t("chat", "send") })
|
|
1774
2158
|
] })
|
|
1775
2159
|
] });
|
|
1776
2160
|
}
|
|
2161
|
+
function VoiceConversation({
|
|
2162
|
+
messages,
|
|
2163
|
+
busy,
|
|
2164
|
+
voice,
|
|
2165
|
+
speakReplies,
|
|
2166
|
+
onExit
|
|
2167
|
+
}) {
|
|
2168
|
+
const { t } = useAginies();
|
|
2169
|
+
const spokenRef = (0, import_react6.useRef)(null);
|
|
2170
|
+
const mountedRef = (0, import_react6.useRef)(true);
|
|
2171
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
2172
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
2173
|
+
(0, import_react6.useEffect)(() => {
|
|
2174
|
+
mountedRef.current = true;
|
|
2175
|
+
spokenRef.current = lastAssistant?.id ?? null;
|
|
2176
|
+
void voice.startListening();
|
|
2177
|
+
return () => {
|
|
2178
|
+
mountedRef.current = false;
|
|
2179
|
+
};
|
|
2180
|
+
}, []);
|
|
2181
|
+
const replyId = lastAssistant?.id;
|
|
2182
|
+
const replyStreaming = lastAssistant?.streaming === true;
|
|
2183
|
+
(0, import_react6.useEffect)(() => {
|
|
2184
|
+
if (!lastAssistant || replyStreaming || replyId === spokenRef.current) return;
|
|
2185
|
+
spokenRef.current = replyId ?? null;
|
|
2186
|
+
const run = async () => {
|
|
2187
|
+
if (speakReplies && lastAssistant.content && !lastAssistant.error) {
|
|
2188
|
+
await voice.speak(lastAssistant.content);
|
|
2189
|
+
}
|
|
2190
|
+
if (mountedRef.current) await voice.startListening();
|
|
2191
|
+
};
|
|
2192
|
+
void run();
|
|
2193
|
+
}, [replyId, replyStreaming]);
|
|
2194
|
+
const status = voice.state === "listening" ? t("voice", "listening") : voice.state === "transcribing" ? t("voice", "transcribing") : voice.state === "speaking" ? t("voice", "speaking") : busy ? t("voice", "thinking") : t("voice", "idle");
|
|
2195
|
+
const hint = voice.state === "listening" ? t("voice", "stopTalking") : voice.state === "speaking" ? t("voice", "interrupt") : voice.state === "transcribing" || busy ? "" : t("voice", "talk");
|
|
2196
|
+
const tap = () => {
|
|
2197
|
+
if (voice.state === "listening") voice.stopListening();
|
|
2198
|
+
else if (voice.state === "speaking") {
|
|
2199
|
+
voice.stopSpeaking();
|
|
2200
|
+
void voice.startListening();
|
|
2201
|
+
} else if (voice.state === "idle" && !busy) void voice.startListening();
|
|
2202
|
+
};
|
|
2203
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "agi-voice", "aria-label": t("voice", "handsFree"), children: [
|
|
2204
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2205
|
+
"button",
|
|
2206
|
+
{
|
|
2207
|
+
type: "button",
|
|
2208
|
+
className: "agi-voice__exit",
|
|
2209
|
+
onClick: onExit,
|
|
2210
|
+
"aria-label": t("voice", "exit"),
|
|
2211
|
+
children: "\xD7"
|
|
2212
|
+
}
|
|
2213
|
+
),
|
|
2214
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-voice__status", "aria-live": "polite", children: status }),
|
|
2215
|
+
lastUser?.content && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "agi-voice__you", children: lastUser.content }),
|
|
2216
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-voice__reply", children: lastAssistant?.content ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Markdown, { className: "agi-md", text: lastAssistant.content }) : busy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Spinner, { label: t("voice", "thinking") }) : null }),
|
|
2217
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2218
|
+
"button",
|
|
2219
|
+
{
|
|
2220
|
+
type: "button",
|
|
2221
|
+
className: cx(
|
|
2222
|
+
"agi-voice__orb",
|
|
2223
|
+
`agi-voice__orb--${voice.state}`,
|
|
2224
|
+
busy && "agi-voice__orb--busy"
|
|
2225
|
+
),
|
|
2226
|
+
style: { ["--agi-level"]: voice.level },
|
|
2227
|
+
onClick: tap,
|
|
2228
|
+
"aria-label": hint || status,
|
|
2229
|
+
disabled: voice.state === "transcribing" || voice.state === "unsupported",
|
|
2230
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicGlyph, { size: 28 })
|
|
2231
|
+
}
|
|
2232
|
+
),
|
|
2233
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-voice__hint", children: hint }),
|
|
2234
|
+
voice.error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agi-voice__error", children: voice.error })
|
|
2235
|
+
] });
|
|
2236
|
+
}
|
|
2237
|
+
function MicGlyph({ size = 16 }) {
|
|
2238
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2239
|
+
"svg",
|
|
2240
|
+
{
|
|
2241
|
+
width: size,
|
|
2242
|
+
height: size,
|
|
2243
|
+
viewBox: "0 0 24 24",
|
|
2244
|
+
fill: "none",
|
|
2245
|
+
stroke: "currentColor",
|
|
2246
|
+
strokeWidth: "1.8",
|
|
2247
|
+
strokeLinecap: "round",
|
|
2248
|
+
strokeLinejoin: "round",
|
|
2249
|
+
"aria-hidden": "true",
|
|
2250
|
+
children: [
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "9", y: "3", width: "6", height: "11", rx: "3" }),
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5 11a7 7 0 0 0 14 0M12 18v3M9 21h6" })
|
|
2253
|
+
]
|
|
2254
|
+
}
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
function HeadsetGlyph() {
|
|
2258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2259
|
+
"svg",
|
|
2260
|
+
{
|
|
2261
|
+
width: "16",
|
|
2262
|
+
height: "16",
|
|
2263
|
+
viewBox: "0 0 24 24",
|
|
2264
|
+
fill: "none",
|
|
2265
|
+
stroke: "currentColor",
|
|
2266
|
+
strokeWidth: "1.8",
|
|
2267
|
+
strokeLinecap: "round",
|
|
2268
|
+
strokeLinejoin: "round",
|
|
2269
|
+
"aria-hidden": "true",
|
|
2270
|
+
children: [
|
|
2271
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M4 14v-2a8 8 0 0 1 16 0v2" }),
|
|
2272
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "3", y: "13", width: "4", height: "6", rx: "1.5" }),
|
|
2273
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "17", y: "13", width: "4", height: "6", rx: "1.5" }),
|
|
2274
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M19 19v1a2 2 0 0 1-2 2h-3" })
|
|
2275
|
+
]
|
|
2276
|
+
}
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
1777
2279
|
function readFile(file) {
|
|
1778
2280
|
return new Promise((resolve, reject) => {
|
|
1779
2281
|
const reader = new FileReader();
|
|
@@ -1837,9 +2339,9 @@ function PasswordAuth({
|
|
|
1837
2339
|
onPassword
|
|
1838
2340
|
}) {
|
|
1839
2341
|
const { t } = useAginies();
|
|
1840
|
-
const [value, setValue] = (0,
|
|
1841
|
-
const [error, setError] = (0,
|
|
1842
|
-
const [pending, setPending] = (0,
|
|
2342
|
+
const [value, setValue] = (0, import_react6.useState)("");
|
|
2343
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
2344
|
+
const [pending, setPending] = (0, import_react6.useState)(false);
|
|
1843
2345
|
const submit = async (e) => {
|
|
1844
2346
|
e.preventDefault();
|
|
1845
2347
|
setPending(true);
|
|
@@ -1869,12 +2371,12 @@ function EmailAuth({
|
|
|
1869
2371
|
onVerifyCode
|
|
1870
2372
|
}) {
|
|
1871
2373
|
const { t } = useAginies();
|
|
1872
|
-
const [email, setEmail] = (0,
|
|
1873
|
-
const [code, setCode] = (0,
|
|
1874
|
-
const [step, setStep] = (0,
|
|
1875
|
-
const [error, setError] = (0,
|
|
1876
|
-
const [notice, setNotice] = (0,
|
|
1877
|
-
const [pending, setPending] = (0,
|
|
2374
|
+
const [email, setEmail] = (0, import_react6.useState)("");
|
|
2375
|
+
const [code, setCode] = (0, import_react6.useState)("");
|
|
2376
|
+
const [step, setStep] = (0, import_react6.useState)("email");
|
|
2377
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
2378
|
+
const [notice, setNotice] = (0, import_react6.useState)(null);
|
|
2379
|
+
const [pending, setPending] = (0, import_react6.useState)(false);
|
|
1878
2380
|
const request = async () => {
|
|
1879
2381
|
setPending(true);
|
|
1880
2382
|
setError(null);
|
|
@@ -2085,7 +2587,7 @@ function CostBars({ items, format = usd, className, ...props }) {
|
|
|
2085
2587
|
}
|
|
2086
2588
|
|
|
2087
2589
|
// src/run/agent-runner.tsx
|
|
2088
|
-
var
|
|
2590
|
+
var import_react7 = require("react");
|
|
2089
2591
|
|
|
2090
2592
|
// src/run/run-client.ts
|
|
2091
2593
|
async function* runAgent(client, workflowId, options, signal) {
|
|
@@ -2225,19 +2727,19 @@ function decodeRunFrame(frame) {
|
|
|
2225
2727
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2226
2728
|
function useAgentRun(workflowId, apiKey) {
|
|
2227
2729
|
const { client, t } = useAginies();
|
|
2228
|
-
const [status, setStatus] = (0,
|
|
2229
|
-
const [text, setText] = (0,
|
|
2230
|
-
const [steps, setSteps] = (0,
|
|
2231
|
-
const [output, setOutput] = (0,
|
|
2232
|
-
const [error, setError] = (0,
|
|
2233
|
-
const [executionId, setExecutionId] = (0,
|
|
2234
|
-
const abortRef = (0,
|
|
2235
|
-
const cancel = (0,
|
|
2730
|
+
const [status, setStatus] = (0, import_react7.useState)("idle");
|
|
2731
|
+
const [text, setText] = (0, import_react7.useState)("");
|
|
2732
|
+
const [steps, setSteps] = (0, import_react7.useState)([]);
|
|
2733
|
+
const [output, setOutput] = (0, import_react7.useState)(null);
|
|
2734
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
2735
|
+
const [executionId, setExecutionId] = (0, import_react7.useState)();
|
|
2736
|
+
const abortRef = (0, import_react7.useRef)(null);
|
|
2737
|
+
const cancel = (0, import_react7.useCallback)(() => {
|
|
2236
2738
|
abortRef.current?.abort();
|
|
2237
2739
|
abortRef.current = null;
|
|
2238
2740
|
setStatus((s) => s === "running" ? "idle" : s);
|
|
2239
2741
|
}, []);
|
|
2240
|
-
const run = (0,
|
|
2742
|
+
const run = (0, import_react7.useCallback)(
|
|
2241
2743
|
async (input) => {
|
|
2242
2744
|
abortRef.current?.abort();
|
|
2243
2745
|
const controller = new AbortController();
|
|
@@ -2314,7 +2816,7 @@ function AgentRunner({
|
|
|
2314
2816
|
const { t } = useAginies();
|
|
2315
2817
|
const enabled = useModule("run");
|
|
2316
2818
|
const agent = useAgentRun(workflowId, apiKey);
|
|
2317
|
-
const [values, setValues] = (0,
|
|
2819
|
+
const [values, setValues] = (0, import_react7.useState)(
|
|
2318
2820
|
() => Object.fromEntries(
|
|
2319
2821
|
fields.map((f) => [f.name, f.defaultValue ?? (f.type === "boolean" ? false : "")])
|
|
2320
2822
|
)
|
|
@@ -2326,9 +2828,9 @@ function AgentRunner({
|
|
|
2326
2828
|
const input = fields.length === 1 && fields[0]?.name === "input" && fields[0]?.type === "textarea" ? values.input : values;
|
|
2327
2829
|
await agent.run(input);
|
|
2328
2830
|
};
|
|
2329
|
-
const onResultRef = (0,
|
|
2831
|
+
const onResultRef = (0, import_react7.useRef)(onResult);
|
|
2330
2832
|
onResultRef.current = onResult;
|
|
2331
|
-
(0,
|
|
2833
|
+
(0, import_react7.useEffect)(() => {
|
|
2332
2834
|
if (agent.status === "done") onResultRef.current?.(agent.output);
|
|
2333
2835
|
}, [agent.status, agent.output]);
|
|
2334
2836
|
if (!enabled) return null;
|
|
@@ -2449,6 +2951,7 @@ function formatOutput(output) {
|
|
|
2449
2951
|
getPausedExecution,
|
|
2450
2952
|
init,
|
|
2451
2953
|
initialValues,
|
|
2954
|
+
isVoiceSupported,
|
|
2452
2955
|
listPausedExecutions,
|
|
2453
2956
|
outputOf,
|
|
2454
2957
|
parseFieldValue,
|
|
@@ -2462,6 +2965,7 @@ function formatOutput(output) {
|
|
|
2462
2965
|
useAginies,
|
|
2463
2966
|
useApproval,
|
|
2464
2967
|
useChat,
|
|
2465
|
-
useModule
|
|
2968
|
+
useModule,
|
|
2969
|
+
useVoice
|
|
2466
2970
|
});
|
|
2467
2971
|
//# sourceMappingURL=index.cjs.map
|