@apteva/apteva-kit 0.1.129 → 0.1.130
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/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +401 -82
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +402 -83
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
|
|
6
6
|
// src/components/Chat/Chat.tsx
|
|
7
|
-
import { useState as
|
|
7
|
+
import { useState as useState10, useEffect as useEffect10, useRef as useRef10, useMemo as useMemo2, useCallback as useCallback5, forwardRef, useImperativeHandle } from "react";
|
|
8
8
|
|
|
9
9
|
// src/components/Chat/MessageList.tsx
|
|
10
10
|
import { useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
@@ -3370,7 +3370,7 @@ var getSpeechRecognition = () => {
|
|
|
3370
3370
|
if (typeof window === "undefined") return null;
|
|
3371
3371
|
return window.SpeechRecognition || window.webkitSpeechRecognition || null;
|
|
3372
3372
|
};
|
|
3373
|
-
function Composer({ onSendMessage, placeholder = "Type a message...", disabled = false, isLoading = false, onStop, onFileUpload, onSwitchMode, speechToText }) {
|
|
3373
|
+
function Composer({ onSendMessage, placeholder = "Type a message...", disabled = false, isLoading = false, onStop, onFileUpload, onSwitchMode, speechToText, enableVoice = false, voiceState = "idle", voicePartialTranscript = "", voiceDuration = 0, onVoiceStart, onVoiceStop }) {
|
|
3374
3374
|
const [text, setText] = useState6("");
|
|
3375
3375
|
const [showMenu, setShowMenu] = useState6(false);
|
|
3376
3376
|
const [pendingFiles, setPendingFiles] = useState6([]);
|
|
@@ -3663,6 +3663,31 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
|
|
|
3663
3663
|
const gridCols = hasMic ? "auto 1fr auto auto" : "auto 1fr auto";
|
|
3664
3664
|
const gridAreas = isRecording ? '"plus waveform waveform stop"' : isMultiLine ? hasMic ? '"textarea textarea textarea textarea" "plus . mic send"' : '"textarea textarea textarea" "plus . send"' : hasMic ? '"plus textarea mic send"' : '"plus textarea send"';
|
|
3665
3665
|
const gridColsRecording = "auto 1fr auto";
|
|
3666
|
+
const voiceActive = voiceState === "active" || voiceState === "connecting";
|
|
3667
|
+
const formatVoiceDuration = (s) => {
|
|
3668
|
+
const m = Math.floor(s / 60);
|
|
3669
|
+
const sec = s % 60;
|
|
3670
|
+
return `${m}:${sec.toString().padStart(2, "0")}`;
|
|
3671
|
+
};
|
|
3672
|
+
const showMicButton = enableVoice && !text.trim() && pendingFiles.length === 0 && !isLoading && !voiceActive;
|
|
3673
|
+
if (voiceActive) {
|
|
3674
|
+
return /* @__PURE__ */ jsx23("div", { className: "px-4 py-3 relative", children: /* @__PURE__ */ jsxs17("div", { className: "apteva-voice-overlay", children: [
|
|
3675
|
+
/* @__PURE__ */ jsx23("div", { className: "apteva-voice-transcript-area", children: voicePartialTranscript ? /* @__PURE__ */ jsx23("span", { className: "apteva-voice-partial", children: voicePartialTranscript }) : voiceState === "connecting" ? /* @__PURE__ */ jsx23("span", { className: "apteva-voice-connecting", children: "Connecting..." }) : /* @__PURE__ */ jsx23("span", { className: "apteva-voice-listening", children: "Listening..." }) }),
|
|
3676
|
+
/* @__PURE__ */ jsxs17("div", { className: "apteva-voice-controls", children: [
|
|
3677
|
+
/* @__PURE__ */ jsx23("span", { className: "apteva-voice-duration", children: formatVoiceDuration(voiceDuration) }),
|
|
3678
|
+
/* @__PURE__ */ jsx23(
|
|
3679
|
+
"button",
|
|
3680
|
+
{
|
|
3681
|
+
onClick: onVoiceStop,
|
|
3682
|
+
className: "apteva-voice-stop-btn",
|
|
3683
|
+
title: "Stop voice mode",
|
|
3684
|
+
children: /* @__PURE__ */ jsx23("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "3", y: "3", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
|
|
3685
|
+
}
|
|
3686
|
+
)
|
|
3687
|
+
] }),
|
|
3688
|
+
/* @__PURE__ */ jsx23("div", { className: "apteva-voice-indicator", children: /* @__PURE__ */ jsx23("div", { className: `apteva-voice-pulse ${voiceState === "active" ? "apteva-voice-pulse-active" : ""}` }) })
|
|
3689
|
+
] }) });
|
|
3690
|
+
}
|
|
3666
3691
|
return /* @__PURE__ */ jsxs17("div", { className: "px-4 py-3 relative", children: [
|
|
3667
3692
|
fileError && /* @__PURE__ */ jsx23("div", { className: "apteva-file-error", children: /* @__PURE__ */ jsxs17("div", { className: "apteva-file-error-content", children: [
|
|
3668
3693
|
/* @__PURE__ */ jsx23("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx23("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
@@ -3806,6 +3831,18 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
|
|
|
3806
3831
|
title: "Stop generation",
|
|
3807
3832
|
children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
|
|
3808
3833
|
}
|
|
3834
|
+
) : showMicButton ? /* @__PURE__ */ jsx23(
|
|
3835
|
+
"button",
|
|
3836
|
+
{
|
|
3837
|
+
onClick: onVoiceStart,
|
|
3838
|
+
className: "apteva-composer-voice-btn w-8 h-8 rounded-lg flex items-center justify-center transition-all flex-shrink-0",
|
|
3839
|
+
title: "Start voice mode",
|
|
3840
|
+
children: /* @__PURE__ */ jsxs17("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
3841
|
+
/* @__PURE__ */ jsx23("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", fill: "currentColor" }),
|
|
3842
|
+
/* @__PURE__ */ jsx23("path", { d: "M19 10v2a7 7 0 01-14 0v-2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
3843
|
+
/* @__PURE__ */ jsx23("path", { d: "M12 19v4M8 23h8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
3844
|
+
] })
|
|
3845
|
+
}
|
|
3809
3846
|
) : /* @__PURE__ */ jsx23(
|
|
3810
3847
|
"button",
|
|
3811
3848
|
{
|
|
@@ -4366,6 +4403,261 @@ function PersistentWidgetPanel({ widgets, onAction }) {
|
|
|
4366
4403
|
] });
|
|
4367
4404
|
}
|
|
4368
4405
|
|
|
4406
|
+
// src/hooks/useVoiceSession.ts
|
|
4407
|
+
import { useState as useState9, useRef as useRef9, useCallback as useCallback4, useEffect as useEffect9 } from "react";
|
|
4408
|
+
|
|
4409
|
+
// src/utils/audio-utils.ts
|
|
4410
|
+
function float32ToInt16(float32Array) {
|
|
4411
|
+
const int16Array = new Int16Array(float32Array.length);
|
|
4412
|
+
for (let i = 0; i < float32Array.length; i++) {
|
|
4413
|
+
const s = Math.max(-1, Math.min(1, float32Array[i]));
|
|
4414
|
+
int16Array[i] = s < 0 ? s * 32768 : s * 32767;
|
|
4415
|
+
}
|
|
4416
|
+
return int16Array;
|
|
4417
|
+
}
|
|
4418
|
+
function int16ToBase64(int16Array) {
|
|
4419
|
+
const uint8Array = new Uint8Array(int16Array.buffer);
|
|
4420
|
+
let binary = "";
|
|
4421
|
+
for (let i = 0; i < uint8Array.length; i++) {
|
|
4422
|
+
binary += String.fromCharCode(uint8Array[i]);
|
|
4423
|
+
}
|
|
4424
|
+
return btoa(binary);
|
|
4425
|
+
}
|
|
4426
|
+
function base64ToFloat32(base64) {
|
|
4427
|
+
const binaryString = atob(base64);
|
|
4428
|
+
const int16Array = new Int16Array(binaryString.length / 2);
|
|
4429
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
4430
|
+
int16Array[i] = binaryString.charCodeAt(i * 2 + 1) << 8 | binaryString.charCodeAt(i * 2);
|
|
4431
|
+
}
|
|
4432
|
+
const float32Array = new Float32Array(int16Array.length);
|
|
4433
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
4434
|
+
float32Array[i] = int16Array[i] / (int16Array[i] < 0 ? 32768 : 32767);
|
|
4435
|
+
}
|
|
4436
|
+
return float32Array;
|
|
4437
|
+
}
|
|
4438
|
+
function resampleAudio(inputData, inputSampleRate, outputSampleRate) {
|
|
4439
|
+
if (inputSampleRate === outputSampleRate) {
|
|
4440
|
+
return inputData;
|
|
4441
|
+
}
|
|
4442
|
+
const ratio = inputSampleRate / outputSampleRate;
|
|
4443
|
+
const outputLength = Math.floor(inputData.length / ratio);
|
|
4444
|
+
const output = new Float32Array(outputLength);
|
|
4445
|
+
for (let i = 0; i < outputLength; i++) {
|
|
4446
|
+
const srcIndex = i * ratio;
|
|
4447
|
+
const srcIndexFloor = Math.floor(srcIndex);
|
|
4448
|
+
const srcIndexCeil = Math.min(srcIndexFloor + 1, inputData.length - 1);
|
|
4449
|
+
const t = srcIndex - srcIndexFloor;
|
|
4450
|
+
output[i] = inputData[srcIndexFloor] * (1 - t) + inputData[srcIndexCeil] * t;
|
|
4451
|
+
}
|
|
4452
|
+
return output;
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
// src/hooks/useVoiceSession.ts
|
|
4456
|
+
function useVoiceSession(config) {
|
|
4457
|
+
const [state, setState] = useState9("idle");
|
|
4458
|
+
const [partialTranscript, setPartialTranscript] = useState9("");
|
|
4459
|
+
const [duration, setDuration] = useState9(0);
|
|
4460
|
+
const wsRef = useRef9(null);
|
|
4461
|
+
const captureCtxRef = useRef9(null);
|
|
4462
|
+
const playbackCtxRef = useRef9(null);
|
|
4463
|
+
const mediaStreamRef = useRef9(null);
|
|
4464
|
+
const processorRef = useRef9(null);
|
|
4465
|
+
const nextPlayTimeRef = useRef9(0);
|
|
4466
|
+
const durationIntervalRef = useRef9(null);
|
|
4467
|
+
const startTimeRef = useRef9(0);
|
|
4468
|
+
const configRef = useRef9(config);
|
|
4469
|
+
configRef.current = config;
|
|
4470
|
+
const cleanup = useCallback4(() => {
|
|
4471
|
+
if (durationIntervalRef.current) {
|
|
4472
|
+
clearInterval(durationIntervalRef.current);
|
|
4473
|
+
durationIntervalRef.current = null;
|
|
4474
|
+
}
|
|
4475
|
+
if (processorRef.current) {
|
|
4476
|
+
processorRef.current.disconnect();
|
|
4477
|
+
processorRef.current = null;
|
|
4478
|
+
}
|
|
4479
|
+
if (mediaStreamRef.current) {
|
|
4480
|
+
mediaStreamRef.current.getTracks().forEach((t) => t.stop());
|
|
4481
|
+
mediaStreamRef.current = null;
|
|
4482
|
+
}
|
|
4483
|
+
if (captureCtxRef.current) {
|
|
4484
|
+
try {
|
|
4485
|
+
captureCtxRef.current.close();
|
|
4486
|
+
} catch (_) {
|
|
4487
|
+
}
|
|
4488
|
+
captureCtxRef.current = null;
|
|
4489
|
+
}
|
|
4490
|
+
if (playbackCtxRef.current) {
|
|
4491
|
+
try {
|
|
4492
|
+
playbackCtxRef.current.close();
|
|
4493
|
+
} catch (_) {
|
|
4494
|
+
}
|
|
4495
|
+
playbackCtxRef.current = null;
|
|
4496
|
+
}
|
|
4497
|
+
if (wsRef.current) {
|
|
4498
|
+
try {
|
|
4499
|
+
wsRef.current.close();
|
|
4500
|
+
} catch (_) {
|
|
4501
|
+
}
|
|
4502
|
+
wsRef.current = null;
|
|
4503
|
+
}
|
|
4504
|
+
nextPlayTimeRef.current = 0;
|
|
4505
|
+
setPartialTranscript("");
|
|
4506
|
+
setDuration(0);
|
|
4507
|
+
}, []);
|
|
4508
|
+
useEffect9(() => {
|
|
4509
|
+
return () => {
|
|
4510
|
+
cleanup();
|
|
4511
|
+
};
|
|
4512
|
+
}, [cleanup]);
|
|
4513
|
+
const playAudioChunk = useCallback4((base64Audio) => {
|
|
4514
|
+
if (!playbackCtxRef.current) {
|
|
4515
|
+
playbackCtxRef.current = new AudioContext({ sampleRate: 24e3 });
|
|
4516
|
+
}
|
|
4517
|
+
const ctx = playbackCtxRef.current;
|
|
4518
|
+
if (ctx.state === "suspended") {
|
|
4519
|
+
ctx.resume();
|
|
4520
|
+
}
|
|
4521
|
+
const float32Data = base64ToFloat32(base64Audio);
|
|
4522
|
+
const audioBuffer = ctx.createBuffer(1, float32Data.length, 24e3);
|
|
4523
|
+
audioBuffer.getChannelData(0).set(float32Data);
|
|
4524
|
+
const source = ctx.createBufferSource();
|
|
4525
|
+
source.buffer = audioBuffer;
|
|
4526
|
+
source.connect(ctx.destination);
|
|
4527
|
+
const currentTime = ctx.currentTime;
|
|
4528
|
+
const startTime = Math.max(currentTime, nextPlayTimeRef.current);
|
|
4529
|
+
source.start(startTime);
|
|
4530
|
+
nextPlayTimeRef.current = startTime + audioBuffer.duration;
|
|
4531
|
+
}, []);
|
|
4532
|
+
const handleMessage = useCallback4((msg) => {
|
|
4533
|
+
const cfg = configRef.current;
|
|
4534
|
+
switch (msg.type) {
|
|
4535
|
+
case "session_created":
|
|
4536
|
+
setState("active");
|
|
4537
|
+
startTimeRef.current = Date.now();
|
|
4538
|
+
durationIntervalRef.current = setInterval(() => {
|
|
4539
|
+
setDuration(Math.floor((Date.now() - startTimeRef.current) / 1e3));
|
|
4540
|
+
}, 1e3);
|
|
4541
|
+
break;
|
|
4542
|
+
case "audio_delta":
|
|
4543
|
+
if (msg.data?.chunk) {
|
|
4544
|
+
playAudioChunk(msg.data.chunk);
|
|
4545
|
+
}
|
|
4546
|
+
break;
|
|
4547
|
+
case "transcript":
|
|
4548
|
+
if (msg.data) {
|
|
4549
|
+
if (msg.data.partial) {
|
|
4550
|
+
setPartialTranscript(msg.data.content);
|
|
4551
|
+
} else {
|
|
4552
|
+
setPartialTranscript("");
|
|
4553
|
+
cfg.onTranscript?.({
|
|
4554
|
+
id: `vt-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
4555
|
+
role: msg.data.role,
|
|
4556
|
+
content: msg.data.content,
|
|
4557
|
+
partial: false,
|
|
4558
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
4559
|
+
});
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
break;
|
|
4563
|
+
case "tool_call":
|
|
4564
|
+
if (msg.data) {
|
|
4565
|
+
nextPlayTimeRef.current = 0;
|
|
4566
|
+
cfg.onTranscript?.({
|
|
4567
|
+
id: `vt-tool-${Date.now()}`,
|
|
4568
|
+
role: "system",
|
|
4569
|
+
content: `Using ${msg.data.name}...`,
|
|
4570
|
+
partial: false,
|
|
4571
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
4572
|
+
});
|
|
4573
|
+
}
|
|
4574
|
+
break;
|
|
4575
|
+
case "tool_result":
|
|
4576
|
+
if (msg.data) {
|
|
4577
|
+
nextPlayTimeRef.current = 0;
|
|
4578
|
+
}
|
|
4579
|
+
break;
|
|
4580
|
+
case "error":
|
|
4581
|
+
setState("error");
|
|
4582
|
+
cfg.onError?.(new Error(msg.data?.message || "Voice session error"));
|
|
4583
|
+
break;
|
|
4584
|
+
}
|
|
4585
|
+
}, [playAudioChunk]);
|
|
4586
|
+
const startCapture = useCallback4(async () => {
|
|
4587
|
+
const ws = wsRef.current;
|
|
4588
|
+
if (!ws) return;
|
|
4589
|
+
try {
|
|
4590
|
+
captureCtxRef.current = new AudioContext();
|
|
4591
|
+
const nativeSampleRate = captureCtxRef.current.sampleRate;
|
|
4592
|
+
mediaStreamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
4593
|
+
const source = captureCtxRef.current.createMediaStreamSource(mediaStreamRef.current);
|
|
4594
|
+
processorRef.current = captureCtxRef.current.createScriptProcessor(2048, 1, 1);
|
|
4595
|
+
processorRef.current.onaudioprocess = (e) => {
|
|
4596
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
4597
|
+
const inputData = e.inputBuffer.getChannelData(0);
|
|
4598
|
+
const resampledData = resampleAudio(inputData, nativeSampleRate, 16e3);
|
|
4599
|
+
const int16Data = float32ToInt16(resampledData);
|
|
4600
|
+
const base64Data = int16ToBase64(int16Data);
|
|
4601
|
+
ws.send(JSON.stringify({
|
|
4602
|
+
type: "audio",
|
|
4603
|
+
data: { chunk: base64Data }
|
|
4604
|
+
}));
|
|
4605
|
+
};
|
|
4606
|
+
source.connect(processorRef.current);
|
|
4607
|
+
processorRef.current.connect(captureCtxRef.current.destination);
|
|
4608
|
+
} catch (e) {
|
|
4609
|
+
configRef.current.onError?.(new Error("Microphone access denied"));
|
|
4610
|
+
cleanup();
|
|
4611
|
+
setState("idle");
|
|
4612
|
+
}
|
|
4613
|
+
}, [cleanup]);
|
|
4614
|
+
const start = useCallback4(() => {
|
|
4615
|
+
if (state !== "idle") return;
|
|
4616
|
+
setState("connecting");
|
|
4617
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
4618
|
+
const wsUrl = `${protocol}//${window.location.host}${config.apiUrl}/voice`;
|
|
4619
|
+
const ws = new WebSocket(wsUrl);
|
|
4620
|
+
wsRef.current = ws;
|
|
4621
|
+
ws.onopen = () => {
|
|
4622
|
+
const provider = configRef.current.provider || "openai";
|
|
4623
|
+
const voice = configRef.current.voice || "ash";
|
|
4624
|
+
ws.send(JSON.stringify({
|
|
4625
|
+
type: "start",
|
|
4626
|
+
data: { provider, voice }
|
|
4627
|
+
}));
|
|
4628
|
+
startCapture();
|
|
4629
|
+
};
|
|
4630
|
+
ws.onmessage = (event) => {
|
|
4631
|
+
try {
|
|
4632
|
+
const msg = JSON.parse(event.data);
|
|
4633
|
+
handleMessage(msg);
|
|
4634
|
+
} catch (_) {
|
|
4635
|
+
}
|
|
4636
|
+
};
|
|
4637
|
+
ws.onerror = () => {
|
|
4638
|
+
setState("error");
|
|
4639
|
+
configRef.current.onError?.(new Error("WebSocket connection failed"));
|
|
4640
|
+
};
|
|
4641
|
+
ws.onclose = () => {
|
|
4642
|
+
cleanup();
|
|
4643
|
+
setState("idle");
|
|
4644
|
+
};
|
|
4645
|
+
}, [state, config.apiUrl, startCapture, handleMessage, cleanup]);
|
|
4646
|
+
const stop = useCallback4(() => {
|
|
4647
|
+
cleanup();
|
|
4648
|
+
setState("idle");
|
|
4649
|
+
}, [cleanup]);
|
|
4650
|
+
const sendText = useCallback4((text) => {
|
|
4651
|
+
const ws = wsRef.current;
|
|
4652
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
4653
|
+
ws.send(JSON.stringify({
|
|
4654
|
+
type: "text",
|
|
4655
|
+
data: { content: text }
|
|
4656
|
+
}));
|
|
4657
|
+
}, []);
|
|
4658
|
+
return { state, partialTranscript, duration, start, stop, sendText };
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4369
4661
|
// src/components/Chat/Chat.tsx
|
|
4370
4662
|
import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
4371
4663
|
var Chat = forwardRef(function Chat2({
|
|
@@ -4418,28 +4710,49 @@ var Chat = forwardRef(function Chat2({
|
|
|
4418
4710
|
onWidgetRender,
|
|
4419
4711
|
// Speech to text
|
|
4420
4712
|
speechToText,
|
|
4713
|
+
// Realtime voice
|
|
4714
|
+
enableVoice = false,
|
|
4715
|
+
voiceProvider,
|
|
4716
|
+
voiceId,
|
|
4421
4717
|
className
|
|
4422
4718
|
}, ref) {
|
|
4423
|
-
const [messages, setMessages] =
|
|
4424
|
-
const [isLoading, setIsLoading] =
|
|
4425
|
-
const [currentThreadId, setCurrentThreadId] =
|
|
4426
|
-
const [mode, setMode] =
|
|
4427
|
-
const [chatToolName, setChatToolName] =
|
|
4428
|
-
const [commandState, setCommandState] =
|
|
4429
|
-
const [commandResult, setCommandResult] =
|
|
4430
|
-
const [commandError, setCommandError] =
|
|
4431
|
-
const [progress, setProgress] =
|
|
4432
|
-
const [commandInput, setCommandInput] =
|
|
4433
|
-
const [streamedContent, setStreamedContent] =
|
|
4434
|
-
const [currentToolName, setCurrentToolName] =
|
|
4435
|
-
const [currentRequestId, setCurrentRequestId] =
|
|
4436
|
-
const [plan, setPlan] =
|
|
4437
|
-
const [pendingCommand, setPendingCommand] =
|
|
4438
|
-
const [internalPlanMode, setInternalPlanMode] =
|
|
4439
|
-
const [showSettingsMenu, setShowSettingsMenu] =
|
|
4440
|
-
const fileInputRef =
|
|
4441
|
-
const
|
|
4442
|
-
|
|
4719
|
+
const [messages, setMessages] = useState10(initialMessages);
|
|
4720
|
+
const [isLoading, setIsLoading] = useState10(false);
|
|
4721
|
+
const [currentThreadId, setCurrentThreadId] = useState10(threadId || null);
|
|
4722
|
+
const [mode, setMode] = useState10(initialMode);
|
|
4723
|
+
const [chatToolName, setChatToolName] = useState10(null);
|
|
4724
|
+
const [commandState, setCommandState] = useState10("idle");
|
|
4725
|
+
const [commandResult, setCommandResult] = useState10(null);
|
|
4726
|
+
const [commandError, setCommandError] = useState10(null);
|
|
4727
|
+
const [progress, setProgress] = useState10(0);
|
|
4728
|
+
const [commandInput, setCommandInput] = useState10("");
|
|
4729
|
+
const [streamedContent, setStreamedContent] = useState10("");
|
|
4730
|
+
const [currentToolName, setCurrentToolName] = useState10(null);
|
|
4731
|
+
const [currentRequestId, setCurrentRequestId] = useState10(null);
|
|
4732
|
+
const [plan, setPlan] = useState10("");
|
|
4733
|
+
const [pendingCommand, setPendingCommand] = useState10("");
|
|
4734
|
+
const [internalPlanMode, setInternalPlanMode] = useState10(planMode);
|
|
4735
|
+
const [showSettingsMenu, setShowSettingsMenu] = useState10(false);
|
|
4736
|
+
const fileInputRef = useRef10(null);
|
|
4737
|
+
const handleVoiceTranscript = useCallback5((entry) => {
|
|
4738
|
+
const msg = {
|
|
4739
|
+
id: entry.id,
|
|
4740
|
+
role: entry.role === "system" ? "assistant" : entry.role,
|
|
4741
|
+
content: entry.content,
|
|
4742
|
+
timestamp: entry.timestamp,
|
|
4743
|
+
metadata: entry.role === "system" ? { isVoiceSystem: true } : { isVoice: true }
|
|
4744
|
+
};
|
|
4745
|
+
setMessages((prev) => [...prev, msg]);
|
|
4746
|
+
}, []);
|
|
4747
|
+
const voice = useVoiceSession({
|
|
4748
|
+
apiUrl: apiUrl || "",
|
|
4749
|
+
provider: voiceProvider,
|
|
4750
|
+
voice: voiceId,
|
|
4751
|
+
onTranscript: handleVoiceTranscript,
|
|
4752
|
+
onError
|
|
4753
|
+
});
|
|
4754
|
+
const [persistentWidgets, setPersistentWidgets] = useState10(/* @__PURE__ */ new Map());
|
|
4755
|
+
const updatePersistentWidgets = useCallback5((msgs) => {
|
|
4443
4756
|
setPersistentWidgets((prev) => {
|
|
4444
4757
|
const next = new Map(prev);
|
|
4445
4758
|
let changed = false;
|
|
@@ -4457,12 +4770,12 @@ var Chat = forwardRef(function Chat2({
|
|
|
4457
4770
|
return changed ? next : prev;
|
|
4458
4771
|
});
|
|
4459
4772
|
}, []);
|
|
4460
|
-
|
|
4773
|
+
useEffect10(() => {
|
|
4461
4774
|
updatePersistentWidgets(messages);
|
|
4462
4775
|
}, [messages, updatePersistentWidgets]);
|
|
4463
4776
|
const persistentWidgetList = useMemo2(() => Array.from(persistentWidgets.values()), [persistentWidgets]);
|
|
4464
4777
|
const persistentWidgetIds = useMemo2(() => new Set(persistentWidgets.keys()), [persistentWidgets]);
|
|
4465
|
-
const handleSendMessageRef =
|
|
4778
|
+
const handleSendMessageRef = useRef10(null);
|
|
4466
4779
|
useImperativeHandle(ref, () => ({
|
|
4467
4780
|
sendMessage: async (text) => {
|
|
4468
4781
|
if (handleSendMessageRef.current) {
|
|
@@ -4483,7 +4796,7 @@ var Chat = forwardRef(function Chat2({
|
|
|
4483
4796
|
return context ? `${context}
|
|
4484
4797
|
${widgetContext}` : widgetContext;
|
|
4485
4798
|
}, [context, enableWidgets, availableWidgets, compactWidgetContext]);
|
|
4486
|
-
|
|
4799
|
+
useEffect10(() => {
|
|
4487
4800
|
if (apiUrl || apiKey) {
|
|
4488
4801
|
aptevaClient.configure({
|
|
4489
4802
|
...apiUrl && { apiUrl },
|
|
@@ -4491,15 +4804,15 @@ ${widgetContext}` : widgetContext;
|
|
|
4491
4804
|
});
|
|
4492
4805
|
}
|
|
4493
4806
|
}, [apiUrl, apiKey]);
|
|
4494
|
-
|
|
4807
|
+
useEffect10(() => {
|
|
4495
4808
|
if (threadId) {
|
|
4496
4809
|
onThreadChange?.(threadId);
|
|
4497
4810
|
}
|
|
4498
4811
|
}, [threadId, onThreadChange]);
|
|
4499
|
-
|
|
4812
|
+
useEffect10(() => {
|
|
4500
4813
|
setInternalPlanMode(planMode);
|
|
4501
4814
|
}, [planMode]);
|
|
4502
|
-
|
|
4815
|
+
useEffect10(() => {
|
|
4503
4816
|
const handleClickOutside = (event) => {
|
|
4504
4817
|
const target = event.target;
|
|
4505
4818
|
if (showSettingsMenu && !target.closest(".settings-menu-container")) {
|
|
@@ -4519,7 +4832,7 @@ ${widgetContext}` : widgetContext;
|
|
|
4519
4832
|
}
|
|
4520
4833
|
};
|
|
4521
4834
|
const defaultPlaceholder = mode === "chat" ? "Type a message..." : "Enter your command...";
|
|
4522
|
-
const handleWidgetAction =
|
|
4835
|
+
const handleWidgetAction = useCallback5((action) => {
|
|
4523
4836
|
onAction?.(action);
|
|
4524
4837
|
if (action.type === "submit" && action.payload?.formData) {
|
|
4525
4838
|
const formData = action.payload.formData;
|
|
@@ -5040,8 +5353,8 @@ ${planToExecute}`;
|
|
|
5040
5353
|
/* @__PURE__ */ jsx26("div", { className: "apteva-chat-title", children: headerTitle }),
|
|
5041
5354
|
/* @__PURE__ */ jsx26("div", { className: cn(
|
|
5042
5355
|
"apteva-chat-status",
|
|
5043
|
-
isLoading ? chatToolName ? "apteva-chat-status-tool" : "apteva-chat-status-thinking" : "apteva-chat-status-ready"
|
|
5044
|
-
), children: isLoading ? chatToolName ? `Using ${chatToolName}...` : "Thinking..." : "Ready" })
|
|
5356
|
+
voice.state === "active" ? "apteva-chat-status-voice" : voice.state === "connecting" ? "apteva-chat-status-thinking" : isLoading ? chatToolName ? "apteva-chat-status-tool" : "apteva-chat-status-thinking" : "apteva-chat-status-ready"
|
|
5357
|
+
), children: voice.state === "active" ? "Voice active" : voice.state === "connecting" ? "Connecting voice..." : isLoading ? chatToolName ? `Using ${chatToolName}...` : "Thinking..." : "Ready" })
|
|
5045
5358
|
] })
|
|
5046
5359
|
] }) }),
|
|
5047
5360
|
mode === "chat" && /* @__PURE__ */ jsxs20(Fragment6, { children: [
|
|
@@ -5074,7 +5387,13 @@ ${planToExecute}`;
|
|
|
5074
5387
|
onStop: handleStop,
|
|
5075
5388
|
onFileUpload,
|
|
5076
5389
|
onSwitchMode: showModeToggle ? () => handleModeChange("command") : void 0,
|
|
5077
|
-
speechToText
|
|
5390
|
+
speechToText,
|
|
5391
|
+
enableVoice,
|
|
5392
|
+
voiceState: voice.state,
|
|
5393
|
+
voicePartialTranscript: voice.partialTranscript,
|
|
5394
|
+
voiceDuration: voice.duration,
|
|
5395
|
+
onVoiceStart: voice.start,
|
|
5396
|
+
onVoiceStop: voice.stop
|
|
5078
5397
|
}
|
|
5079
5398
|
)
|
|
5080
5399
|
] }),
|
|
@@ -5117,11 +5436,11 @@ ${planToExecute}`;
|
|
|
5117
5436
|
});
|
|
5118
5437
|
|
|
5119
5438
|
// src/components/Chat/CommandOutput.tsx
|
|
5120
|
-
import { useState as
|
|
5439
|
+
import { useState as useState11 } from "react";
|
|
5121
5440
|
import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
5122
5441
|
|
|
5123
5442
|
// src/components/Command/Command.tsx
|
|
5124
|
-
import React, { useState as
|
|
5443
|
+
import React, { useState as useState12, useEffect as useEffect11 } from "react";
|
|
5125
5444
|
import { Fragment as Fragment7, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
5126
5445
|
function Command({
|
|
5127
5446
|
agentId,
|
|
@@ -5149,28 +5468,28 @@ function Command({
|
|
|
5149
5468
|
resultRenderer,
|
|
5150
5469
|
className
|
|
5151
5470
|
}) {
|
|
5152
|
-
const [state, setState] =
|
|
5153
|
-
const [result, setResult] =
|
|
5154
|
-
const [error, setError] =
|
|
5155
|
-
const [progress, setProgress] =
|
|
5156
|
-
const [command, setCommand] =
|
|
5157
|
-
const [streamedContent, setStreamedContent] =
|
|
5158
|
-
const [plan, setPlan] =
|
|
5159
|
-
const [pendingCommand, setPendingCommand] =
|
|
5160
|
-
const [showPlanDetails, setShowPlanDetails] =
|
|
5161
|
-
const [uploadedFiles, setUploadedFiles] =
|
|
5162
|
-
const [showSettingsMenu, setShowSettingsMenu] =
|
|
5163
|
-
const [internalPlanMode, setInternalPlanMode] =
|
|
5471
|
+
const [state, setState] = useState12("idle");
|
|
5472
|
+
const [result, setResult] = useState12(null);
|
|
5473
|
+
const [error, setError] = useState12(null);
|
|
5474
|
+
const [progress, setProgress] = useState12(0);
|
|
5475
|
+
const [command, setCommand] = useState12(initialCommand || "");
|
|
5476
|
+
const [streamedContent, setStreamedContent] = useState12("");
|
|
5477
|
+
const [plan, setPlan] = useState12("");
|
|
5478
|
+
const [pendingCommand, setPendingCommand] = useState12("");
|
|
5479
|
+
const [showPlanDetails, setShowPlanDetails] = useState12(false);
|
|
5480
|
+
const [uploadedFiles, setUploadedFiles] = useState12([]);
|
|
5481
|
+
const [showSettingsMenu, setShowSettingsMenu] = useState12(false);
|
|
5482
|
+
const [internalPlanMode, setInternalPlanMode] = useState12(planMode);
|
|
5164
5483
|
const fileInputRef = React.useRef(null);
|
|
5165
|
-
|
|
5484
|
+
useEffect11(() => {
|
|
5166
5485
|
if (autoExecute && state === "idle" && command) {
|
|
5167
5486
|
executeCommand();
|
|
5168
5487
|
}
|
|
5169
5488
|
}, [autoExecute]);
|
|
5170
|
-
|
|
5489
|
+
useEffect11(() => {
|
|
5171
5490
|
setInternalPlanMode(planMode);
|
|
5172
5491
|
}, [planMode]);
|
|
5173
|
-
|
|
5492
|
+
useEffect11(() => {
|
|
5174
5493
|
const handleClickOutside = (event) => {
|
|
5175
5494
|
const target = event.target;
|
|
5176
5495
|
if (showSettingsMenu && !target.closest(".settings-menu-container")) {
|
|
@@ -6092,7 +6411,7 @@ ${planToExecute}`;
|
|
|
6092
6411
|
}
|
|
6093
6412
|
|
|
6094
6413
|
// src/components/Prompt/Prompt.tsx
|
|
6095
|
-
import { useState as
|
|
6414
|
+
import { useState as useState13 } from "react";
|
|
6096
6415
|
import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
6097
6416
|
function Prompt({
|
|
6098
6417
|
agentId,
|
|
@@ -6110,9 +6429,9 @@ function Prompt({
|
|
|
6110
6429
|
showSuggestions = false,
|
|
6111
6430
|
className
|
|
6112
6431
|
}) {
|
|
6113
|
-
const [value, setValue] =
|
|
6114
|
-
const [isLoading, setIsLoading] =
|
|
6115
|
-
const [suggestions] =
|
|
6432
|
+
const [value, setValue] = useState13(initialValue);
|
|
6433
|
+
const [isLoading, setIsLoading] = useState13(false);
|
|
6434
|
+
const [suggestions] = useState13(["Plan a trip", "Write a description", "Analyze data"]);
|
|
6116
6435
|
const handleChange = (e) => {
|
|
6117
6436
|
const newValue = e.target.value;
|
|
6118
6437
|
if (!maxLength || newValue.length <= maxLength) {
|
|
@@ -6203,7 +6522,7 @@ function Prompt({
|
|
|
6203
6522
|
}
|
|
6204
6523
|
|
|
6205
6524
|
// src/components/Stream/Stream.tsx
|
|
6206
|
-
import { useState as
|
|
6525
|
+
import { useState as useState14, useEffect as useEffect12 } from "react";
|
|
6207
6526
|
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
6208
6527
|
function Stream({
|
|
6209
6528
|
agentId,
|
|
@@ -6220,10 +6539,10 @@ function Stream({
|
|
|
6220
6539
|
typingSpeed = 30,
|
|
6221
6540
|
className
|
|
6222
6541
|
}) {
|
|
6223
|
-
const [text, setText] =
|
|
6224
|
-
const [isStreaming, setIsStreaming] =
|
|
6225
|
-
const [isComplete, setIsComplete] =
|
|
6226
|
-
|
|
6542
|
+
const [text, setText] = useState14("");
|
|
6543
|
+
const [isStreaming, setIsStreaming] = useState14(false);
|
|
6544
|
+
const [isComplete, setIsComplete] = useState14(false);
|
|
6545
|
+
useEffect12(() => {
|
|
6227
6546
|
if (autoStart && !isStreaming && !isComplete) {
|
|
6228
6547
|
startStreaming();
|
|
6229
6548
|
}
|
|
@@ -6300,7 +6619,7 @@ function Stream({
|
|
|
6300
6619
|
}
|
|
6301
6620
|
|
|
6302
6621
|
// src/components/Threads/ThreadList.tsx
|
|
6303
|
-
import { useState as
|
|
6622
|
+
import { useState as useState15 } from "react";
|
|
6304
6623
|
|
|
6305
6624
|
// src/components/Threads/ThreadItem.tsx
|
|
6306
6625
|
import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
@@ -6364,7 +6683,7 @@ function ThreadList({
|
|
|
6364
6683
|
showSearch = false,
|
|
6365
6684
|
groupBy = "none"
|
|
6366
6685
|
}) {
|
|
6367
|
-
const [searchQuery, setSearchQuery] =
|
|
6686
|
+
const [searchQuery, setSearchQuery] = useState15("");
|
|
6368
6687
|
const filteredThreads = threads.filter(
|
|
6369
6688
|
(thread) => thread.title.toLowerCase().includes(searchQuery.toLowerCase()) || thread.preview?.toLowerCase().includes(searchQuery.toLowerCase())
|
|
6370
6689
|
);
|
|
@@ -6494,10 +6813,10 @@ function Threads({
|
|
|
6494
6813
|
}
|
|
6495
6814
|
|
|
6496
6815
|
// src/components/AutoInterface/AutoInterface.tsx
|
|
6497
|
-
import { useState as
|
|
6816
|
+
import { useState as useState17, useRef as useRef11, useCallback as useCallback6, useEffect as useEffect13 } from "react";
|
|
6498
6817
|
|
|
6499
6818
|
// src/components/AutoInterface/LayoutRenderer.tsx
|
|
6500
|
-
import { useState as
|
|
6819
|
+
import { useState as useState16 } from "react";
|
|
6501
6820
|
import { Fragment as Fragment8, jsx as jsx34, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
6502
6821
|
var gapClasses = {
|
|
6503
6822
|
none: "gap-0",
|
|
@@ -6613,7 +6932,7 @@ function SidebarLayout({ node, renderNode }) {
|
|
|
6613
6932
|
function TabsLayout({ node, renderNode }) {
|
|
6614
6933
|
const { labels = [], defaultTab = 0 } = node.props || {};
|
|
6615
6934
|
const children = node.children || [];
|
|
6616
|
-
const [activeTab, setActiveTab] =
|
|
6935
|
+
const [activeTab, setActiveTab] = useState16(defaultTab);
|
|
6617
6936
|
return /* @__PURE__ */ jsxs28("div", { children: [
|
|
6618
6937
|
/* @__PURE__ */ jsx34("div", { className: "flex border-b border-neutral-200 dark:border-neutral-700 mb-4", children: labels.map((label, idx) => /* @__PURE__ */ jsx34(
|
|
6619
6938
|
"button",
|
|
@@ -6764,19 +7083,19 @@ function AutoInterface({
|
|
|
6764
7083
|
theme,
|
|
6765
7084
|
className
|
|
6766
7085
|
}) {
|
|
6767
|
-
const [interfaceSpec, setInterfaceSpec] =
|
|
6768
|
-
const [isGenerating, setIsGenerating] =
|
|
6769
|
-
const [chatCollapsed, setChatCollapsed] =
|
|
6770
|
-
const chatRef =
|
|
7086
|
+
const [interfaceSpec, setInterfaceSpec] = useState17(initialInterface || null);
|
|
7087
|
+
const [isGenerating, setIsGenerating] = useState17(false);
|
|
7088
|
+
const [chatCollapsed, setChatCollapsed] = useState17(false);
|
|
7089
|
+
const chatRef = useRef11(null);
|
|
6771
7090
|
const systemContext = [
|
|
6772
7091
|
generateInterfaceContext(),
|
|
6773
7092
|
context || ""
|
|
6774
7093
|
].filter(Boolean).join("\n\n");
|
|
6775
|
-
const updateInterface =
|
|
7094
|
+
const updateInterface = useCallback6((newSpec) => {
|
|
6776
7095
|
setInterfaceSpec(newSpec);
|
|
6777
7096
|
onInterfaceChange?.(newSpec);
|
|
6778
7097
|
}, [onInterfaceChange]);
|
|
6779
|
-
const handleAction =
|
|
7098
|
+
const handleAction = useCallback6((action) => {
|
|
6780
7099
|
onAction?.(action);
|
|
6781
7100
|
if (chatRef.current) {
|
|
6782
7101
|
chatRef.current.sendMessage(
|
|
@@ -6784,7 +7103,7 @@ function AutoInterface({
|
|
|
6784
7103
|
);
|
|
6785
7104
|
}
|
|
6786
7105
|
}, [onAction]);
|
|
6787
|
-
const handleMessageComplete =
|
|
7106
|
+
const handleMessageComplete = useCallback6((result) => {
|
|
6788
7107
|
if (!result?.data) return;
|
|
6789
7108
|
const text = typeof result.data === "string" ? result.data : result.data.message || "";
|
|
6790
7109
|
console.log("[AutoInterface] Chat message complete, text (" + text.length + " chars):", text.substring(0, 300));
|
|
@@ -6805,7 +7124,7 @@ function AutoInterface({
|
|
|
6805
7124
|
}
|
|
6806
7125
|
setIsGenerating(false);
|
|
6807
7126
|
}, [interfaceSpec, updateInterface]);
|
|
6808
|
-
|
|
7127
|
+
useEffect13(() => {
|
|
6809
7128
|
if (!initialPrompt || initialInterface || useMock) return;
|
|
6810
7129
|
if (!apiUrl) return;
|
|
6811
7130
|
let cancelled = false;
|
|
@@ -6930,29 +7249,29 @@ function getThemeScript() {
|
|
|
6930
7249
|
}
|
|
6931
7250
|
|
|
6932
7251
|
// src/hooks/useInterfaceState.ts
|
|
6933
|
-
import { useState as
|
|
7252
|
+
import { useState as useState18, useCallback as useCallback7 } from "react";
|
|
6934
7253
|
function useInterfaceState(initialSpec) {
|
|
6935
|
-
const [spec, setSpec] =
|
|
6936
|
-
const [isStreaming, setIsStreaming] =
|
|
6937
|
-
const setInterface =
|
|
7254
|
+
const [spec, setSpec] = useState18(initialSpec || null);
|
|
7255
|
+
const [isStreaming, setIsStreaming] = useState18(false);
|
|
7256
|
+
const setInterface = useCallback7((newSpec) => {
|
|
6938
7257
|
setSpec(newSpec);
|
|
6939
7258
|
}, []);
|
|
6940
|
-
const clearInterface =
|
|
7259
|
+
const clearInterface = useCallback7(() => {
|
|
6941
7260
|
setSpec(null);
|
|
6942
7261
|
}, []);
|
|
6943
|
-
const applyInterfaceUpdate =
|
|
7262
|
+
const applyInterfaceUpdate = useCallback7((update) => {
|
|
6944
7263
|
setSpec((prev) => {
|
|
6945
7264
|
if (!prev) return prev;
|
|
6946
7265
|
return applyUpdate(prev, update);
|
|
6947
7266
|
});
|
|
6948
7267
|
}, []);
|
|
6949
|
-
const applyInterfaceUpdates =
|
|
7268
|
+
const applyInterfaceUpdates = useCallback7((updates) => {
|
|
6950
7269
|
setSpec((prev) => {
|
|
6951
7270
|
if (!prev) return prev;
|
|
6952
7271
|
return applyUpdates(prev, updates);
|
|
6953
7272
|
});
|
|
6954
7273
|
}, []);
|
|
6955
|
-
const getNode =
|
|
7274
|
+
const getNode = useCallback7((id) => {
|
|
6956
7275
|
if (!spec) return null;
|
|
6957
7276
|
return findNode(spec.root, id);
|
|
6958
7277
|
}, [spec]);
|
|
@@ -6969,7 +7288,7 @@ function useInterfaceState(initialSpec) {
|
|
|
6969
7288
|
}
|
|
6970
7289
|
|
|
6971
7290
|
// src/hooks/useInterfaceAI.ts
|
|
6972
|
-
import { useCallback as
|
|
7291
|
+
import { useCallback as useCallback8, useRef as useRef12 } from "react";
|
|
6973
7292
|
function useInterfaceAI({
|
|
6974
7293
|
agentId,
|
|
6975
7294
|
apiUrl,
|
|
@@ -6981,15 +7300,15 @@ function useInterfaceAI({
|
|
|
6981
7300
|
onStreamStart,
|
|
6982
7301
|
onStreamEnd
|
|
6983
7302
|
}) {
|
|
6984
|
-
const threadIdRef =
|
|
6985
|
-
const accumulatedTextRef =
|
|
7303
|
+
const threadIdRef = useRef12(null);
|
|
7304
|
+
const accumulatedTextRef = useRef12("");
|
|
6986
7305
|
if (apiUrl || apiKey) {
|
|
6987
7306
|
aptevaClient.configure({
|
|
6988
7307
|
...apiUrl && { apiUrl },
|
|
6989
7308
|
...apiKey && { apiKey }
|
|
6990
7309
|
});
|
|
6991
7310
|
}
|
|
6992
|
-
const sendMessage =
|
|
7311
|
+
const sendMessage = useCallback8(async (message) => {
|
|
6993
7312
|
accumulatedTextRef.current = "";
|
|
6994
7313
|
onStreamStart?.();
|
|
6995
7314
|
const systemPrompt = [
|