Files
dify/web/service/workflow.ts
yyh 30b9295156 Merge remote-tracking branch 'origin/build/feat/hitl' into wip/hitl-merge-web-conflicts-20260206-175434
# Conflicts:
#	api/.env.example
#	api/core/app/apps/advanced_chat/app_generator.py
#	api/core/app/apps/advanced_chat/app_runner.py
#	api/core/app/apps/advanced_chat/generate_task_pipeline.py
#	api/core/app/apps/workflow/app_generator.py
#	api/core/app/apps/workflow/app_runner.py
#	api/core/app/entities/queue_entities.py
#	api/core/workflow/node_events/__init__.py
#	api/core/workflow/runtime/graph_runtime_state.py
#	api/fields/message_fields.py
#	api/services/workflow_service.py
#	web/app/components/app/app-publisher/index.tsx
#	web/app/components/base/chat/chat/answer/index.tsx
#	web/app/components/base/chat/chat/hooks.ts
#	web/app/components/base/chat/chat/type.ts
#	web/app/components/base/prompt-editor/index.tsx
#	web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts
#	web/app/components/share/text-generation/result/header.tsx
#	web/app/components/workflow-app/components/workflow-header/features-trigger.tsx
#	web/app/components/workflow-app/hooks/use-workflow-run.ts
#	web/app/components/workflow/hooks/use-checklist.ts
#	web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts
#	web/app/components/workflow/hooks/use-workflow.ts
#	web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx
#	web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx
#	web/app/components/workflow/nodes/_base/node.tsx
#	web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/components/placeholder.tsx
#	web/app/components/workflow/panel/debug-and-preview/hooks.ts
#	web/app/components/workflow/panel/workflow-preview.tsx
#	web/app/components/workflow/store/workflow/workflow-slice.ts
#	web/eslint-suppressions.json
#	web/i18n/en-US/common.json
#	web/i18n/zh-Hans/common.json
#	web/i18n/zh-Hant/common.json
#	web/service/workflow.ts
2026-02-06 19:13:51 +08:00

180 lines
6.2 KiB
TypeScript

