点击确认
This commit is contained in:
1 parent
1ff8860b98
commit
deb9993a64
3 files changed
+77
-47
No files matched your search
@@ -171,18 +171,18 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{isRoundTableRunning && isOrchestrating ? (
|
{isRoundTableRunning ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsRoundTableRunning(false);
|
setIsRoundTableRunning(false);
|
||||||
onTriggerToast("⏸️ 会商模拟暂挂!智能体挂起...");
|
onTriggerToast("⏸️ 会商已暂停");
|
||||||
}}
|
}}
|
||||||
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"
|
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" />
|
<Pause className="w-3.5 h-3.5" />
|
||||||
挂起协商
|
暂停会商
|
||||||
</button>
|
</button>
|
||||||
) : !isRoundTableRunning ? (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsRoundTableRunning(true);
|
setIsRoundTableRunning(true);
|
||||||
@@ -193,7 +193,7 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
<Play className="w-3.5 h-3.5 fill-emerald-800" />
|
<Play className="w-3.5 h-3.5 fill-emerald-800" />
|
||||||
恢复会商
|
恢复会商
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -280,6 +280,9 @@ export function useStep2Orchestration(
|
|||||||
const orchestrationCancelRef = useRef<boolean>(false);
|
const orchestrationCancelRef = useRef<boolean>(false);
|
||||||
const step2InitRef = useRef<boolean>(false);
|
const step2InitRef = useRef<boolean>(false);
|
||||||
const activeAbortersRef = useRef<Set<AbortController>>(new Set());
|
const activeAbortersRef = useRef<Set<AbortController>>(new Set());
|
||||||
|
/** 干预研审:暂停当前轮次后待执行的 leader prompt。 */
|
||||||
|
const pendingInjectPromptRef = useRef<string | null>(null);
|
||||||
|
const [injectFlushToken, setInjectFlushToken] = useState(0);
|
||||||
// selectedModel / agentConfigs / agentNameToRole / threadIds / coordinatorName
|
// selectedModel / agentConfigs / agentNameToRole / threadIds / coordinatorName
|
||||||
// 等等被 runOrchestration 在循环里反复读取。把它们打到 ref 上,可以让
|
// 等等被 runOrchestration 在循环里反复读取。把它们打到 ref 上,可以让
|
||||||
// runOrchestration 的 useCallback deps 大幅收窄,避免每次模型切换、agentConfigs
|
// runOrchestration 的 useCallback deps 大幅收窄,避免每次模型切换、agentConfigs
|
||||||
@@ -1043,7 +1046,11 @@ export function useStep2Orchestration(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentStep !== 2) return;
|
if (currentStep !== 2) return;
|
||||||
if (!isRoundTableRunning) {
|
if (!isRoundTableRunning) {
|
||||||
stopOrchestration();
|
// 干预研审会先写入 pending 再置 false;此时 stop 已在 handler 里触发,
|
||||||
|
// 避免 effect 再次 stop 误杀随后 flush 启动的 inject 编排。
|
||||||
|
if (!pendingInjectPromptRef.current) {
|
||||||
|
stopOrchestration();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -1061,6 +1068,21 @@ export function useStep2Orchestration(
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isRoundTableRunning]);
|
}, [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]);
|
||||||
|
|
||||||
// ─── Public handlers ─────────────────────────────────────────────
|
// ─── Public handlers ─────────────────────────────────────────────
|
||||||
const handleInjectToRoundtable = useCallback(() => {
|
const handleInjectToRoundtable = useCallback(() => {
|
||||||
if (!customAgentInput.trim()) return;
|
if (!customAgentInput.trim()) return;
|
||||||
@@ -1068,28 +1090,31 @@ export function useStep2Orchestration(
|
|||||||
triggerToast("⚠️ 圆桌尚未就绪,请稍候");
|
triggerToast("⚠️ 圆桌尚未就绪,请稍候");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isOrchestratingRef.current) {
|
|
||||||
triggerToast("⏳ 当前轮次仍在进行中,请等待或暂停后再发送");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userText = customAgentInput.trim();
|
const userText = customAgentInput.trim();
|
||||||
setCustomAgentInput("");
|
setCustomAgentInput("");
|
||||||
|
|
||||||
|
const prompt = `用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`;
|
||||||
|
pendingInjectPromptRef.current = prompt;
|
||||||
|
|
||||||
|
// 与 Step2Panel 右上角「暂停会商 / 恢复会商」联动:立即进入挂起态。
|
||||||
|
setIsRoundTableRunning(false);
|
||||||
|
stopOrchestration();
|
||||||
|
|
||||||
appendDialogue({
|
appendDialogue({
|
||||||
sender: "用户 (插话干预)",
|
sender: "用户 (插话干预)",
|
||||||
avatarType: "human",
|
avatarType: "human",
|
||||||
content: `💡 (人工指令介入) ${userText}`,
|
content: `💡 (人工指令介入) ${userText}`,
|
||||||
confidence: "最高指令",
|
confidence: "最高指令",
|
||||||
});
|
});
|
||||||
triggerToast("您的人工纠偏指令已同步至圆桌节点!");
|
|
||||||
|
|
||||||
void runOrchestration(
|
if (isOrchestratingRef.current) {
|
||||||
`用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`,
|
triggerToast("⏸️ 会商已暂停,当前轮次结束后将处理您的干预指令");
|
||||||
threadIdsRef.current,
|
} else {
|
||||||
coordinatorNameRef.current,
|
triggerToast("⏸️ 会商已暂停,正在处理您的干预指令…");
|
||||||
);
|
}
|
||||||
}, [appendDialogue, customAgentInput, runOrchestration, triggerToast]);
|
setInjectFlushToken((n) => n + 1);
|
||||||
|
}, [appendDialogue, customAgentInput, stopOrchestration, triggerToast]);
|
||||||
|
|
||||||
const submitClarification = useCallback(
|
const submitClarification = useCallback(
|
||||||
(rawAnswer: string) => {
|
(rawAnswer: string) => {
|
||||||
@@ -1237,6 +1262,7 @@ export function useStep2Orchestration(
|
|||||||
setArtifacts([]);
|
setArtifacts([]);
|
||||||
seatStubMessagesRef.current = {};
|
seatStubMessagesRef.current = {};
|
||||||
setSeatStubMessages({});
|
setSeatStubMessages({});
|
||||||
|
pendingInjectPromptRef.current = null;
|
||||||
deselectArtifact();
|
deselectArtifact();
|
||||||
step2InitRef.current = false;
|
step2InitRef.current = false;
|
||||||
}, [abortAllInFlight, deselectArtifact, setArtifacts]);
|
}, [abortAllInFlight, deselectArtifact, setArtifacts]);
|
||||||
|
|||||||
@@ -351,37 +351,41 @@ export default function RoundtablePlanningPage() {
|
|||||||
新建任务
|
新建任务
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
{currentStep !== 2 && (
|
||||||
id="start-clear-btn"
|
<>
|
||||||
onClick={() => {
|
<button
|
||||||
setCurrentStep(1);
|
id="start-clear-btn"
|
||||||
triggerToast("已切换至第一阶段:人机问答意图澄清");
|
onClick={() => {
|
||||||
}}
|
setCurrentStep(1);
|
||||||
className="bg-primary text-white hover:bg-[var(--brand-900)] text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all shadow-sm shadow-primary/10 active:scale-95"
|
triggerToast("已切换至第一阶段:人机问答意图澄清");
|
||||||
>
|
}}
|
||||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.5">
|
className="bg-primary text-white hover:bg-[var(--brand-900)] text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all shadow-sm shadow-primary/10 active:scale-95"
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
>
|
||||||
</svg>
|
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.5">
|
||||||
开始澄清
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
</button>
|
</svg>
|
||||||
|
开始澄清
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="human-intervention-btn"
|
id="human-intervention-btn"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setRoles((prev) =>
|
setRoles((prev) =>
|
||||||
prev.map((r) =>
|
prev.map((r) =>
|
||||||
r.id === 1
|
r.id === 1
|
||||||
? { ...r, status: r.status === "在线" ? "等待激活" : "在线" }
|
? { ...r, status: r.status === "在线" ? "等待激活" : "在线" }
|
||||||
: r,
|
: r,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
triggerToast("指挥专家状态变更!人工介入切换");
|
triggerToast("指挥专家状态变更!人工介入切换");
|
||||||
}}
|
}}
|
||||||
className="bg-white hover:bg-slate-50 border border-slate-200 text-slate-700 text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all dark:border-border dark:bg-secondary dark:text-foreground dark:hover:bg-accent"
|
className="bg-white hover:bg-slate-50 border border-slate-200 text-slate-700 text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all dark:border-border dark:bg-secondary dark:text-foreground dark:hover:bg-accent"
|
||||||
>
|
>
|
||||||
<Users className="w-3.5 h-3.5 text-slate-500 dark:text-muted-foreground" />
|
<Users className="w-3.5 h-3.5 text-slate-500 dark:text-muted-foreground" />
|
||||||
人工介入
|
人工介入
|
||||||
</button>
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="save-draft-header-btn"
|
id="save-draft-header-btn"
|
||||||
|
|||||||
Reference in new issue
Block a user