@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.js
CHANGED
|
@@ -15,6 +15,7 @@ var trimSlash = (s) => s.replace(/\/+$/, "");
|
|
|
15
15
|
var AginiesClient = class {
|
|
16
16
|
baseUrl;
|
|
17
17
|
token;
|
|
18
|
+
hosted;
|
|
18
19
|
locale;
|
|
19
20
|
fetchImpl;
|
|
20
21
|
state = { status: "idle" };
|
|
@@ -23,9 +24,12 @@ var AginiesClient = class {
|
|
|
23
24
|
session = null;
|
|
24
25
|
constructor(config) {
|
|
25
26
|
if (!config?.baseUrl) throw new AginiesError("baseUrl is required", 0, "MISSING_BASE_URL");
|
|
26
|
-
if (!config?.token
|
|
27
|
+
if (!config?.token && !config?.hosted) {
|
|
28
|
+
throw new AginiesError("token is required", 0, "MISSING_TOKEN");
|
|
29
|
+
}
|
|
27
30
|
this.baseUrl = trimSlash(config.baseUrl);
|
|
28
|
-
this.token = config.token;
|
|
31
|
+
this.token = config.token ?? null;
|
|
32
|
+
this.hosted = config.hosted === true;
|
|
29
33
|
this.locale = config.locale ?? detectLocale();
|
|
30
34
|
this.fetchImpl = config.fetch ?? ((...args) => fetch(...args));
|
|
31
35
|
}
|
|
@@ -52,7 +56,7 @@ var AginiesClient = class {
|
|
|
52
56
|
const res = await this.fetchImpl(`${this.baseUrl}/api/ui/activate`, {
|
|
53
57
|
method: "POST",
|
|
54
58
|
headers: { "Content-Type": "application/json" },
|
|
55
|
-
body: JSON.stringify({ token: this.token })
|
|
59
|
+
body: JSON.stringify(this.hosted ? { hosted: true } : { token: this.token })
|
|
56
60
|
});
|
|
57
61
|
if (!res.ok) {
|
|
58
62
|
const body2 = await safeJson(res);
|
|
@@ -127,6 +131,30 @@ var AginiesClient = class {
|
|
|
127
131
|
this.assertActive();
|
|
128
132
|
return this.request(path2, init2);
|
|
129
133
|
}
|
|
134
|
+
/** Sends a recording to the platform's transcription model. */
|
|
135
|
+
async transcribe(identifier, audio, filename = "turn.webm", language) {
|
|
136
|
+
this.assertActive();
|
|
137
|
+
const form = new FormData();
|
|
138
|
+
form.append("file", audio, filename);
|
|
139
|
+
if (language) form.append("language", language);
|
|
140
|
+
const res = await this.request(`/api/chat/${encodeURIComponent(identifier)}/voice/transcribe`, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
body: form
|
|
143
|
+
});
|
|
144
|
+
if (!res.ok) throw await voiceError(res);
|
|
145
|
+
return await res.json();
|
|
146
|
+
}
|
|
147
|
+
/** Audio for a reply from the platform's synthesis endpoint. */
|
|
148
|
+
async speak(identifier, text) {
|
|
149
|
+
this.assertActive();
|
|
150
|
+
const res = await this.request(`/api/chat/${encodeURIComponent(identifier)}/voice/speak`, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: { "Content-Type": "application/json" },
|
|
153
|
+
body: JSON.stringify({ text })
|
|
154
|
+
});
|
|
155
|
+
if (!res.ok) throw await voiceError(res);
|
|
156
|
+
return res.blob();
|
|
157
|
+
}
|
|
130
158
|
/** Hosted-chat configuration. Resolves to an auth requirement instead of throwing on 401. */
|
|
131
159
|
async getChat(identifier) {
|
|
132
160
|
this.assertActive();
|
|
@@ -216,6 +244,15 @@ function decodeFrame(frame) {
|
|
|
216
244
|
}
|
|
217
245
|
return null;
|
|
218
246
|
}
|
|
247
|
+
async function voiceError(res) {
|
|
248
|
+
const body = await safeJson(res);
|
|
249
|
+
if (res.status === 401 && body && typeof body.authRequired === "string") {
|
|
250
|
+
return new AginiesError("Authentication required", 401, "AUTH_REQUIRED");
|
|
251
|
+
}
|
|
252
|
+
if (res.status === 503)
|
|
253
|
+
return new AginiesError("Voice is not available", 503, "VOICE_UNAVAILABLE");
|
|
254
|
+
return new AginiesError(body?.error || `HTTP ${res.status}`, res.status);
|
|
255
|
+
}
|
|
219
256
|
async function safeJson(res) {
|
|
220
257
|
try {
|
|
221
258
|
return await res.json();
|
|
@@ -514,6 +551,35 @@ var STRINGS = {
|
|
|
514
551
|
},
|
|
515
552
|
ssoButton: { tr: "Kurumsal giri\u015F", en: "Sign in" }
|
|
516
553
|
},
|
|
554
|
+
voice: {
|
|
555
|
+
talk: { tr: "Konu\u015Fmak i\xE7in dokunun", en: "Tap to talk" },
|
|
556
|
+
stopTalking: { tr: "Bitirmek i\xE7in dokunun", en: "Tap when done" },
|
|
557
|
+
dictate: { tr: "Sesle yaz", en: "Dictate" },
|
|
558
|
+
handsFree: { tr: "Sesli sohbet", en: "Voice conversation" },
|
|
559
|
+
exit: { tr: "Sesli sohbetten \xE7\u0131k", en: "Leave voice conversation" },
|
|
560
|
+
listening: { tr: "Dinliyor", en: "Listening" },
|
|
561
|
+
transcribing: { tr: "Yaz\u0131ya d\xF6k\xFCl\xFCyor", en: "Transcribing" },
|
|
562
|
+
thinking: { tr: "Yan\u0131t haz\u0131rlan\u0131yor", en: "Working on it" },
|
|
563
|
+
speaking: { tr: "Konu\u015Fuyor", en: "Speaking" },
|
|
564
|
+
idle: { tr: "Haz\u0131r", en: "Ready" },
|
|
565
|
+
interrupt: { tr: "S\xF6z\xFCn\xFC kesmek i\xE7in dokunun", en: "Tap to interrupt" },
|
|
566
|
+
micDenied: {
|
|
567
|
+
tr: "Mikrofon izni verilmedi. Taray\u0131c\u0131 ayarlar\u0131ndan izin verin.",
|
|
568
|
+
en: "Microphone access was denied. Allow it in your browser settings."
|
|
569
|
+
},
|
|
570
|
+
unsupported: {
|
|
571
|
+
tr: "Bu taray\u0131c\u0131 ses kayd\u0131n\u0131 desteklemiyor.",
|
|
572
|
+
en: "This browser cannot record audio."
|
|
573
|
+
},
|
|
574
|
+
unavailable: {
|
|
575
|
+
tr: "Ses \xF6zelli\u011Fi bu sohbet i\xE7in a\xE7\u0131k de\u011Fil.",
|
|
576
|
+
en: "Voice is not enabled for this chat."
|
|
577
|
+
},
|
|
578
|
+
error: {
|
|
579
|
+
tr: "Ses i\u015Flenemedi. L\xFCtfen tekrar deneyin.",
|
|
580
|
+
en: "Voice could not be processed. Please try again."
|
|
581
|
+
}
|
|
582
|
+
},
|
|
517
583
|
run: {
|
|
518
584
|
submit: { tr: "\xC7al\u0131\u015Ft\u0131r", en: "Run" },
|
|
519
585
|
cancel: { tr: "\u0130ptal", en: "Cancel" },
|
|
@@ -964,7 +1030,7 @@ function renderField(f, value, set, disabled) {
|
|
|
964
1030
|
}
|
|
965
1031
|
|
|
966
1032
|
// src/chat/chat-widget.tsx
|
|
967
|
-
import { useCallback as
|
|
1033
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
|
|
968
1034
|
|
|
969
1035
|
// src/chat/markdown.tsx
|
|
970
1036
|
import { Fragment as Fragment3 } from "react";
|
|
@@ -1274,20 +1340,258 @@ function Pie({ labels, data }) {
|
|
|
1274
1340
|
] });
|
|
1275
1341
|
}
|
|
1276
1342
|
|
|
1343
|
+
// src/chat/voice.ts
|
|
1344
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
1345
|
+
var MIME_CANDIDATES = [
|
|
1346
|
+
"audio/webm;codecs=opus",
|
|
1347
|
+
"audio/webm",
|
|
1348
|
+
"audio/mp4",
|
|
1349
|
+
"audio/ogg;codecs=opus"
|
|
1350
|
+
];
|
|
1351
|
+
var MIN_CLIP_BYTES = 800;
|
|
1352
|
+
var SPEECH_RMS = 0.02;
|
|
1353
|
+
function isVoiceSupported() {
|
|
1354
|
+
return typeof window !== "undefined" && typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia && typeof MediaRecorder !== "undefined";
|
|
1355
|
+
}
|
|
1356
|
+
function useVoice({
|
|
1357
|
+
identifier,
|
|
1358
|
+
onTranscript,
|
|
1359
|
+
language,
|
|
1360
|
+
silenceMs = 1400,
|
|
1361
|
+
maxMs = 6e4
|
|
1362
|
+
}) {
|
|
1363
|
+
const { client, t } = useAginies();
|
|
1364
|
+
const supported = isVoiceSupported();
|
|
1365
|
+
const [state, setState] = useState3(supported ? "idle" : "unsupported");
|
|
1366
|
+
const [error, setError] = useState3(null);
|
|
1367
|
+
const [level, setLevel] = useState3(0);
|
|
1368
|
+
const recorderRef = useRef2(null);
|
|
1369
|
+
const streamRef = useRef2(null);
|
|
1370
|
+
const chunksRef = useRef2([]);
|
|
1371
|
+
const discardRef = useRef2(false);
|
|
1372
|
+
const timersRef = useRef2([]);
|
|
1373
|
+
const meterRef = useRef2(null);
|
|
1374
|
+
const audioRef = useRef2(null);
|
|
1375
|
+
const audioUrlRef = useRef2(null);
|
|
1376
|
+
const startingRef = useRef2(false);
|
|
1377
|
+
const onTranscriptRef = useRef2(onTranscript);
|
|
1378
|
+
onTranscriptRef.current = onTranscript;
|
|
1379
|
+
const clearTimers = useCallback2(() => {
|
|
1380
|
+
for (const id of timersRef.current) window.clearTimeout(id);
|
|
1381
|
+
timersRef.current = [];
|
|
1382
|
+
if (meterRef.current) {
|
|
1383
|
+
window.clearInterval(meterRef.current.interval);
|
|
1384
|
+
void meterRef.current.ctx.close().catch(() => {
|
|
1385
|
+
});
|
|
1386
|
+
meterRef.current = null;
|
|
1387
|
+
}
|
|
1388
|
+
setLevel(0);
|
|
1389
|
+
}, []);
|
|
1390
|
+
const releaseStream = useCallback2(() => {
|
|
1391
|
+
for (const track of streamRef.current?.getTracks() ?? []) track.stop();
|
|
1392
|
+
streamRef.current = null;
|
|
1393
|
+
}, []);
|
|
1394
|
+
const stopSpeaking = useCallback2(() => {
|
|
1395
|
+
const audio = audioRef.current;
|
|
1396
|
+
if (audio) {
|
|
1397
|
+
audio.pause();
|
|
1398
|
+
audio.src = "";
|
|
1399
|
+
audioRef.current = null;
|
|
1400
|
+
}
|
|
1401
|
+
if (audioUrlRef.current) {
|
|
1402
|
+
URL.revokeObjectURL(audioUrlRef.current);
|
|
1403
|
+
audioUrlRef.current = null;
|
|
1404
|
+
}
|
|
1405
|
+
setState((s) => s === "speaking" ? "idle" : s);
|
|
1406
|
+
}, []);
|
|
1407
|
+
const finish = useCallback2((recorder) => {
|
|
1408
|
+
recorder.stop();
|
|
1409
|
+
}, []);
|
|
1410
|
+
const stopListening = useCallback2(() => {
|
|
1411
|
+
const recorder = recorderRef.current;
|
|
1412
|
+
if (!recorder || recorder.state === "inactive") return;
|
|
1413
|
+
discardRef.current = false;
|
|
1414
|
+
finish(recorder);
|
|
1415
|
+
}, [finish]);
|
|
1416
|
+
const cancelListening = useCallback2(() => {
|
|
1417
|
+
const recorder = recorderRef.current;
|
|
1418
|
+
if (!recorder || recorder.state === "inactive") return;
|
|
1419
|
+
discardRef.current = true;
|
|
1420
|
+
finish(recorder);
|
|
1421
|
+
}, [finish]);
|
|
1422
|
+
const startMeter = useCallback2(
|
|
1423
|
+
(stream, onSilence) => {
|
|
1424
|
+
const Ctx = typeof AudioContext !== "undefined" ? AudioContext : window.webkitAudioContext;
|
|
1425
|
+
if (!Ctx) return;
|
|
1426
|
+
try {
|
|
1427
|
+
const ctx = new Ctx();
|
|
1428
|
+
const analyser = ctx.createAnalyser();
|
|
1429
|
+
analyser.fftSize = 2048;
|
|
1430
|
+
ctx.createMediaStreamSource(stream).connect(analyser);
|
|
1431
|
+
const data = new Uint8Array(analyser.fftSize);
|
|
1432
|
+
let heard = false;
|
|
1433
|
+
let lastVoice = Date.now();
|
|
1434
|
+
const interval = window.setInterval(() => {
|
|
1435
|
+
analyser.getByteTimeDomainData(data);
|
|
1436
|
+
let sum = 0;
|
|
1437
|
+
for (const v of data) {
|
|
1438
|
+
const n = (v - 128) / 128;
|
|
1439
|
+
sum += n * n;
|
|
1440
|
+
}
|
|
1441
|
+
const rms = Math.sqrt(sum / data.length);
|
|
1442
|
+
setLevel(Math.min(1, rms * 6));
|
|
1443
|
+
if (rms > SPEECH_RMS) {
|
|
1444
|
+
heard = true;
|
|
1445
|
+
lastVoice = Date.now();
|
|
1446
|
+
} else if (heard && Date.now() - lastVoice > silenceMs) {
|
|
1447
|
+
onSilence();
|
|
1448
|
+
}
|
|
1449
|
+
}, 100);
|
|
1450
|
+
meterRef.current = { ctx, interval };
|
|
1451
|
+
} catch {
|
|
1452
|
+
}
|
|
1453
|
+
},
|
|
1454
|
+
[silenceMs]
|
|
1455
|
+
);
|
|
1456
|
+
const startListening = useCallback2(async () => {
|
|
1457
|
+
if (!supported) {
|
|
1458
|
+
setState("unsupported");
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (startingRef.current) return;
|
|
1462
|
+
if (recorderRef.current && recorderRef.current.state !== "inactive") return;
|
|
1463
|
+
startingRef.current = true;
|
|
1464
|
+
stopSpeaking();
|
|
1465
|
+
setError(null);
|
|
1466
|
+
let stream;
|
|
1467
|
+
try {
|
|
1468
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
1469
|
+
} catch {
|
|
1470
|
+
startingRef.current = false;
|
|
1471
|
+
setState("denied");
|
|
1472
|
+
setError(t("voice", "micDenied"));
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
startingRef.current = false;
|
|
1476
|
+
streamRef.current = stream;
|
|
1477
|
+
const mimeType = MIME_CANDIDATES.find((m) => MediaRecorder.isTypeSupported?.(m));
|
|
1478
|
+
const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : void 0);
|
|
1479
|
+
recorderRef.current = recorder;
|
|
1480
|
+
chunksRef.current = [];
|
|
1481
|
+
discardRef.current = false;
|
|
1482
|
+
recorder.ondataavailable = (e) => {
|
|
1483
|
+
if (e.data && e.data.size > 0) chunksRef.current.push(e.data);
|
|
1484
|
+
};
|
|
1485
|
+
recorder.onstop = () => {
|
|
1486
|
+
clearTimers();
|
|
1487
|
+
releaseStream();
|
|
1488
|
+
recorderRef.current = null;
|
|
1489
|
+
const type = recorder.mimeType || mimeType || "audio/webm";
|
|
1490
|
+
const clip = new Blob(chunksRef.current, { type });
|
|
1491
|
+
chunksRef.current = [];
|
|
1492
|
+
if (discardRef.current || clip.size < MIN_CLIP_BYTES) {
|
|
1493
|
+
setState("idle");
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
setState("transcribing");
|
|
1497
|
+
const ext = type.includes("mp4") ? "m4a" : type.includes("ogg") ? "ogg" : "webm";
|
|
1498
|
+
client.transcribe(identifier, clip, `turn.${ext}`, language).then((result) => {
|
|
1499
|
+
setState("idle");
|
|
1500
|
+
if (result.transcript) onTranscriptRef.current(result.transcript);
|
|
1501
|
+
}).catch((err) => {
|
|
1502
|
+
setState("idle");
|
|
1503
|
+
setError(
|
|
1504
|
+
err instanceof AginiesError && err.code === "VOICE_UNAVAILABLE" ? t("voice", "unavailable") : t("voice", "error")
|
|
1505
|
+
);
|
|
1506
|
+
});
|
|
1507
|
+
};
|
|
1508
|
+
recorder.start(250);
|
|
1509
|
+
setState("listening");
|
|
1510
|
+
startMeter(stream, () => stopListening());
|
|
1511
|
+
timersRef.current.push(window.setTimeout(() => stopListening(), maxMs));
|
|
1512
|
+
}, [
|
|
1513
|
+
supported,
|
|
1514
|
+
stopSpeaking,
|
|
1515
|
+
t,
|
|
1516
|
+
client,
|
|
1517
|
+
identifier,
|
|
1518
|
+
language,
|
|
1519
|
+
maxMs,
|
|
1520
|
+
stopListening,
|
|
1521
|
+
clearTimers,
|
|
1522
|
+
releaseStream,
|
|
1523
|
+
startMeter
|
|
1524
|
+
]);
|
|
1525
|
+
const speak = useCallback2(
|
|
1526
|
+
async (text) => {
|
|
1527
|
+
stopSpeaking();
|
|
1528
|
+
setError(null);
|
|
1529
|
+
setState("speaking");
|
|
1530
|
+
try {
|
|
1531
|
+
const blob = await client.speak(identifier, text);
|
|
1532
|
+
const url = URL.createObjectURL(blob);
|
|
1533
|
+
audioUrlRef.current = url;
|
|
1534
|
+
const audio = new Audio(url);
|
|
1535
|
+
audioRef.current = audio;
|
|
1536
|
+
await new Promise((resolve) => {
|
|
1537
|
+
audio.onended = () => resolve();
|
|
1538
|
+
audio.onerror = () => resolve();
|
|
1539
|
+
audio.onpause = () => resolve();
|
|
1540
|
+
audio.play().catch(() => resolve());
|
|
1541
|
+
});
|
|
1542
|
+
} catch (err) {
|
|
1543
|
+
setError(
|
|
1544
|
+
err instanceof AginiesError && err.code === "VOICE_UNAVAILABLE" ? t("voice", "unavailable") : t("voice", "error")
|
|
1545
|
+
);
|
|
1546
|
+
} finally {
|
|
1547
|
+
if (audioUrlRef.current) URL.revokeObjectURL(audioUrlRef.current);
|
|
1548
|
+
audioUrlRef.current = null;
|
|
1549
|
+
audioRef.current = null;
|
|
1550
|
+
setState((s) => s === "speaking" ? "idle" : s);
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
[client, identifier, stopSpeaking, t]
|
|
1554
|
+
);
|
|
1555
|
+
useEffect3(
|
|
1556
|
+
() => () => {
|
|
1557
|
+
discardRef.current = true;
|
|
1558
|
+
if (recorderRef.current && recorderRef.current.state !== "inactive")
|
|
1559
|
+
recorderRef.current.stop();
|
|
1560
|
+
clearTimers();
|
|
1561
|
+
releaseStream();
|
|
1562
|
+
const audio = audioRef.current;
|
|
1563
|
+
if (audio) audio.pause();
|
|
1564
|
+
if (audioUrlRef.current) URL.revokeObjectURL(audioUrlRef.current);
|
|
1565
|
+
},
|
|
1566
|
+
[clearTimers, releaseStream]
|
|
1567
|
+
);
|
|
1568
|
+
return {
|
|
1569
|
+
state,
|
|
1570
|
+
error,
|
|
1571
|
+
supported,
|
|
1572
|
+
level,
|
|
1573
|
+
startListening,
|
|
1574
|
+
stopListening,
|
|
1575
|
+
cancelListening,
|
|
1576
|
+
speak,
|
|
1577
|
+
stopSpeaking
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1277
1581
|
// src/chat/chat-widget.tsx
|
|
1278
1582
|
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1279
1583
|
var ATTACHMENT_LIMITS = { maxFiles: 5, maxBytes: 10 * 1024 * 1024 };
|
|
1280
1584
|
function useChat(identifier, enabled = true) {
|
|
1281
1585
|
const { client, t } = useAginies();
|
|
1282
|
-
const [config, setConfig] =
|
|
1283
|
-
const [authNeed, setAuthNeed] =
|
|
1284
|
-
const [authTitle, setAuthTitle] =
|
|
1285
|
-
const [loadError, setLoadError] =
|
|
1286
|
-
const [messages, setMessages] =
|
|
1287
|
-
const [busy, setBusy] =
|
|
1288
|
-
const [conversationId] =
|
|
1289
|
-
const abortRef =
|
|
1290
|
-
const load =
|
|
1586
|
+
const [config, setConfig] = useState4(null);
|
|
1587
|
+
const [authNeed, setAuthNeed] = useState4(null);
|
|
1588
|
+
const [authTitle, setAuthTitle] = useState4();
|
|
1589
|
+
const [loadError, setLoadError] = useState4(null);
|
|
1590
|
+
const [messages, setMessages] = useState4([]);
|
|
1591
|
+
const [busy, setBusy] = useState4(false);
|
|
1592
|
+
const [conversationId] = useState4(() => randomId());
|
|
1593
|
+
const abortRef = useRef3(null);
|
|
1594
|
+
const load = useCallback3(async () => {
|
|
1291
1595
|
setLoadError(null);
|
|
1292
1596
|
try {
|
|
1293
1597
|
const res = await client.getChat(identifier);
|
|
@@ -1304,11 +1608,11 @@ function useChat(identifier, enabled = true) {
|
|
|
1304
1608
|
);
|
|
1305
1609
|
}
|
|
1306
1610
|
}, [client, identifier, t]);
|
|
1307
|
-
|
|
1611
|
+
useEffect4(() => {
|
|
1308
1612
|
if (enabled) void load();
|
|
1309
1613
|
}, [load, enabled]);
|
|
1310
1614
|
const chatPath = `/api/chat/${encodeURIComponent(identifier)}`;
|
|
1311
|
-
const authenticate =
|
|
1615
|
+
const authenticate = useCallback3(
|
|
1312
1616
|
async ({ password }) => {
|
|
1313
1617
|
const res = await client.fetchRaw(chatPath, {
|
|
1314
1618
|
method: "POST",
|
|
@@ -1323,7 +1627,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1323
1627
|
},
|
|
1324
1628
|
[client, chatPath, conversationId, load]
|
|
1325
1629
|
);
|
|
1326
|
-
const requestCode =
|
|
1630
|
+
const requestCode = useCallback3(
|
|
1327
1631
|
async (email) => {
|
|
1328
1632
|
const res = await client.fetchRaw(`${chatPath}/otp`, {
|
|
1329
1633
|
method: "POST",
|
|
@@ -1335,7 +1639,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1335
1639
|
},
|
|
1336
1640
|
[client, chatPath]
|
|
1337
1641
|
);
|
|
1338
|
-
const verifyCode =
|
|
1642
|
+
const verifyCode = useCallback3(
|
|
1339
1643
|
async (email, otp) => {
|
|
1340
1644
|
const res = await client.fetchRaw(`${chatPath}/otp`, {
|
|
1341
1645
|
method: "PUT",
|
|
@@ -1350,7 +1654,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1350
1654
|
},
|
|
1351
1655
|
[client, chatPath, load]
|
|
1352
1656
|
);
|
|
1353
|
-
const stop =
|
|
1657
|
+
const stop = useCallback3(() => {
|
|
1354
1658
|
abortRef.current?.abort();
|
|
1355
1659
|
abortRef.current = null;
|
|
1356
1660
|
setBusy(false);
|
|
@@ -1363,7 +1667,7 @@ function useChat(identifier, enabled = true) {
|
|
|
1363
1667
|
];
|
|
1364
1668
|
});
|
|
1365
1669
|
}, [t]);
|
|
1366
|
-
const send =
|
|
1670
|
+
const send = useCallback3(
|
|
1367
1671
|
async (text, files = []) => {
|
|
1368
1672
|
const input = text.trim();
|
|
1369
1673
|
if (!input && files.length === 0 || busy) return;
|
|
@@ -1455,15 +1759,36 @@ function ChatWidget({
|
|
|
1455
1759
|
launcherLabel,
|
|
1456
1760
|
defaultOpen = false,
|
|
1457
1761
|
theme = "auto",
|
|
1762
|
+
voice = true,
|
|
1458
1763
|
className
|
|
1459
1764
|
}) {
|
|
1460
|
-
const [open, setOpen] =
|
|
1765
|
+
const [open, setOpen] = useState4(defaultOpen || mode !== "bubble");
|
|
1461
1766
|
const { t } = useAginies();
|
|
1462
1767
|
const enabled = useModule("chat");
|
|
1463
1768
|
const chat = useChat(identifier, enabled);
|
|
1769
|
+
const [voiceMode, setVoiceMode] = useState4(false);
|
|
1770
|
+
const [dictation, setDictation] = useState4(null);
|
|
1771
|
+
const voiceModeRef = useRef3(voiceMode);
|
|
1772
|
+
voiceModeRef.current = voiceMode;
|
|
1773
|
+
const sendRef = useRef3(chat.send);
|
|
1774
|
+
sendRef.current = chat.send;
|
|
1775
|
+
const voiceControls = useVoice({
|
|
1776
|
+
identifier,
|
|
1777
|
+
onTranscript: (text) => {
|
|
1778
|
+
if (voiceModeRef.current) void sendRef.current(text);
|
|
1779
|
+
else setDictation({ id: Date.now(), text });
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
const capabilities = chat.config?.voice;
|
|
1783
|
+
const voiceAvailable = voice && voiceControls.supported && capabilities?.stt === true;
|
|
1464
1784
|
const title = chat.config?.title ?? chat.authTitle ?? launcherLabel ?? "Aginies";
|
|
1465
1785
|
const themeClass = theme === "auto" ? void 0 : theme === "dark" ? "dark" : "light";
|
|
1466
1786
|
if (!enabled) return null;
|
|
1787
|
+
const leaveVoiceMode = () => {
|
|
1788
|
+
voiceControls.cancelListening();
|
|
1789
|
+
voiceControls.stopSpeaking();
|
|
1790
|
+
setVoiceMode(false);
|
|
1791
|
+
};
|
|
1467
1792
|
const panel = /* @__PURE__ */ jsxs5(
|
|
1468
1793
|
"section",
|
|
1469
1794
|
{
|
|
@@ -1501,7 +1826,16 @@ function ChatWidget({
|
|
|
1501
1826
|
onRequestCode: chat.requestCode,
|
|
1502
1827
|
onVerifyCode: chat.verifyCode
|
|
1503
1828
|
}
|
|
1504
|
-
) : !chat.config ? /* @__PURE__ */ jsx6("div", { className: "agi-chat__state", children: /* @__PURE__ */ jsx6(Spinner, { label: t("common", "loading") }) }) : /* @__PURE__ */
|
|
1829
|
+
) : !chat.config ? /* @__PURE__ */ jsx6("div", { className: "agi-chat__state", children: /* @__PURE__ */ jsx6(Spinner, { label: t("common", "loading") }) }) : voiceMode ? /* @__PURE__ */ jsx6(
|
|
1830
|
+
VoiceConversation,
|
|
1831
|
+
{
|
|
1832
|
+
messages: chat.messages,
|
|
1833
|
+
busy: chat.busy,
|
|
1834
|
+
voice: voiceControls,
|
|
1835
|
+
speakReplies: capabilities?.tts === true,
|
|
1836
|
+
onExit: leaveVoiceMode
|
|
1837
|
+
}
|
|
1838
|
+
) : /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
1505
1839
|
/* @__PURE__ */ jsx6(
|
|
1506
1840
|
MessageList,
|
|
1507
1841
|
{
|
|
@@ -1510,7 +1844,22 @@ function ChatWidget({
|
|
|
1510
1844
|
onAction: (action) => void chat.send(action)
|
|
1511
1845
|
}
|
|
1512
1846
|
),
|
|
1513
|
-
/* @__PURE__ */ jsx6(
|
|
1847
|
+
/* @__PURE__ */ jsx6(
|
|
1848
|
+
Composer,
|
|
1849
|
+
{
|
|
1850
|
+
busy: chat.busy,
|
|
1851
|
+
onSend: chat.send,
|
|
1852
|
+
onStop: chat.stop,
|
|
1853
|
+
dictation,
|
|
1854
|
+
voice: voiceAvailable ? {
|
|
1855
|
+
state: voiceControls.state,
|
|
1856
|
+
error: voiceControls.error,
|
|
1857
|
+
start: voiceControls.startListening,
|
|
1858
|
+
stop: voiceControls.stopListening,
|
|
1859
|
+
onHandsFree: () => setVoiceMode(true)
|
|
1860
|
+
} : void 0
|
|
1861
|
+
}
|
|
1862
|
+
)
|
|
1514
1863
|
] }),
|
|
1515
1864
|
/* @__PURE__ */ jsx6("footer", { className: "agi-chat__foot", children: /* @__PURE__ */ jsx6("a", { href: "https://www.aginies.com", target: "_blank", rel: "noreferrer", children: t("chat", "poweredBy") }) })
|
|
1516
1865
|
]
|
|
@@ -1565,53 +1914,57 @@ function MessageList({
|
|
|
1565
1914
|
onAction
|
|
1566
1915
|
}) {
|
|
1567
1916
|
const { t } = useAginies();
|
|
1568
|
-
const endRef =
|
|
1569
|
-
|
|
1917
|
+
const endRef = useRef3(null);
|
|
1918
|
+
useEffect4(() => {
|
|
1570
1919
|
endRef.current?.scrollIntoView?.({ block: "end" });
|
|
1571
1920
|
}, []);
|
|
1572
|
-
return (
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
/* @__PURE__ */
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
"
|
|
1581
|
-
|
|
1582
|
-
className:
|
|
1583
|
-
children: [
|
|
1584
|
-
/* @__PURE__ */ jsx6("
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
/* @__PURE__ */ jsx6("div", { ref: endRef })
|
|
1602
|
-
] })
|
|
1603
|
-
);
|
|
1921
|
+
return /* @__PURE__ */ jsxs5("div", { className: "agi-chat__messages", role: "log", "aria-live": "polite", children: [
|
|
1922
|
+
/* @__PURE__ */ jsxs5("div", { className: "agi-msg agi-msg--assistant", children: [
|
|
1923
|
+
/* @__PURE__ */ jsx6("span", { className: "agi-msg__who", children: t("chat", "assistant") }),
|
|
1924
|
+
/* @__PURE__ */ jsx6("div", { className: "agi-msg__body", children: welcome })
|
|
1925
|
+
] }),
|
|
1926
|
+
messages.map((m) => /* @__PURE__ */ jsxs5(
|
|
1927
|
+
"div",
|
|
1928
|
+
{
|
|
1929
|
+
className: cx("agi-msg", `agi-msg--${m.role}`, m.error && "agi-msg--error"),
|
|
1930
|
+
children: [
|
|
1931
|
+
/* @__PURE__ */ jsx6("span", { className: "agi-msg__who", children: m.role === "user" ? t("chat", "you") : t("chat", "assistant") }),
|
|
1932
|
+
/* @__PURE__ */ jsxs5("div", { className: "agi-msg__body", children: [
|
|
1933
|
+
m.structured ? /* @__PURE__ */ jsx6(StructuredUI, { response: m.structured, onAction }) : m.content ? m.role === "assistant" && !m.error ? /* @__PURE__ */ jsx6(Markdown, { className: "agi-md", text: m.content }) : /* @__PURE__ */ jsx6("p", { children: m.content }) : m.streaming ? /* @__PURE__ */ jsxs5("span", { className: "agi-msg__thinking", children: [
|
|
1934
|
+
/* @__PURE__ */ jsx6(Spinner, { label: t("chat", "thinking") }),
|
|
1935
|
+
" ",
|
|
1936
|
+
t("chat", "thinking")
|
|
1937
|
+
] }) : null,
|
|
1938
|
+
m.attachments && m.attachments.length > 0 && /* @__PURE__ */ jsx6("ul", { className: "agi-msg__files", "aria-label": t("chat", "attachments"), children: m.attachments.map((file, n) => /* @__PURE__ */ jsxs5("li", { className: "agi-file", children: [
|
|
1939
|
+
/* @__PURE__ */ jsx6(FileGlyph, {}),
|
|
1940
|
+
/* @__PURE__ */ jsx6("span", { className: "agi-file__name", children: file.name }),
|
|
1941
|
+
/* @__PURE__ */ jsx6("span", { className: "agi-file__size", children: formatBytes(file.size) })
|
|
1942
|
+
] }, n)) })
|
|
1943
|
+
] })
|
|
1944
|
+
]
|
|
1945
|
+
},
|
|
1946
|
+
m.id
|
|
1947
|
+
)),
|
|
1948
|
+
/* @__PURE__ */ jsx6("div", { ref: endRef })
|
|
1949
|
+
] });
|
|
1604
1950
|
}
|
|
1605
1951
|
function Composer({
|
|
1606
1952
|
busy,
|
|
1607
1953
|
onSend,
|
|
1608
|
-
onStop
|
|
1954
|
+
onStop,
|
|
1955
|
+
voice,
|
|
1956
|
+
dictation
|
|
1609
1957
|
}) {
|
|
1610
1958
|
const { t } = useAginies();
|
|
1611
|
-
const [value, setValue] =
|
|
1612
|
-
const [files, setFiles] =
|
|
1613
|
-
const [fileError, setFileError] =
|
|
1614
|
-
const fileInput =
|
|
1959
|
+
const [value, setValue] = useState4("");
|
|
1960
|
+
const [files, setFiles] = useState4([]);
|
|
1961
|
+
const [fileError, setFileError] = useState4(null);
|
|
1962
|
+
const fileInput = useRef3(null);
|
|
1963
|
+
useEffect4(() => {
|
|
1964
|
+
if (dictation) setValue((v) => v.trim() ? `${v.trimEnd()} ${dictation.text}` : dictation.text);
|
|
1965
|
+
}, [dictation]);
|
|
1966
|
+
const listening = voice?.state === "listening";
|
|
1967
|
+
const transcribing = voice?.state === "transcribing";
|
|
1615
1968
|
const submit = (e) => {
|
|
1616
1969
|
e.preventDefault();
|
|
1617
1970
|
if (busy) return;
|
|
@@ -1657,6 +2010,7 @@ function Composer({
|
|
|
1657
2010
|
] }, n)),
|
|
1658
2011
|
fileError && /* @__PURE__ */ jsx6("span", { className: "agi-chat__file-error", children: fileError })
|
|
1659
2012
|
] }),
|
|
2013
|
+
voice?.error && /* @__PURE__ */ jsx6("div", { className: "agi-chat__file-error", children: voice.error }),
|
|
1660
2014
|
/* @__PURE__ */ jsxs5("div", { className: "agi-chat__row", children: [
|
|
1661
2015
|
/* @__PURE__ */ jsx6(
|
|
1662
2016
|
"input",
|
|
@@ -1700,10 +2054,156 @@ function Composer({
|
|
|
1700
2054
|
autoComplete: "off"
|
|
1701
2055
|
}
|
|
1702
2056
|
),
|
|
2057
|
+
voice && /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2058
|
+
/* @__PURE__ */ jsx6(
|
|
2059
|
+
Button,
|
|
2060
|
+
{
|
|
2061
|
+
variant: "ghost",
|
|
2062
|
+
type: "button",
|
|
2063
|
+
className: cx("agi-chat__mic", listening && "agi-chat__mic--on"),
|
|
2064
|
+
"aria-label": listening ? t("voice", "stopTalking") : t("voice", "dictate"),
|
|
2065
|
+
"aria-pressed": listening,
|
|
2066
|
+
title: listening ? t("voice", "stopTalking") : t("voice", "dictate"),
|
|
2067
|
+
disabled: busy || transcribing,
|
|
2068
|
+
onClick: () => listening ? voice.stop() : void voice.start(),
|
|
2069
|
+
children: transcribing ? /* @__PURE__ */ jsx6(Spinner, { label: t("voice", "transcribing") }) : /* @__PURE__ */ jsx6(MicGlyph, {})
|
|
2070
|
+
}
|
|
2071
|
+
),
|
|
2072
|
+
/* @__PURE__ */ jsx6(
|
|
2073
|
+
Button,
|
|
2074
|
+
{
|
|
2075
|
+
variant: "ghost",
|
|
2076
|
+
type: "button",
|
|
2077
|
+
"aria-label": t("voice", "handsFree"),
|
|
2078
|
+
title: t("voice", "handsFree"),
|
|
2079
|
+
disabled: busy || listening || transcribing,
|
|
2080
|
+
onClick: voice.onHandsFree,
|
|
2081
|
+
children: /* @__PURE__ */ jsx6(HeadsetGlyph, {})
|
|
2082
|
+
}
|
|
2083
|
+
)
|
|
2084
|
+
] }),
|
|
1703
2085
|
busy ? /* @__PURE__ */ jsx6(Button, { variant: "outline", onClick: onStop, children: t("chat", "stop") }) : /* @__PURE__ */ jsx6(Button, { variant: "signal", type: "submit", disabled: !canSend, children: t("chat", "send") })
|
|
1704
2086
|
] })
|
|
1705
2087
|
] });
|
|
1706
2088
|
}
|
|
2089
|
+
function VoiceConversation({
|
|
2090
|
+
messages,
|
|
2091
|
+
busy,
|
|
2092
|
+
voice,
|
|
2093
|
+
speakReplies,
|
|
2094
|
+
onExit
|
|
2095
|
+
}) {
|
|
2096
|
+
const { t } = useAginies();
|
|
2097
|
+
const spokenRef = useRef3(null);
|
|
2098
|
+
const mountedRef = useRef3(true);
|
|
2099
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
2100
|
+
const lastUser = [...messages].reverse().find((m) => m.role === "user");
|
|
2101
|
+
useEffect4(() => {
|
|
2102
|
+
mountedRef.current = true;
|
|
2103
|
+
spokenRef.current = lastAssistant?.id ?? null;
|
|
2104
|
+
void voice.startListening();
|
|
2105
|
+
return () => {
|
|
2106
|
+
mountedRef.current = false;
|
|
2107
|
+
};
|
|
2108
|
+
}, []);
|
|
2109
|
+
const replyId = lastAssistant?.id;
|
|
2110
|
+
const replyStreaming = lastAssistant?.streaming === true;
|
|
2111
|
+
useEffect4(() => {
|
|
2112
|
+
if (!lastAssistant || replyStreaming || replyId === spokenRef.current) return;
|
|
2113
|
+
spokenRef.current = replyId ?? null;
|
|
2114
|
+
const run = async () => {
|
|
2115
|
+
if (speakReplies && lastAssistant.content && !lastAssistant.error) {
|
|
2116
|
+
await voice.speak(lastAssistant.content);
|
|
2117
|
+
}
|
|
2118
|
+
if (mountedRef.current) await voice.startListening();
|
|
2119
|
+
};
|
|
2120
|
+
void run();
|
|
2121
|
+
}, [replyId, replyStreaming]);
|
|
2122
|
+
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");
|
|
2123
|
+
const hint = voice.state === "listening" ? t("voice", "stopTalking") : voice.state === "speaking" ? t("voice", "interrupt") : voice.state === "transcribing" || busy ? "" : t("voice", "talk");
|
|
2124
|
+
const tap = () => {
|
|
2125
|
+
if (voice.state === "listening") voice.stopListening();
|
|
2126
|
+
else if (voice.state === "speaking") {
|
|
2127
|
+
voice.stopSpeaking();
|
|
2128
|
+
void voice.startListening();
|
|
2129
|
+
} else if (voice.state === "idle" && !busy) void voice.startListening();
|
|
2130
|
+
};
|
|
2131
|
+
return /* @__PURE__ */ jsxs5("section", { className: "agi-voice", "aria-label": t("voice", "handsFree"), children: [
|
|
2132
|
+
/* @__PURE__ */ jsx6(
|
|
2133
|
+
"button",
|
|
2134
|
+
{
|
|
2135
|
+
type: "button",
|
|
2136
|
+
className: "agi-voice__exit",
|
|
2137
|
+
onClick: onExit,
|
|
2138
|
+
"aria-label": t("voice", "exit"),
|
|
2139
|
+
children: "\xD7"
|
|
2140
|
+
}
|
|
2141
|
+
),
|
|
2142
|
+
/* @__PURE__ */ jsx6("div", { className: "agi-voice__status", "aria-live": "polite", children: status }),
|
|
2143
|
+
lastUser?.content && /* @__PURE__ */ jsx6("p", { className: "agi-voice__you", children: lastUser.content }),
|
|
2144
|
+
/* @__PURE__ */ jsx6("div", { className: "agi-voice__reply", children: lastAssistant?.content ? /* @__PURE__ */ jsx6(Markdown, { className: "agi-md", text: lastAssistant.content }) : busy ? /* @__PURE__ */ jsx6(Spinner, { label: t("voice", "thinking") }) : null }),
|
|
2145
|
+
/* @__PURE__ */ jsx6(
|
|
2146
|
+
"button",
|
|
2147
|
+
{
|
|
2148
|
+
type: "button",
|
|
2149
|
+
className: cx(
|
|
2150
|
+
"agi-voice__orb",
|
|
2151
|
+
`agi-voice__orb--${voice.state}`,
|
|
2152
|
+
busy && "agi-voice__orb--busy"
|
|
2153
|
+
),
|
|
2154
|
+
style: { ["--agi-level"]: voice.level },
|
|
2155
|
+
onClick: tap,
|
|
2156
|
+
"aria-label": hint || status,
|
|
2157
|
+
disabled: voice.state === "transcribing" || voice.state === "unsupported",
|
|
2158
|
+
children: /* @__PURE__ */ jsx6(MicGlyph, { size: 28 })
|
|
2159
|
+
}
|
|
2160
|
+
),
|
|
2161
|
+
/* @__PURE__ */ jsx6("div", { className: "agi-voice__hint", children: hint }),
|
|
2162
|
+
voice.error && /* @__PURE__ */ jsx6("div", { className: "agi-voice__error", children: voice.error })
|
|
2163
|
+
] });
|
|
2164
|
+
}
|
|
2165
|
+
function MicGlyph({ size = 16 }) {
|
|
2166
|
+
return /* @__PURE__ */ jsxs5(
|
|
2167
|
+
"svg",
|
|
2168
|
+
{
|
|
2169
|
+
width: size,
|
|
2170
|
+
height: size,
|
|
2171
|
+
viewBox: "0 0 24 24",
|
|
2172
|
+
fill: "none",
|
|
2173
|
+
stroke: "currentColor",
|
|
2174
|
+
strokeWidth: "1.8",
|
|
2175
|
+
strokeLinecap: "round",
|
|
2176
|
+
strokeLinejoin: "round",
|
|
2177
|
+
"aria-hidden": "true",
|
|
2178
|
+
children: [
|
|
2179
|
+
/* @__PURE__ */ jsx6("rect", { x: "9", y: "3", width: "6", height: "11", rx: "3" }),
|
|
2180
|
+
/* @__PURE__ */ jsx6("path", { d: "M5 11a7 7 0 0 0 14 0M12 18v3M9 21h6" })
|
|
2181
|
+
]
|
|
2182
|
+
}
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
function HeadsetGlyph() {
|
|
2186
|
+
return /* @__PURE__ */ jsxs5(
|
|
2187
|
+
"svg",
|
|
2188
|
+
{
|
|
2189
|
+
width: "16",
|
|
2190
|
+
height: "16",
|
|
2191
|
+
viewBox: "0 0 24 24",
|
|
2192
|
+
fill: "none",
|
|
2193
|
+
stroke: "currentColor",
|
|
2194
|
+
strokeWidth: "1.8",
|
|
2195
|
+
strokeLinecap: "round",
|
|
2196
|
+
strokeLinejoin: "round",
|
|
2197
|
+
"aria-hidden": "true",
|
|
2198
|
+
children: [
|
|
2199
|
+
/* @__PURE__ */ jsx6("path", { d: "M4 14v-2a8 8 0 0 1 16 0v2" }),
|
|
2200
|
+
/* @__PURE__ */ jsx6("rect", { x: "3", y: "13", width: "4", height: "6", rx: "1.5" }),
|
|
2201
|
+
/* @__PURE__ */ jsx6("rect", { x: "17", y: "13", width: "4", height: "6", rx: "1.5" }),
|
|
2202
|
+
/* @__PURE__ */ jsx6("path", { d: "M19 19v1a2 2 0 0 1-2 2h-3" })
|
|
2203
|
+
]
|
|
2204
|
+
}
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
1707
2207
|
function readFile(file) {
|
|
1708
2208
|
return new Promise((resolve, reject) => {
|
|
1709
2209
|
const reader = new FileReader();
|
|
@@ -1767,9 +2267,9 @@ function PasswordAuth({
|
|
|
1767
2267
|
onPassword
|
|
1768
2268
|
}) {
|
|
1769
2269
|
const { t } = useAginies();
|
|
1770
|
-
const [value, setValue] =
|
|
1771
|
-
const [error, setError] =
|
|
1772
|
-
const [pending, setPending] =
|
|
2270
|
+
const [value, setValue] = useState4("");
|
|
2271
|
+
const [error, setError] = useState4(null);
|
|
2272
|
+
const [pending, setPending] = useState4(false);
|
|
1773
2273
|
const submit = async (e) => {
|
|
1774
2274
|
e.preventDefault();
|
|
1775
2275
|
setPending(true);
|
|
@@ -1799,12 +2299,12 @@ function EmailAuth({
|
|
|
1799
2299
|
onVerifyCode
|
|
1800
2300
|
}) {
|
|
1801
2301
|
const { t } = useAginies();
|
|
1802
|
-
const [email, setEmail] =
|
|
1803
|
-
const [code, setCode] =
|
|
1804
|
-
const [step, setStep] =
|
|
1805
|
-
const [error, setError] =
|
|
1806
|
-
const [notice, setNotice] =
|
|
1807
|
-
const [pending, setPending] =
|
|
2302
|
+
const [email, setEmail] = useState4("");
|
|
2303
|
+
const [code, setCode] = useState4("");
|
|
2304
|
+
const [step, setStep] = useState4("email");
|
|
2305
|
+
const [error, setError] = useState4(null);
|
|
2306
|
+
const [notice, setNotice] = useState4(null);
|
|
2307
|
+
const [pending, setPending] = useState4(false);
|
|
1808
2308
|
const request = async () => {
|
|
1809
2309
|
setPending(true);
|
|
1810
2310
|
setError(null);
|
|
@@ -2015,7 +2515,7 @@ function CostBars({ items, format = usd, className, ...props }) {
|
|
|
2015
2515
|
}
|
|
2016
2516
|
|
|
2017
2517
|
// src/run/agent-runner.tsx
|
|
2018
|
-
import { useCallback as
|
|
2518
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
|
|
2019
2519
|
|
|
2020
2520
|
// src/run/run-client.ts
|
|
2021
2521
|
async function* runAgent(client, workflowId, options, signal) {
|
|
@@ -2155,19 +2655,19 @@ function decodeRunFrame(frame) {
|
|
|
2155
2655
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2156
2656
|
function useAgentRun(workflowId, apiKey) {
|
|
2157
2657
|
const { client, t } = useAginies();
|
|
2158
|
-
const [status, setStatus] =
|
|
2159
|
-
const [text, setText] =
|
|
2160
|
-
const [steps, setSteps] =
|
|
2161
|
-
const [output, setOutput] =
|
|
2162
|
-
const [error, setError] =
|
|
2163
|
-
const [executionId, setExecutionId] =
|
|
2164
|
-
const abortRef =
|
|
2165
|
-
const cancel =
|
|
2658
|
+
const [status, setStatus] = useState5("idle");
|
|
2659
|
+
const [text, setText] = useState5("");
|
|
2660
|
+
const [steps, setSteps] = useState5([]);
|
|
2661
|
+
const [output, setOutput] = useState5(null);
|
|
2662
|
+
const [error, setError] = useState5(null);
|
|
2663
|
+
const [executionId, setExecutionId] = useState5();
|
|
2664
|
+
const abortRef = useRef4(null);
|
|
2665
|
+
const cancel = useCallback4(() => {
|
|
2166
2666
|
abortRef.current?.abort();
|
|
2167
2667
|
abortRef.current = null;
|
|
2168
2668
|
setStatus((s) => s === "running" ? "idle" : s);
|
|
2169
2669
|
}, []);
|
|
2170
|
-
const run =
|
|
2670
|
+
const run = useCallback4(
|
|
2171
2671
|
async (input) => {
|
|
2172
2672
|
abortRef.current?.abort();
|
|
2173
2673
|
const controller = new AbortController();
|
|
@@ -2244,7 +2744,7 @@ function AgentRunner({
|
|
|
2244
2744
|
const { t } = useAginies();
|
|
2245
2745
|
const enabled = useModule("run");
|
|
2246
2746
|
const agent = useAgentRun(workflowId, apiKey);
|
|
2247
|
-
const [values, setValues] =
|
|
2747
|
+
const [values, setValues] = useState5(
|
|
2248
2748
|
() => Object.fromEntries(
|
|
2249
2749
|
fields.map((f) => [f.name, f.defaultValue ?? (f.type === "boolean" ? false : "")])
|
|
2250
2750
|
)
|
|
@@ -2256,9 +2756,9 @@ function AgentRunner({
|
|
|
2256
2756
|
const input = fields.length === 1 && fields[0]?.name === "input" && fields[0]?.type === "textarea" ? values.input : values;
|
|
2257
2757
|
await agent.run(input);
|
|
2258
2758
|
};
|
|
2259
|
-
const onResultRef =
|
|
2759
|
+
const onResultRef = useRef4(onResult);
|
|
2260
2760
|
onResultRef.current = onResult;
|
|
2261
|
-
|
|
2761
|
+
useEffect5(() => {
|
|
2262
2762
|
if (agent.status === "done") onResultRef.current?.(agent.output);
|
|
2263
2763
|
}, [agent.status, agent.output]);
|
|
2264
2764
|
if (!enabled) return null;
|
|
@@ -2378,6 +2878,7 @@ export {
|
|
|
2378
2878
|
getPausedExecution,
|
|
2379
2879
|
init,
|
|
2380
2880
|
initialValues,
|
|
2881
|
+
isVoiceSupported,
|
|
2381
2882
|
listPausedExecutions,
|
|
2382
2883
|
outputOf,
|
|
2383
2884
|
parseFieldValue,
|
|
@@ -2391,6 +2892,7 @@ export {
|
|
|
2391
2892
|
useAginies,
|
|
2392
2893
|
useApproval,
|
|
2393
2894
|
useChat,
|
|
2394
|
-
useModule
|
|
2895
|
+
useModule,
|
|
2896
|
+
useVoice
|
|
2395
2897
|
};
|
|
2396
2898
|
//# sourceMappingURL=index.js.map
|