Enhance workflow engine and integration in backend and frontend

- Introduce a new WorkflowEngine class to manage workflow graphs, enabling dynamic node-based interactions.
- Update AssistantConfig to include a graph field for workflow definitions, allowing for flexible configuration.
- Modify pipeline execution to support workflow-driven dialogue, integrating node transitions and system prompts based on active nodes.
- Enhance frontend components to visualize active nodes and provide debugging capabilities, including highlighting the current node during interactions.
- Refactor existing components to accommodate new workflow functionalities and improve overall user experience.
This commit is contained in:
Xin Wang
2026-06-15 15:32:10 +08:00
parent c2a39257ff
commit aae0342a57
10 changed files with 361 additions and 16 deletions

View File

@@ -83,7 +83,10 @@ function microphoneErrorMessage(error: unknown): string {
return errorMessage(error, "无法访问麦克风。");
}
export function useVoicePreview(assistantId: string | null) {
export function useVoicePreview(
assistantId: string | null,
onNodeActive?: (nodeId: string | null) => void,
) {
const [status, setStatus] = useState<VoicePreviewStatus>("idle");
const [error, setError] = useState<string | null>(null);
const [micWarning, setMicWarning] = useState<string | null>(null);
@@ -102,6 +105,11 @@ export function useVoicePreview(assistantId: string | null) {
const localStreamRef = useRef<MediaStream | null>(null);
const startingRef = useRef(false);
const messageSeqRef = useRef(0);
// 工作流激活节点回调存进 ref,避免把它挂进 connect 依赖反复重建连接
const onNodeActiveRef = useRef(onNodeActive);
useEffect(() => {
onNodeActiveRef.current = onNodeActive;
}, [onNodeActive]);
// 枚举可用麦克风。未授权前 label 为空,授权(连接)后再刷新即可拿到名称。
const refreshDevices = useCallback(async () => {
@@ -170,6 +178,7 @@ export function useVoicePreview(assistantId: string | null) {
localStreamRef.current = null;
if (audioRef.current) audioRef.current.srcObject = null;
startingRef.current = false;
onNodeActiveRef.current?.(null);
}, []);
const disconnect = useCallback(() => {
@@ -377,6 +386,12 @@ export function useVoicePreview(assistantId: string | null) {
sequence: messageSeqRef.current,
};
setMessages((prev) => sortMessages([...prev, next]));
} else if (
msg?.type === "node-active" &&
typeof msg.nodeId === "string"
) {
// 工作流:后端报告当前激活节点,交给画布高亮
onNodeActiveRef.current?.(msg.nodeId);
}
} catch {
/* 非 JSON / 未知消息,忽略 */