暂停修改

This commit is contained in:
董宏凯 committed 2026-05-25 17:56:40 +08:00
1 parent 525218f9a7
commit dffb8a955b
1 file changed
+65 -29
@@ -436,7 +436,6 @@ export default function RoundtablePlanningPage() {
const [step2RoundtableDialogues, setStep2RoundtableDialogues] = useState<any[]>([]);
// Inputs for Step 2 conversation simulation controls
const [roundTableSpeed, setRoundTableSpeed] = useState<string>("正常");
const [isRoundTableRunning, setIsRoundTableRunning] = useState<boolean>(true);
const [customAgentInput, setCustomAgentInput] = useState<string>("");
// Whether the roundtable visualizer + constraint console panel is collapsed.
@@ -465,6 +464,7 @@ export default function RoundtablePlanningPage() {
// early-returned because the ref was already set; net result: the async
// landed but its setState got skipped, leaving intentThreadId null forever).
const step1InitGenRef = useRef<number>(0);
const intentAborterRef = useRef<AbortController | null>(null);
// Multi-agent orchestration state (lives only for one step-2 visit).
const [threadIds, setThreadIds] = useState<ThreadIdMap | null>(null);
@@ -516,6 +516,17 @@ export default function RoundtablePlanningPage() {
activeAbortersRef.current.clear();
}, []);
const stopIntentStream = useCallback(() => {
if (intentAborterRef.current) {
try {
intentAborterRef.current.abort();
} catch {
/* noop */
}
intentAborterRef.current = null;
}
}, []);
// Toast auto-dismiss logic
useEffect(() => {
if (showToast) {
@@ -524,6 +535,11 @@ export default function RoundtablePlanningPage() {
}
}, [showToast]);
useEffect(() => {
if (currentStep === 1) return;
stopIntentStream();
}, [currentStep, stopIntentStream]);
// Auto-follow the streamed conversations: track scroll position, stop
// following when the user scrolls up, expose a `回到底部` button that
// re-engages auto-follow. Same hook for both steps.
@@ -1172,6 +1188,7 @@ export default function RoundtablePlanningPage() {
// Stop anything in flight before swapping state out.
orchestrationCancelRef.current = true;
abortAllInFlight();
stopIntentStream();
setIsOrchestrating(false);
setIsInitializing(false);
setPendingClarification(null);
@@ -1201,7 +1218,7 @@ export default function RoundtablePlanningPage() {
// doesn't change and the effect wouldn't re-fire.
runStep1Init();
triggerToast("✨ 已开始新任务,请在第一步与任务理解智能体澄清需求");
}, [abortAllInFlight, runStep1Init]);
}, [abortAllInFlight, runStep1Init, stopIntentStream]);
// ── Draft helpers ──────────────────────────────────────────────────────
// Build a fresh snapshot of the current in-memory session. Called by every
@@ -1307,6 +1324,7 @@ export default function RoundtablePlanningPage() {
// Cancel anything in-flight before swapping state out from under it.
orchestrationCancelRef.current = true;
abortAllInFlight();
stopIntentStream();
setIsOrchestrating(false);
setIsInitializing(false);
setPendingClarification(null);
@@ -1347,7 +1365,7 @@ export default function RoundtablePlanningPage() {
setCurrentStep(draft.furthestStep);
triggerToast(`📂 已加载草稿:${draft.name}`);
},
[drafts, abortAllInFlight],
[drafts, abortAllInFlight, stopIntentStream],
);
const handleDeleteDraft = useCallback(
@@ -1470,11 +1488,14 @@ export default function RoundtablePlanningPage() {
};
setIsIntentSending(true);
const aborter = new AbortController();
intentAborterRef.current = aborter;
try {
const res = await streamIntent({
threadId: intentThreadId,
message: trimmed,
model: DEFAULT_MODEL,
signal: aborter.signal,
onTextDelta: (delta) => {
setInteractiveMessages(prev =>
prev.map(m => (m.id === agentMsgId ? { ...m, content: (m.content ?? "") + delta } : m)),
@@ -1529,16 +1550,36 @@ export default function RoundtablePlanningPage() {
triggerToast(`❌ 智能体调用失败: ${res.content}`);
}
} catch (err) {
console.error("intent stream failed:", err);
setInteractiveMessages(prev =>
prev.map(m =>
m.id === agentMsgId
? { ...m, content: `(错误) ${(err as Error).message}`, streaming: false } as any
: m,
),
);
triggerToast(`❌ 发送失败: ${(err as Error).message}`);
if ((err as Error).name === "AbortError" || aborter.signal.aborted) {
setInteractiveMessages(prev =>
prev.map(m =>
m.id === agentMsgId
? {
...m,
content: (m.content ?? "").trim()
? `${m.content ?? ""}\n\n(已暂停)`
: "(已暂停)",
streaming: false,
} as any
: m,
),
);
triggerToast("⏸️ 已暂停任务理解回复");
} else {
console.error("intent stream failed:", err);
setInteractiveMessages(prev =>
prev.map(m =>
m.id === agentMsgId
? { ...m, content: `(错误) ${(err as Error).message}`, streaming: false } as any
: m,
),
);
triggerToast(`❌ 发送失败: ${(err as Error).message}`);
}
} finally {
if (intentAborterRef.current === aborter) {
intentAborterRef.current = null;
}
setIsIntentSending(false);
}
};
@@ -2603,6 +2644,18 @@ export default function RoundtablePlanningPage() {
{isIntentSending ? "发送中" : "发送"}
</button>
{isIntentSending && (
<button
id="pause-intent-btn"
type="button"
onClick={() => stopIntentStream()}
className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-xs font-bold px-3 py-2.5 rounded-xl flex items-center gap-1.5 transition border border-amber-200/40 shadow-sm active:scale-95"
>
<Pause className="w-3 h-3" />
暂停
</button>
)}
<button
id="suggest-followup-btn"
onClick={handleAgentFollowUp}
@@ -2676,23 +2729,6 @@ export default function RoundtablePlanningPage() {
</div>
</div>
<div className="flex items-center gap-2.5">
{/* Interactive speed controls */}
<div className="flex items-center bg-slate-50 border border-slate-200 rounded-xl p-0.5 text-[10px] shrink-0 font-bold font-sans shadow-inner">
<span className="px-2 text-slate-400 font-semibold selection:bg-transparent">研商步长</span>
<button
onClick={() => { setRoundTableSpeed("慢速"); triggerToast("🐌 已切换为 0.5x 慢速拟合监听模式"); }}
className={`px-1.5 py-0.5 rounded transition ${roundTableSpeed === "慢速" ? "bg-white text-primary font-extrabold shadow-sm" : "text-slate-500 hover:text-slate-800"}`}
>慢速</button>
<button
onClick={() => { setRoundTableSpeed("正常"); triggerToast("⏱️ 已恢复为 1.0x 标准协商状态值"); }}
className={`px-1.5 py-0.5 rounded transition ${roundTableSpeed === "正常" ? "bg-white text-primary font-extrabold shadow-sm" : "text-slate-500 hover:text-slate-800"}`}
>正常</button>
<button
onClick={() => { setRoundTableSpeed("极速"); triggerToast("🚀 已并行为 2.5x 极速算力重算模式"); }}
className={`px-1.5 py-0.5 rounded transition ${roundTableSpeed === "极速" ? "bg-white text-primary font-extrabold shadow-sm" : "text-slate-500 hover:text-slate-800"}`}
>极速</button>
</div>
{/* Suspend / resume button visibility:
- Show "挂起协商" only while a run is actually in flight
(isOrchestrating + still nominally running). When the