summaryrefslogtreecommitdiff
path: root/frontend/src/store
diff options
context:
space:
mode:
authorblackhao <13851610112@163.com>2025-12-06 01:30:57 -0600
committerblackhao <13851610112@163.com>2025-12-06 01:30:57 -0600
commit93dbe11014cf967690727c25e89d9d1075008c24 (patch)
treee168becbfff0e699f49021c1b3de6918e7f0a124 /frontend/src/store
parentbcb44d5a7c4b17afd7ba64be5b497d74afc69fb6 (diff)
Diffstat (limited to 'frontend/src/store')
-rw-r--r--frontend/src/store/flowStore.ts191
1 files changed, 191 insertions, 0 deletions
diff --git a/frontend/src/store/flowStore.ts b/frontend/src/store/flowStore.ts
index d2114aa..0c90357 100644
--- a/frontend/src/store/flowStore.ts
+++ b/frontend/src/store/flowStore.ts
@@ -38,6 +38,9 @@ export interface NodeData {
systemPrompt: string;
userPrompt: string;
mergeStrategy: 'raw' | 'smart';
+ enableGoogleSearch?: boolean;
+ reasoningEffort: 'low' | 'medium' | 'high'; // For OpenAI reasoning models
+ disabled?: boolean; // Greyed out, no interaction
// Traces logic
traces: Trace[]; // INCOMING Traces
@@ -53,10 +56,21 @@ export interface NodeData {
export type LLMNode = Node<NodeData>;
+// Archived node template (for reuse)
+export interface ArchivedNode {
+ id: string;
+ label: string;
+ model: string;
+ systemPrompt: string;
+ temperature: number;
+ reasoningEffort: 'low' | 'medium' | 'high';
+}
+
interface FlowState {
nodes: LLMNode[];
edges: Edge[];
selectedNodeId: string | null;
+ archivedNodes: ArchivedNode[]; // Stored node templates
onNodesChange: OnNodesChange;
onEdgesChange: OnEdgesChange;
@@ -72,6 +86,16 @@ interface FlowState {
deleteEdge: (edgeId: string) => void;
deleteNode: (nodeId: string) => void;
deleteBranch: (startNodeId?: string, startEdgeId?: string) => void;
+
+ // Archive actions
+ toggleNodeDisabled: (nodeId: string) => void;
+ archiveNode: (nodeId: string) => void;
+ removeFromArchive: (archiveId: string) => void;
+ createNodeFromArchive: (archiveId: string, position: { x: number; y: number }) => void;
+
+ // Trace disable
+ toggleTraceDisabled: (edgeId: string) => void;
+ updateEdgeStyles: () => void;
propagateTraces: () => void;
}
@@ -90,6 +114,7 @@ const useFlowStore = create<FlowState>((set, get) => ({
nodes: [],
edges: [],
selectedNodeId: null,
+ archivedNodes: [],
onNodesChange: (changes: NodeChange[]) => {
set({
@@ -274,6 +299,172 @@ const useFlowStore = create<FlowState>((set, get) => ({
get().propagateTraces();
},
+ toggleNodeDisabled: (nodeId: string) => {
+ const node = get().nodes.find(n => n.id === nodeId);
+ if (node) {
+ const newDisabled = !node.data.disabled;
+ // Update node data AND draggable property
+ set(state => ({
+ nodes: state.nodes.map(n => {
+ if (n.id === nodeId) {
+ return {
+ ...n,
+ draggable: !newDisabled, // Disable dragging when node is disabled
+ selectable: !newDisabled, // Disable selection when node is disabled
+ data: { ...n.data, disabled: newDisabled }
+ };
+ }
+ return n;
+ })
+ }));
+ // Update edge styles to reflect disabled state
+ setTimeout(() => get().updateEdgeStyles(), 0);
+ }
+ },
+
+ archiveNode: (nodeId: string) => {
+ const node = get().nodes.find(n => n.id === nodeId);
+ if (!node) return;
+
+ const archived: ArchivedNode = {
+ id: `archive_${Date.now()}`,
+ label: node.data.label,
+ model: node.data.model,
+ systemPrompt: node.data.systemPrompt,
+ temperature: node.data.temperature,
+ reasoningEffort: node.data.reasoningEffort || 'medium'
+ };
+
+ set(state => ({
+ archivedNodes: [...state.archivedNodes, archived]
+ }));
+ },
+
+ removeFromArchive: (archiveId: string) => {
+ set(state => ({
+ archivedNodes: state.archivedNodes.filter(a => a.id !== archiveId)
+ }));
+ },
+
+ createNodeFromArchive: (archiveId: string, position: { x: number; y: number }) => {
+ const archived = get().archivedNodes.find(a => a.id === archiveId);
+ if (!archived) return;
+
+ const newNode: LLMNode = {
+ id: `node_${Date.now()}`,
+ type: 'llmNode',
+ position,
+ data: {
+ label: archived.label,
+ model: archived.model,
+ temperature: archived.temperature,
+ systemPrompt: archived.systemPrompt,
+ userPrompt: '',
+ mergeStrategy: 'smart',
+ reasoningEffort: archived.reasoningEffort,
+ traces: [],
+ outgoingTraces: [],
+ forkedTraces: [],
+ activeTraceIds: [],
+ response: '',
+ status: 'idle',
+ inputs: 1
+ }
+ };
+
+ get().addNode(newNode);
+ },
+
+ toggleTraceDisabled: (edgeId: string) => {
+ const { edges, nodes } = get();
+ const edge = edges.find(e => e.id === edgeId);
+ if (!edge) return;
+
+ // Find all nodes connected through this trace (BIDIRECTIONAL)
+ const nodesInTrace = new Set<string>();
+ const visitedEdges = new Set<string>();
+
+ // Traverse downstream (source -> target direction)
+ const traverseDownstream = (currentNodeId: string) => {
+ nodesInTrace.add(currentNodeId);
+
+ const outgoing = edges.filter(e => e.source === currentNodeId);
+ outgoing.forEach(nextEdge => {
+ if (visitedEdges.has(nextEdge.id)) return;
+ visitedEdges.add(nextEdge.id);
+ traverseDownstream(nextEdge.target);
+ });
+ };
+
+ // Traverse upstream (target -> source direction)
+ const traverseUpstream = (currentNodeId: string) => {
+ nodesInTrace.add(currentNodeId);
+
+ const incoming = edges.filter(e => e.target === currentNodeId);
+ incoming.forEach(prevEdge => {
+ if (visitedEdges.has(prevEdge.id)) return;
+ visitedEdges.add(prevEdge.id);
+ traverseUpstream(prevEdge.source);
+ });
+ };
+
+ // Start bidirectional traversal from clicked edge
+ visitedEdges.add(edge.id);
+
+ // Go upstream from source (including source itself)
+ traverseUpstream(edge.source);
+
+ // Go downstream from target (including target itself)
+ traverseDownstream(edge.target);
+
+ // Check if any node in this trace is disabled
+ const anyDisabled = Array.from(nodesInTrace).some(
+ nodeId => nodes.find(n => n.id === nodeId)?.data.disabled
+ );
+
+ // Toggle: if any disabled -> enable all, else disable all
+ const newDisabledState = !anyDisabled;
+
+ set(state => ({
+ nodes: state.nodes.map(node => {
+ if (nodesInTrace.has(node.id)) {
+ return {
+ ...node,
+ draggable: !newDisabledState,
+ selectable: !newDisabledState,
+ data: { ...node.data, disabled: newDisabledState }
+ };
+ }
+ return node;
+ })
+ }));
+
+ // Update edge styles
+ get().updateEdgeStyles();
+ },
+
+ updateEdgeStyles: () => {
+ const { nodes, edges } = get();
+
+ const updatedEdges = edges.map(edge => {
+ const sourceNode = nodes.find(n => n.id === edge.source);
+ const targetNode = nodes.find(n => n.id === edge.target);
+
+ const isDisabled = sourceNode?.data.disabled || targetNode?.data.disabled;
+
+ return {
+ ...edge,
+ style: {
+ ...edge.style,
+ opacity: isDisabled ? 0.3 : 1,
+ strokeDasharray: isDisabled ? '5,5' : undefined
+ }
+ };
+ });
+
+ set({ edges: updatedEdges });
+ },
+
propagateTraces: () => {
const { nodes, edges } = get();