Skip to content

Commit

Permalink
Merge branch 'feat/workflow' into deploy/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
zxhlyh committed Mar 19, 2024
2 parents ddc0ddf + f41a619 commit 6c1aca5
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 35 deletions.
7 changes: 5 additions & 2 deletions web/app/components/app-sidebar/app-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,15 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
)}
>
<div className={cn(
'w-full h-[256px] bg-center bg-no-repeat bg-contain',
'w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl',
showSwitchTip === 'chat' && s.expertPic,
showSwitchTip === 'completion' && s.completionPic,
)}/>
<div className='px-4 pb-2'>
<div className='text-gray-700 text-md leading-6 font-semibold'>{t('app.newApp.advanced')}</div>
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
{t('app.newApp.advanced')}
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
</div>
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor')}</div>
<div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions web/app/components/app/create-app-dialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const CreateAppDialog = ({ show, onSuccess, onClose }: CreateAppDialogProps) =>
>
{showInstruction === 'BASIC' && (
<>
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain', s.basicPic)}/>
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', s.basicPic)}/>
<div className='px-4 pb-2'>
<div className='text-gray-700 text-md leading-6 font-semibold'>{t('app.newApp.basic')}</div>
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.basicFor')}</div>
Expand All @@ -54,7 +54,7 @@ const CreateAppDialog = ({ show, onSuccess, onClose }: CreateAppDialogProps) =>
)}
{showInstruction === 'ADVANCED' && (
<>
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain', s.advancedPic)}/>
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', s.advancedPic)}/>
<div className='px-4 pb-2'>
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
{t('app.newApp.advanced')}
Expand Down
2 changes: 1 addition & 1 deletion web/app/components/workflow/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export const NODE_WIDTH = 220
export const X_OFFSET = 64
export const NODE_WIDTH_X_OFFSET = NODE_WIDTH + X_OFFSET
export const Y_OFFSET = 39
export const TREE_DEEPTH = 30
export const MAX_TREE_DEEPTH = 30
export const START_INITIAL_POSITION = { x: 80, y: 282 }
export const AUTO_LAYOUT_OFFSET = {
x: -42,
Expand Down
36 changes: 22 additions & 14 deletions web/app/components/workflow/header/publish.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import {
useNodesSyncDraft,
useWorkflow,
useWorkflowRun,
} from '../hooks'
import Button from '@/app/components/base/button'
import {
Expand All @@ -20,12 +21,15 @@ import {
} from '@/app/components/base/portal-to-follow-elem'
import { publishWorkflow } from '@/service/workflow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useToastContext } from '@/app/components/base/toast'

const Publish = () => {
const { t } = useTranslation()
const { notify } = useToastContext()
const [published, setPublished] = useState(false)
const workflowStore = useWorkflowStore()
const { formatTimeFromNow } = useWorkflow()
const { handleCheckBeforePublish } = useWorkflowRun()
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const runningStatus = useStore(s => s.runningStatus)
const draftUpdatedAt = useStore(s => s.draftUpdatedAt)
Expand All @@ -34,16 +38,20 @@ const Publish = () => {

const handlePublish = async () => {
const appId = useAppStore.getState().appDetail?.id
try {
const res = await publishWorkflow(`/apps/${appId}/workflows/publish`)

if (res) {
setPublished(true)
workflowStore.getState().setPublishedAt(res.created_at)
if (handleCheckBeforePublish()) {
try {
const res = await publishWorkflow(`/apps/${appId}/workflows/publish`)

if (res) {
notify({ type: 'success', message: t('common.api.actionSuccess') })
setPublished(true)
workflowStore.getState().setPublishedAt(res.created_at)
}
}
catch (e) {
setPublished(false)
}
}
catch (e) {
setPublished(false)
}
}

Expand Down Expand Up @@ -84,11 +92,7 @@ const Publish = () => {
${runningStatus && 'cursor-not-allowed opacity-50'}
`}
>
{
published
? t('workflow.common.published')
: t('workflow.common.publish')
}
{t('workflow.common.publish')}
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[11]'>
Expand All @@ -109,7 +113,11 @@ const Publish = () => {
onClick={handlePublish}
disabled={published}
>
{t('workflow.common.publish')}
{
published
? t('workflow.common.published')
: t('workflow.common.publish')
}
</Button>
</div>
{
Expand Down
9 changes: 8 additions & 1 deletion web/app/components/workflow/hooks/use-nodes-sync-draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useStore,
useWorkflowStore,
} from '../store'
import { BlockEnum } from '../types'
import { syncWorkflowDraft } from '@/service/workflow'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
import { useStore as useAppStore } from '@/app/components/app/store'
Expand All @@ -28,8 +29,14 @@ export const useNodesSyncDraft = () => {
const appId = useAppStore.getState().appDetail?.id

if (appId) {
const nodes = getNodes()
const hasStartNode = nodes.find(node => node.data.type === BlockEnum.Start)

if (!hasStartNode)
return

const features = featuresStore!.getState().features
const producedNodes = produce(getNodes(), (draft) => {
const producedNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
Object.keys(node.data).forEach((key) => {
if (key.startsWith('_'))
Expand Down
37 changes: 37 additions & 0 deletions web/app/components/workflow/hooks/use-workflow-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { useTranslation } from 'react-i18next'
import produce from 'immer'
import { useWorkflowStore } from '../store'
import {
NodeRunningStatus,
WorkflowRunningStatus,
} from '../types'
import { MAX_TREE_DEEPTH } from '../constants'
import { useNodesExtraData } from './use-nodes-data'
import { useWorkflow } from './use-workflow'
import { useStore as useAppStore } from '@/app/components/app/store'
import type { IOtherOptions } from '@/service/base'
import { ssePost } from '@/service/base'
Expand All @@ -17,12 +21,17 @@ import {
stopWorkflowRun,
} from '@/service/workflow'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
import { useToastContext } from '@/app/components/base/toast'

export const useWorkflowRun = () => {
const { t } = useTranslation()
const { notify } = useToastContext()
const store = useStoreApi()
const workflowStore = useWorkflowStore()
const reactflow = useReactFlow()
const featuresStore = useFeaturesStore()
const nodesExtraData = useNodesExtraData()
const { getValidTreeNodes } = useWorkflow()

const handleBackupDraft = useCallback(() => {
const {
Expand Down Expand Up @@ -206,11 +215,39 @@ export const useWorkflowRun = () => {
}
}, [store, reactflow, featuresStore, workflowStore])

const handleCheckBeforePublish = useCallback(() => {
const {
validNodes,
maxDepth,
} = getValidTreeNodes()

if (!validNodes.length)
return false

if (maxDepth > MAX_TREE_DEEPTH) {
notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEEPTH }) })
return false
}

for (let i = 0; i < validNodes.length; i++) {
const node = validNodes[i]
const { errorMessage } = nodesExtraData[node.data.type].checkValid(node.data, t)

if (errorMessage) {
notify({ type: 'error', message: `[${node.data.title}] ${errorMessage}` })
return false
}
}

return true
}, [getValidTreeNodes, nodesExtraData, notify, t])

return {
handleBackupDraft,
handleRunSetting,
handleRun,
handleStopRun,
handleRestoreFromPublishedWorkflow,
handleCheckBeforePublish,
}
}
45 changes: 45 additions & 0 deletions web/app/components/workflow/hooks/use-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,58 @@ export const useWorkflow = () => {
return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow()
}, [locale])

const getValidTreeNodes = useCallback(() => {
const {
getNodes,
edges,
} = store.getState()
const nodes = getNodes()

const startNode = nodes.find(node => node.data.type === BlockEnum.Start)

if (!startNode) {
return {
validNodes: [],
maxDepth: 0,
}
}

const list: Node[] = [startNode]
let maxDepth = 1

const traverse = (root: Node, depth: number) => {
if (depth > maxDepth)
maxDepth = depth

const outgoers = getOutgoers(root, nodes, edges)

if (outgoers.length) {
outgoers.forEach((outgoer) => {
list.push(outgoer)
traverse(outgoer, depth + 1)
})
}
else {
list.push(root)
}
}

traverse(startNode, maxDepth)

return {
validNodes: uniqBy(list, 'id'),
maxDepth,
}
}, [store])

return {
handleLayout,
getTreeLeafNodes,
getBeforeNodesInSameBranch,
getAfterNodesInSameBranch,
isValidConnection,
formatTimeFromNow,
getValidTreeNodes,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const useOneStepRun = <T>({
moreDataForCheckValid,
}: Params<T>) => {
const { t } = useTranslation()
const { getBeforeNodesInSameBranch } = useWorkflow()
const { getBeforeNodesInSameBranch } = useWorkflow() as any
const isChatMode = useIsChatMode()

const allOutputVars = toNodeOutputVars(getBeforeNodesInSameBranch(id), isChatMode)
Expand Down
20 changes: 13 additions & 7 deletions web/app/components/workflow/nodes/question-classifier/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { BlockEnum } from '../../types'
import type { QuestionClassifierNodeType } from './types'
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'

const i18nPrefix = 'workflow'

const nodeDefault: NodeDefault<QuestionClassifierNodeType> = {
defaultValue: {
query_variable_selector: [],
Expand All @@ -26,15 +28,19 @@ const nodeDefault: NodeDefault<QuestionClassifierNodeType> = {
const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS
return nodes.filter(type => type !== BlockEnum.VariableAssigner)
},
checkValid(payload: QuestionClassifierNodeType) {
let isValid = true
checkValid(payload: QuestionClassifierNodeType, t: any) {
let errorMessages = ''
if (payload.type) {
isValid = true
errorMessages = ''
}
if (!errorMessages && (!payload.query_variable_selector || payload.query_variable_selector.length === 0))
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.questionClassifiers.inputVars`) })

if (!errorMessages && !payload.model.provider)
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.questionClassifiers.model`) })

if (!errorMessages && (!payload.classes || payload.classes.length === 0))
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, { field: t(`${i18nPrefix}.nodes.questionClassifiers.class`) })

return {
isValid,
isValid: !errorMessages,
errorMessage: errorMessages,
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
value={inputs.query_variable_selector}
onChange={handleQueryVarChange}
filterVar={filterVar}
width={387}
/>
</Field>
<Field
Expand Down
18 changes: 11 additions & 7 deletions web/app/components/workflow/nodes/template-transform/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import type { TemplateTransformNodeType } from './types'
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
const i18nPrefix = 'workflow.errorMsg'

const nodeDefault: NodeDefault<TemplateTransformNodeType> = {
defaultValue: {
Expand All @@ -17,15 +18,18 @@ const nodeDefault: NodeDefault<TemplateTransformNodeType> = {
const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS
return nodes
},
checkValid(payload: TemplateTransformNodeType) {
let isValid = true
checkValid(payload: TemplateTransformNodeType, t: any) {
let errorMessages = ''
if (payload.type) {
isValid = true
errorMessages = ''
}
const { template, variables } = payload

if (!errorMessages && variables.filter(v => !v.variable).length > 0)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.variable`) })
if (!errorMessages && variables.filter(v => !v.value_selector.length).length > 0)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t(`${i18nPrefix}.fields.variableValue`) })
if (!errorMessages && !template)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.templateTransform.code') })
return {
isValid,
isValid: !errorMessages,
errorMessage: errorMessages,
}
},
Expand Down
1 change: 1 addition & 0 deletions web/i18n/en-US/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const translation = {
variableNamePlaceholder: 'Variable name',
setVarValuePlaceholder: 'Set variable',
needConnecttip: 'This step is not connected to anything',
maxTreeDepth: 'Maximum limit of {{depth}} nodes per branch',
},
errorMsg: {
fieldRequired: '{{field}} is required',
Expand Down
1 change: 1 addition & 0 deletions web/i18n/zh-Hans/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const translation = {
variableNamePlaceholder: '变量名',
setVarValuePlaceholder: '设置变量值',
needConnecttip: '此节点尚未连接到其他节点',
maxTreeDepth: '每个分支最大限制 {{depth}} 个节点',
},
errorMsg: {
fieldRequired: '{{field}} 不能为空',
Expand Down

0 comments on commit 6c1aca5

Please sign in to comment.