Merge branch 'feat/queue-based-graph-engine' into feat/rag-2

This commit is contained in:
-LAN-
2025-09-08 14:30:43 +08:00
828 changed files with 7240 additions and 2951 deletions

View File

@ -1,5 +1,5 @@
'use client'
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'
import React, { useEffect, useImperativeHandle, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll'
import Item from './item'
@ -17,18 +17,20 @@ export type ListProps = {
tags: string[]
toolContentClassName?: string
disableMaxWidth?: boolean
ref?: React.Ref<ListRef>
}
export type ListRef = { handleScroll: () => void }
const List = forwardRef<ListRef, ListProps>(({
const List = ({
wrapElemRef,
searchText,
tags,
list,
toolContentClassName,
disableMaxWidth = false,
}, ref) => {
ref,
}: ListProps) => {
const { t } = useTranslation()
const noFilter = !searchText && tags.length === 0
const hasRes = list.length > 0
@ -125,7 +127,7 @@ const List = forwardRef<ListRef, ListProps>(({
</div>
</>
)
})
}
List.displayName = 'List'

View File

@ -77,6 +77,10 @@ export const LLM_OUTPUT_STRUCT: Var[] = [
variable: 'text',
type: VarType.string,
},
{
variable: 'reasoning_content',
type: VarType.string,
},
{
variable: 'usage',
type: VarType.object,

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useMemo } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
@ -13,7 +13,7 @@ type Params = {
id: string,
payload: AgentNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types'
import { useMemo } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
@ -9,7 +9,7 @@ type Params = {
id: string,
payload: AssignerNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
@ -8,7 +8,7 @@ type Params = {
id: string,
payload: CodeNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import type { DocExtractorNodeType } from './types'
@ -11,7 +11,7 @@ type Params = {
id: string,
payload: DocExtractorNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
@ -8,7 +8,7 @@ type Params = {
id: string,
payload: HttpNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types'
import { useCallback } from 'react'
import type { CaseItem, Condition, IfElseNodeType } from './types'
@ -7,7 +7,7 @@ type Params = {
id: string,
payload: IfElseNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
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'
@ -16,7 +16,7 @@ type Params = {
id: string,
payload: IterationNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import { useTranslation } from 'react-i18next'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { InputVarType } from '@/app/components/workflow/types'
@ -11,7 +11,7 @@ type Params = {
id: string,
payload: KnowledgeRetrievalNodeType
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -0,0 +1,40 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import Switch from '@/app/components/base/switch'
type ReasoningFormatConfigProps = {
value?: 'tagged' | 'separated'
onChange: (value: 'tagged' | 'separated') => void
readonly?: boolean
}
const ReasoningFormatConfig: FC<ReasoningFormatConfigProps> = ({
value = 'tagged',
onChange,
readonly = false,
}) => {
const { t } = useTranslation()
return (
<Field
title={t('workflow.nodes.llm.reasoningFormat.title')}
tooltip={t('workflow.nodes.llm.reasoningFormat.tooltip')}
operations={
// ON = separated, OFF = tagged
<Switch
defaultValue={value === 'separated'}
onChange={enabled => onChange(enabled ? 'separated' : 'tagged')}
size='md'
disabled={readonly}
key={value}
/>
}
>
<div />
</Field>
)
}
export default ReasoningFormatConfig

View File

@ -17,6 +17,7 @@ import type { NodePanelProps } from '@/app/components/workflow/types'
import Tooltip from '@/app/components/base/tooltip'
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
import StructureOutput from './components/structure-output'
import ReasoningFormatConfig from './components/reasoning-format-config'
import Switch from '@/app/components/base/switch'
import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
@ -61,6 +62,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
handleStructureOutputEnableChange,
handleStructureOutputChange,
filterJinja2InputVar,
handleReasoningFormatChange,
} = useConfig(id, data)
const model = inputs.model
@ -239,6 +241,14 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
config={inputs.vision?.configs}
onConfigChange={handleVisionResolutionChange}
/>
{/* Reasoning Format */}
<ReasoningFormatConfig
// Default to tagged for backward compatibility
value={inputs.reasoning_format || 'tagged'}
onChange={handleReasoningFormatChange}
readonly={readOnly}
/>
</div>
<Split />
<OutputVars

View File

@ -17,6 +17,7 @@ export type LLMNodeType = CommonNodeType & {
}
structured_output_enabled?: boolean
structured_output?: StructuredOutput
reasoning_format?: 'tagged' | 'separated'
}
export enum Type {

View File

@ -315,6 +315,14 @@ const useConfig = (id: string, payload: LLMNodeType) => {
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type)
}, [])
// reasoning format
const handleReasoningFormatChange = useCallback((reasoningFormat: 'tagged' | 'separated') => {
const newInputs = produce(inputs, (draft) => {
draft.reasoning_format = reasoningFormat
})
setInputs(newInputs)
}, [inputs, setInputs])
const {
availableVars,
availableNodesWithParent,
@ -355,6 +363,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
setStructuredOutputCollapsed,
handleStructureOutputEnableChange,
filterJinja2InputVar,
handleReasoningFormatChange,
}
}

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import { useTranslation } from 'react-i18next'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
import type { InputVar, PromptItem, Var, Variable } from '@/app/components/workflow/types'
@ -18,7 +18,7 @@ type Params = {
id: string,
payload: LLMNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import { useTranslation } from 'react-i18next'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
import type { InputVar, Var, Variable } from '@/app/components/workflow/types'
@ -17,7 +17,7 @@ type Params = {
id: string,
payload: ParameterExtractorNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import { useTranslation } from 'react-i18next'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
import type { InputVar, Var, Variable } from '@/app/components/workflow/types'
@ -17,7 +17,7 @@ type Params = {
id: string,
payload: QuestionClassifierNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import { useTranslation } from 'react-i18next'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
import type { ValueSelector } from '@/app/components/workflow/types'
@ -10,7 +10,7 @@ type Params = {
id: string,
payload: StartNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
@ -8,7 +8,7 @@ type Params = {
id: string,
payload: TemplateTransformNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import { useCallback, useMemo, useState } from 'react'
import useNodeCrud from '../_base/hooks/use-node-crud'
@ -15,7 +15,7 @@ type Params = {
id: string,
payload: ToolNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -1,4 +1,4 @@
import type { MutableRefObject } from 'react'
import type { RefObject } from 'react'
import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types'
import { useCallback } from 'react'
import type { VariableAssignerNodeType } from './types'
@ -7,7 +7,7 @@ type Params = {
id: string,
payload: VariableAssignerNodeType,
runInputData: Record<string, any>
runInputDataRef: MutableRefObject<Record<string, any>>
runInputDataRef: RefObject<Record<string, any>>
getInputVars: (textList: string[]) => InputVar[]
setRunInputData: (data: Record<string, any>) => void
toVarInputs: (variables: Variable[]) => InputVar[]

View File

@ -33,7 +33,7 @@ const TracingPanel: FC<TracingPanelProps> = ({
}) => {
const { t } = useTranslation()
const treeNodes = formatNodeList(list, t)
const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set())
const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(() => new Set())
const [hoveredParallel, setHoveredParallel] = useState<string | null>(null)
const toggleCollapse = (id: string) => {

View File

@ -72,22 +72,18 @@ const ValueContent = ({
const [fileValue, setFileValue] = useState<any>(formatFileValue(currentVar))
const { run: debounceValueChange } = useDebounceFn(handleValueChange, { wait: 500 })
if (showTextEditor) {
if (currentVar.value_type === 'number')
setValue(JSON.stringify(currentVar.value))
if (!currentVar.value)
setValue('')
setValue(currentVar.value)
}
if (showJSONEditor)
setJson(currentVar.value ? JSON.stringify(currentVar.value, null, 2) : '')
// update default value when id changed
useEffect(() => {
if (showTextEditor) {
if (currentVar.value_type === 'number')
return setValue(JSON.stringify(currentVar.value))
if (!currentVar.value)
return setValue('')
setValue(currentVar.value)
}
if (showJSONEditor)
setJson(currentVar.value ? JSON.stringify(currentVar.value, null, 2) : '')
if (showFileEditor)
setFileValue(formatFileValue(currentVar))
}, [currentVar.id, currentVar.value])
if (showFileEditor)
setFileValue(formatFileValue(currentVar))
const handleTextChange = (value: string) => {
if (isTruncated)