重构交互类和展示类工具流程
This commit is contained in:
1 parent
9acb38c9e6
commit
2a0875452f
33 files changed
+2645
-3534
No files matched your search
+16
-86
@@ -1,103 +1,33 @@
|
||||
// pending.js -- 待确认决策处理(卡片渲染已迁移至 pending-overlay.js)
|
||||
// pending.js -- 人工请求决策提交(卡片渲染在 pending-overlay.js,数据源为 live snapshot)
|
||||
|
||||
import { $ } from './utils.js';
|
||||
import { api } from './api.js';
|
||||
import { startPollLiveToolCalls } from './toolcalls.js';
|
||||
import { state } from './state.js';
|
||||
import { updatePendingOverlay } from './pending-overlay.js';
|
||||
|
||||
let renderSessionsFn = null;
|
||||
let appendThinkingIndicatorFn = null;
|
||||
let renderMessagesFn = null;
|
||||
let streamAssistantMessageFn = null;
|
||||
let injectLiveToolCallsFn = null;
|
||||
let scrollChatToBottomFn = null;
|
||||
|
||||
export function setRenderFnDeps(deps) {
|
||||
renderSessionsFn = deps.renderSessions;
|
||||
appendThinkingIndicatorFn = deps.appendThinkingIndicator;
|
||||
renderMessagesFn = deps.renderMessages;
|
||||
streamAssistantMessageFn = deps.streamAssistantMessage;
|
||||
injectLiveToolCallsFn = deps.injectLiveToolCalls;
|
||||
scrollChatToBottomFn = deps.scrollChatToBottom;
|
||||
}
|
||||
|
||||
async function handleHumanAction(approvalId, decision, instruction, card) {
|
||||
if (!approvalId || state.sending) return;
|
||||
state.sending = true;
|
||||
/**
|
||||
* 提交一个等待中的人工请求的决策。
|
||||
*
|
||||
* 阻塞式交互模型下,本函数只负责把答案交给后端:
|
||||
* 后端写入等待注册表并唤醒阻塞中的工具,图在后台线程自然继续;
|
||||
* 浮窗由下一次 live snapshot 自动移除,最终结果由
|
||||
* /api/chat/complete 或 /api/chat/stream 返回——无需重渲染消息、
|
||||
* 无需重启 live 流、更不会清空已积累的工具调用列表。
|
||||
*/
|
||||
async function handleHumanAction(requestId, decision, instruction, card) {
|
||||
if (!requestId || state.sending) return;
|
||||
state.sending = true; // 本轮仍在后台运行,保持发送按钮禁用
|
||||
const sendBtn = $("sendBtn");
|
||||
if (sendBtn) sendBtn.disabled = true;
|
||||
if (card) card.classList.add("pending-action-card--resolved");
|
||||
|
||||
// 复用 sendChat 留下的现有 thinking 气泡(不删除不重建)
|
||||
const chatMessages = $("chatMessages");
|
||||
let typingEl = chatMessages ? chatMessages.querySelector(".msg-typing") : null;
|
||||
if (!typingEl) {
|
||||
// 兜底:如果没有现有气泡(如页面刷新后),创建一个新的
|
||||
typingEl = appendThinkingIndicatorFn ? appendThinkingIndicatorFn() : null;
|
||||
}
|
||||
|
||||
// 将 pending 工具调用标记为完成,带上用户输入的结果
|
||||
if (typingEl && injectLiveToolCallsFn) {
|
||||
const resultText = instruction
|
||||
|| (decision === "reject" ? "用户拒绝" : decision === "skip" ? "用户已跳过" : "用户已确认");
|
||||
injectLiveToolCallsFn(typingEl, [{
|
||||
name: "human_interaction",
|
||||
status: "done",
|
||||
result_text: resultText,
|
||||
}]);
|
||||
}
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append("session_id", state.sessionId);
|
||||
fd.append("approval_id", approvalId);
|
||||
fd.append("approval_id", requestId);
|
||||
fd.append("decision", decision);
|
||||
fd.append("instruction", instruction || "");
|
||||
fd.append("output_type", state.outputMode || "text");
|
||||
|
||||
const stopToolPoll = startPollLiveToolCalls(state.sessionId, typingEl, injectLiveToolCallsFn);
|
||||
|
||||
try {
|
||||
const done = await api("/api/human-action", { method: "POST", body: fd });
|
||||
stopToolPoll();
|
||||
// Update state
|
||||
state.sessionId = done.sessionId;
|
||||
state.sessions.length = 0;
|
||||
state.sessions.push(...(done.sessions || []));
|
||||
if (renderSessionsFn) renderSessionsFn();
|
||||
const messages = done.messages || [];
|
||||
const last = messages[messages.length - 1];
|
||||
const hasNewPending = done.pending_actions && done.pending_actions.length > 0;
|
||||
|
||||
if (last && last.role === "assistant") {
|
||||
// 最终回复:移除 thinking 气泡,渲染 assistant
|
||||
if (typingEl && typingEl.parentNode) typingEl.remove();
|
||||
typingEl = null;
|
||||
if (renderMessagesFn) renderMessagesFn(messages.slice(0, -1));
|
||||
if (streamAssistantMessageFn) await streamAssistantMessageFn(last);
|
||||
} else if (hasNewPending) {
|
||||
// 又产生了 pending:保留 thinking 气泡,更新浮窗
|
||||
// typingEl 不删除,继续用于下一次交互
|
||||
} else {
|
||||
if (typingEl && typingEl.parentNode) typingEl.remove();
|
||||
typingEl = null;
|
||||
if (renderMessagesFn) renderMessagesFn(messages);
|
||||
}
|
||||
updatePendingOverlay(done.pending_actions);
|
||||
state._pendingHumanAction = hasNewPending;
|
||||
} finally {
|
||||
if (typingEl && typingEl.parentNode && !state._pendingHumanAction) {
|
||||
typingEl.remove();
|
||||
}
|
||||
state.sending = false;
|
||||
if (!state._pendingHumanAction) {
|
||||
if (sendBtn) {
|
||||
sendBtn.disabled = false;
|
||||
sendBtn.innerHTML = '<img src="/image/发送.svg" alt="" aria-hidden="true" class="send-btn-icon" />';
|
||||
sendBtn.classList.remove("btn-pause");
|
||||
}
|
||||
}
|
||||
}
|
||||
await api("/api/human-action", { method: "POST", body: fd });
|
||||
// 注意:不恢复 state.sending——本轮任务尚未结束,按钮状态由 sendChat 完成时统一恢复
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
Reference in new issue
Block a user