Merge remote-tracking branch 'myori/main' into feat/collaboration2

This commit is contained in:
hjlarry
2026-01-17 10:22:41 +08:00
6266 changed files with 544217 additions and 224655 deletions

View File

@ -1,25 +1,25 @@
import type { IterationNodeType } from './types'
import type {
OnSelectBlock,
} from '@/app/components/workflow/types'
import {
RiAddLine,
} from '@remixicon/react'
import {
memo,
useCallback,
} from 'react'
import {
RiAddLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import BlockSelector from '@/app/components/workflow/block-selector'
import {
BlockEnum,
} from '@/app/components/workflow/types'
import { cn } from '@/utils/classnames'
import {
useAvailableBlocks,
useNodesInteractions,
useNodesReadOnly,
} from '../../hooks'
import type { IterationNodeType } from './types'
import cn from '@/utils/classnames'
import BlockSelector from '@/app/components/workflow/block-selector'
import type {
OnSelectBlock,
} from '@/app/components/workflow/types'
import {
BlockEnum,
} from '@/app/components/workflow/types'
type AddBlockProps = {
iterationNodeId: string
@ -52,24 +52,25 @@ const AddBlock = ({
'system-sm-medium relative inline-flex h-8 cursor-pointer items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 text-components-button-secondary-text shadow-xs backdrop-blur-[5px] hover:bg-components-button-secondary-bg-hover',
`${nodesReadOnly && '!cursor-not-allowed bg-components-button-secondary-bg-disabled'}`,
open && 'bg-components-button-secondary-bg-hover',
)}>
<RiAddLine className='mr-1 h-4 w-4' />
{t('workflow.common.addBlock')}
)}
>
<RiAddLine className="mr-1 h-4 w-4" />
{t('common.addBlock', { ns: 'workflow' })}
</div>
)
}, [nodesReadOnly, t])
return (
<div className='absolute left-14 top-7 z-10 flex h-8 items-center'>
<div className='group/insert relative h-0.5 w-16 bg-gray-300'>
<div className='absolute right-0 top-1/2 h-2 w-0.5 -translate-y-1/2 bg-primary-500'></div>
<div className="absolute left-14 top-7 z-10 flex h-8 items-center">
<div className="group/insert relative h-0.5 w-16 bg-gray-300">
<div className="absolute right-0 top-1/2 h-2 w-0.5 -translate-y-1/2 bg-primary-500"></div>
</div>
<BlockSelector
disabled={nodesReadOnly}
onSelect={handleSelect}
trigger={renderTriggerElement}
triggerInnerClassName='inline-flex'
popupClassName='!min-w-[256px]'
triggerInnerClassName="inline-flex"
popupClassName="!min-w-[256px]"
availableBlocksTypes={availableNextBlocks}
/>
</div>

View File

@ -1,9 +1,10 @@
import { BlockEnum, ErrorHandleMode } from '../../types'
import type { NodeDefault } from '../../types'
import type { IterationNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
const i18nPrefix = 'workflow'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum, ErrorHandleMode } from '../../types'
const i18nPrefix = ''
const metaData = genNodeMetaData({
classification: BlockClassificationEnum.Logic,
@ -31,8 +32,9 @@ const nodeDefault: NodeDefault<IterationNodeType> = {
!errorMessages
&& (!payload.iterator_selector || payload.iterator_selector.length === 0)
) {
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, {
field: t(`${i18nPrefix}.nodes.iteration.input`),
errorMessages = t(`${i18nPrefix}errorMsg.fieldRequired`, {
ns: 'workflow',
field: t(`${i18nPrefix}nodes.iteration.input`, { ns: 'workflow' }),
})
}
@ -40,8 +42,9 @@ const nodeDefault: NodeDefault<IterationNodeType> = {
!errorMessages
&& (!payload.output_selector || payload.output_selector.length === 0)
) {
errorMessages = t(`${i18nPrefix}.errorMsg.fieldRequired`, {
field: t(`${i18nPrefix}.nodes.iteration.output`),
errorMessages = t(`${i18nPrefix}errorMsg.fieldRequired`, {
ns: 'workflow',
field: t(`${i18nPrefix}nodes.iteration.output`, { ns: 'workflow' }),
})
}

View File

@ -1,24 +1,24 @@
import type { FC } from 'react'
import type { IterationNodeType } from './types'
import type { NodeProps } from '@/app/components/workflow/types'
import {
memo,
useEffect,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import {
Background,
useNodesInitialized,
useViewport,
} from 'reactflow'
import { useTranslation } from 'react-i18next'
import { IterationStartNodeDumb } from '../iteration-start'
import { useNodeIterationInteractions } from './use-interactions'
import type { IterationNodeType } from './types'
import AddBlock from './add-block'
import cn from '@/utils/classnames'
import type { NodeProps } from '@/app/components/workflow/types'
import Toast from '@/app/components/base/toast'
import { cn } from '@/utils/classnames'
import { IterationStartNodeDumb } from '../iteration-start'
import AddBlock from './add-block'
import { useNodeIterationInteractions } from './use-interactions'
const i18nPrefix = 'workflow.nodes.iteration'
const i18nPrefix = 'nodes.iteration'
const Node: FC<NodeProps<IterationNodeType>> = ({
id,
@ -36,7 +36,7 @@ const Node: FC<NodeProps<IterationNodeType>> = ({
if (data.is_parallel && showTips) {
Toast.notify({
type: 'warning',
message: t(`${i18nPrefix}.answerNodeWarningDesc`),
message: t(`${i18nPrefix}.answerNodeWarningDesc`, { ns: 'workflow' }),
duration: 5000,
})
setShowTips(false)
@ -46,13 +46,14 @@ const Node: FC<NodeProps<IterationNodeType>> = ({
return (
<div className={cn(
'relative h-full min-h-[90px] w-full min-w-[240px] rounded-2xl bg-workflow-canvas-workflow-bg',
)}>
)}
>
<Background
id={`iteration-background-${id}`}
className='!z-0 rounded-2xl'
className="!z-0 rounded-2xl"
gap={[14 / zoom, 14 / zoom]}
size={2 / zoom}
color='var(--color-workflow-canvas-workflow-dot-color)'
color="var(--color-workflow-canvas-workflow-dot-color)"
/>
{
data._isCandidate && (

View File

@ -1,20 +1,21 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
import Split from '../_base/components/split'
import { MIN_ITERATION_PARALLEL_NUM } from '../../constants'
import type { IterationNodeType } from './types'
import useConfig from './use-config'
import { ErrorHandleMode, type NodePanelProps } from '@/app/components/workflow/types'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import Switch from '@/app/components/base/switch'
import type { NodePanelProps } from '@/app/components/workflow/types'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import Select from '@/app/components/base/select'
import Slider from '@/app/components/base/slider'
import Input from '@/app/components/base/input'
import Switch from '@/app/components/base/switch'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { ErrorHandleMode } from '@/app/components/workflow/types'
import { MAX_PARALLEL_LIMIT } from '@/config'
import { MIN_ITERATION_PARALLEL_NUM } from '../../constants'
import Split from '../_base/components/split'
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
import useConfig from './use-config'
const i18nPrefix = 'workflow.nodes.iteration'
const i18nPrefix = 'nodes.iteration'
const Panel: FC<NodePanelProps<IterationNodeType>> = ({
id,
@ -24,15 +25,15 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
const responseMethod = [
{
value: ErrorHandleMode.Terminated,
name: t(`${i18nPrefix}.ErrorMethod.operationTerminated`),
name: t(`${i18nPrefix}.ErrorMethod.operationTerminated`, { ns: 'workflow' }),
},
{
value: ErrorHandleMode.ContinueOnError,
name: t(`${i18nPrefix}.ErrorMethod.continueOnError`),
name: t(`${i18nPrefix}.ErrorMethod.continueOnError`, { ns: 'workflow' }),
},
{
value: ErrorHandleMode.RemoveAbnormalOutput,
name: t(`${i18nPrefix}.ErrorMethod.removeAbnormalOutput`),
name: t(`${i18nPrefix}.ErrorMethod.removeAbnormalOutput`, { ns: 'workflow' }),
},
]
const {
@ -50,13 +51,13 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
} = useConfig(id, data)
return (
<div className='pb-2 pt-2'>
<div className='space-y-4 px-4 pb-4'>
<div className="pb-2 pt-2">
<div className="space-y-4 px-4 pb-4">
<Field
title={t(`${i18nPrefix}.input`)}
title={t(`${i18nPrefix}.input`, { ns: 'workflow' })}
required
operations={(
<div className='system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary'>Array</div>
<div className="system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary">Array</div>
)}
>
<VarReferencePicker
@ -70,12 +71,12 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
</Field>
</div>
<Split />
<div className='mt-2 space-y-4 px-4 pb-4'>
<div className="mt-2 space-y-4 px-4 pb-4">
<Field
title={t(`${i18nPrefix}.output`)}
title={t(`${i18nPrefix}.output`, { ns: 'workflow' })}
required
operations={(
<div className='system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary'>Array</div>
<div className="system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary">Array</div>
)}
>
<VarReferencePicker
@ -89,42 +90,44 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
/>
</Field>
</div>
<div className='px-4 pb-2'>
<Field title={t(`${i18nPrefix}.parallelMode`)} tooltip={<div className='w-[230px]'>{t(`${i18nPrefix}.parallelPanelDesc`)}</div>} inline>
<div className="px-4 pb-2">
<Field title={t(`${i18nPrefix}.parallelMode`, { ns: 'workflow' })} tooltip={<div className="w-[230px]">{t(`${i18nPrefix}.parallelPanelDesc`, { ns: 'workflow' })}</div>} inline>
<Switch defaultValue={inputs.is_parallel} onChange={changeParallel} />
</Field>
</div>
{
inputs.is_parallel && (<div className='px-4 pb-2'>
<Field title={t(`${i18nPrefix}.MaxParallelismTitle`)} isSubTitle tooltip={<div className='w-[230px]'>{t(`${i18nPrefix}.MaxParallelismDesc`)}</div>}>
<div className='row flex'>
<Input type='number' wrapperClassName='w-18 mr-4 ' max={MAX_PARALLEL_LIMIT} min={MIN_ITERATION_PARALLEL_NUM} value={inputs.parallel_nums} onChange={(e) => { changeParallelNums(Number(e.target.value)) }} />
<Slider
value={inputs.parallel_nums}
onChange={changeParallelNums}
max={MAX_PARALLEL_LIMIT}
min={MIN_ITERATION_PARALLEL_NUM}
className=' mt-4 flex-1 shrink-0'
/>
</div>
inputs.is_parallel && (
<div className="px-4 pb-2">
<Field title={t(`${i18nPrefix}.MaxParallelismTitle`, { ns: 'workflow' })} isSubTitle tooltip={<div className="w-[230px]">{t(`${i18nPrefix}.MaxParallelismDesc`, { ns: 'workflow' })}</div>}>
<div className="row flex">
<Input type="number" wrapperClassName="w-18 mr-4 " max={MAX_PARALLEL_LIMIT} min={MIN_ITERATION_PARALLEL_NUM} value={inputs.parallel_nums} onChange={(e) => { changeParallelNums(Number(e.target.value)) }} />
<Slider
value={inputs.parallel_nums}
onChange={changeParallelNums}
max={MAX_PARALLEL_LIMIT}
min={MIN_ITERATION_PARALLEL_NUM}
className=" mt-4 flex-1 shrink-0"
/>
</div>
</Field>
</div>)
</Field>
</div>
)
}
<Split />
<div className='px-4 py-2'>
<Field title={t(`${i18nPrefix}.errorResponseMethod`)} >
<div className="px-4 py-2">
<Field title={t(`${i18nPrefix}.errorResponseMethod`, { ns: 'workflow' })}>
<Select items={responseMethod} defaultValue={inputs.error_handle_mode} onSelect={changeErrorResponseMode} allowSearch={false} />
</Field>
</div>
<Split />
<div className='px-4 py-2'>
<div className="px-4 py-2">
<Field
title={t(`${i18nPrefix}.flattenOutput`)}
tooltip={<div className='w-[230px]'>{t(`${i18nPrefix}.flattenOutputDesc`)}</div>}
title={t(`${i18nPrefix}.flattenOutput`, { ns: 'workflow' })}
tooltip={<div className="w-[230px]">{t(`${i18nPrefix}.flattenOutputDesc`, { ns: 'workflow' })}</div>}
inline
>
<Switch defaultValue={inputs.flatten_output} onChange={changeFlattenOutput} />

View File

@ -1,26 +1,26 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import {
useIsChatMode,
useNodesReadOnly,
useWorkflow,
} from '../../hooks'
import { VarType } from '../../types'
import type { ErrorHandleMode, ValueSelector, Var } from '../../types'
import useNodeCrud from '../_base/hooks/use-node-crud'
import type { IterationNodeType } from './types'
import { toNodeOutputVars } from '../_base/components/variable/utils'
import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import type { Item } from '@/app/components/base/select'
import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud'
import { isEqual } from 'lodash-es'
import { useStore } from '../../store'
import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { isEqual } from 'es-toolkit/predicate'
import { produce } from 'immer'
import { useCallback } from 'react'
import {
useAllBuiltInTools,
useAllCustomTools,
useAllMCPTools,
useAllWorkflowTools,
} from '@/service/use-tools'
import {
useIsChatMode,
useNodesReadOnly,
useWorkflow,
} from '../../hooks'
import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud'
import { useStore } from '../../store'
import { VarType } from '../../types'
import { toNodeOutputVars } from '../_base/components/variable/utils'
import useNodeCrud from '../_base/hooks/use-node-crud'
const useConfig = (id: string, payload: IterationNodeType) => {
const {

View File

@ -1,21 +1,21 @@
import { useCallback } from 'react'
import { produce } from 'immer'
import { useTranslation } from 'react-i18next'
import type {
BlockEnum,
ChildNodeTypeCount,
Node,
} from '../../types'
import { produce } from 'immer'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useNodesMetaData } from '@/app/components/workflow/hooks'
import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow'
import {
ITERATION_PADDING,
} from '../../constants'
import {
generateNewNode,
getNodeCustomTypeByNodeDataType,
} from '../../utils'
import {
ITERATION_PADDING,
} from '../../constants'
import { CUSTOM_ITERATION_START_NODE } from '../iteration-start/constants'
import { useNodesMetaData } from '@/app/components/workflow/hooks'
import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow'
export const useNodeIterationInteractions = () => {
const { t } = useTranslation()
@ -75,7 +75,7 @@ export const useNodeIterationInteractions = () => {
const handleNodeIterationChildDrag = useCallback((node: Node) => {
const { nodes } = collaborativeWorkflow.getState()
const restrictPosition: { x?: number; y?: number } = { x: undefined, y: undefined }
const restrictPosition: { x?: number, y?: number } = { x: undefined, y: undefined }
if (node.data.isInIteration) {
const parentNode = nodes.find(n => n.id === node.parentId)
@ -116,7 +116,7 @@ export const useNodeIterationInteractions = () => {
const childNodeType = child.data.type as BlockEnum
const nodesWithSameType = nodes.filter(node => node.data.type === childNodeType)
if(!childNodeTypeCount[childNodeType])
if (!childNodeTypeCount[childNodeType])
childNodeTypeCount[childNodeType] = nodesWithSameType.length + 1
else
childNodeTypeCount[childNodeType] = childNodeTypeCount[childNodeType] + 1
@ -130,7 +130,7 @@ export const useNodeIterationInteractions = () => {
_isBundled: false,
_connectedSourceHandleIds: [],
_connectedTargetHandleIds: [],
title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${childNodeType}`)} ${childNodeTypeCount[childNodeType]}` : t(`workflow.blocks.${childNodeType}`),
title: nodesWithSameType.length > 0 ? `${t(`blocks.${childNodeType}`, { ns: 'workflow' })} ${childNodeTypeCount[childNodeType]}` : t(`blocks.${childNodeType}`, { ns: 'workflow' }),
iteration_id: newNodeId,
type: childNodeType,
},

View File

@ -1,20 +1,20 @@
import type { RefObject } from 'react'
import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import type { IterationNodeType } from './types'
import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types'
import type { NodeTracing } from '@/types/workflow'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import formatTracing from '@/app/components/workflow/run/utils/format-log'
import { InputVarType, VarType } from '@/app/components/workflow/types'
import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config'
import { useIsNodeInIteration, useWorkflow } from '../../hooks'
import { getNodeInfoById, getNodeUsedVarPassToServerKey, getNodeUsedVars, isSystemVar } from '../_base/components/variable/utils'
import { InputVarType, VarType } from '@/app/components/workflow/types'
import formatTracing from '@/app/components/workflow/run/utils/format-log'
import type { NodeTracing } from '@/types/workflow'
import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config'
const i18nPrefix = 'workflow.nodes.iteration'
const i18nPrefix = 'nodes.iteration'
type Params = {
id: string,
payload: IterationNodeType,
id: string
payload: IterationNodeType
runInputData: Record<string, any>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
@ -117,7 +117,7 @@ const useSingleRunFormParams = ({
onChange: setInputVarValues,
},
{
label: t(`${i18nPrefix}.input`)!,
label: t(`${i18nPrefix}.input`, { ns: 'workflow' })!,
inputs: [{
label: '',
variable: iteratorInputKey,
@ -138,7 +138,7 @@ const useSingleRunFormParams = ({
return [payload.iterator_selector]
}
const getDependentVar = (variable: string) => {
if(variable === iteratorInputKey)
if (variable === iteratorInputKey)
return payload.iterator_selector
}