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

# Conflicts:
#	api/core/memory/token_buffer_memory.py
#	api/core/rag/extractor/notion_extractor.py
#	api/core/repositories/sqlalchemy_workflow_node_execution_repository.py
#	api/core/variables/variables.py
#	api/core/workflow/graph/graph.py
#	api/core/workflow/graph_engine/entities/event.py
#	api/services/dataset_service.py
#	web/app/components/app-sidebar/index.tsx
#	web/app/components/base/tag-management/selector.tsx
#	web/app/components/base/toast/index.tsx
#	web/app/components/datasets/create/website/index.tsx
#	web/app/components/datasets/create/website/jina-reader/base/options-wrap.tsx
#	web/app/components/workflow/header/version-history-button.tsx
#	web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts
#	web/app/components/workflow/hooks/use-workflow-interactions.ts
#	web/app/components/workflow/panel/version-history-panel/index.tsx
#	web/service/base.ts
This commit is contained in:
jyong
2025-09-03 15:01:06 +08:00
572 changed files with 16030 additions and 7973 deletions

View File

@ -64,7 +64,7 @@ const AddVariablePopupWithPosition = ({
} as any,
],
hideEnv: true,
hideChatVar: true,
hideChatVar: !isChatMode,
isChatMode,
filterVar: filterVar(outputType as VarType),
})

View File

@ -93,7 +93,7 @@ export type AgentStrategySelectorProps = {
}
export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => {
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const { value, onChange, canChooseMCPTool } = props
const [open, setOpen] = useState(false)

View File

@ -236,7 +236,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
'zh-Hans': '/guides/workflow/node/agent#选择-agent-策略',
'ja-JP': '/guides/workflow/node/agent#エージェント戦略の選択',
})}
className='text-text-accent-secondary' target='_blank'>
className='text-text-accent-secondary' target='_blank'>
{t('workflow.nodes.agent.learnMore')}
</Link>
</div>}

View File

