Merge remote-tracking branch 'origin/main' into feat/trigger

This commit is contained in:
yessenia
2025-09-25 17:14:24 +08:00
3013 changed files with 148826 additions and 44294 deletions

View File

@ -7,10 +7,9 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types'
import { DSL_EXPORT_CHECK } from '@/app/components/workflow/constants'
import { START_INITIAL_POSITION } from '@/app/components/workflow/constants'
import { generateNewNode } from '@/app/components/workflow/utils'
import { useNodesInitialData } from '@/app/components/workflow/hooks'
import { useStore } from '@/app/components/workflow/store'
import { useStoreApi } from 'reactflow'
import PluginDependency from '@/app/components/workflow/plugin-dependency'
import PluginDependency from '../../workflow/plugin-dependency'
import {
useDSL,
usePanelInteractions,
@ -23,7 +22,11 @@ import dynamic from 'next/dynamic'
import { BlockEnum } from '@/app/components/workflow/types'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
import { useAutoOnboarding } from '../hooks/use-auto-onboarding'
import { useAvailableNodesMetaData } from '../hooks'
const Features = dynamic(() => import('@/app/components/workflow/features'), {
ssr: false,
})
const UpdateDSLModal = dynamic(() => import('@/app/components/workflow/update-dsl-modal'), {
ssr: false,
})
@ -37,13 +40,14 @@ const WorkflowOnboardingModal = dynamic(() => import('./workflow-onboarding-moda
const WorkflowChildren = () => {
const { eventEmitter } = useEventEmitterContextContext()
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
const showFeaturesPanel = useStore(s => s.showFeaturesPanel)
const showImportDSLModal = useStore(s => s.showImportDSLModal)
const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal)
const showOnboarding = useStore(s => s.showOnboarding)
const setShowOnboarding = useStore(s => s.setShowOnboarding)
const setHasSelectedStartNode = useStore(s => s.setHasSelectedStartNode)
const reactFlowStore = useStoreApi()
const nodesInitialData = useNodesInitialData()
const availableNodesMetaData = useAvailableNodesMetaData()
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { handleOnboardingClose } = useAutoOnboarding()
const {
@ -65,13 +69,13 @@ const WorkflowChildren = () => {
const handleSelectStartNode = useCallback((nodeType: BlockEnum, toolConfig?: ToolDefaultValue) => {
const nodeData = nodeType === BlockEnum.Start
? nodesInitialData.start
: { ...nodesInitialData[nodeType], ...toolConfig }
? availableNodesMetaData.nodesMap?.[BlockEnum.Start]
: { ...availableNodesMetaData.nodesMap?.[nodeType], ...toolConfig }
const { newNode } = generateNewNode({
data: {
...nodeData,
},
} as any,
position: START_INITIAL_POSITION,
})
@ -79,8 +83,8 @@ const WorkflowChildren = () => {
setNodes([newNode])
setEdges([])
setShowOnboarding(false)
setHasSelectedStartNode(true)
setShowOnboarding?.(false)
setHasSelectedStartNode?.(true)
handleSyncWorkflowDraft(true, false, {
onSuccess: () => {
@ -90,11 +94,14 @@ const WorkflowChildren = () => {
console.error('Failed to save node to draft')
},
})
}, [nodesInitialData, setShowOnboarding, setHasSelectedStartNode, reactFlowStore, handleSyncWorkflowDraft])
}, [availableNodesMetaData, setShowOnboarding, setHasSelectedStartNode, reactFlowStore, handleSyncWorkflowDraft])
return (
<>
<PluginDependency />
{
showFeaturesPanel && <Features />
}
{
showOnboarding && (
<WorkflowOnboardingModal
@ -108,7 +115,7 @@ const WorkflowChildren = () => {
showImportDSLModal && (
<UpdateDSLModal
onCancel={() => setShowImportDSLModal(false)}
onBackup={exportCheck}
onBackup={exportCheck!}
onImport={handlePaneContextmenuCancel}
/>
)
@ -117,7 +124,7 @@ const WorkflowChildren = () => {
secretEnvList.length > 0 && (
<DSLExportConfirmModal
envList={secretEnvList}
onConfirm={handleExportDSL}
onConfirm={handleExportDSL!}
onClose={() => setSecretEnvList([])}
/>
)

View File

@ -3,20 +3,27 @@ import {
useCallback,
useMemo,
} from 'react'
import { useStore as useReactflowStore } from 'reactflow'
import { useEdges, useNodes, useStore as useReactflowStore } from 'reactflow'
import { RiApps2AddLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import {
useStore,
useWorkflowStore,
} from '@/app/components/workflow/store'
import {
useChecklist,
useChecklistBeforePublish,
useNodesReadOnly,
useNodesSyncDraft,
useWorkflowRunValidation,
// useWorkflowRunValidation,
} from '@/app/components/workflow/hooks'
import Button from '@/app/components/base/button'
import AppPublisher from '@/app/components/app/app-publisher'
import { useFeatures } from '@/app/components/base/features/hooks'
import type {
CommonEdgeType,
CommonNodeType,
} from '@/app/components/workflow/types'
import {
BlockEnum,
InputVarType,
@ -27,14 +34,17 @@ import { useInvalidateAppTriggers } from '@/service/use-tools'
import type { PublishWorkflowParams } from '@/types/workflow'
import { fetchAppDetail } from '@/service/apps'
import { useStore as useAppStore } from '@/app/components/app/store'
import useTheme from '@/hooks/use-theme'
import cn from '@/utils/classnames'
const AppPublisherTrigger = () => {
const FeaturesTrigger = () => {
const { t } = useTranslation()
const { theme } = useTheme()
const workflowStore = useWorkflowStore()
const appDetail = useAppStore(s => s.appDetail)
const appID = appDetail?.id
const setAppDetail = useAppStore(s => s.setAppDetail)
const { nodesReadOnly } = useNodesReadOnly()
const { nodesReadOnly, getNodesReadOnly } = useNodesReadOnly()
const publishedAt = useStore(s => s.publishedAt)
const draftUpdatedAt = useStore(s => s.draftUpdatedAt)
const toolPublished = useStore(s => s.toolPublished)
@ -63,9 +73,20 @@ const AppPublisherTrigger = () => {
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { notify } = useToastContext()
const resetWorkflowVersionHistory = useResetWorkflowVersionHistory(appDetail!.id)
const resetWorkflowVersionHistory = useResetWorkflowVersionHistory()
const invalidateAppTriggers = useInvalidateAppTriggers()
const handleShowFeatures = useCallback(() => {
const {
showFeaturesPanel,
isRestoring,
setShowFeaturesPanel,
} = workflowStore.getState()
if (getNodesReadOnly() && !isRestoring)
return
setShowFeaturesPanel(!showFeaturesPanel)
}, [workflowStore, getNodesReadOnly])
const updateAppDetail = useCallback(async () => {
try {
const res = await fetchAppDetail({ url: '/apps', id: appID! })
@ -76,18 +97,27 @@ const AppPublisherTrigger = () => {
}
}, [appID, setAppDetail])
const { mutateAsync: publishWorkflow } = usePublishWorkflow(appID!)
const { validateBeforeRun } = useWorkflowRunValidation()
const { mutateAsync: publishWorkflow } = usePublishWorkflow()
// const { validateBeforeRun } = useWorkflowRunValidation()
const nodes = useNodes<CommonNodeType>()
const edges = useEdges<CommonEdgeType>()
const needWarningNodes = useChecklist(nodes, edges)
const updatePublishedWorkflow = useInvalidateAppWorkflow()
const onPublish = useCallback(async (params?: PublishWorkflowParams) => {
// First check if there are any items in the checklist
if (!validateBeforeRun())
// if (!validateBeforeRun())
// throw new Error('Checklist has unresolved items')
if (needWarningNodes.length > 0) {
notify({ type: 'error', message: t('workflow.panel.checklistTip') })
throw new Error('Checklist has unresolved items')
}
// Then perform the detailed validation
if (await handleCheckBeforePublish()) {
const res = await publishWorkflow({
url: `/apps/${appID}/workflows/publish`,
title: params?.title || '',
releaseNotes: params?.releaseNotes || '',
})
@ -104,7 +134,7 @@ const AppPublisherTrigger = () => {
else {
throw new Error('Checklist failed')
}
}, [validateBeforeRun, handleCheckBeforePublish, publishWorkflow, updatePublishedWorkflow, appID, updateAppDetail, invalidateAppTriggers, workflowStore, resetWorkflowVersionHistory])
}, [needWarningNodes, handleCheckBeforePublish, publishWorkflow, notify, appID, t, updatePublishedWorkflow, updateAppDetail, workflowStore, resetWorkflowVersionHistory, invalidateAppTriggers])
const onPublisherToggle = useCallback((state: boolean) => {
if (state)
@ -116,20 +146,32 @@ const AppPublisherTrigger = () => {
}, [workflowStore])
return (
<AppPublisher
{...{
publishedAt,
draftUpdatedAt,
disabled: nodesReadOnly,
toolPublished,
inputs: variables,
onRefreshData: handleToolConfigureUpdate,
onPublish,
onToggle: onPublisherToggle,
crossAxisOffset: 4,
}}
/>
<>
<Button
className={cn(
'text-components-button-secondary-text',
theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm',
)}
onClick={handleShowFeatures}
>
<RiApps2AddLine className='mr-1 h-4 w-4 text-components-button-secondary-text' />
{t('workflow.common.features')}
</Button>
<AppPublisher
{...{
publishedAt,
draftUpdatedAt,
disabled: nodesReadOnly,
toolPublished,
inputs: variables,
onRefreshData: handleToolConfigureUpdate,
onPublish,
onToggle: onPublisherToggle,
crossAxisOffset: 4,
}}
/>
</>
)
}
export default memo(AppPublisherTrigger)
export default memo(FeaturesTrigger)

View File

@ -1,31 +1,66 @@
import { useMemo } from 'react'
import {
memo,
useCallback,
useMemo,
} from 'react'
import { useShallow } from 'zustand/react/shallow'
import type { HeaderProps } from '@/app/components/workflow/header'
import Header from '@/app/components/workflow/header'
import { useStore as useAppStore } from '@/app/components/app/store'
import {
fetchWorkflowRunHistory,
} from '@/service/workflow'
import ChatVariableTrigger from './chat-variable-trigger'
import AppPublisherTrigger from './app-publisher-trigger'
import FeaturesTrigger from './features-trigger'
import { useResetWorkflowVersionHistory } from '@/service/use-workflow'
import { useIsChatMode } from '../../hooks'
const WorkflowHeader = () => {
const appDetail = useAppStore(s => s.appDetail)
const resetWorkflowVersionHistory = useResetWorkflowVersionHistory(appDetail!.id)
const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
appDetail: state.appDetail,
setCurrentLogItem: state.setCurrentLogItem,
setShowMessageLogModal: state.setShowMessageLogModal,
})))
const resetWorkflowVersionHistory = useResetWorkflowVersionHistory()
const isChatMode = useIsChatMode()
const handleClearLogAndMessageModal = useCallback(() => {
setCurrentLogItem()
setShowMessageLogModal(false)
}, [setCurrentLogItem, setShowMessageLogModal])
const viewHistoryProps = useMemo(() => {
return {
onClearLogAndMessageModal: handleClearLogAndMessageModal,
historyUrl: isChatMode ? `/apps/${appDetail!.id}/advanced-chat/workflow-runs` : `/apps/${appDetail!.id}/workflow-runs`,
historyFetcher: fetchWorkflowRunHistory,
}
}, [appDetail, isChatMode, handleClearLogAndMessageModal])
const headerProps: HeaderProps = useMemo(() => {
return {
normal: {
components: {
left: <ChatVariableTrigger />,
middle: <AppPublisherTrigger />,
middle: <FeaturesTrigger />,
},
runAndHistoryProps: {
showRunButton: !isChatMode,
showPreviewButton: isChatMode,
viewHistoryProps,
},
},
viewHistory: {
viewHistoryProps,
},
restoring: {
onRestoreSettled: resetWorkflowVersionHistory,
},
}
}, [resetWorkflowVersionHistory])
}, [resetWorkflowVersionHistory, isChatMode, viewHistoryProps])
return (
<Header {...headerProps} />
)
}
export default WorkflowHeader
export default memo(WorkflowHeader)

View File

@ -7,7 +7,10 @@ import { WorkflowWithInnerContext } from '@/app/components/workflow'
import type { WorkflowProps } from '@/app/components/workflow'
import WorkflowChildren from './workflow-children'
import {
useAvailableNodesMetaData,
useConfigsMap,
useDSL,
useGetRunAndTraceUrl,
useInspectVarsCrud,
useNodesSyncDraft,
useSetWorkflowVarsWithValue,
@ -15,7 +18,7 @@ import {
useWorkflowRun,
useWorkflowStartRun,
} from '../hooks'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import { useWorkflowStore } from '@/app/components/workflow/store'
type WorkflowMainProps = Pick<WorkflowProps, 'nodes' | 'edges' | 'viewport'>
const WorkflowMain = ({
@ -64,10 +67,16 @@ const WorkflowMain = ({
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
} = useWorkflowStartRun()
const appId = useStore(s => s.appId)
const availableNodesMetaData = useAvailableNodesMetaData()
const { getWorkflowRunAndTraceUrl } = useGetRunAndTraceUrl()
const {
exportCheck,
handleExportDSL,
} = useDSL()
const configsMap = useConfigsMap()
const { fetchInspectVars } = useSetWorkflowVarsWithValue({
flowId: appId,
...useConfigsMap(),
...configsMap,
})
const {
hasNodeInspectVars,
@ -85,7 +94,6 @@ const WorkflowMain = ({
resetConversationVar,
invalidateConversationVarValues,
} = useInspectVarsCrud()
const configsMap = useConfigsMap()
const hooksStore = useMemo(() => {
return {
@ -100,6 +108,10 @@ const WorkflowMain = ({
handleStartWorkflowRun,
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
availableNodesMetaData,
getWorkflowRunAndTraceUrl,
exportCheck,
handleExportDSL,
fetchInspectVars,
hasNodeInspectVars,
hasSetInspectVar,
@ -129,6 +141,10 @@ const WorkflowMain = ({
handleStartWorkflowRun,
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
availableNodesMetaData,
getWorkflowRunAndTraceUrl,
exportCheck,
handleExportDSL,
fetchInspectVars,
hasNodeInspectVars,
hasSetInspectVar,
@ -153,7 +169,7 @@ const WorkflowMain = ({
edges={edges}
viewport={viewport}
onWorkflowDataUpdate={handleWorkflowDataUpdate}
hooksStore={hooksStore}
hooksStore={hooksStore as any}
>
<WorkflowChildren />
</WorkflowWithInnerContext>

View File

@ -1,4 +1,7 @@
import { useMemo } from 'react'
import {
memo,
useMemo,
} from 'react'
import { useShallow } from 'zustand/react/shallow'
import { useStore } from '@/app/components/workflow/store'
import {
@ -30,9 +33,6 @@ const ChatVariablePanel = dynamic(() => import('@/app/components/workflow/panel/
const GlobalVariablePanel = dynamic(() => import('@/app/components/workflow/panel/global-variable-panel'), {
ssr: false,
})
const VersionHistoryPanel = dynamic(() => import('@/app/components/workflow/panel/version-history-panel'), {
ssr: false,
})
const WorkflowPanelOnLeft = () => {
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
@ -67,7 +67,6 @@ const WorkflowPanelOnRight = () => {
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
const showChatVariablePanel = useStore(s => s.showChatVariablePanel)
const showGlobalVariablePanel = useStore(s => s.showGlobalVariablePanel)
const showWorkflowVersionHistoryPanel = useStore(s => s.showWorkflowVersionHistoryPanel)
return (
<>
@ -101,27 +100,34 @@ const WorkflowPanelOnRight = () => {
<GlobalVariablePanel />
)
}
{
showWorkflowVersionHistoryPanel && (
<VersionHistoryPanel/>
)
}
</>
)
}
const WorkflowPanel = () => {
const appDetail = useAppStore(s => s.appDetail)
const versionHistoryPanelProps = useMemo(() => {
const appId = appDetail?.id
return {
getVersionListUrl: `/apps/${appId}/workflows`,
deleteVersionUrl: (versionId: string) => `/apps/${appId}/workflows/${versionId}`,
updateVersionUrl: (versionId: string) => `/apps/${appId}/workflows/${versionId}`,
latestVersionId: appDetail?.workflow?.id,
}
}, [appDetail?.id, appDetail?.workflow?.id])
const panelProps: PanelProps = useMemo(() => {
return {
components: {
left: <WorkflowPanelOnLeft />,
right: <WorkflowPanelOnRight />,
},
versionHistoryPanelProps,
}
}, [])
}, [versionHistoryPanelProps])
return (
<Panel {...panelProps} />
)
}
export default WorkflowPanel
export default memo(WorkflowPanel)

View File

@ -4,7 +4,10 @@ export * from './use-nodes-sync-draft'
export * from './use-workflow-run'
export * from './use-workflow-start-run'
export * from './use-is-chat-mode'
export * from './use-available-nodes-meta-data'
export * from './use-workflow-refresh-draft'
export * from './use-get-run-and-trace-url'
export * from './use-DSL'
export * from '../../workflow/hooks/use-fetch-workflow-inspect-vars'
export * from './use-inspect-vars-crud'
export * from './use-configs-map'

View File

@ -0,0 +1,82 @@
import {
useCallback,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import {
DSL_EXPORT_CHECK,
} from '@/app/components/workflow/constants'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { fetchWorkflowDraft } from '@/service/workflow'
import { exportAppConfig } from '@/service/apps'
import { useToastContext } from '@/app/components/base/toast'
import { useStore as useAppStore } from '@/app/components/app/store'
export const useDSL = () => {
const { t } = useTranslation()
const { notify } = useToastContext()
const { eventEmitter } = useEventEmitterContextContext()
const [exporting, setExporting] = useState(false)
const { doSyncWorkflowDraft } = useNodesSyncDraft()
const appDetail = useAppStore(s => s.appDetail)
const handleExportDSL = useCallback(async (include = false, workflowId?: string) => {
if (!appDetail)
return
if (exporting)
return
try {
setExporting(true)
await doSyncWorkflowDraft()
const { data } = await exportAppConfig({
appID: appDetail.id,
include,
workflowID: workflowId,
})
const a = document.createElement('a')
const file = new Blob([data], { type: 'application/yaml' })
const url = URL.createObjectURL(file)
a.href = url
a.download = `${appDetail.name}.yml`
a.click()
URL.revokeObjectURL(url)
}
catch {
notify({ type: 'error', message: t('app.exportFailed') })
}
finally {
setExporting(false)
}
}, [appDetail, notify, t, doSyncWorkflowDraft, exporting])
const exportCheck = useCallback(async () => {
if (!appDetail)
return
try {
const workflowDraft = await fetchWorkflowDraft(`/apps/${appDetail?.id}/workflows/draft`)
const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret')
if (list.length === 0) {
handleExportDSL()
return
}
eventEmitter?.emit({
type: DSL_EXPORT_CHECK,
payload: {
data: list,
},
} as any)
}
catch {
notify({ type: 'error', message: t('app.exportFailed') })
}
}, [appDetail, eventEmitter, handleExportDSL, notify, t])
return {
exportCheck,
handleExportDSL,
}
}

View File

@ -0,0 +1,76 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useGetLanguage } from '@/context/i18n'
import StartDefault from '@/app/components/workflow/nodes/start/default'
import TriggerWebhookDefault from '@/app/components/workflow/nodes/trigger-webhook/default'
import TriggerScheduleDefault from '@/app/components/workflow/nodes/trigger-schedule/default'
import TriggerPluginDefault from '@/app/components/workflow/nodes/trigger-plugin/default'
import EndDefault from '@/app/components/workflow/nodes/end/default'
import AnswerDefault from '@/app/components/workflow/nodes/answer/default'
import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node'
import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store'
import { useIsChatMode } from './use-is-chat-mode'
import { BlockEnum } from '@/app/components/workflow/types'
export const useAvailableNodesMetaData = () => {
const { t } = useTranslation()
const isChatMode = useIsChatMode()
const language = useGetLanguage()
const mergedNodesMetaData = useMemo(() => [
...WORKFLOW_COMMON_NODES,
StartDefault,
...(
isChatMode
? [AnswerDefault]
: [
EndDefault,
TriggerWebhookDefault,
TriggerScheduleDefault,
TriggerPluginDefault,
]
),
], [isChatMode])
const prefixLink = useMemo(() => {
if (language === 'zh_Hans')
return 'https://docs.dify.ai/zh-hans/guides/workflow/node/'
return 'https://docs.dify.ai/guides/workflow/node/'
}, [language])
const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => {
const { metaData } = node
const title = t(`workflow.blocks.${metaData.type}`)
const description = t(`workflow.blocksAbout.${metaData.type}`)
return {
...node,
metaData: {
...metaData,
title,
description,
helpLinkUri: `${prefixLink}${metaData.helpLinkUri}`,
},
defaultValue: {
...node.defaultValue,
type: metaData.type,
title,
},
}
}), [mergedNodesMetaData, t, prefixLink])
const availableNodesMetaDataMap = useMemo(() => availableNodesMetaData.reduce((acc, node) => {
acc![node.metaData.type] = node
return acc
}, {} as AvailableNodesMetaData['nodesMap']), [availableNodesMetaData])
return useMemo(() => {
return {
nodes: availableNodesMetaData,
nodesMap: {
...availableNodesMetaDataMap,
[BlockEnum.VariableAssigner]: availableNodesMetaDataMap?.[BlockEnum.VariableAggregator],
},
}
}, [availableNodesMetaData, availableNodesMetaDataMap])
}

View File

@ -1,13 +1,16 @@
import { useMemo } from 'react'
import { useStore } from '@/app/components/workflow/store'
import { FlowType } from '@/types/common'
import { useFeatures } from '@/app/components/base/features/hooks'
export const useConfigsMap = () => {
const appId = useStore(s => s.appId)
const fileSettings = useFeatures(s => s.features.file)
return useMemo(() => {
return {
flowId: appId,
conversationVarsUrl: `apps/${appId}/workflows/draft/conversation-variables`,
systemVarsUrl: `apps/${appId}/workflows/draft/system-variables`,
flowId: appId!,
flowType: FlowType.appFlow,
fileSettings,
}
}, [appId])
}

View File

@ -0,0 +1,18 @@
import { useCallback } from 'react'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useGetRunAndTraceUrl = () => {
const workflowStore = useWorkflowStore()
const getWorkflowRunAndTraceUrl = useCallback((runId: string) => {
const { appId } = workflowStore.getState()
return {
runUrl: `/apps/${appId}/workflow-runs/${runId}`,
traceUrl: `/apps/${appId}/workflow-runs/${runId}/node-executions`,
}
}, [workflowStore])
return {
getWorkflowRunAndTraceUrl,
}
}

View File

@ -1,12 +1,9 @@
import { useStore } from '@/app/components/workflow/store'
import { useInspectVarsCrudCommon } from '../../workflow/hooks/use-inspect-vars-crud-common'
import { useConfigsMap } from './use-configs-map'
export const useInspectVarsCrud = () => {
const appId = useStore(s => s.appId)
const configsMap = useConfigsMap()
const apis = useInspectVarsCrudCommon({
flowId: appId,
...configsMap,
})

View File

@ -5,6 +5,7 @@ import { useParams } from 'next/navigation'
import {
useWorkflowStore,
} from '@/app/components/workflow/store'
import { BlockEnum } from '@/app/components/workflow/types'
import {
useNodesReadOnly,
} from '@/app/components/workflow/hooks/use-workflow'
@ -27,20 +28,20 @@ export const useNodesSyncDraft = () => {
edges,
transform,
} = store.getState()
const nodes = getNodes()
const [x, y, zoom] = transform
const {
appId,
conversationVariables,
environmentVariables,
syncWorkflowDraftHash,
isWorkflowDataLoaded,
// isWorkflowDataLoaded,
} = workflowStore.getState()
if (appId) {
const nodes = getNodes()
if (appId && !!nodes.length) {
const hasStartNode = nodes.find(node => node.data.type === BlockEnum.Start)
// Prevent sync if workflow data hasn't been loaded yet
if (!isWorkflowDataLoaded)
if (!hasStartNode)
return null
const features = featuresStore!.getState().features
@ -52,7 +53,7 @@ export const useNodesSyncDraft = () => {
})
})
})
const producedEdges = produce(edges, (draft) => {
const producedEdges = produce(edges.filter(edge => !edge.data?._isTemp), (draft) => {
draft.forEach((edge) => {
Object.keys(edge.data).forEach((key) => {
if (key.startsWith('_'))

View File

@ -17,6 +17,8 @@ import {
} from '@/service/workflow'
import type { FetchWorkflowDraftResponse } from '@/types/workflow'
import { useWorkflowConfig } from '@/service/use-workflow'
import type { FileUploadConfigResponse } from '@/models/common'
export const useWorkflowInit = () => {
const workflowStore = useWorkflowStore()
const {
@ -28,7 +30,7 @@ export const useWorkflowInit = () => {
const [data, setData] = useState<FetchWorkflowDraftResponse>()
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
workflowStore.setState({ appId: appDetail.id })
workflowStore.setState({ appId: appDetail.id, appName: appDetail.name })
}, [appDetail.id, workflowStore])
const handleUpdateWorkflowConfig = useCallback((config: Record<string, any>) => {
@ -36,7 +38,16 @@ export const useWorkflowInit = () => {
setWorkflowConfig(config)
}, [workflowStore])
useWorkflowConfig(appDetail.id, handleUpdateWorkflowConfig)
useWorkflowConfig(`/apps/${appDetail.id}/workflows/draft/config`, handleUpdateWorkflowConfig)
const handleUpdateWorkflowFileUploadConfig = useCallback((config: FileUploadConfigResponse) => {
const { setFileUploadConfig } = workflowStore.getState()
setFileUploadConfig(config)
}, [workflowStore])
const {
data: fileUploadConfigResponse,
isLoading: isFileUploadConfigLoading,
} = useWorkflowConfig('/files/upload', handleUpdateWorkflowFileUploadConfig)
const handleGetInitialWorkflowData = useCallback(async () => {
try {
@ -125,6 +136,7 @@ export const useWorkflowInit = () => {
return {
data,
isLoading,
isLoading: isLoading || isFileUploadConfigLoading,
fileUploadConfigResponse,
}
}

View File

@ -31,9 +31,10 @@ export const useWorkflowRun = () => {
const { doSyncWorkflowDraft } = useNodesSyncDraft()
const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
const pathname = usePathname()
const appId = useAppStore.getState().appDetail?.id
const invalidAllLastRun = useInvalidAllLastRun(appId as string)
const configsMap = useConfigsMap()
const { flowId, flowType } = configsMap
const invalidAllLastRun = useInvalidAllLastRun(flowType, flowId)
const { fetchInspectVars } = useSetWorkflowVarsWithValue({
...configsMap,
})
@ -201,7 +202,7 @@ export const useWorkflowRun = () => {
if (onWorkflowFinished)
onWorkflowFinished(params)
if (isInWorkflowDebug) {
fetchInspectVars()
fetchInspectVars({})
invalidAllLastRun()
}
},

View File

@ -1,17 +1,25 @@
import { useTranslation } from 'react-i18next'
import { generateNewNode } from '@/app/components/workflow/utils'
import {
NODE_WIDTH_X_OFFSET,
START_INITIAL_POSITION,
} from '@/app/components/workflow/constants'
import { useNodesInitialData } from '@/app/components/workflow/hooks'
import { useIsChatMode } from './use-is-chat-mode'
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
import startDefault from '@/app/components/workflow/nodes/start/default'
import llmDefault from '@/app/components/workflow/nodes/llm/default'
import answerDefault from '@/app/components/workflow/nodes/answer/default'
export const useWorkflowTemplate = () => {
const isChatMode = useIsChatMode()
const nodesInitialData = useNodesInitialData()
const { t } = useTranslation()
const { newNode: startNode } = generateNewNode({
data: nodesInitialData.start,
data: {
...startDefault.defaultValue as StartNodeType,
type: startDefault.metaData.type,
title: t(`workflow.blocks.${startDefault.metaData.type}`),
},
position: START_INITIAL_POSITION,
})
@ -19,12 +27,14 @@ export const useWorkflowTemplate = () => {
const { newNode: llmNode } = generateNewNode({
id: 'llm',
data: {
...nodesInitialData.llm,
...llmDefault.defaultValue,
memory: {
window: { enabled: false, size: 10 },
query_prompt_template: '{{#sys.query#}}\n\n{{#sys.files#}}',
},
selected: true,
type: llmDefault.metaData.type,
title: t(`workflow.blocks.${llmDefault.metaData.type}`),
},
position: {
x: START_INITIAL_POSITION.x + NODE_WIDTH_X_OFFSET,
@ -35,8 +45,10 @@ export const useWorkflowTemplate = () => {
const { newNode: answerNode } = generateNewNode({
id: 'answer',
data: {
...nodesInitialData.answer,
...answerDefault.defaultValue,
answer: `{{#${llmNode.id}.text#}}`,
type: answerDefault.metaData.type,
title: t(`workflow.blocks.${answerDefault.metaData.type}`),
},
position: {
x: START_INITIAL_POSITION.x + NODE_WIDTH_X_OFFSET * 2,

View File

@ -4,7 +4,6 @@ import {
useEffect,
useMemo,
} from 'react'
import useSWR from 'swr'
import {
SupportUploadFileTypes,
} from '@/app/components/workflow/types'
@ -23,12 +22,12 @@ import Loading from '@/app/components/base/loading'
import { FeaturesProvider } from '@/app/components/base/features'
import type { Features as FeaturesData } from '@/app/components/base/features/types'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import { fetchFileUploadConfig } from '@/service/common'
import { useAppContext } from '@/context/app-context'
import WorkflowWithDefaultContext from '@/app/components/workflow'
import {
WorkflowContextProvider,
} from '@/app/components/workflow/context'
import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store'
import { createWorkflowSlice } from './store/workflow/workflow-slice'
import WorkflowAppMain from './components/workflow-main'
@ -36,10 +35,10 @@ const WorkflowAppWithAdditionalContext = () => {
const {
data,
isLoading,
fileUploadConfigResponse,
} = useWorkflowInit()
const workflowStore = useWorkflowStore()
const { isLoadingCurrentWorkspace, currentWorkspace } = useAppContext()
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
// Initialize trigger status at application level
const { setTriggerStatuses } = useTriggerStatusStore()
@ -145,7 +144,7 @@ const WorkflowAppWithAdditionalContext = () => {
const WorkflowAppWrapper = () => {
return (
<WorkflowContextProvider
injectWorkflowStoreSliceFn={createWorkflowSlice}
injectWorkflowStoreSliceFn={createWorkflowSlice as InjectWorkflowStoreSliceFn}
>
<WorkflowAppWithAdditionalContext />
</WorkflowContextProvider>

View File

@ -2,6 +2,7 @@ import type { StateCreator } from 'zustand'
export type WorkflowSliceShape = {
appId: string
appName: string
notInitialWorkflow: boolean
setNotInitialWorkflow: (notInitialWorkflow: boolean) => void
nodesDefaultConfigs: Record<string, any>
@ -17,6 +18,7 @@ export type WorkflowSliceShape = {
export type CreateWorkflowSlice = StateCreator<WorkflowSliceShape>
export const createWorkflowSlice: StateCreator<WorkflowSliceShape> = set => ({
appId: '',
appName: '',
notInitialWorkflow: false,
setNotInitialWorkflow: notInitialWorkflow => set(() => ({ notInitialWorkflow })),
nodesDefaultConfigs: {},