refactor(workflow): streamline workflow settings management
- Introduced new utility functions `settingsFromWorkflowGraph` and `workflowGraphWithSettings` to encapsulate settings extraction and graph updates. - Updated `useWorkflowEditorState` to utilize the new utility functions for improved clarity and maintainability. - Refactored `AssistantPage` and `WorkflowCanvas` components to leverage the new settings management functions, ensuring consistent handling of workflow settings across the application. - Removed redundant settings handling code, enhancing overall code cleanliness.
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
settingsFromWorkflowGraph,
|
||||||
|
workflowGraphWithSettings,
|
||||||
|
} from "@/components/workflow/graph-settings";
|
||||||
import type { WorkflowSettings } from "@/components/workflow/types";
|
import type { WorkflowSettings } from "@/components/workflow/types";
|
||||||
import { defaultGraph, type WorkflowGraph } from "@/components/workflow/specs";
|
import { defaultGraph, type WorkflowGraph } from "@/components/workflow/specs";
|
||||||
import type { DynamicVariableDefinition } from "@/lib/api";
|
import type { DynamicVariableDefinition } from "@/lib/api";
|
||||||
@@ -13,26 +17,6 @@ type WorkflowPanel =
|
|||||||
| { type: "edge"; edgeId: string }
|
| { type: "edge"; edgeId: string }
|
||||||
| null;
|
| null;
|
||||||
|
|
||||||
function settingsFromGraph(graph: WorkflowGraph): WorkflowSettings {
|
|
||||||
return {
|
|
||||||
globalPrompt: graph.settings.globalPrompt,
|
|
||||||
llm: graph.settings.defaultLlmResourceId,
|
|
||||||
asr: graph.settings.defaultAsrResourceId,
|
|
||||||
tts: graph.settings.defaultTtsResourceId,
|
|
||||||
visionEnabled: graph.settings.visionEnabled,
|
|
||||||
visionModelResourceId: graph.settings.visionModelResourceId,
|
|
||||||
toolIds: graph.settings.toolIds,
|
|
||||||
knowledgeBaseId: graph.settings.knowledgeBaseId,
|
|
||||||
knowledgeRetrievalConfig: {
|
|
||||||
mode: graph.settings.knowledgeMode,
|
|
||||||
topN: graph.settings.knowledgeTopN,
|
|
||||||
scoreThreshold: graph.settings.knowledgeScoreThreshold,
|
|
||||||
},
|
|
||||||
allowInterrupt: graph.settings.enableInterrupt,
|
|
||||||
turnConfig: graph.settings.turnConfig,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Owns Workflow editor state; AssistantPage only coordinates loading and saving. */
|
/** Owns Workflow editor state; AssistantPage only coordinates loading and saving. */
|
||||||
export function useWorkflowEditorState() {
|
export function useWorkflowEditorState() {
|
||||||
const [workflowName, setWorkflowName] = useState("");
|
const [workflowName, setWorkflowName] = useState("");
|
||||||
@@ -40,29 +24,11 @@ export function useWorkflowEditorState() {
|
|||||||
defaultGraph(),
|
defaultGraph(),
|
||||||
);
|
);
|
||||||
const workflowSettings = useMemo(
|
const workflowSettings = useMemo(
|
||||||
() => settingsFromGraph(workflowGraph),
|
() => settingsFromWorkflowGraph(workflowGraph),
|
||||||
[workflowGraph],
|
[workflowGraph],
|
||||||
);
|
);
|
||||||
const setWorkflowSettings = useCallback((settings: WorkflowSettings) => {
|
const setWorkflowSettings = useCallback((settings: WorkflowSettings) => {
|
||||||
setWorkflowGraph((graph) => ({
|
setWorkflowGraph((graph) => workflowGraphWithSettings(graph, settings));
|
||||||
...graph,
|
|
||||||
settings: {
|
|
||||||
globalPrompt: settings.globalPrompt,
|
|
||||||
defaultLlmResourceId: settings.llm ?? "",
|
|
||||||
defaultAsrResourceId: settings.asr ?? "",
|
|
||||||
defaultTtsResourceId: settings.tts ?? "",
|
|
||||||
visionEnabled: settings.visionEnabled,
|
|
||||||
visionModelResourceId: settings.visionModelResourceId,
|
|
||||||
toolIds: settings.toolIds,
|
|
||||||
knowledgeBaseId: settings.knowledgeBaseId,
|
|
||||||
knowledgeMode: settings.knowledgeRetrievalConfig.mode,
|
|
||||||
knowledgeTopN: settings.knowledgeRetrievalConfig.topN,
|
|
||||||
knowledgeScoreThreshold:
|
|
||||||
settings.knowledgeRetrievalConfig.scoreThreshold,
|
|
||||||
enableInterrupt: settings.allowInterrupt,
|
|
||||||
turnConfig: settings.turnConfig,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}, []);
|
}, []);
|
||||||
const [workflowDynamicVariableDefinitions, setWorkflowDynamicVariableDefinitions] =
|
const [workflowDynamicVariableDefinitions, setWorkflowDynamicVariableDefinitions] =
|
||||||
useState<Record<string, DynamicVariableDefinition>>({});
|
useState<Record<string, DynamicVariableDefinition>>({});
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import {
|
|||||||
type TurnConfig,
|
type TurnConfig,
|
||||||
} from "@/lib/api";
|
} from "@/lib/api";
|
||||||
import type { WorkflowSettings } from "@/components/workflow/types";
|
import type { WorkflowSettings } from "@/components/workflow/types";
|
||||||
|
import { workflowGraphWithSettings } from "@/components/workflow/graph-settings";
|
||||||
import { SectionCard } from "@/components/editor/section-card";
|
import { SectionCard } from "@/components/editor/section-card";
|
||||||
import {
|
import {
|
||||||
defaultGraph,
|
defaultGraph,
|
||||||
@@ -571,7 +572,6 @@ export function AssistantPage(props: AssistantPageProps) {
|
|||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
name: workflowName,
|
name: workflowName,
|
||||||
graph: workflowGraph,
|
graph: workflowGraph,
|
||||||
settings: workflowSettings,
|
|
||||||
dynamicVariableDefinitions: effectiveWorkflowDynamicVariableDefinitions,
|
dynamicVariableDefinitions: effectiveWorkflowDynamicVariableDefinitions,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -721,12 +721,14 @@ export function AssistantPage(props: AssistantPageProps) {
|
|||||||
graph.settings?.turnConfig ?? assistant.turnConfig,
|
graph.settings?.turnConfig ?? assistant.turnConfig,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
// 编辑器会把兼容字段和默认值写回 graph。状态与保存基线必须使用
|
||||||
|
// 同一个规范化结果,否则页面刚打开就会被误判为有未保存改动。
|
||||||
|
const normalizedGraph = workflowGraphWithSettings(graph, wfSettings);
|
||||||
setWorkflowName(assistant.name);
|
setWorkflowName(assistant.name);
|
||||||
setWorkflowGraph(graph);
|
setWorkflowGraph(normalizedGraph);
|
||||||
setWorkflowSettings(wfSettings);
|
|
||||||
const dynamicVariableDefinitions =
|
const dynamicVariableDefinitions =
|
||||||
activeWorkflowDynamicVariableDefinitions(
|
activeWorkflowDynamicVariableDefinitions(
|
||||||
graph,
|
normalizedGraph,
|
||||||
assistant.dynamicVariableDefinitions ?? {},
|
assistant.dynamicVariableDefinitions ?? {},
|
||||||
);
|
);
|
||||||
setWorkflowDynamicVariableDefinitions(
|
setWorkflowDynamicVariableDefinitions(
|
||||||
@@ -735,8 +737,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
|||||||
setSavedSnapshot(
|
setSavedSnapshot(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
name: assistant.name,
|
name: assistant.name,
|
||||||
graph,
|
graph: normalizedGraph,
|
||||||
settings: wfSettings,
|
|
||||||
dynamicVariableDefinitions,
|
dynamicVariableDefinitions,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -818,7 +819,6 @@ export function AssistantPage(props: AssistantPageProps) {
|
|||||||
? JSON.stringify({
|
? JSON.stringify({
|
||||||
name: workflowName,
|
name: workflowName,
|
||||||
graph: workflowGraph,
|
graph: workflowGraph,
|
||||||
settings: workflowSettings,
|
|
||||||
dynamicVariableDefinitions:
|
dynamicVariableDefinitions:
|
||||||
effectiveWorkflowDynamicVariableDefinitions,
|
effectiveWorkflowDynamicVariableDefinitions,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
type WorkflowNodeType,
|
type WorkflowNodeType,
|
||||||
} from "./specs";
|
} from "./specs";
|
||||||
import type { WorkflowEditorProps } from "./types";
|
import type { WorkflowEditorProps } from "./types";
|
||||||
|
import { workflowGraphWithSettings } from "./graph-settings";
|
||||||
|
|
||||||
let nodeSeq = 0;
|
let nodeSeq = 0;
|
||||||
|
|
||||||
@@ -160,47 +161,28 @@ export function WorkflowCanvas({
|
|||||||
const [addPosition, setAddPosition] = useState<{ x: number; y: number } | null>(null);
|
const [addPosition, setAddPosition] = useState<{ x: number; y: number } | null>(null);
|
||||||
const { screenToFlowPosition } = useReactFlow();
|
const { screenToFlowPosition } = useReactFlow();
|
||||||
|
|
||||||
// 回传画布状态给外部(助手 graph)。用 ref 避免把 onChange 放进依赖导致循环。
|
// 只在可持久化的画布内容真正变化时回传。React Flow 挂载时会写入节点尺寸等
|
||||||
|
// 内部状态,这些不属于 workflow graph,不能因此把刚加载的助手标记为未保存。
|
||||||
const onChangeRef = useRef(onChange);
|
const onChangeRef = useRef(onChange);
|
||||||
|
const lastSyncedGraphRef = useRef(
|
||||||
|
JSON.stringify(
|
||||||
|
workflowGraphWithSettings(
|
||||||
|
fromFlow(initial.nodes, initial.edges),
|
||||||
|
settings,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChangeRef.current = onChange;
|
onChangeRef.current = onChange;
|
||||||
}, [onChange]);
|
}, [onChange]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const graph = fromFlow(nodes, edges);
|
const graph = workflowGraphWithSettings(fromFlow(nodes, edges), settings);
|
||||||
graph.settings = {
|
const nextGraphJson = JSON.stringify(graph);
|
||||||
globalPrompt: settings.globalPrompt,
|
if (nextGraphJson === lastSyncedGraphRef.current) return;
|
||||||
defaultLlmResourceId: settings.llm ?? "",
|
|
||||||
defaultAsrResourceId: settings.asr ?? "",
|
lastSyncedGraphRef.current = nextGraphJson;
|
||||||
defaultTtsResourceId: settings.tts ?? "",
|
|
||||||
visionEnabled: settings.visionEnabled,
|
|
||||||
visionModelResourceId: settings.visionModelResourceId,
|
|
||||||
toolIds: settings.toolIds,
|
|
||||||
knowledgeBaseId: settings.knowledgeBaseId,
|
|
||||||
knowledgeMode: settings.knowledgeRetrievalConfig.mode,
|
|
||||||
knowledgeTopN: settings.knowledgeRetrievalConfig.topN,
|
|
||||||
knowledgeScoreThreshold:
|
|
||||||
settings.knowledgeRetrievalConfig.scoreThreshold,
|
|
||||||
enableInterrupt: settings.allowInterrupt,
|
|
||||||
turnConfig: settings.turnConfig,
|
|
||||||
};
|
|
||||||
onChangeRef.current?.(graph);
|
onChangeRef.current?.(graph);
|
||||||
}, [
|
}, [nodes, edges, settings]);
|
||||||
nodes,
|
|
||||||
edges,
|
|
||||||
settings.globalPrompt,
|
|
||||||
settings.llm,
|
|
||||||
settings.asr,
|
|
||||||
settings.tts,
|
|
||||||
settings.visionEnabled,
|
|
||||||
settings.visionModelResourceId,
|
|
||||||
settings.toolIds,
|
|
||||||
settings.knowledgeBaseId,
|
|
||||||
settings.knowledgeRetrievalConfig.mode,
|
|
||||||
settings.knowledgeRetrievalConfig.topN,
|
|
||||||
settings.knowledgeRetrievalConfig.scoreThreshold,
|
|
||||||
settings.allowInterrupt,
|
|
||||||
settings.turnConfig,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onConnect = useCallback(
|
const onConnect = useCallback(
|
||||||
(connection: Connection) => {
|
(connection: Connection) => {
|
||||||
|
|||||||
51
frontend/src/components/workflow/graph-settings.ts
Normal file
51
frontend/src/components/workflow/graph-settings.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import type { WorkflowGraph } from "@/components/workflow/specs";
|
||||||
|
import type { WorkflowSettings } from "@/components/workflow/types";
|
||||||
|
|
||||||
|
/** Read the editor-facing settings from the settings stored in a workflow graph. */
|
||||||
|
export function settingsFromWorkflowGraph(
|
||||||
|
graph: WorkflowGraph,
|
||||||
|
): WorkflowSettings {
|
||||||
|
return {
|
||||||
|
globalPrompt: graph.settings.globalPrompt,
|
||||||
|
llm: graph.settings.defaultLlmResourceId,
|
||||||
|
asr: graph.settings.defaultAsrResourceId,
|
||||||
|
tts: graph.settings.defaultTtsResourceId,
|
||||||
|
visionEnabled: graph.settings.visionEnabled,
|
||||||
|
visionModelResourceId: graph.settings.visionModelResourceId,
|
||||||
|
toolIds: graph.settings.toolIds,
|
||||||
|
knowledgeBaseId: graph.settings.knowledgeBaseId,
|
||||||
|
knowledgeRetrievalConfig: {
|
||||||
|
mode: graph.settings.knowledgeMode,
|
||||||
|
topN: graph.settings.knowledgeTopN,
|
||||||
|
scoreThreshold: graph.settings.knowledgeScoreThreshold,
|
||||||
|
},
|
||||||
|
allowInterrupt: graph.settings.enableInterrupt,
|
||||||
|
turnConfig: graph.settings.turnConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a graph whose persisted settings match the editor-facing settings. */
|
||||||
|
export function workflowGraphWithSettings(
|
||||||
|
graph: WorkflowGraph,
|
||||||
|
settings: WorkflowSettings,
|
||||||
|
): WorkflowGraph {
|
||||||
|
return {
|
||||||
|
...graph,
|
||||||
|
settings: {
|
||||||
|
globalPrompt: settings.globalPrompt,
|
||||||
|
defaultLlmResourceId: settings.llm ?? "",
|
||||||
|
defaultAsrResourceId: settings.asr ?? "",
|
||||||
|
defaultTtsResourceId: settings.tts ?? "",
|
||||||
|
visionEnabled: settings.visionEnabled,
|
||||||
|
visionModelResourceId: settings.visionModelResourceId,
|
||||||
|
toolIds: settings.toolIds,
|
||||||
|
knowledgeBaseId: settings.knowledgeBaseId,
|
||||||
|
knowledgeMode: settings.knowledgeRetrievalConfig.mode,
|
||||||
|
knowledgeTopN: settings.knowledgeRetrievalConfig.topN,
|
||||||
|
knowledgeScoreThreshold:
|
||||||
|
settings.knowledgeRetrievalConfig.scoreThreshold,
|
||||||
|
enableInterrupt: settings.allowInterrupt,
|
||||||
|
turnConfig: settings.turnConfig,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user