代码修改
This commit is contained in:
1 parent
8c53594df7
commit
37a9cca82d
3 files changed
+67
-56
No files matched your search
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* 状态全部来自 `useStep2Orchestration` —— 本组件不调任何后端、不持久化。
|
||||
*/
|
||||
import { useRef } from "react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import {
|
||||
Cpu,
|
||||
FileIcon,
|
||||
@@ -80,7 +80,6 @@ export interface Step2PanelProps {
|
||||
/** 主聊天里 `present_files` 会在气泡下方渲染卡片;Step 2 沿用同一交互。 */
|
||||
openArtifactPreview: (threadId: string, path: string) => void;
|
||||
// ─── Setters ──────────────────────────────────────────────────
|
||||
setIsRoundTableRunning: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setClarificationDraft: React.Dispatch<React.SetStateAction<string>>;
|
||||
setCustomAgentInput: React.Dispatch<React.SetStateAction<string>>;
|
||||
setAgentConfigs: React.Dispatch<
|
||||
@@ -88,6 +87,8 @@ export interface Step2PanelProps {
|
||||
>;
|
||||
setSelectedAgentId: React.Dispatch<React.SetStateAction<number | null>>;
|
||||
// ─── Handlers ─────────────────────────────────────────────────
|
||||
onPauseRoundtable: () => void;
|
||||
onResumeRoundtable: () => void;
|
||||
onSubmitClarification: (answer: string) => void;
|
||||
onInjectToRoundtable: () => void;
|
||||
onSavePersona: (id: number) => void;
|
||||
@@ -116,11 +117,12 @@ export function Step2Panel(props: Step2PanelProps) {
|
||||
seatStubMessages,
|
||||
openArtifactFromStep,
|
||||
openArtifactPreview,
|
||||
setIsRoundTableRunning,
|
||||
setClarificationDraft,
|
||||
setCustomAgentInput,
|
||||
setAgentConfigs,
|
||||
setSelectedAgentId,
|
||||
onPauseRoundtable,
|
||||
onResumeRoundtable,
|
||||
onSubmitClarification,
|
||||
onInjectToRoundtable,
|
||||
onSavePersona,
|
||||
@@ -132,6 +134,15 @@ export function Step2Panel(props: Step2PanelProps) {
|
||||
const { atBottom: step2AtBottom, scrollToBottom: scrollStep2ToBottom } =
|
||||
useAutoScroll(step2ScrollRef, [step2RoundtableDialogues]);
|
||||
|
||||
// 干预研审 → 暂停会商联动反馈:点击「干预研审」瞬间让右上角按钮高亮闪烁,
|
||||
// 让用户清楚看到状态已切换到「恢复会商」。
|
||||
const [pauseFlash, setPauseFlash] = useState(false);
|
||||
const handleInject = useCallback(() => {
|
||||
onInjectToRoundtable();
|
||||
setPauseFlash(true);
|
||||
window.setTimeout(() => setPauseFlash(false), 1600);
|
||||
}, [onInjectToRoundtable]);
|
||||
|
||||
const isSandboxStreaming =
|
||||
isOrchestrating ||
|
||||
step2RoundtableDialogues.some((dialogue) => dialogue.streaming === true);
|
||||
@@ -177,10 +188,8 @@ export function Step2Panel(props: Step2PanelProps) {
|
||||
</select>
|
||||
{isRoundTableRunning ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsRoundTableRunning(false);
|
||||
onTriggerToast("⏸️ 会商已暂停");
|
||||
}}
|
||||
type="button"
|
||||
onClick={onPauseRoundtable}
|
||||
className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-amber-200/40 shadow-sm"
|
||||
>
|
||||
<Pause className="w-3.5 h-3.5" />
|
||||
@@ -188,11 +197,13 @@ export function Step2Panel(props: Step2PanelProps) {
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsRoundTableRunning(true);
|
||||
onTriggerToast("▶️ 恢复大模型并行博弈与推理决策");
|
||||
}}
|
||||
className="bg-emerald-100 hover:bg-emerald-200 text-emerald-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-emerald-200/40 shadow-sm"
|
||||
type="button"
|
||||
onClick={onResumeRoundtable}
|
||||
className={`bg-emerald-100 hover:bg-emerald-200 text-emerald-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-emerald-200/40 shadow-sm ${
|
||||
pauseFlash
|
||||
? "ring-2 ring-emerald-400 ring-offset-1 animate-pulse"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<Play className="w-3.5 h-3.5 fill-emerald-800" />
|
||||
恢复会商
|
||||
@@ -324,14 +335,15 @@ export function Step2Panel(props: Step2PanelProps) {
|
||||
value={customAgentInput}
|
||||
onChange={(e) => setCustomAgentInput(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") onInjectToRoundtable();
|
||||
if (e.key === "Enter") handleInject();
|
||||
}}
|
||||
placeholder="作为指挥官,您可以随时插话输入指令,对智能体博弈朝向实施人工干预..."
|
||||
className="flex-1 bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 font-medium"
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={onInjectToRoundtable}
|
||||
onClick={handleInject}
|
||||
title="发送干预指令并暂停会商;点击右上角「恢复会商」继续执行"
|
||||
className="bg-primary hover:bg-[var(--brand-900)] text-white text-xs font-bold px-4 py-2.5 rounded-xl transition shrink-0 cursor-pointer active:scale-95 shadow-md shadow-primary/10"
|
||||
>
|
||||
干预研审
|
||||
|
||||
@@ -171,9 +171,13 @@ export interface UseStep2OrchestrationReturn {
|
||||
// ─── Handlers ───────────────────────────────────────────────────────
|
||||
/** 中断当前编排循环 + 所有 in-flight SSE。 */
|
||||
stopOrchestration: () => void;
|
||||
/** 与 Step2Panel「暂停会商」按钮相同 —— 挂起编排并中止 in-flight SSE。 */
|
||||
pauseRoundtable: () => void;
|
||||
/** 与 Step2Panel「恢复会商」按钮相同 —— 恢复自动研讨(含待处理的干预指令)。 */
|
||||
resumeRoundtable: () => void;
|
||||
/** 中断所有 in-flight SSE(给新建任务 / 加载草稿用)。 */
|
||||
abortAllInFlight: () => void;
|
||||
/** 用户「干预研审」按钮 —— 追加一条人工指令气泡并触发一轮编排。 */
|
||||
/** 用户「干预研审」—— 先暂停会商,干预指令在恢复后执行。 */
|
||||
handleInjectToRoundtable: () => void;
|
||||
/** 总控澄清回复 —— 把回答拼到 prompt 里再起一轮编排。 */
|
||||
submitClarification: (rawAnswer: string) => void;
|
||||
@@ -296,9 +300,8 @@ export function useStep2Orchestration(
|
||||
const orchestrationCancelRef = useRef<boolean>(false);
|
||||
const step2InitRef = useRef<boolean>(false);
|
||||
const activeAbortersRef = useRef<Set<AbortController>>(new Set());
|
||||
/** 干预研审:暂停当前轮次后待执行的 leader prompt。 */
|
||||
/** 干预研审:恢复会商后待执行的 leader prompt。 */
|
||||
const pendingInjectPromptRef = useRef<string | null>(null);
|
||||
const [injectFlushToken, setInjectFlushToken] = useState(0);
|
||||
// selectedModel / agentConfigs / agentNameToRole / threadIds / coordinatorName
|
||||
// 等等被 runOrchestration 在循环里反复读取。把它们打到 ref 上,可以让
|
||||
// runOrchestration 的 useCallback deps 大幅收窄,避免每次模型切换、agentConfigs
|
||||
@@ -1112,17 +1115,11 @@ export function useStep2Orchestration(
|
||||
|
||||
// 挂起 / 恢复:与上面的 init effect 独立。`isRoundTableRunning` toggle 时:
|
||||
// - false → stopOrchestration
|
||||
// - true 且 idle → 触发一次「请继续」的 leader cycle
|
||||
// 注意 deps 故意只有 isRoundTableRunning,不再额外加 currentStep —— 回到 Step 2
|
||||
// 检视讨论时不应该自动 fire 新的 leader 调用。
|
||||
// - true 且 idle → 触发 leader cycle(若有干预指令则优先执行)
|
||||
useEffect(() => {
|
||||
if (currentStep !== 2) return;
|
||||
if (!isRoundTableRunning) {
|
||||
// 干预研审会先写入 pending 再置 false;此时 stop 已在 handler 里触发,
|
||||
// 避免 effect 再次 stop 误杀随后 flush 启动的 inject 编排。
|
||||
if (!pendingInjectPromptRef.current) {
|
||||
stopOrchestration();
|
||||
}
|
||||
stopOrchestration();
|
||||
return;
|
||||
}
|
||||
if (
|
||||
@@ -1132,6 +1129,18 @@ export function useStep2Orchestration(
|
||||
!coordinatorNameRef.current
|
||||
)
|
||||
return;
|
||||
|
||||
const pending = pendingInjectPromptRef.current;
|
||||
if (pending) {
|
||||
pendingInjectPromptRef.current = null;
|
||||
void runOrchestration(
|
||||
pending,
|
||||
threadIdsRef.current,
|
||||
coordinatorNameRef.current,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
void runOrchestration(
|
||||
"用户已恢复研讨,请继续上一轮的协调工作。",
|
||||
threadIdsRef.current,
|
||||
@@ -1140,20 +1149,16 @@ export function useStep2Orchestration(
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isRoundTableRunning]);
|
||||
|
||||
// 干预研审:当前轮次停止后 flush 待执行的 inject 编排(编排期间 UI 仍为「恢复会商」)。
|
||||
useEffect(() => {
|
||||
if (isOrchestrating) return;
|
||||
const pending = pendingInjectPromptRef.current;
|
||||
if (!pending) return;
|
||||
if (!threadIdsRef.current || !coordinatorNameRef.current) return;
|
||||
pendingInjectPromptRef.current = null;
|
||||
triggerToast("您的人工纠偏指令已同步至圆桌节点!");
|
||||
void runOrchestration(
|
||||
pending,
|
||||
threadIdsRef.current,
|
||||
coordinatorNameRef.current,
|
||||
);
|
||||
}, [isOrchestrating, injectFlushToken, runOrchestration, triggerToast]);
|
||||
const pauseRoundtable = useCallback(() => {
|
||||
setIsRoundTableRunning(false);
|
||||
stopOrchestration();
|
||||
triggerToast("⏸️ 会商已暂停");
|
||||
}, [stopOrchestration, triggerToast]);
|
||||
|
||||
const resumeRoundtable = useCallback(() => {
|
||||
setIsRoundTableRunning(true);
|
||||
triggerToast("▶️ 恢复大模型并行博弈与推理决策");
|
||||
}, [triggerToast]);
|
||||
|
||||
// ─── Public handlers ─────────────────────────────────────────────
|
||||
const handleInjectToRoundtable = useCallback(() => {
|
||||
@@ -1166,12 +1171,8 @@ export function useStep2Orchestration(
|
||||
const userText = customAgentInput.trim();
|
||||
setCustomAgentInput("");
|
||||
|
||||
const prompt = `用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`;
|
||||
pendingInjectPromptRef.current = prompt;
|
||||
|
||||
// 与 Step2Panel 右上角「暂停会商 / 恢复会商」联动:立即进入挂起态。
|
||||
setIsRoundTableRunning(false);
|
||||
stopOrchestration();
|
||||
// 与右上角「暂停会商」完全相同的挂起逻辑。
|
||||
pauseRoundtable();
|
||||
|
||||
appendDialogue({
|
||||
sender: "用户 (插话干预)",
|
||||
@@ -1180,13 +1181,8 @@ export function useStep2Orchestration(
|
||||
confidence: "最高指令",
|
||||
});
|
||||
|
||||
if (isOrchestratingRef.current) {
|
||||
triggerToast("⏸️ 会商已暂停,当前轮次结束后将处理您的干预指令");
|
||||
} else {
|
||||
triggerToast("⏸️ 会商已暂停,正在处理您的干预指令…");
|
||||
}
|
||||
setInjectFlushToken((n) => n + 1);
|
||||
}, [appendDialogue, customAgentInput, stopOrchestration, triggerToast]);
|
||||
pendingInjectPromptRef.current = `用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`;
|
||||
}, [appendDialogue, customAgentInput, pauseRoundtable, triggerToast]);
|
||||
|
||||
const submitClarification = useCallback(
|
||||
(rawAnswer: string) => {
|
||||
@@ -1378,6 +1374,8 @@ export function useStep2Orchestration(
|
||||
setAgentConfigs,
|
||||
resetInitGate,
|
||||
stopOrchestration,
|
||||
pauseRoundtable,
|
||||
resumeRoundtable,
|
||||
abortAllInFlight,
|
||||
handleInjectToRoundtable,
|
||||
submitClarification,
|
||||
|
||||
@@ -496,7 +496,8 @@ export default function RoundtablePlanningPage() {
|
||||
seatStubMessages={step2.seatStubMessages}
|
||||
openArtifactFromStep={step2.openArtifactFromStep}
|
||||
openArtifactPreview={step2.openArtifactPreview}
|
||||
setIsRoundTableRunning={step2.setIsRoundTableRunning}
|
||||
onPauseRoundtable={step2.pauseRoundtable}
|
||||
onResumeRoundtable={step2.resumeRoundtable}
|
||||
setClarificationDraft={step2.setClarificationDraft}
|
||||
setCustomAgentInput={step2.setCustomAgentInput}
|
||||
setAgentConfigs={step2.setAgentConfigs}
|
||||
@@ -956,8 +957,8 @@ function LeftSidebar({
|
||||
<IntentSectionIcon className="h-5 w-5" />
|
||||
</CollapsedSidebarSection>
|
||||
|
||||
<CollapsedSidebarSection title="参与角色" className="flex-1 min-h-[120px]">
|
||||
<div className="flex flex-col items-center gap-2 overflow-y-auto max-h-[320px] custom-scrollbar">
|
||||
<CollapsedSidebarSection title="参与角色">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
{roles.map((role) => (
|
||||
<RoleAvatarBadge
|
||||
key={role.id}
|
||||
@@ -1245,7 +1246,7 @@ function CollapsedSidebarSection({
|
||||
<div
|
||||
title={title}
|
||||
className={cn(
|
||||
"w-full bg-white border border-slate-200/50 rounded-2xl p-2 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center",
|
||||
"w-full shrink-0 bg-white border border-slate-200/50 rounded-2xl p-2 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user