feat: last run frontend (#21369)
The frontend of feat: Persist Variables for Enhanced Debugging Workflow (#20699). Co-authored-by: jZonG <jzongcode@gmail.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
import produce from 'immer'
|
||||
import type { NodeWithVar, VarInInspect } from '@/types/workflow'
|
||||
import type { ValueSelector } from '../../../types'
|
||||
|
||||
type InspectVarsState = {
|
||||
currentFocusNodeId: string | null
|
||||
nodesWithInspectVars: NodeWithVar[] // the nodes have data
|
||||
conversationVars: VarInInspect[]
|
||||
}
|
||||
|
||||
type InspectVarsActions = {
|
||||
setCurrentFocusNodeId: (nodeId: string | null) => void
|
||||
setNodesWithInspectVars: (payload: NodeWithVar[]) => void
|
||||
deleteAllInspectVars: () => void
|
||||
setNodeInspectVars: (nodeId: string, payload: VarInInspect[]) => void
|
||||
deleteNodeInspectVars: (nodeId: string) => void
|
||||
setInspectVarValue: (nodeId: string, name: string, value: any) => void
|
||||
resetToLastRunVar: (nodeId: string, varId: string, value: any) => void
|
||||
renameInspectVarName: (nodeId: string, varId: string, selector: ValueSelector) => void
|
||||
deleteInspectVar: (nodeId: string, varId: string) => void
|
||||
}
|
||||
|
||||
export type InspectVarsSliceShape = InspectVarsState & InspectVarsActions
|
||||
|
||||
export const createInspectVarsSlice: StateCreator<InspectVarsSliceShape> = (set, get) => {
|
||||
return ({
|
||||
currentFocusNodeId: null,
|
||||
nodesWithInspectVars: [],
|
||||
conversationVars: [],
|
||||
setCurrentFocusNodeId: (nodeId) => {
|
||||
set(() => ({
|
||||
currentFocusNodeId: nodeId,
|
||||
}))
|
||||
},
|
||||
setNodesWithInspectVars: (payload) => {
|
||||
set(() => ({
|
||||
nodesWithInspectVars: payload,
|
||||
}))
|
||||
},
|
||||
deleteAllInspectVars: () => {
|
||||
set(() => ({
|
||||
nodesWithInspectVars: [],
|
||||
}))
|
||||
},
|
||||
setNodeInspectVars: (nodeId, payload) => {
|
||||
set((state) => {
|
||||
const prevNodes = state.nodesWithInspectVars
|
||||
const nodes = produce(prevNodes, (draft) => {
|
||||
const index = prevNodes.findIndex(node => node.nodeId === nodeId)
|
||||
if (index !== -1) {
|
||||
draft[index].vars = payload
|
||||
draft[index].isValueFetched = true
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
nodesWithInspectVars: nodes,
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteNodeInspectVars: (nodeId) => {
|
||||
set((state: InspectVarsSliceShape) => {
|
||||
const nodes = state.nodesWithInspectVars.filter(node => node.nodeId !== nodeId)
|
||||
return {
|
||||
nodesWithInspectVars: nodes,
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
setInspectVarValue: (nodeId, varId, value) => {
|
||||
set((state: InspectVarsSliceShape) => {
|
||||
const nodes = produce(state.nodesWithInspectVars, (draft) => {
|
||||
const targetNode = draft.find(node => node.nodeId === nodeId)
|
||||
if (!targetNode)
|
||||
return
|
||||
const targetVar = targetNode.vars.find(varItem => varItem.id === varId)
|
||||
if(!targetVar)
|
||||
return
|
||||
targetVar.value = value
|
||||
targetVar.edited = true
|
||||
},
|
||||
)
|
||||
return {
|
||||
nodesWithInspectVars: nodes,
|
||||
}
|
||||
})
|
||||
},
|
||||
resetToLastRunVar: (nodeId, varId, value) => {
|
||||
set((state: InspectVarsSliceShape) => {
|
||||
const nodes = produce(state.nodesWithInspectVars, (draft) => {
|
||||
const targetNode = draft.find(node => node.nodeId === nodeId)
|
||||
if (!targetNode)
|
||||
return
|
||||
const targetVar = targetNode.vars.find(varItem => varItem.id === varId)
|
||||
if(!targetVar)
|
||||
return
|
||||
targetVar.value = value
|
||||
targetVar.edited = false
|
||||
},
|
||||
)
|
||||
return {
|
||||
nodesWithInspectVars: nodes,
|
||||
}
|
||||
})
|
||||
},
|
||||
renameInspectVarName: (nodeId, varId, selector) => {
|
||||
set((state: InspectVarsSliceShape) => {
|
||||
const nodes = produce(state.nodesWithInspectVars, (draft) => {
|
||||
const targetNode = draft.find(node => node.nodeId === nodeId)
|
||||
if (!targetNode)
|
||||
return
|
||||
const targetVar = targetNode.vars.find(varItem => varItem.id === varId)
|
||||
if(!targetVar)
|
||||
return
|
||||
targetVar.name = selector[1]
|
||||
targetVar.selector = selector
|
||||
},
|
||||
)
|
||||
return {
|
||||
nodesWithInspectVars: nodes,
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteInspectVar: (nodeId, varId) => {
|
||||
set((state: InspectVarsSliceShape) => {
|
||||
const nodes = produce(state.nodesWithInspectVars, (draft) => {
|
||||
const targetNode = draft.find(node => node.nodeId === nodeId)
|
||||
if (!targetNode)
|
||||
return
|
||||
const needChangeVarIndex = targetNode.vars.findIndex(varItem => varItem.id === varId)
|
||||
if (needChangeVarIndex !== -1)
|
||||
targetNode.vars.splice(needChangeVarIndex, 1)
|
||||
},
|
||||
)
|
||||
return {
|
||||
nodesWithInspectVars: nodes,
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
import { VarType } from '../../../types'
|
||||
import type { VarInInspect } from '@/types/workflow'
|
||||
import { VarInInspectType } from '@/types/workflow'
|
||||
|
||||
export const vars: VarInInspect[] = [
|
||||
{
|
||||
id: 'xxx',
|
||||
type: VarInInspectType.node,
|
||||
name: 'text00',
|
||||
description: '',
|
||||
selector: ['1745476079387', 'text'],
|
||||
value_type: VarType.string,
|
||||
value: 'text value...',
|
||||
edited: false,
|
||||
},
|
||||
{
|
||||
id: 'fdklajljgldjglkagjlk',
|
||||
type: VarInInspectType.node,
|
||||
name: 'text',
|
||||
description: '',
|
||||
selector: ['1712386917734', 'text'],
|
||||
value_type: VarType.string,
|
||||
value: 'made zhizhang',
|
||||
edited: false,
|
||||
},
|
||||
]
|
||||
|
||||
export const conversationVars: VarInInspect[] = [
|
||||
{
|
||||
id: 'con1',
|
||||
type: VarInInspectType.conversation,
|
||||
name: 'conversationVar 1',
|
||||
description: '',
|
||||
selector: ['conversation', 'var1'],
|
||||
value_type: VarType.string,
|
||||
value: 'conversation var value...',
|
||||
edited: false,
|
||||
},
|
||||
{
|
||||
id: 'con2',
|
||||
type: VarInInspectType.conversation,
|
||||
name: 'conversationVar 2',
|
||||
description: '',
|
||||
selector: ['conversation', 'var2'],
|
||||
value_type: VarType.number,
|
||||
value: 456,
|
||||
edited: false,
|
||||
},
|
||||
]
|
||||
|
||||
export const systemVars: VarInInspect[] = [
|
||||
{
|
||||
id: 'sys1',
|
||||
type: VarInInspectType.system,
|
||||
name: 'query',
|
||||
description: '',
|
||||
selector: ['sys', 'query'],
|
||||
value_type: VarType.string,
|
||||
value: 'Hello robot!',
|
||||
edited: false,
|
||||
},
|
||||
{
|
||||
id: 'sys2',
|
||||
type: VarInInspectType.system,
|
||||
name: 'user_id',
|
||||
description: '',
|
||||
selector: ['sys', 'user_id'],
|
||||
value_type: VarType.string,
|
||||
value: 'djflakjerlkjdlksfjslakjsdfl',
|
||||
edited: false,
|
||||
},
|
||||
]
|
@@ -28,7 +28,12 @@ import type { WorkflowDraftSliceShape } from './workflow-draft-slice'
|
||||
import { createWorkflowDraftSlice } from './workflow-draft-slice'
|
||||
import type { WorkflowSliceShape } from './workflow-slice'
|
||||
import { createWorkflowSlice } from './workflow-slice'
|
||||
import type { InspectVarsSliceShape } from './debug/inspect-vars-slice'
|
||||
import { createInspectVarsSlice } from './debug/inspect-vars-slice'
|
||||
|
||||
import { WorkflowContext } from '@/app/components/workflow/context'
|
||||
import type { LayoutSliceShape } from './layout-slice'
|
||||
import { createLayoutSlice } from './layout-slice'
|
||||
import type { WorkflowSliceShape as WorkflowAppSliceShape } from '@/app/components/workflow-app/store/workflow/workflow-slice'
|
||||
|
||||
export type Shape =
|
||||
@@ -43,6 +48,8 @@ export type Shape =
|
||||
VersionSliceShape &
|
||||
WorkflowDraftSliceShape &
|
||||
WorkflowSliceShape &
|
||||
InspectVarsSliceShape &
|
||||
LayoutSliceShape &
|
||||
WorkflowAppSliceShape
|
||||
|
||||
type CreateWorkflowStoreParams = {
|
||||
@@ -64,6 +71,8 @@ export const createWorkflowStore = (params: CreateWorkflowStoreParams) => {
|
||||
...createVersionSlice(...args),
|
||||
...createWorkflowDraftSlice(...args),
|
||||
...createWorkflowSlice(...args),
|
||||
...createInspectVarsSlice(...args),
|
||||
...createLayoutSlice(...args),
|
||||
...(injectWorkflowStoreSliceFn?.(...args) || {} as WorkflowAppSliceShape),
|
||||
}))
|
||||
}
|
||||
|
44
web/app/components/workflow/store/workflow/layout-slice.ts
Normal file
44
web/app/components/workflow/store/workflow/layout-slice.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
|
||||
export type LayoutSliceShape = {
|
||||
workflowCanvasWidth?: number
|
||||
workflowCanvasHeight?: number
|
||||
setWorkflowCanvasWidth: (width: number) => void
|
||||
setWorkflowCanvasHeight: (height: number) => void
|
||||
// rightPanelWidth - otherPanelWidth = nodePanelWidth
|
||||
rightPanelWidth?: number
|
||||
setRightPanelWidth: (width: number) => void
|
||||
nodePanelWidth: number
|
||||
setNodePanelWidth: (width: number) => void
|
||||
otherPanelWidth: number
|
||||
setOtherPanelWidth: (width: number) => void
|
||||
bottomPanelWidth: number // min-width = 400px; default-width = auto || 480px;
|
||||
setBottomPanelWidth: (width: number) => void
|
||||
bottomPanelHeight: number
|
||||
setBottomPanelHeight: (height: number) => void
|
||||
variableInspectPanelHeight: number // min-height = 120px; default-height = 320px;
|
||||
setVariableInspectPanelHeight: (height: number) => void
|
||||
maximizeCanvas: boolean
|
||||
setMaximizeCanvas: (maximize: boolean) => void
|
||||
}
|
||||
|
||||
export const createLayoutSlice: StateCreator<LayoutSliceShape> = set => ({
|
||||
workflowCanvasWidth: undefined,
|
||||
workflowCanvasHeight: undefined,
|
||||
setWorkflowCanvasWidth: width => set(() => ({ workflowCanvasWidth: width })),
|
||||
setWorkflowCanvasHeight: height => set(() => ({ workflowCanvasHeight: height })),
|
||||
rightPanelWidth: undefined,
|
||||
setRightPanelWidth: width => set(() => ({ rightPanelWidth: width })),
|
||||
nodePanelWidth: localStorage.getItem('workflow-node-panel-width') ? Number.parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 400,
|
||||
setNodePanelWidth: width => set(() => ({ nodePanelWidth: width })),
|
||||
otherPanelWidth: 400,
|
||||
setOtherPanelWidth: width => set(() => ({ otherPanelWidth: width })),
|
||||
bottomPanelWidth: 480,
|
||||
setBottomPanelWidth: width => set(() => ({ bottomPanelWidth: width })),
|
||||
bottomPanelHeight: 324,
|
||||
setBottomPanelHeight: height => set(() => ({ bottomPanelHeight: height })),
|
||||
variableInspectPanelHeight: localStorage.getItem('workflow-variable-inpsect-panel-height') ? Number.parseFloat(localStorage.getItem('workflow-variable-inpsect-panel-height')!) : 320,
|
||||
setVariableInspectPanelHeight: height => set(() => ({ variableInspectPanelHeight: height })),
|
||||
maximizeCanvas: localStorage.getItem('workflow-canvas-maximize') === 'true',
|
||||
setMaximizeCanvas: maximize => set(() => ({ maximizeCanvas: maximize })),
|
||||
})
|
@@ -15,6 +15,10 @@ export type PanelSliceShape = {
|
||||
left: number
|
||||
}
|
||||
setPanelMenu: (panelMenu: PanelSliceShape['panelMenu']) => void
|
||||
showVariableInspectPanel: boolean
|
||||
setShowVariableInspectPanel: (showVariableInspectPanel: boolean) => void
|
||||
initShowLastRunTab: boolean
|
||||
setInitShowLastRunTab: (initShowLastRunTab: boolean) => void
|
||||
}
|
||||
|
||||
export const createPanelSlice: StateCreator<PanelSliceShape> = set => ({
|
||||
@@ -29,4 +33,8 @@ export const createPanelSlice: StateCreator<PanelSliceShape> = set => ({
|
||||
setShowDebugAndPreviewPanel: showDebugAndPreviewPanel => set(() => ({ showDebugAndPreviewPanel })),
|
||||
panelMenu: undefined,
|
||||
setPanelMenu: panelMenu => set(() => ({ panelMenu })),
|
||||
showVariableInspectPanel: false,
|
||||
setShowVariableInspectPanel: showVariableInspectPanel => set(() => ({ showVariableInspectPanel })),
|
||||
initShowLastRunTab: false,
|
||||
setInitShowLastRunTab: initShowLastRunTab => set(() => ({ initShowLastRunTab })),
|
||||
})
|
||||
|
Reference in New Issue
Block a user