This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
import type { NodeDefault } from '../../types'
import type { DocExtractorNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
const i18nPrefix = 'workflow.errorMsg'
const metaData = genNodeMetaData({
classification: BlockClassificationEnum.Transform,
sort: 4,
type: BlockEnum.DocExtractor,
helpLinkUri: 'doc-extractor',
})
const nodeDefault: NodeDefault<DocExtractorNodeType> = {
metaData,
defaultValue: {
variable_selector: [],
is_array_file: false,
},
checkValid(payload: DocExtractorNodeType, t: any) {
let errorMessages = ''
const { variable_selector: variable } = payload
if (!errorMessages && !variable?.length)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') })
return {
isValid: !errorMessages,
errorMessage: errorMessages,
}
},
}
export default nodeDefault

View File

@@ -0,0 +1,39 @@
import type { FC } from 'react'
import React from 'react'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { useTranslation } from 'react-i18next'
import type { DocExtractorNodeType } from './types'
import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types'
import {
VariableLabelInNode,
} from '@/app/components/workflow/nodes/_base/components/variable/variable-label'
const i18nPrefix = 'workflow.nodes.docExtractor'
const NodeComponent: FC<NodeProps<DocExtractorNodeType>> = ({
data,
}) => {
const { t } = useTranslation()
const nodes: Node[] = useNodes()
const { variable_selector: variable } = data
if (!variable || variable.length === 0)
return null
const isSystem = isSystemVar(variable)
const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0])
return (
<div className='relative mb-1 px-3 py-1'>
<div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t(`${i18nPrefix}.inputVar`)}</div>
<VariableLabelInNode
variables={variable}
nodeType={node?.data.type}
nodeTitle={node?.data.title}
/>
</div>
)
}
export default React.memo(NodeComponent)

View File

@@ -0,0 +1,89 @@
import type { FC } from 'react'
import React from 'react'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
import OutputVars, { VarItem } from '../_base/components/output-vars'
import Split from '../_base/components/split'
import { useNodeHelpLink } from '../_base/hooks/use-node-help-link'
import useConfig from './use-config'
import type { DocExtractorNodeType } from './types'
import { fetchSupportFileTypes } from '@/service/datasets'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { BlockEnum, type NodePanelProps } from '@/app/components/workflow/types'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n-config/language'
const i18nPrefix = 'workflow.nodes.docExtractor'
const Panel: FC<NodePanelProps<DocExtractorNodeType>> = ({
id,
data,
}) => {
const { t } = useTranslation()
const { locale } = useContext(I18n)
const link = useNodeHelpLink(BlockEnum.DocExtractor)
const { data: supportFileTypesResponse } = useSWR({ url: '/files/support-type' }, fetchSupportFileTypes)
const supportTypes = supportFileTypesResponse?.allowed_extensions || []
const supportTypesShowNames = (() => {
const extensionMap: { [key: string]: string } = {
md: 'markdown',
pptx: 'pptx',
htm: 'html',
xlsx: 'xlsx',
docx: 'docx',
}
return [...supportTypes]
.map(item => extensionMap[item] || item) // map to standardized extension
.map(item => item.toLowerCase()) // convert to lower case
.filter((item, index, self) => self.indexOf(item) === index) // remove duplicates
.join(locale !== LanguagesSupported[1] ? ', ' : '、 ')
})()
const {
readOnly,
inputs,
handleVarChanges,
filterVar,
} = useConfig(id, data)
return (
<div className='mt-2'>
<div className='space-y-4 px-4 pb-4'>
<Field
title={t(`${i18nPrefix}.inputVar`)}
required
>
<>
<VarReferencePicker
readonly={readOnly}
nodeId={id}
isShowNodeName
value={inputs.variable_selector || []}
onChange={handleVarChanges}
filterVar={filterVar}
typePlaceHolder='File | Array[File]'
/>
<div className='body-xs-regular mt-1 py-0.5 text-text-tertiary'>
{t(`${i18nPrefix}.supportFileTypes`, { types: supportTypesShowNames })}
<a className='text-text-accent' href={link} target='_blank'>{t(`${i18nPrefix}.learnMore`)}</a>
</div>
</>
</Field>
</div>
<Split />
<div>
<OutputVars>
<VarItem
name='text'
type={inputs.is_array_file ? 'array[string]' : 'string'}
description={t(`${i18nPrefix}.outputVars.text`)}
/>
</OutputVars>
</div>
</div>
)
}
export default React.memo(Panel)

