fix(voice): 修复语音页黑屏(onToolCall TDZ)
onToolCall 依赖数组引用 pushMessage,但被插在 pushMessage 声明之前 → Cannot access 'pushMessage' before initialization → 渲染崩溃黑屏。 已移到 pushMessage 之后(198→230),与 start/sendGreeting 同类问题一并规避
This commit is contained in:
@@ -151,33 +151,6 @@ export default function VoiceAssistant() {
|
|||||||
console.warn('[vision] 自动问候已发送:', GREETING_TEXT);
|
console.warn('[vision] 自动问候已发送:', GREETING_TEXT);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ── 工具调用执行器:LLM 请求工具 → 转发后端 /api/tools/exec → 结果回传 ──
|
|
||||||
const onToolCall = useCallback(async (e) => {
|
|
||||||
const { name, arguments: argsStr, callId } = e?.detail ?? {};
|
|
||||||
if (!name || !callId) return;
|
|
||||||
let args = {};
|
|
||||||
try { args = JSON.parse(argsStr || '{}'); } catch { /* 忽略非法参数 */ }
|
|
||||||
console.warn(`[tools] LLM 调用工具: ${name}`, args);
|
|
||||||
pushMessage('assistant', `🔧 调用工具:${name}`, false);
|
|
||||||
let output;
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${API_BASE}/api/tools/exec`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ name, args }),
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
output = data?.result ?? JSON.stringify(data);
|
|
||||||
} catch (err) {
|
|
||||||
output = JSON.stringify({ error: String(err) });
|
|
||||||
}
|
|
||||||
const c = clientRef.current;
|
|
||||||
if (c) {
|
|
||||||
c.sendToolOutput(callId, output);
|
|
||||||
c.requestResponse();
|
|
||||||
}
|
|
||||||
}, [pushMessage]);
|
|
||||||
|
|
||||||
// ── 右下角气泡渲染(复刻原版 ui/chat.js)───────────────────────────────
|
// ── 右下角气泡渲染(复刻原版 ui/chat.js)───────────────────────────────
|
||||||
// 流程:新气泡插入 → rAF 加 `.in`(淡入)→ 4s 后加 `.out`(淡出)→ 400ms 移除。
|
// 流程:新气泡插入 → rAF 加 `.in`(淡入)→ 4s 后加 `.out`(淡出)→ 400ms 移除。
|
||||||
// 持续更新的气泡(partial 逐字流)续期 4s;栈上限 8 条,最旧优先淡出,
|
// 持续更新的气泡(partial 逐字流)续期 4s;栈上限 8 条,最旧优先淡出,
|
||||||
@@ -253,6 +226,33 @@ export default function VoiceAssistant() {
|
|||||||
setMessages(next);
|
setMessages(next);
|
||||||
}, [scheduleBubbleDismiss, markLeaving]);
|
}, [scheduleBubbleDismiss, markLeaving]);
|
||||||
|
|
||||||
|
// ── 工具调用执行器:LLM 请求工具 → 转发后端 /api/tools/exec → 结果回传 ──
|
||||||
|
const onToolCall = useCallback(async (e) => {
|
||||||
|
const { name, arguments: argsStr, callId } = e?.detail ?? {};
|
||||||
|
if (!name || !callId) return;
|
||||||
|
let args = {};
|
||||||
|
try { args = JSON.parse(argsStr || '{}'); } catch { /* 忽略非法参数 */ }
|
||||||
|
console.warn(`[tools] LLM 调用工具: ${name}`, args);
|
||||||
|
pushMessage('assistant', `🔧 调用工具:${name}`, false);
|
||||||
|
let output;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE}/api/tools/exec`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ name, args }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
output = data?.result ?? JSON.stringify(data);
|
||||||
|
} catch (err) {
|
||||||
|
output = JSON.stringify({ error: String(err) });
|
||||||
|
}
|
||||||
|
const c = clientRef.current;
|
||||||
|
if (c) {
|
||||||
|
c.sendToolOutput(callId, output);
|
||||||
|
c.requestResponse();
|
||||||
|
}
|
||||||
|
}, [pushMessage]);
|
||||||
|
|
||||||
// 结束会话:淡出全部气泡(原版 teardown 行为)
|
// 结束会话:淡出全部气泡(原版 teardown 行为)
|
||||||
const dismissAllBubbles = useCallback(() => {
|
const dismissAllBubbles = useCallback(() => {
|
||||||
const ids = messagesRef.current.map((m) => m.id);
|
const ids = messagesRef.current.map((m) => m.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user