@ -38,7 +38,7 @@ const Field: FC<Props> = ({
<div className={cn(className, inline && 'flex w-full items-center justify-between')}>
<div
onClick={() => supportFold && toggleFold()}
className={cn('sticky top-0 z-10 flex items-center justify-between bg-components-panel-bg', supportFold && 'cursor-pointer')}>
className={cn('sticky top-0 flex items-center justify-between bg-components-panel-bg', supportFold && 'cursor-pointer')}>
<div className='flex h-6 items-center'>
<div className={cn(isSubTitle ? 'system-xs-medium-uppercase text-text-tertiary' : 'system-sm-semibold-uppercase text-text-secondary')}>
{title} {required && <span className='text-text-destructive'>*</span>}

View File

@ -11,7 +11,7 @@ const McpToolNotSupportTooltip: FC = () => {
<Tooltip
popupContent={
<div className='w-[256px]'>
{t('plugin.detailPanel.toolSelector.unsupportedMCPTool')}
{t('plugin.detailPanel.toolSelector.unsupportedMCPTool')}
</div>
}
>

View File

@ -2,7 +2,7 @@
import type { FC } from 'react'
import React from 'react'
import cn from '@/utils/classnames'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
import VarHighlight from '@/app/components/app/configuration/base/var-highlight'
type Props = {
isFocus?: boolean
onFocus?: () => void
@ -22,11 +22,24 @@ const SupportVarInput: FC<Props> = ({
textClassName,
readonly,
}) => {
const withHightContent = (value || '')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\{\{([^}]+)\}\}/g, varHighlightHTML({ name: '$1', className: '!mb-0' })) // `<span class="${highLightClassName}">{{$1}}</span>`
.replace(/\n/g, '<br />')
const renderSafeContent = (inputValue: string) => {
const parts = inputValue.split(/(\{\{[^}]+\}\}|\n)/g)
return parts.map((part, index) => {
const variableMatch = part.match(/^\{\{([^}]+)\}\}$/)
if (variableMatch) {
return (
<VarHighlight
key={`var-${index}`}
name={variableMatch[1]}
/>
)
}
if (part === '\n')
return <br key={`br-${index}`} />
return <span key={`text-${index}`}>{part}</span>
})
}
return (
<div
@ -42,9 +55,9 @@ const SupportVarInput: FC<Props> = ({
<div
className={cn(textClassName, 'h-full w-0 grow truncate whitespace-nowrap')}
title={value}
dangerouslySetInnerHTML={{
__html: withHightContent,
}}></div>
>
{renderSafeContent(value || '')}
</div>
)}
</div>
)

View File

@ -55,11 +55,11 @@ const VarList: FC<Props> = ({
const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true)
if (!isValid) {
setToastHandle(Toast.notify({
type: 'error',
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
}))
return
}
type: 'error',
message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
}))
return
}
if (list.some(item => item.variable?.trim() === newKey.trim())) {
console.log('new key', newKey.trim())
setToastHandle(Toast.notify({

View File

@ -82,11 +82,11 @@ const LastRun: FC<Props> = ({
}, [nodeId])
const handlePageVisibilityChange = useCallback(() => {
if (document.visibilityState === 'hidden')
setPageHasHide(true)
else
setPageShowed(true)
}, [])
if (document.visibilityState === 'hidden')
setPageHasHide(true)
else
setPageShowed(true)
}, [])
useEffect(() => {
document.addEventListener('visibilitychange', handlePageVisibilityChange)

View File

@ -118,8 +118,8 @@ function useOutputVarList<T>({
const [removedVar, setRemovedVar] = useState<ValueSelector>([])
const removeVarInNode = useCallback(() => {
const varId = nodesWithInspectVars.find(node => node.nodeId === id)?.vars.find((varItem) => {
return varItem.name === removedVar[1]
})?.id
return varItem.name === removedVar[1]
})?.id
if(varId)
deleteInspectVar(id, varId)
removeUsedVarInNodes(removedVar)
@ -143,8 +143,8 @@ function useOutputVarList<T>({
setInputs(newInputs)
onOutputKeyOrdersChange(outputKeyOrders.filter((_, i) => i !== index))
const varId = nodesWithInspectVars.find(node => node.nodeId === id)?.vars.find((varItem) => {
return varItem.name === key
})?.id
return varItem.name === key
})?.id
if(varId)
deleteInspectVar(id, varId)
}, [outputKeyOrders, isVarUsedInNodes, id, inputs, setInputs, onOutputKeyOrdersChange, nodesWithInspectVars, deleteInspectVar, showRemoveVarConfirm, varKey])

View File

@ -45,10 +45,10 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
const resetEditor = useStore(s => s.setControlPromptEditorRerenderKey)
return <div className='my-2'>
<Field
required
title={t('workflow.nodes.agent.strategy.label')}
className='px-4 py-2'
tooltip={t('workflow.nodes.agent.strategy.tooltip')} >
required
title={t('workflow.nodes.agent.strategy.label')}
className='px-4 py-2'
tooltip={t('workflow.nodes.agent.strategy.tooltip')} >
<AgentStrategy
strategy={inputs.agent_strategy_name ? {
agent_strategy_provider_name: inputs.agent_strategy_provider_name!,

View File

@ -76,7 +76,7 @@ const useSingleRunFormParams = ({
return formatTracing([runResult], t)[0]
}, [runResult, t])
const getDependentVars = () => {
const getDependentVars = () => {
return varInputs.map((item) => {
// Guard against null/undefined variable to prevent app crash
if (!item.variable || typeof item.variable !== 'string')

View File

@ -69,7 +69,7 @@ const VarList: FC<Props> = ({
if (item.value === WriteMode.set || item.value === WriteMode.increment || item.value === WriteMode.decrement
|| item.value === WriteMode.multiply || item.value === WriteMode.divide) {
if(varType === VarType.boolean)
draft[index].value = false
draft[index].value = false
draft[index].input_type = AssignerNodeInputType.constant
}
else {

View File

@ -31,8 +31,6 @@ const useKeyValueList = (value: string, onChange: (value: string) => void, noFil
const newValue = list.filter(item => item.key && item.value).map(item => `${item.key}:${item.value}`).join('\n')
if (newValue !== value)
onChange(newValue)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [list, noFilter])
const addItem = useCallback(() => {
setList([...list, {

View File

@ -51,7 +51,6 @@ const useConfig = (id: string, payload: HttpNodeType) => {
setInputs(newInputs)
setIsDataReady(true)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultConfig])
const handleMethodChange = useCallback((method: Method) => {

View File

@ -31,7 +31,6 @@ const MetadataTrigger = ({
handleRemoveCondition(condition.id)
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [metadataList, handleRemoveCondition, selectedDatasetsLoaded])
return (

View File

@ -172,7 +172,6 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
}
})
setInputs(newInput)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentProvider?.provider, currentModel, currentRerankModel, rerankDefaultModel])
const [selectedDatasets, setSelectedDatasets] = useState<DataSet[]>([])
const [rerankModelOpen, setRerankModelOpen] = useState(false)
@ -229,7 +228,6 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
setInputs(newInputs)
setSelectedDatasetsLoaded(true)
})()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
@ -241,7 +239,6 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => {
setInputs(produce(inputs, (draft) => {
draft.query_variable_selector = query_variable_selector
}))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const handleOnDatasetsChange = useCallback((newDatasets: DataSet[]) => {

View File

@ -34,7 +34,6 @@ const JsonImporter: FC<JsonImporterProps> = ({
const rect = importBtnRef.current.getBoundingClientRect()
updateBtnWidth(rect.width)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const handleTrigger = useCallback((e: React.MouseEvent<HTMLElement, MouseEvent>) => {

View File

@ -140,7 +140,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
<ConfigPrompt
readOnly={readOnly}
nodeId={id}
filterVar={filterInputVar}
filterVar={isShowVars ? filterJinja2InputVar : filterInputVar}
isChatModel={isChatModel}
isChatApp={isChatMode}
isShowContext

View File

@ -308,7 +308,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
}, [])
const filterJinja2InputVar = useCallback((varPayload: Var) => {
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.arrayBoolean, VarType.arrayObject, VarType.object, VarType.array, VarType.boolean].includes(varPayload.type)
}, [])
const filterMemoryPromptVar = useCallback((varPayload: Var) => {

View File

@ -155,7 +155,6 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => {
return
setModelChanged(false)
handleVisionConfigAfterModelChanged()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isVisionModel, modelChanged])
const {

View File

@ -84,10 +84,10 @@ const ClassList: FC<Props> = ({
})()
return (
<div key={item.id}
className={cn(
'group relative rounded-[10px] bg-components-panel-bg',
`-ml-${handleSideWidth} min-h-[40px] px-0 py-0`,
)}>
className={cn(
'group relative rounded-[10px] bg-components-panel-bg',
`-ml-${handleSideWidth} min-h-[40px] px-0 py-0`,
)}>
<div >
<Item
className={cn(canDrag && 'handle')}

View File

@ -88,7 +88,6 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
return
setModelChanged(false)
handleVisionConfigAfterModelChanged()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isVisionModel, modelChanged])
const handleQueryVarChange = useCallback((newVar: ValueSelector | string) => {
@ -110,7 +109,6 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => {
query_variable_selector: inputs.query_variable_selector.length > 0 ? inputs.query_variable_selector : query_variable_selector,
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [defaultConfig])
const handleClassesChange = useCallback((newClasses: any) => {

View File

@ -61,7 +61,7 @@ const useSingleRunFormParams = ({
Object.keys(inputs.tool_parameters).forEach((key: string) => {
const { type, value } = inputs.tool_parameters[key]
if (type === VarType.constant && (value === undefined || value === null)) {
if(!draft.tool_parameters || !draft.tool_parameters[key])
if(!draft.tool_parameters || !draft.tool_parameters[key])
return
draft[key] = value
}