View File

@@ -0,0 +1,6 @@
import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
export type DocExtractorNodeType = CommonNodeType & {
variable_selector: ValueSelector
is_array_file: boolean
}

View File

@@ -0,0 +1,67 @@
import { useCallback, useMemo } from 'react'
import { produce } from 'immer'
import { useStoreApi } from 'reactflow'
import type { ValueSelector, Var } from '../../types'
import { VarType } from '../../types'
import type { DocExtractorNodeType } from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import {
useIsChatMode,
useNodesReadOnly,
useWorkflow,
useWorkflowVariables,
} from '@/app/components/workflow/hooks'
const useConfig = (id: string, payload: DocExtractorNodeType) => {
const { nodesReadOnly: readOnly } = useNodesReadOnly()
const { inputs, setInputs } = useNodeCrud<DocExtractorNodeType>(id, payload)
const filterVar = useCallback((varPayload: Var) => {
return varPayload.type === VarType.file || varPayload.type === VarType.arrayFile
}, [])
const isChatMode = useIsChatMode()
const store = useStoreApi()
const { getBeforeNodesInSameBranch } = useWorkflow()
const {
getNodes,
} = store.getState()
const currentNode = getNodes().find(n => n.id === id)
const isInIteration = payload.isInIteration
const iterationNode = isInIteration ? getNodes().find(n => n.id === currentNode!.parentId) : null
const isInLoop = payload.isInLoop
const loopNode = isInLoop ? getNodes().find(n => n.id === currentNode!.parentId) : null
const availableNodes = useMemo(() => {
return getBeforeNodesInSameBranch(id)
}, [getBeforeNodesInSameBranch, id])
const { getCurrentVariableType } = useWorkflowVariables()
const getType = useCallback((variable?: ValueSelector) => {
const varType = getCurrentVariableType({
parentNode: isInIteration ? iterationNode : loopNode,
valueSelector: variable || [],
availableNodes,
isChatMode,
isConstant: false,
})
return varType
}, [getCurrentVariableType, isInIteration, availableNodes, isChatMode, iterationNode, loopNode])
const handleVarChanges = useCallback((variable: ValueSelector | string) => {
const newInputs = produce(inputs, (draft) => {
draft.variable_selector = variable as ValueSelector
draft.is_array_file = getType(draft.variable_selector) === VarType.arrayFile
})
setInputs(newInputs)
}, [getType, inputs, setInputs])
return {
readOnly,
inputs,
filterVar,
handleVarChanges,
}
}
export default useConfig

View File

@@ -0,0 +1,64 @@
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import type { DocExtractorNodeType } from './types'
import { useTranslation } from 'react-i18next'
import { InputVarType } from '@/app/components/workflow/types'
const i18nPrefix = 'workflow.nodes.docExtractor'
type Params = {
id: string,
payload: DocExtractorNodeType,
runInputData: Record<string, any>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]
}
const useSingleRunFormParams = ({
payload,
runInputData,
setRunInputData,
}: Params) => {
const { t } = useTranslation()
const files = runInputData.files
const setFiles = useCallback((newFiles: []) => {
setRunInputData({
...runInputData,
files: newFiles,
})
}, [runInputData, setRunInputData])
const forms = useMemo(() => {
return [
{
inputs: [{
label: t(`${i18nPrefix}.inputVar`)!,
variable: 'files',
type: InputVarType.multiFiles,
required: true,
}],
values: { files },
onChange: (keyValue: Record<string, any>) => setFiles(keyValue.files),
},
]
}, [files, setFiles, t])
const getDependentVars = () => {
return [payload.variable_selector]
}
const getDependentVar = (variable: string) => {
if(variable === 'files')
return payload.variable_selector
}
return {
forms,
getDependentVars,
getDependentVar,
}
}
export default useSingleRunFormParams