chore: change var inspect slice

This commit is contained in:
Joel 2025-04-23 17:57:05 +08:00
parent 1593ef0640
commit 8cd8da99d6
8 changed files with 188 additions and 110 deletions

View File

@ -2,11 +2,11 @@ import { useWorkflowStore } from '../store'
const useCurrentVars = () => { const useCurrentVars = () => {
const workflowStore = useWorkflowStore() const workflowStore = useWorkflowStore()
const { const {
currentNodes, nodes: currentNodes,
getCurrentVar, getInspectVar: getCurrentVar,
setCurrentVar, setInspectVar: setCurrentVar,
clearCurrentVars, clearInspectVars: clearCurrentVars,
clearCurrentNodeVars, clearNodeInspectVars: clearCurrentNodeVars,
getLastRunVar, getLastRunVar,
getLastRunInfos, getLastRunInfos,
} = workflowStore.getState() } = workflowStore.getState()

View File

@ -5,13 +5,19 @@ import { NodeRunningStatus } from '@/app/components/workflow/types'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import NoData from './no-data' import NoData from './no-data'
import { useLastRun } from '@/service/use-workflow'
import Loading from '@/app/components/base/loading'
type Props = { type Props = {
isDataFromHistory: boolean
appId: string
nodeId: string nodeId: string
runningStatus: NodeRunningStatus runningStatus?: NodeRunningStatus
} }
const LastRun: FC<Props> = ({ const LastRun: FC<Props> = ({
isDataFromHistory,
appId,
nodeId, nodeId,
runningStatus, runningStatus,
}) => { }) => {
@ -20,22 +26,31 @@ const LastRun: FC<Props> = ({
const { const {
getLastRunNodeInfo, getLastRunNodeInfo,
} = workflowStore.getState() } = workflowStore.getState()
const [runResult, setRunResult] = useState(getLastRunNodeInfo(nodeId)) const { data: runResultFromHistory, isFetching } = useLastRun(appId, nodeId, isDataFromHistory)
const [runResultFromSingleRun, setRunResult] = useState(isDataFromHistory ? getLastRunNodeInfo(nodeId) : null)
const runResult = isDataFromHistory ? runResultFromHistory : runResultFromSingleRun
const isRunning = runningStatus === NodeRunningStatus.Running const isRunning = runningStatus === NodeRunningStatus.Running
// get data from current running result
useEffect(() => { useEffect(() => {
if (isDataFromHistory)
return
setRunResult(getLastRunNodeInfo(nodeId)) setRunResult(getLastRunNodeInfo(nodeId))
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [runningStatus]) }, [runningStatus, isDataFromHistory])
const handleSingleRun = () => { const handleSingleRun = () => {
console.log('run') console.log('run')
} }
if (isDataFromHistory && isFetching)
return <Loading />
if (isRunning) if (isRunning)
return <ResultPanel status='running' showSteps={false} /> return <ResultPanel status='running' showSteps={false} />
if (!runResult) { if (!runResultFromSingleRun) {
return ( return (
<NoData onSingleRun={handleSingleRun} /> <NoData onSingleRun={handleSingleRun} />
) )

View File

@ -156,7 +156,7 @@ const useOneStepRun = <T>({
const workflowStore = useWorkflowStore() const workflowStore = useWorkflowStore()
const { const {
setLastRunNodeInfo, setLastRunNodeInfo,
setCurrentNodeVars, setNodeInspectVars: setCurrentNodeVars,
setShowSingleRunPanel, setShowSingleRunPanel,
} = workflowStore.getState() } = workflowStore.getState()
const [runResult, doSetRunResult] = useState<NodeRunResult | null>(null) const [runResult, doSetRunResult] = useState<NodeRunResult | null>(null)

View File

@ -1,96 +0,0 @@
import type { StateCreator } from 'zustand'
import produce from 'immer'
import type { NodeTracing } from '@/types/workflow'
// TODO: Missing var type
type NodeVars = NodeTracing
type CurrentVarsState = {
currentNodes: NodeVars[]
}
type CurrentVarsActions = {
setCurrentVars: (vars: NodeVars[]) => void
getCurrentVars: () => NodeVars[]
clearCurrentVars: () => void
setCurrentNodeVars: (nodeId: string, payload: NodeVars) => void
clearCurrentNodeVars: (nodeId: string) => void
getCurrentNodeVars: (nodeId: string) => NodeVars | undefined
hasCurrentNodeVars: (nodeId: string) => boolean
setCurrentVar: (nodeId: string, key: string, value: any) => void
getCurrentVar: (nodeId: string, key: string) => any
}
export type CurrentVarsSliceShape = CurrentVarsState & CurrentVarsActions
export const createCurrentVarsSlice: StateCreator<CurrentVarsSliceShape> = (set, get) => {
return ({
currentNodes: [],
setCurrentVars: (vars) => {
set(() => ({
currentNodes: vars,
}))
},
getCurrentVars: () => {
return get().currentNodes
},
clearCurrentVars: () => {
set(() => ({
currentNodes: [],
}))
},
setCurrentNodeVars: (nodeId, payload) => {
set((state) => {
const prevNodes = state.currentNodes
const nodes = produce(prevNodes, (draft) => {
const index = prevNodes.findIndex(node => node.id === nodeId)
if (index === -1)
draft.push(payload)
else
draft[index] = payload
})
return {
currentNodes: nodes,
}
})
},
clearCurrentNodeVars: (nodeId) => {
set(produce((state: CurrentVarsSliceShape) => {
const nodes = state.currentNodes.filter(node => node.node_id !== nodeId)
state.currentNodes = nodes
},
))
},
getCurrentNodeVars: (nodeId) => {
const nodes = get().currentNodes
return nodes.find(node => node.node_id === nodeId)
},
hasCurrentNodeVars: (nodeId) => {
return !!get().getCurrentNodeVars(nodeId)
},
setCurrentVar: (nodeId, key, value) => {
set(produce((state: CurrentVarsSliceShape) => {
const nodes = state.currentNodes.map((node) => {
if (node.id === nodeId) {
return produce(node, (draft) => {
if (!draft.outputs)
draft.outputs = {}
draft.outputs[key] = value
})
}
return node
})
state.currentNodes = nodes
}))
},
getCurrentVar(nodeId, key) {
const node = get().getCurrentNodeVars(nodeId)
if (!node)
return undefined
const variable = node.outputs?.[key]
return variable
},
})
}

View File

@ -30,8 +30,8 @@ import type { WorkflowSliceShape } from './workflow-slice'
import { createWorkflowSlice } from './workflow-slice' import { createWorkflowSlice } from './workflow-slice'
import type { LastRunSliceShape } from './last-run-slice' import type { LastRunSliceShape } from './last-run-slice'
import { createLastRunSlice } from './last-run-slice' import { createLastRunSlice } from './last-run-slice'
import type { CurrentVarsSliceShape } from './current-vars-slice' import type { CurrentVarsSliceShape } from './var-inspect-slice'
import { createCurrentVarsSlice } from './current-vars-slice' import { createInspectVarsSlice } from './var-inspect-slice'
import { WorkflowContext } from '@/app/components/workflow/context' import { WorkflowContext } from '@/app/components/workflow/context'
import type { LayoutSliceShape } from './layout-slice' import type { LayoutSliceShape } from './layout-slice'
@ -75,7 +75,7 @@ export const createWorkflowStore = (params: CreateWorkflowStoreParams) => {
...createWorkflowDraftSlice(...args), ...createWorkflowDraftSlice(...args),
...createWorkflowSlice(...args), ...createWorkflowSlice(...args),
...createLastRunSlice(...args), ...createLastRunSlice(...args),
...createCurrentVarsSlice(...args), ...createInspectVarsSlice(...args),
...createLayoutSlice(...args), ...createLayoutSlice(...args),
...(injectWorkflowStoreSliceFn?.(...args) || {} as WorkflowAppSliceShape), ...(injectWorkflowStoreSliceFn?.(...args) || {} as WorkflowAppSliceShape),
})) }))

View File

@ -0,0 +1,96 @@
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
nodes: NodeWithVar[] // the nodes have data
conversationVars: VarInInspect[]
}
type InspectVarsActions = {
getAllInspectVars: () => NodeWithVar[]
clearInspectVars: () => void
setNodeInspectVars: (nodeId: string, payload: NodeWithVar) => void
clearNodeInspectVars: (nodeId: string) => void
getNodeInspectVars: (nodeId: string) => NodeWithVar | undefined
hasNodeInspectVars: (nodeId: string) => boolean
setInspectVar: (nodeId: string, selector: ValueSelector, value: any) => void
getInspectVar: (nodeId: string, selector: ValueSelector) => any
}
export type CurrentVarsSliceShape = InspectVarsState & InspectVarsActions
export const createInspectVarsSlice: StateCreator<CurrentVarsSliceShape> = (set, get) => {
return ({
currentFocusNodeId: null,
nodes: [],
conversationVars: [],
getAllInspectVars: () => {
return get().nodes
},
clearInspectVars: () => {
set(() => ({
nodes: [],
}))
},
setNodeInspectVars: (nodeId, payload) => {
set((state) => {
const prevNodes = state.nodes
const nodes = produce(prevNodes, (draft) => {
const index = prevNodes.findIndex(node => node.nodeId === nodeId)
if (index === -1)
draft.push(payload)
else
draft[index] = payload
})
return {
nodes,
}
})
},
clearNodeInspectVars: (nodeId) => {
set(produce((state: CurrentVarsSliceShape) => {
const nodes = state.nodes.filter(node => node.nodeId !== nodeId)
state.nodes = nodes
},
))
},
getNodeInspectVars: (nodeId) => {
const nodes = get().nodes
return nodes.find(node => node.nodeId === nodeId)
},
hasNodeInspectVars: (nodeId) => {
return !!get().getNodeInspectVars(nodeId)
},
setInspectVar: (nodeId, selector, value) => {
set(produce((state: CurrentVarsSliceShape) => {
const nodes = state.nodes.map((node) => {
if (node.nodeId === nodeId) {
return produce(node, (draft) => {
const needChangeVarIndex = draft.vars.findIndex((varItem) => {
return varItem.selector.join('.') === selector.join('.')
})
if (needChangeVarIndex !== -1)
draft.vars[needChangeVarIndex].value = value
})
}
return node
})
state.nodes = nodes
}))
},
getInspectVar(nodeId, key) {
const node = get().getNodeInspectVars(nodeId)
if (!node)
return undefined
const variable = node.vars.find((varItem) => {
return varItem.selector.join('.') === key.join('.')
})?.value
return variable
},
})
}

View File

@ -4,12 +4,14 @@ import type {
FetchWorkflowDraftPageParams, FetchWorkflowDraftPageParams,
FetchWorkflowDraftPageResponse, FetchWorkflowDraftPageResponse,
FetchWorkflowDraftResponse, FetchWorkflowDraftResponse,
NodeTracing,
PublishWorkflowParams, PublishWorkflowParams,
UpdateWorkflowParams, UpdateWorkflowParams,
WorkflowConfigResponse, WorkflowConfigResponse,
} from '@/types/workflow' } from '@/types/workflow'
import type { CommonResponse } from '@/models/common' import type { CommonResponse } from '@/models/common'
import { useReset } from './use-base' import { useReset } from './use-base'
import { sleep } from '@/utils'
const NAME_SPACE = 'workflow' const NAME_SPACE = 'workflow'
@ -85,3 +87,39 @@ export const usePublishWorkflow = (appId: string) => {
}), }),
}) })
} }
export const useLastRun = (appID: string, nodeId: string, enabled: boolean) => {
return useQuery<NodeTracing>({
enabled,
queryKey: [NAME_SPACE, 'last-run', appID, nodeId],
queryFn: async () => {
// TODO: mock data
await sleep(1000)
return Promise.resolve({
node_id: nodeId,
status: 'success',
node_type: 'llm',
title: 'LLM',
inputs: null,
outputs: {
text: '"abc" is a simple sequence of three letters. Is there anything specific you\'d like to know about it, or are you just testing the system? \n\nLet me know if you have any other questions or tasks! 😊 \n',
usage: {
prompt_tokens: 3,
prompt_unit_price: '0',
prompt_price_unit: '0.000001',
prompt_price: '0',
completion_tokens: 48,
completion_unit_price: '0',
completion_price_unit: '0.000001',
completion_price: '0',
total_tokens: 51,
total_price: '0',
currency: 'USD',
latency: 0.7095853444188833,
},
finish_reason: '1',
},
} as any)
},
})
}

View File

@ -1,5 +1,5 @@
import type { Viewport } from 'reactflow' import type { Viewport } from 'reactflow'
import type { BlockEnum, ConversationVariable, Edge, EnvironmentVariable, InputVar, Node, Variable } from '@/app/components/workflow/types' import type { BlockEnum, ConversationVariable, Edge, EnvironmentVariable, InputVar, Node, ValueSelector, VarType, Variable } from '@/app/components/workflow/types'
import type { TransferMethod } from '@/types/app' import type { TransferMethod } from '@/types/app'
import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types'
import type { BeforeRunFormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form' import type { BeforeRunFormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form'
@ -370,3 +370,28 @@ export type PanelProps = {
} }
export type NodeRunResult = NodeTracing export type NodeRunResult = NodeTracing
// Var Inspect
export enum VarInInspectType {
conversation = 'conversation',
environment = 'environment',
node = 'node',
}
export type VarInInspect = {
id: string
type: VarInInspectType
name: string
description: string
selector: ValueSelector
value_type: VarType
value: any
edited: boolean
}
export type NodeWithVar = {
nodeId: string
nodeType: BlockEnum
title: string
vars: VarInInspect[]
}