暂停修改
This commit is contained in:
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
|
||||
|
||||
Reference in new issue
Block a user