import type { BlockEnum, ConversationVariable, EnvironmentVariable } from '@/app/components/workflow/types'
import type { WorkflowDraftFeaturesPayload } from '@/contract/console/workflow'
import type { CommonResponse } from '@/models/common'
import type { FlowType } from '@/types/common'
import type {
ConversationVariableResponse,
FetchWorkflowDraftResponse,
HumanInputFormData,
NestedNodeGraphPayload,
NestedNodeGraphResponse,
NodesDefaultConfigsResponse,
VarInInspect,
} from '@/types/workflow'
import { get, post } from './base'
import { consoleClient } from './client'
import { getFlowPrefix } from './utils'
import { sanitizeWorkflowDraftPayload } from './workflow-payload'
export type { WorkflowDraftFeaturesPayload } from '@/contract/console/workflow'
export const fetchWorkflowDraft = (url: string) => {
return get(url, {}, { silent: true }) as Promise<FetchWorkflowDraftResponse>
}
export const syncWorkflowDraft = ({ url, params, canNotSaveEmpty }: {
url: string
params: Pick<FetchWorkflowDraftResponse, 'graph' | 'features' | 'environment_variables' | 'conversation_variables'>
canNotSaveEmpty?: boolean
}) => {
// when graph adn skill type changed, it would pass empty nodes array...Temp prevent sync in this case
if (params.graph.nodes.length === 0 && canNotSaveEmpty) {
throw new Error('Cannot sync workflow draft with zero nodes.')
}
const sanitized = sanitizeWorkflowDraftPayload(params)
return post<CommonResponse & { updated_at: number, hash: string }>(url, { body: sanitized }, { silent: true })
}
export const fetchNodesDefaultConfigs = (url: string) => {
return get<NodesDefaultConfigsResponse>(url)
}
export const fetchNestedNodeGraph = (flowType: FlowType, flowId: string, payload: NestedNodeGraphPayload) => {
return post<NestedNodeGraphResponse>(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nested-node-graph`, { body: payload }, { silent: true })
}
export const singleNodeRun = (flowType: FlowType, flowId: string, nodeId: string, params: object) => {
return post(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nodes/${nodeId}/run`, { body: params })
}
export const getIterationSingleNodeRunUrl = (flowType: FlowType, isChatFlow: boolean, flowId: string, nodeId: string) => {
return `${getFlowPrefix(flowType)}/${flowId}/${isChatFlow ? 'advanced-chat/' : ''}workflows/draft/iteration/nodes/${nodeId}/run`
}
export const getLoopSingleNodeRunUrl = (flowType: FlowType, isChatFlow: boolean, flowId: string, nodeId: string) => {
return `${getFlowPrefix(flowType)}/${flowId}/${isChatFlow ? 'advanced-chat/' : ''}workflows/draft/loop/nodes/${nodeId}/run`
}
export const fetchPublishedWorkflow = (url: string) => {
return get<FetchWorkflowDraftResponse>(url)
}
export const stopWorkflowRun = (url: string) => {
return post<CommonResponse>(url)
}
export const fetchNodeDefault = (appId: string, blockType: BlockEnum, query = {}) => {
return get(`apps/${appId}/workflows/default-workflow-block-configs/${blockType}`, {
params: { q: JSON.stringify(query) },
})
}
export const fetchPipelineNodeDefault = (pipelineId: string, blockType: BlockEnum, query = {}) => {
return get(`rag/pipelines/${pipelineId}/workflows/default-workflow-block-configs/${blockType}`, {
params: { q: JSON.stringify(query) },
})
}
export const fetchCurrentValueOfConversationVariable = ({
url,
params,
}: {
url: string
params: { conversation_id: string }
}) => {
return get<ConversationVariableResponse>(url, { params })
}
const fetchAllInspectVarsOnePage = async (flowType: FlowType, flowId: string, page: number): Promise<{ total: number, items: VarInInspect[] }> => {
return get(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/variables`, {
params: { page, limit: 100 },
})
}
export const fetchAllInspectVars = async (flowType: FlowType, flowId: string): Promise<VarInInspect[]> => {
const res = await fetchAllInspectVarsOnePage(flowType, flowId, 1)
const { items, total } = res
if (total <= 100)
return items
const pageCount = Math.ceil(total / 100)
const promises = []
for (let i = 2; i <= pageCount; i++)
promises.push(fetchAllInspectVarsOnePage(flowType, flowId, i))
const restData = await Promise.all(promises)
restData.forEach(({ items: item }) => {
items.push(...item)
})
return items
}
export const fetchNodeInspectVars = async (flowType: FlowType, flowId: string, nodeId: string): Promise<VarInInspect[]> => {
const { items } = (await get(`${getFlowPrefix(flowType)}/${flowId}/workflows/draft/nodes/${nodeId}/variables`)) as { items: VarInInspect[] }
return items
}
// Environment Variables API
export const fetchEnvironmentVariables = async (appId: string): Promise<EnvironmentVariable[]> => {
const response = await consoleClient.workflowDraft.environmentVariables({
params: { appId },
})
return response.items
}
export const updateEnvironmentVariables = ({ appId, environmentVariables }: {
appId: string
environmentVariables: EnvironmentVariable[]
}) => {
return consoleClient.workflowDraft.updateEnvironmentVariables({
params: { appId },
body: { environment_variables: environmentVariables },
})
}
export const updateConversationVariables = ({ appId, conversationVariables }: {
appId: string
conversationVariables: ConversationVariable[]
}) => {
return consoleClient.workflowDraft.updateConversationVariables({
params: { appId },
body: { conversation_variables: conversationVariables },
})
}
export const updateFeatures = ({ appId, features }: {
appId: string
features: WorkflowDraftFeaturesPayload
}) => {
return consoleClient.workflowDraft.updateFeatures({
params: { appId },
body: { features },
})
}
export const submitHumanInputForm = (token: string, data: {
inputs: Record<string, string>
action: string
}) => {
return post(`/form/human_input/${token}`, { body: data })
}
export const fetchHumanInputNodeStepRunForm = (
url: string,
data: {
inputs: Record<string, string>
},
) => {
return post<HumanInputFormData>(`${url}/preview`, { body: data })
}
export const submitHumanInputNodeStepRunForm = (
url: string,
data: {
inputs: Record<string, string> | undefined
form_inputs: Record<string, string> | undefined
action: string
},
) => {
return post<CommonResponse>(`${url}/run`, { body: data })
}