mirror of
https://github.com/langgenius/dify.git
synced 2026-04-24 04:45:51 +08:00
Merge remote-tracking branch 'origin/main' into feat/trigger
This commit is contained in:
@ -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'
|
||||
|
||||
82
web/app/components/workflow-app/hooks/use-DSL.ts
Normal file
82
web/app/components/workflow-app/hooks/use-DSL.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
})
|
||||
|
||||
|
||||
@ -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('_'))
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
},
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user