merge feat/plugins
This commit is contained in:
commit
bc78803171
@ -17,6 +17,7 @@ export enum FormTypeEnum {
|
||||
file = 'file',
|
||||
modelSelector = 'model-selector',
|
||||
toolSelector = 'tool-selector',
|
||||
multiToolSelector = 'array[tools]',
|
||||
appSelector = 'app-selector',
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector'
|
||||
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||
import RadioE from '@/app/components/base/radio/ui'
|
||||
|
||||
@ -328,7 +329,35 @@ function Form<
|
||||
scope={scope}
|
||||
disabled={readonly}
|
||||
value={value[variable]}
|
||||
onSelect={item => handleFormChange(variable, item as any)} />
|
||||
onSelect={item => handleFormChange(variable, item as any)}
|
||||
onDelete={() => handleFormChange(variable, null as any)}
|
||||
/>
|
||||
{fieldMoreInfo?.(formSchema)}
|
||||
{validating && changeKey === variable && <ValidatingTip />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (formSchema.type === FormTypeEnum.multiToolSelector) {
|
||||
const {
|
||||
variable,
|
||||
label,
|
||||
tooltip,
|
||||
required,
|
||||
scope,
|
||||
} = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput)
|
||||
|
||||
return (
|
||||
<div key={variable} className={cn(itemClassName, 'py-3')}>
|
||||
<MultipleToolSelector
|
||||
disabled={readonly}
|
||||
scope={scope}
|
||||
label={label[language] || label.en_US}
|
||||
required={required}
|
||||
tooltip={tooltip?.[language] || tooltip?.en_US}
|
||||
value={value[variable]}
|
||||
onChange={item => handleFormChange(variable, item as any)}
|
||||
/>
|
||||
{fieldMoreInfo?.(formSchema)}
|
||||
{validating && changeKey === variable && <ValidatingTip />}
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@ import ActionList from './action-list'
|
||||
import ModelList from './model-list'
|
||||
import AgentStrategyList from './agent-strategy-list'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
import type { PluginDetail } from '@/app/components/plugins/types'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
@ -33,9 +33,6 @@ const PluginDetailPanel: FC<Props> = ({
|
||||
console.log('tool change', val)
|
||||
setValue(val)
|
||||
}
|
||||
const testDelete = () => {
|
||||
setValue(undefined)
|
||||
}
|
||||
|
||||
if (!detail)
|
||||
return null
|
||||
@ -64,10 +61,10 @@ const PluginDetailPanel: FC<Props> = ({
|
||||
{!!detail.declaration.model && <ModelList detail={detail} />}
|
||||
{false && (
|
||||
<div className='px-4 py-2'>
|
||||
<ToolSelector
|
||||
value={value}
|
||||
onSelect={item => testChange(item)}
|
||||
onDelete={testDelete}
|
||||
<MultipleToolSelector
|
||||
value={value || []}
|
||||
label='TOOLS'
|
||||
onChange={testChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,12 +1,148 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiAddLine,
|
||||
RiArrowDropDownLine,
|
||||
RiQuestionLine,
|
||||
} from '@remixicon/react'
|
||||
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import type { ToolValue } from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
value: any[]
|
||||
disabled?: boolean
|
||||
value: ToolValue[]
|
||||
label: string
|
||||
required?: boolean
|
||||
tooltip?: any
|
||||
supportCollapse?: boolean
|
||||
scope?: string
|
||||
onChange: (value: ToolValue[]) => void
|
||||
}
|
||||
|
||||
const MultipleToolSelector = ({ value }: Props) => {
|
||||
const MultipleToolSelector = ({
|
||||
disabled,
|
||||
value,
|
||||
label,
|
||||
required,
|
||||
tooltip,
|
||||
supportCollapse,
|
||||
scope,
|
||||
onChange,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const enabledCount = value.filter(item => item.enabled).length
|
||||
// collapse control
|
||||
const [collapse, setCollapse] = React.useState(false)
|
||||
const handleCollapse = () => {
|
||||
if (supportCollapse)
|
||||
setCollapse(!collapse)
|
||||
}
|
||||
|
||||
// add tool
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const handleAdd = (val: ToolValue) => {
|
||||
const newValue = [...value, val]
|
||||
// deduplication
|
||||
const deduplication = newValue.reduce((acc, cur) => {
|
||||
if (!acc.find(item => item.provider_name === cur.provider_name && item.tool_name === cur.tool_name))
|
||||
acc.push(cur)
|
||||
return acc
|
||||
}, [] as ToolValue[])
|
||||
// update value
|
||||
onChange(deduplication)
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
// delete tool
|
||||
const handleDelete = (index: number) => {
|
||||
const newValue = [...value]
|
||||
newValue.splice(index, 1)
|
||||
onChange(newValue)
|
||||
}
|
||||
|
||||
// configure tool
|
||||
const handleConfigure = (val: ToolValue, index: number) => {
|
||||
const newValue = [...value]
|
||||
newValue[index] = val
|
||||
onChange(newValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<div></div>
|
||||
<>
|
||||
<div className='flex items-center mb-1'>
|
||||
<div
|
||||
className={cn('relative grow flex items-center gap-0.5', supportCollapse && 'cursor-pointer')}
|
||||
onClick={handleCollapse}
|
||||
>
|
||||
<div className='h-6 flex items-center text-text-secondary system-sm-semibold-uppercase'>{label}</div>
|
||||
{required && <div className='text-error-main'>*</div>}
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={tooltip}
|
||||
needsDelay
|
||||
>
|
||||
<div><RiQuestionLine className='w-3.5 h-3.5 text-text-quaternary hover:text-text-tertiary'/></div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{supportCollapse && (
|
||||
<div className='absolute -left-4 top-1'>
|
||||
<RiArrowDropDownLine
|
||||
className={cn(
|
||||
'w-4 h-4 text-text-tertiary',
|
||||
collapse && 'transform -rotate-90',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{value.length > 0 && (
|
||||
<>
|
||||
<div className='flex items-center gap-1 text-text-tertiary system-xs-medium'>
|
||||
<span>{`${enabledCount}/${value.length}`}</span>
|
||||
<span>{t('appDebug.agent.tools.enabled')}</span>
|
||||
</div>
|
||||
<Divider type='vertical' className='ml-3 mr-1 h-3' />
|
||||
</>
|
||||
)}
|
||||
{!disabled && (
|
||||
<ActionButton className='mx-1' onClick={() => setOpen(!open)}>
|
||||
<RiAddLine className='w-4 h-4' />
|
||||
</ActionButton>
|
||||
)}
|
||||
</div>
|
||||
{!collapse && (
|
||||
<>
|
||||
<ToolSelector
|
||||
scope={scope}
|
||||
value={undefined}
|
||||
onSelect={handleAdd}
|
||||
controlledState={open}
|
||||
onControlledStateChange={setOpen}
|
||||
trigger={
|
||||
<div className=''></div>
|
||||
}
|
||||
/>
|
||||
{value.length === 0 && (
|
||||
<div className='p-3 flex justify-center rounded-[10px] bg-background-section text-text-tertiary system-xs-regular'>{t('plugin.detailPanel.toolSelector.empty')}</div>
|
||||
)}
|
||||
{value.length > 0 && value.map((item, index) => (
|
||||
<div className='mb-1' key={index}>
|
||||
<ToolSelector
|
||||
scope={scope}
|
||||
value={item}
|
||||
onSelect={item => handleConfigure(item, index)}
|
||||
onDelete={() => handleDelete(index)}
|
||||
supportEnableSwitch
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -40,16 +40,20 @@ import type {
|
||||
} from '@floating-ui/react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export type ToolValue = {
|
||||
provider_name: string
|
||||
tool_name: string
|
||||
parameters?: Record<string, any>
|
||||
enabled?: boolean
|
||||
extra?: Record<string, any>
|
||||
}
|
||||
|
||||
type Props = {
|
||||
value?: {
|
||||
provider_name: string
|
||||
tool_name: string
|
||||
parameters?: Record<string, any>
|
||||
extra?: Record<string, any>
|
||||
}
|
||||
disabled?: boolean
|
||||
placement?: Placement
|
||||
offset?: OffsetOptions
|
||||
scope?: string
|
||||
value?: ToolValue
|
||||
onSelect: (tool: {
|
||||
provider_name: string
|
||||
tool_name: string
|
||||
@ -57,8 +61,11 @@ type Props = {
|
||||
extra?: Record<string, any>
|
||||
}) => void
|
||||
onDelete?: () => void
|
||||
supportEnableSwitch?: boolean
|
||||
supportAddCustomTool?: boolean
|
||||
scope?: string
|
||||
trigger?: React.ReactNode
|
||||
controlledState?: boolean
|
||||
onControlledStateChange?: (state: boolean) => void
|
||||
}
|
||||
const ToolSelector: FC<Props> = ({
|
||||
value,
|
||||
@ -68,6 +75,10 @@ const ToolSelector: FC<Props> = ({
|
||||
onSelect,
|
||||
onDelete,
|
||||
scope,
|
||||
supportEnableSwitch,
|
||||
trigger,
|
||||
controlledState,
|
||||
onControlledStateChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isShow, onShowChange] = useState(false)
|
||||
@ -95,14 +106,13 @@ const ToolSelector: FC<Props> = ({
|
||||
provider_name: tool.provider_id,
|
||||
tool_name: tool.tool_name,
|
||||
parameters: paramValues,
|
||||
enabled: tool.is_team_authorization,
|
||||
extra: {
|
||||
description: '',
|
||||
},
|
||||
}
|
||||
onSelect(toolValue)
|
||||
setIsShowChooseTool(false)
|
||||
// if (tool.provider_type === CollectionType.builtIn && tool.is_team_authorization)
|
||||
// onShowChange(false)
|
||||
}
|
||||
|
||||
const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
@ -130,6 +140,13 @@ const ToolSelector: FC<Props> = ({
|
||||
onSelect(toolValue as any)
|
||||
}
|
||||
|
||||
const handleEnabledChange = (state: boolean) => {
|
||||
onSelect({
|
||||
...value,
|
||||
enabled: state,
|
||||
} as any)
|
||||
}
|
||||
|
||||
// authorization
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
const [isShowSettingAuth, setShowSettingAuth] = useState(false)
|
||||
@ -152,14 +169,15 @@ const ToolSelector: FC<Props> = ({
|
||||
<PortalToFollowElem
|
||||
placement={placement}
|
||||
offset={offset}
|
||||
open={isShow}
|
||||
onOpenChange={onShowChange}
|
||||
open={trigger ? controlledState : isShow}
|
||||
onOpenChange={trigger ? onControlledStateChange : onShowChange}
|
||||
>
|
||||
<PortalToFollowElemTrigger
|
||||
className='w-full'
|
||||
onClick={handleTriggerClick}
|
||||
>
|
||||
{!value?.provider_name && (
|
||||
{trigger}
|
||||
{!trigger && !value?.provider_name && (
|
||||
<ToolTrigger
|
||||
isConfigure
|
||||
open={isShow}
|
||||
@ -167,16 +185,20 @@ const ToolSelector: FC<Props> = ({
|
||||
provider={currentProvider}
|
||||
/>
|
||||
)}
|
||||
{value?.provider_name && (
|
||||
{!trigger && value?.provider_name && (
|
||||
<ToolItem
|
||||
open={isShow}
|
||||
icon={currentProvider?.icon}
|
||||
providerName={value.provider_name}
|
||||
toolName={value.tool_name}
|
||||
showSwitch={supportEnableSwitch}
|
||||
switchValue={value.enabled}
|
||||
onSwitchChange={handleEnabledChange}
|
||||
onDelete={onDelete}
|
||||
noAuth={currentProvider && !currentProvider.is_team_authorization}
|
||||
onAuth={() => setShowSettingAuth(true)}
|
||||
// uninstalled
|
||||
// uninstalled TODO
|
||||
// isError TODO
|
||||
errorTip={<div className='space-y-1 text-xs'>
|
||||
<h3 className='text-text-primary font-semibold'>{t('workflow.nodes.agent.pluginNotInstalled')}</h3>
|
||||
<p className='text-text-secondary tracking-tight'>{t('workflow.nodes.agent.pluginNotInstalledDesc')}</p>
|
||||
|
@ -77,7 +77,10 @@ const ToolItem = ({
|
||||
)}
|
||||
<div
|
||||
className='p-1 rounded-md text-text-tertiary cursor-pointer hover:text-text-destructive'
|
||||
onClick={onDelete}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onDelete?.()
|
||||
}}
|
||||
onMouseOver={() => setIsDeleting(true)}
|
||||
onMouseLeave={() => setIsDeleting(false)}
|
||||
>
|
||||
@ -85,11 +88,13 @@ const ToolItem = ({
|
||||
</div>
|
||||
</div>
|
||||
{!isError && !uninstalled && !noAuth && showSwitch && (
|
||||
<Switch
|
||||
className='mr-1'
|
||||
size='md'
|
||||
defaultValue={switchValue}
|
||||
onChange={onSwitchChange} />
|
||||
<div className='mr-1' onClick={e => e.stopPropagation()}>
|
||||
<Switch
|
||||
size='md'
|
||||
defaultValue={switchValue}
|
||||
onChange={onSwitchChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!isError && !uninstalled && noAuth && (
|
||||
<Button variant='secondary' size='small' onClick={onAuth}>
|
||||
|
@ -10,6 +10,7 @@ import { Agent } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { InputNumber } from '@/app/components/base/input-number'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
import Field from './field'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
@ -160,18 +161,31 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
}
|
||||
return (
|
||||
<Field title={'tool selector'} tooltip={'tool selector'}>
|
||||
<Field title={schema.label[language]} tooltip={schema.tooltip?.[language]}>
|
||||
<ToolSelector
|
||||
scope={schema.scope}
|
||||
value={value}
|
||||
onSelect={item => onChange(item)}
|
||||
onDelete={() => onChange(null)}
|
||||
/>
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
case 'array[tools]': {
|
||||
return <Field title={'tool selector'} tooltip={'tool selector'}>
|
||||
multiple tool selector TODO
|
||||
</Field>
|
||||
const value = props.value[schema.variable]
|
||||
const onChange = (value: any) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
}
|
||||
return (
|
||||
<MultipleToolSelector
|
||||
scope={schema.scope}
|
||||
value={value}
|
||||
label={schema.label[language]}
|
||||
tooltip={schema.tooltip?.[language]}
|
||||
onChange={onChange}
|
||||
supportCollapse
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,10 @@ import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
import { RetryResultPanel } from '@/app/components/workflow/run/retry-log'
|
||||
import type { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { Emoji } from '@/app/components/tools/types'
|
||||
import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel'
|
||||
import SpecialResultPanel from '@/app/components/workflow/run/special-result-panel'
|
||||
|
||||
const i18nPrefix = 'workflow.singleRun'
|
||||
|
||||
@ -34,9 +34,8 @@ type BeforeRunFormProps = {
|
||||
runningStatus: NodeRunningStatus
|
||||
result?: JSX.Element
|
||||
forms: FormProps[]
|
||||
retryDetails?: NodeTracing[]
|
||||
onRetryDetailBack?: any
|
||||
}
|
||||
showSpecialResultPanel?: boolean
|
||||
} & Partial<SpecialResultPanelProps>
|
||||
|
||||
function formatValue(value: string | any, type: InputVarType) {
|
||||
if (type === InputVarType.number)
|
||||
@ -66,8 +65,8 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
|
||||
runningStatus,
|
||||
result,
|
||||
forms,
|
||||
retryDetails,
|
||||
onRetryDetailBack = () => { },
|
||||
showSpecialResultPanel,
|
||||
...restResultPanelParams
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -141,24 +140,14 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
retryDetails?.length && (
|
||||
showSpecialResultPanel && (
|
||||
<div className='h-0 grow overflow-y-auto pb-4'>
|
||||
<RetryResultPanel
|
||||
list={retryDetails.map((item, index) => ({
|
||||
...item,
|
||||
title: `${t('workflow.nodes.common.retry.retry')} ${index + 1}`,
|
||||
node_type: nodeType!,
|
||||
extras: {
|
||||
icon: toolIcon!,
|
||||
},
|
||||
}))}
|
||||
onBack={onRetryDetailBack}
|
||||
/>
|
||||
<SpecialResultPanel {...restResultPanelParams} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!retryDetails?.length && (
|
||||
!showSpecialResultPanel && (
|
||||
<div className='h-0 grow overflow-y-auto pb-4'>
|
||||
<div className='mt-3 px-4 space-y-4'>
|
||||
{forms.map((form, index) => (
|
||||
|
@ -142,7 +142,7 @@ const useOneStepRun = <T>({
|
||||
const { handleNodeDataUpdate }: { handleNodeDataUpdate: (data: any) => void } = useNodeDataUpdate()
|
||||
const [canShowSingleRun, setCanShowSingleRun] = useState(false)
|
||||
const isShowSingleRun = data._isSingleRun && canShowSingleRun
|
||||
const [iterationRunResult, setIterationRunResult] = useState<NodeTracing[][]>([])
|
||||
const [iterationRunResult, setIterationRunResult] = useState<NodeTracing[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (!checkValid) {
|
||||
@ -173,7 +173,7 @@ const useOneStepRun = <T>({
|
||||
const workflowStore = useWorkflowStore()
|
||||
useEffect(() => {
|
||||
workflowStore.getState().setShowSingleRunPanel(!!isShowSingleRun)
|
||||
}, [isShowSingleRun])
|
||||
}, [isShowSingleRun, workflowStore])
|
||||
|
||||
const hideSingleRun = () => {
|
||||
handleNodeDataUpdate({
|
||||
@ -211,7 +211,7 @@ const useOneStepRun = <T>({
|
||||
}
|
||||
else {
|
||||
setIterationRunResult([])
|
||||
let _iterationResult: NodeTracing[][] = []
|
||||
let _iterationResult: NodeTracing[] = []
|
||||
let _runResult: any = null
|
||||
ssePost(
|
||||
getIterationSingleNodeRunUrl(isChatMode, appId!, id),
|
||||
@ -231,27 +231,43 @@ const useOneStepRun = <T>({
|
||||
_runResult.created_by = iterationData.created_by.name
|
||||
setRunResult(_runResult)
|
||||
},
|
||||
onIterationNext: () => {
|
||||
// iteration next trigger time is triggered one more time than iterationTimes
|
||||
if (_iterationResult.length >= iterationTimes!)
|
||||
return
|
||||
|
||||
onIterationStart: (params) => {
|
||||
const newIterationRunResult = produce(_iterationResult, (draft) => {
|
||||
draft.push([])
|
||||
draft.push({
|
||||
...params.data,
|
||||
status: NodeRunningStatus.Running,
|
||||
})
|
||||
})
|
||||
_iterationResult = newIterationRunResult
|
||||
setIterationRunResult(newIterationRunResult)
|
||||
},
|
||||
onIterationNext: () => {
|
||||
// iteration next trigger time is triggered one more time than iterationTimes
|
||||
if (_iterationResult.length >= iterationTimes!)
|
||||
return _iterationResult.length >= iterationTimes!
|
||||
},
|
||||
onIterationFinish: (params) => {
|
||||
_runResult = params.data
|
||||
setRunResult(_runResult)
|
||||
const iterationRunResult = _iterationResult
|
||||
const currentIndex = iterationRunResult.findIndex(trace => trace.id === params.data.id)
|
||||
const newIterationRunResult = produce(iterationRunResult, (draft) => {
|
||||
if (currentIndex > -1) {
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...data,
|
||||
}
|
||||
}
|
||||
})
|
||||
_iterationResult = newIterationRunResult
|
||||
setIterationRunResult(newIterationRunResult)
|
||||
},
|
||||
onNodeStarted: (params) => {
|
||||
const newIterationRunResult = produce(_iterationResult, (draft) => {
|
||||
draft[draft.length - 1].push({
|
||||
draft.push({
|
||||
...params.data,
|
||||
status: NodeRunningStatus.Running,
|
||||
} as NodeTracing)
|
||||
})
|
||||
})
|
||||
_iterationResult = newIterationRunResult
|
||||
setIterationRunResult(newIterationRunResult)
|
||||
@ -260,18 +276,25 @@ const useOneStepRun = <T>({
|
||||
const iterationRunResult = _iterationResult
|
||||
|
||||
const { data } = params
|
||||
const currentIndex = iterationRunResult[iterationRunResult.length - 1].findIndex(trace => trace.node_id === data.node_id)
|
||||
const currentIndex = iterationRunResult.findIndex(trace => trace.id === data.id)
|
||||
const newIterationRunResult = produce(iterationRunResult, (draft) => {
|
||||
if (currentIndex > -1) {
|
||||
draft[draft.length - 1][currentIndex] = {
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...data,
|
||||
status: NodeRunningStatus.Succeeded,
|
||||
} as NodeTracing
|
||||
}
|
||||
}
|
||||
})
|
||||
_iterationResult = newIterationRunResult
|
||||
setIterationRunResult(newIterationRunResult)
|
||||
},
|
||||
onNodeRetry: (params) => {
|
||||
const newIterationRunResult = produce(_iterationResult, (draft) => {
|
||||
draft.push(params.data)
|
||||
})
|
||||
_iterationResult = newIterationRunResult
|
||||
setIterationRunResult(newIterationRunResult)
|
||||
},
|
||||
onError: () => {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
|
@ -9,6 +9,7 @@ import { ToolIcon } from './components/tool-icon'
|
||||
import useConfig from './use-config'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useInstalledPluginList } from '@/service/use-plugins'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
|
||||
const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||
const { inputs, currentStrategy } = useConfig(props.id, props.data)
|
||||
@ -16,11 +17,26 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||
const pluginList = useInstalledPluginList()
|
||||
// TODO: Implement models
|
||||
const models = useMemo(() => {
|
||||
const models = []
|
||||
if (!inputs) return []
|
||||
// if selected, show in node
|
||||
// if required and not selected, show empty selector
|
||||
// if not required and not selected, show nothing
|
||||
}, [currentStrategy, inputs.agent_parameters])
|
||||
const models = currentStrategy?.parameters
|
||||
.filter(param => param.type === FormTypeEnum.modelSelector)
|
||||
.reduce((acc, param) => {
|
||||
const item = inputs.agent_parameters?.[param.name]
|
||||
if (!item) {
|
||||
if (param.required) {
|
||||
acc.push({ param: param.name })
|
||||
return acc
|
||||
}
|
||||
else { return acc }
|
||||
}
|
||||
acc.push({ provider: item.provider, model: item.model, param: param.name })
|
||||
return acc
|
||||
}, [] as Array<{ param: string } | { provider: string, model: string, param: string }>) || []
|
||||
return models
|
||||
}, [currentStrategy, inputs])
|
||||
|
||||
const tools = useMemo(() => {
|
||||
const tools: Array<ToolIconProps> = []
|
||||
@ -49,24 +65,26 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||
{inputs.agent_strategy_label}
|
||||
</SettingItem>
|
||||
: <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />}
|
||||
<Group
|
||||
{models.length && <Group
|
||||
label={<GroupLabel className='mt-1'>
|
||||
{t('workflow.nodes.agent.model')}
|
||||
</GroupLabel>}
|
||||
>
|
||||
<ModelSelector
|
||||
modelList={[]}
|
||||
readonly
|
||||
/>
|
||||
<ModelSelector
|
||||
modelList={[]}
|
||||
readonly
|
||||
/>
|
||||
<ModelSelector
|
||||
modelList={[]}
|
||||
readonly
|
||||
/>
|
||||
</Group>
|
||||
{models.map((model) => {
|
||||
return <ModelSelector
|
||||
key={model.param}
|
||||
modelList={[]}
|
||||
defaultModel={
|
||||
'provider' in model
|
||||
? {
|
||||
provider: model.provider,
|
||||
model: model.model,
|
||||
}
|
||||
: undefined}
|
||||
readonly
|
||||
/>
|
||||
})}
|
||||
</Group>}
|
||||
<Group label={<GroupLabel className='mt-1'>
|
||||
{t('workflow.nodes.agent.toolbox')}
|
||||
</GroupLabel>}>
|
||||
|
@ -5,7 +5,7 @@ export type AgentNodeType = CommonNodeType & {
|
||||
agent_strategy_provider_name?: string
|
||||
agent_strategy_name?: string
|
||||
agent_strategy_label?: string
|
||||
agent_parameters?: ToolVarInputs,
|
||||
agent_parameters?: Record<string, any>
|
||||
agent_configurations?: Record<string, ToolVarInputs>
|
||||
output_schema: Record<string, any>
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import { FileArrow01 } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import type { NodePanelProps } from '@/app/components/workflow/types'
|
||||
import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form'
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import { useRetryDetailShowInSingleRun } from '@/app/components/workflow/nodes/_base/components/retry/hooks'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.http'
|
||||
|
||||
@ -61,10 +60,6 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({
|
||||
hideCurlPanel,
|
||||
handleCurlImport,
|
||||
} = useConfig(id, data)
|
||||
const {
|
||||
retryDetails,
|
||||
handleRetryDetailsChange,
|
||||
} = useRetryDetailShowInSingleRun()
|
||||
// To prevent prompt editor in body not update data.
|
||||
if (!isDataReady)
|
||||
return null
|
||||
@ -198,9 +193,7 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({
|
||||
runningStatus={runningStatus}
|
||||
onRun={handleRun}
|
||||
onStop={handleStop}
|
||||
retryDetails={retryDetails}
|
||||
onRetryDetailBack={handleRetryDetailsChange}
|
||||
result={<ResultPanel {...runResult} showSteps={false} onShowRetryDetail={handleRetryDetailsChange} />}
|
||||
result={<ResultPanel {...runResult} showSteps={false} />}
|
||||
/>
|
||||
)}
|
||||
{(isShowCurlPanel && !readOnly) && (
|
||||
|
@ -1,13 +1,9 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiArrowRightSLine,
|
||||
} from '@remixicon/react'
|
||||
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
||||
import Split from '../_base/components/split'
|
||||
import ResultPanel from '../../run/result-panel'
|
||||
import { IterationResultPanel } from '../../run/iteration-log'
|
||||
import { MAX_ITERATION_PARALLEL_NUM, MIN_ITERATION_PARALLEL_NUM } from '../../constants'
|
||||
import type { IterationNodeType } from './types'
|
||||
import useConfig from './use-config'
|
||||
@ -18,6 +14,9 @@ import Switch from '@/app/components/base/switch'
|
||||
import Select from '@/app/components/base/select'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import Input from '@/app/components/base/input'
|
||||
import formatTracing from '@/app/components/workflow/run/utils/format-log'
|
||||
|
||||
import { useLogs } from '@/app/components/workflow/run/hooks'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.iteration'
|
||||
|
||||
@ -50,9 +49,6 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
|
||||
handleOutputVarChange,
|
||||
isShowSingleRun,
|
||||
hideSingleRun,
|
||||
isShowIterationDetail,
|
||||
backToSingleRun,
|
||||
showIterationDetail,
|
||||
runningStatus,
|
||||
handleRun,
|
||||
handleStop,
|
||||
@ -69,6 +65,9 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
|
||||
changeParallelNums,
|
||||
} = useConfig(id, data)
|
||||
|
||||
const nodeInfo = formatTracing(iterationRunResult, t)[0]
|
||||
const logsParams = useLogs()
|
||||
|
||||
return (
|
||||
<div className='pt-2 pb-2'>
|
||||
<div className='px-4 pb-4 space-y-4'>
|
||||
@ -163,26 +162,12 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({
|
||||
runningStatus={runningStatus}
|
||||
onRun={handleRun}
|
||||
onStop={handleStop}
|
||||
{...logsParams}
|
||||
result={
|
||||
<div className='mt-3'>
|
||||
<div className='px-4'>
|
||||
<div className='flex items-center h-[34px] justify-between px-3 bg-gray-100 border-[0.5px] border-gray-200 rounded-lg cursor-pointer' onClick={showIterationDetail}>
|
||||
<div className='leading-[18px] text-[13px] font-medium text-gray-700'>{t(`${i18nPrefix}.iteration`, { count: iterationRunResult.length })}</div>
|
||||
<RiArrowRightSLine className='w-3.5 h-3.5 text-gray-500' />
|
||||
</div>
|
||||
<Split className='mt-3' />
|
||||
</div>
|
||||
<ResultPanel {...runResult} showSteps={false} />
|
||||
</div>
|
||||
<ResultPanel {...runResult} showSteps={false} nodeInfo={nodeInfo} {...logsParams} />
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{isShowIterationDetail && (
|
||||
<IterationResultPanel
|
||||
onBack={backToSingleRun}
|
||||
list={iterationRunResult}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/c
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import { useRetryDetailShowInSingleRun } from '@/app/components/workflow/nodes/_base/components/retry/hooks'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.llm'
|
||||
|
||||
@ -70,10 +69,6 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
runResult,
|
||||
filterJinjia2InputVar,
|
||||
} = useConfig(id, data)
|
||||
const {
|
||||
retryDetails,
|
||||
handleRetryDetailsChange,
|
||||
} = useRetryDetailShowInSingleRun()
|
||||
|
||||
const model = inputs.model
|
||||
|
||||
@ -293,9 +288,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
runningStatus={runningStatus}
|
||||
onRun={handleRun}
|
||||
onStop={handleStop}
|
||||
retryDetails={retryDetails}
|
||||
onRetryDetailBack={handleRetryDetailsChange}
|
||||
result={<ResultPanel {...runResult} showSteps={false} onShowRetryDetail={handleRetryDetailsChange} />}
|
||||
result={<ResultPanel {...runResult} showSteps={false} />}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -14,7 +14,6 @@ import Loading from '@/app/components/base/loading'
|
||||
import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import { useRetryDetailShowInSingleRun } from '@/app/components/workflow/nodes/_base/components/retry/hooks'
|
||||
import { useToolIcon } from '@/app/components/workflow/hooks'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.tool'
|
||||
@ -52,10 +51,6 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
|
||||
outputSchema,
|
||||
} = useConfig(id, data)
|
||||
const toolIcon = useToolIcon(data)
|
||||
const {
|
||||
retryDetails,
|
||||
handleRetryDetailsChange,
|
||||
} = useRetryDetailShowInSingleRun()
|
||||
|
||||
if (isLoading) {
|
||||
return <div className='flex h-[200px] items-center justify-center'>
|
||||
@ -166,9 +161,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
|
||||
runningStatus={runningStatus}
|
||||
onRun={handleRun}
|
||||
onStop={handleStop}
|
||||
retryDetails={retryDetails}
|
||||
onRetryDetailBack={handleRetryDetailsChange}
|
||||
result={<ResultPanel {...runResult} showSteps={false} onShowRetryDetail={handleRetryDetailsChange} />}
|
||||
result={<ResultPanel {...runResult} showSteps={false} />}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -6,17 +6,14 @@ import type {
|
||||
NodeTracing,
|
||||
} from '@/types/workflow'
|
||||
import { Iteration } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
|
||||
type IterationLogTriggerProps = {
|
||||
nodeInfo: NodeTracing
|
||||
onShowIterationResultList: (iterationResultList: NodeTracing[][], iterationResultDurationMap: IterationDurationMap) => void
|
||||
justShowIterationNavArrow?: boolean
|
||||
}
|
||||
const IterationLogTrigger = ({
|
||||
nodeInfo,
|
||||
onShowIterationResultList,
|
||||
justShowIterationNavArrow,
|
||||
}: IterationLogTriggerProps) => {
|
||||
const { t } = useTranslation()
|
||||
const getErrorCount = (details: NodeTracing[][] | undefined) => {
|
||||
@ -41,31 +38,19 @@ const IterationLogTrigger = ({
|
||||
onShowIterationResultList(nodeInfo.details || [], nodeInfo?.iterDurationMap || nodeInfo.execution_metadata?.iteration_duration_map || {})
|
||||
}
|
||||
return (
|
||||
<div className='mt-2 mb-1 !px-2'>
|
||||
<Button
|
||||
className='flex items-center w-full self-stretch gap-2 px-3 py-2 bg-components-button-tertiary-bg-hover hover:bg-components-button-tertiary-bg-hover rounded-lg cursor-pointer border-none'
|
||||
onClick={handleOnShowIterationDetail}
|
||||
>
|
||||
<Iteration className='w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
<div className='flex-1 text-left system-sm-medium text-components-button-tertiary-text'>{t('workflow.nodes.iteration.iteration', { count: getCount(nodeInfo.details?.length, nodeInfo.metadata?.iterator_length) })}{getErrorCount(nodeInfo.details) > 0 && (
|
||||
<>
|
||||
{t('workflow.nodes.iteration.comma')}
|
||||
{t('workflow.nodes.iteration.error', { count: getErrorCount(nodeInfo.details) })}
|
||||
</>
|
||||
)}</div>
|
||||
{justShowIterationNavArrow
|
||||
? (
|
||||
<RiArrowRightSLine className='w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
)
|
||||
: (
|
||||
<div className='flex items-center space-x-1 text-[#155EEF]'>
|
||||
<div className='text-[13px] font-normal '>{t('workflow.common.viewDetailInTracingPanel')}</div>
|
||||
<RiArrowRightSLine className='w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
<Split className='mt-2' />
|
||||
</div>
|
||||
<Button
|
||||
className='flex items-center w-full self-stretch gap-2 px-3 py-2 bg-components-button-tertiary-bg-hover hover:bg-components-button-tertiary-bg-hover rounded-lg cursor-pointer border-none'
|
||||
onClick={handleOnShowIterationDetail}
|
||||
>
|
||||
<Iteration className='w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
<div className='flex-1 text-left system-sm-medium text-components-button-tertiary-text'>{t('workflow.nodes.iteration.iteration', { count: getCount(nodeInfo.details?.length, nodeInfo.metadata?.iterator_length) })}{getErrorCount(nodeInfo.details) > 0 && (
|
||||
<>
|
||||
{t('workflow.nodes.iteration.comma')}
|
||||
{t('workflow.nodes.iteration.error', { count: getErrorCount(nodeInfo.details) })}
|
||||
</>
|
||||
)}</div>
|
||||
<RiArrowRightSLine className='w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,6 @@ type Props = {
|
||||
onShowRetryDetail?: (detail: NodeTracing[]) => void
|
||||
onShowAgentResultList?: (detail: AgentLogItemWithChildren[]) => void
|
||||
notShowIterationNav?: boolean
|
||||
justShowIterationNavArrow?: boolean
|
||||
justShowRetryNavArrow?: boolean
|
||||
}
|
||||
|
||||
const NodePanel: FC<Props> = ({
|
||||
@ -50,7 +48,6 @@ const NodePanel: FC<Props> = ({
|
||||
onShowRetryDetail,
|
||||
onShowAgentResultList,
|
||||
notShowIterationNav,
|
||||
justShowIterationNavArrow,
|
||||
}) => {
|
||||
const [collapseState, doSetCollapseState] = useState<boolean>(true)
|
||||
const setCollapseState = useCallback((state: boolean) => {
|
||||
@ -138,7 +135,6 @@ const NodePanel: FC<Props> = ({
|
||||
<IterationLogTrigger
|
||||
nodeInfo={nodeInfo}
|
||||
onShowIterationResultList={onShowIterationDetail}
|
||||
justShowIterationNavArrow={justShowIterationNavArrow}
|
||||
/>
|
||||
)}
|
||||
{isRetryNode && onShowRetryDetail && (
|
||||
|
@ -1,19 +1,20 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiArrowRightSLine,
|
||||
RiRestartFill,
|
||||
} from '@remixicon/react'
|
||||
import StatusPanel from './status'
|
||||
import MetaData from './meta'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { hasRetryNode } from '@/app/components/workflow/utils'
|
||||
import { IterationLogTrigger } from '@/app/components/workflow/run/iteration-log'
|
||||
import { RetryLogTrigger } from '@/app/components/workflow/run/retry-log'
|
||||
import { AgentLogTrigger } from '@/app/components/workflow/run/agent-log'
|
||||
|
||||
type ResultPanelProps = {
|
||||
nodeInfo?: NodeTracing
|
||||
inputs?: string
|
||||
process_data?: string
|
||||
outputs?: string
|
||||
@ -28,11 +29,13 @@ type ResultPanelProps = {
|
||||
showSteps?: boolean
|
||||
exceptionCounts?: number
|
||||
execution_metadata?: any
|
||||
retry_events?: NodeTracing[]
|
||||
onShowRetryDetail?: (retries: NodeTracing[]) => void
|
||||
handleShowIterationResultList?: (detail: NodeTracing[][], iterDurationMap: any) => void
|
||||
onShowRetryDetail?: (detail: NodeTracing[]) => void
|
||||
onShowAgentResultList?: () => void
|
||||
}
|
||||
|
||||
const ResultPanel: FC<ResultPanelProps> = ({
|
||||
nodeInfo,
|
||||
inputs,
|
||||
process_data,
|
||||
outputs,
|
||||
@ -46,10 +49,14 @@ const ResultPanel: FC<ResultPanelProps> = ({
|
||||
showSteps,
|
||||
exceptionCounts,
|
||||
execution_metadata,
|
||||
retry_events,
|
||||
handleShowIterationResultList,
|
||||
onShowRetryDetail,
|
||||
onShowAgentResultList,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const isIterationNode = nodeInfo?.node_type === BlockEnum.Iteration
|
||||
const isRetryNode = hasRetryNode(nodeInfo?.node_type) && nodeInfo?.retryDetail
|
||||
const isAgentNode = nodeInfo?.node_type === BlockEnum.Agent
|
||||
|
||||
return (
|
||||
<div className='bg-components-panel-bg py-2'>
|
||||
@ -62,23 +69,32 @@ const ResultPanel: FC<ResultPanelProps> = ({
|
||||
exceptionCounts={exceptionCounts}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
retry_events?.length && onShowRetryDetail && (
|
||||
<div className='px-4'>
|
||||
<Button
|
||||
className='flex items-center justify-between w-full'
|
||||
variant='tertiary'
|
||||
onClick={() => onShowRetryDetail(retry_events)}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
<RiRestartFill className='mr-0.5 w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
{t('workflow.nodes.common.retry.retries', { num: retry_events?.length })}
|
||||
</div>
|
||||
<RiArrowRightSLine className='w-4 h-4 text-components-button-tertiary-text shrink-0' />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='px-4'>
|
||||
{
|
||||
isIterationNode && handleShowIterationResultList && (
|
||||
<IterationLogTrigger
|
||||
nodeInfo={nodeInfo}
|
||||
onShowIterationResultList={handleShowIterationResultList}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
isRetryNode && onShowRetryDetail && (
|
||||
<RetryLogTrigger
|
||||
nodeInfo={nodeInfo}
|
||||
onShowRetryResultList={onShowRetryDetail}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
isAgentNode && onShowAgentResultList && (
|
||||
<AgentLogTrigger
|
||||
nodeInfo={nodeInfo}
|
||||
onShowAgentResultList={onShowAgentResultList}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='px-4 py-2 flex flex-col gap-2'>
|
||||
<CodeEditor
|
||||
readOnly
|
||||
|
@ -7,18 +7,18 @@ import type {
|
||||
NodeTracing,
|
||||
} from '@/types/workflow'
|
||||
|
||||
type SpecialResultPanelProps = {
|
||||
showRetryDetail: boolean
|
||||
setShowRetryDetailFalse: () => void
|
||||
retryResultList: NodeTracing[]
|
||||
export type SpecialResultPanelProps = {
|
||||
showRetryDetail?: boolean
|
||||
setShowRetryDetailFalse?: () => void
|
||||
retryResultList?: NodeTracing[]
|
||||
|
||||
showIteratingDetail: boolean
|
||||
setShowIteratingDetailFalse: () => void
|
||||
iterationResultList: NodeTracing[][]
|
||||
iterationResultDurationMap: IterationDurationMap
|
||||
showIteratingDetail?: boolean
|
||||
setShowIteratingDetailFalse?: () => void
|
||||
iterationResultList?: NodeTracing[][]
|
||||
iterationResultDurationMap?: IterationDurationMap
|
||||
|
||||
agentResultList: AgentLogItemWithChildren[]
|
||||
setAgentResultList: (list: AgentLogItemWithChildren[]) => void
|
||||
agentResultList?: AgentLogItemWithChildren[]
|
||||
setAgentResultList?: (list: AgentLogItemWithChildren[]) => void
|
||||
}
|
||||
const SpecialResultPanel = ({
|
||||
showRetryDetail,
|
||||
@ -36,7 +36,7 @@ const SpecialResultPanel = ({
|
||||
return (
|
||||
<>
|
||||
{
|
||||
showRetryDetail && (
|
||||
!!showRetryDetail && !!retryResultList?.length && setShowRetryDetailFalse && (
|
||||
<RetryResultPanel
|
||||
list={retryResultList}
|
||||
onBack={setShowRetryDetailFalse}
|
||||
@ -44,7 +44,7 @@ const SpecialResultPanel = ({
|
||||
)
|
||||
}
|
||||
{
|
||||
showIteratingDetail && (
|
||||
showIteratingDetail && !!iterationResultList?.length && setShowIteratingDetailFalse && (
|
||||
<IterationResultPanel
|
||||
list={iterationResultList}
|
||||
onBack={setShowIteratingDetailFalse}
|
||||
@ -53,7 +53,7 @@ const SpecialResultPanel = ({
|
||||
)
|
||||
}
|
||||
{
|
||||
!!agentResultList.length && (
|
||||
!!agentResultList?.length && setAgentResultList && (
|
||||
<AgentResultPanel
|
||||
list={agentResultList}
|
||||
setAgentResultList={setAgentResultList}
|
||||
|
@ -137,8 +137,6 @@ const TracingPanel: FC<TracingPanelProps> = ({
|
||||
onShowIterationDetail={handleShowIterationResultList}
|
||||
onShowRetryDetail={handleShowRetryResultList}
|
||||
onShowAgentResultList={setAgentResultList}
|
||||
justShowIterationNavArrow={true}
|
||||
justShowRetryNavArrow={true}
|
||||
hideInfo={hideNodeInfo}
|
||||
hideProcessDetail={hideNodeProcessDetail}
|
||||
/>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { AgentLogItem, AgentLogItemWithChildren, NodeTracing } from '@/types/workflow'
|
||||
|
||||
const supportedAgentLogNodes = [BlockEnum.Agent, BlockEnum.Tool]
|
||||
|
||||
const listToTree = (logs: AgentLogItem[]) => {
|
||||
if (!logs || logs.length === 0)
|
||||
return []
|
||||
@ -24,7 +26,7 @@ const listToTree = (logs: AgentLogItem[]) => {
|
||||
}
|
||||
const format = (list: NodeTracing[]): NodeTracing[] => {
|
||||
const result: NodeTracing[] = list.map((item) => {
|
||||
if (item.node_type === BlockEnum.Agent && item.execution_metadata?.agent_log && item.execution_metadata?.agent_log.length > 0)
|
||||
if (supportedAgentLogNodes.includes(item.node_type) && item.execution_metadata?.agent_log && item.execution_metadata?.agent_log.length > 0)
|
||||
item.agentLog = listToTree(item.execution_metadata.agent_log)
|
||||
|
||||
return item
|
||||
|
@ -1,22 +1,25 @@
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
|
||||
function printNodeStructure(node: NodeTracing, level: number) {
|
||||
const indent = ' '.repeat(level)
|
||||
function printNodeStructure(node: NodeTracing, depth: number) {
|
||||
const indent = ' '.repeat(depth)
|
||||
console.log(`${indent}${node.title}`)
|
||||
if (node.parallelDetail?.children) {
|
||||
node.parallelDetail.children.forEach((child) => {
|
||||
printNodeStructure(child, level + 1)
|
||||
printNodeStructure(child, depth + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function addTitle({
|
||||
list, level, parallelNumRecord,
|
||||
list, depth, belongParallelIndexInfo,
|
||||
}: {
|
||||
list: NodeTracing[], level: number, parallelNumRecord: Record<string, number>
|
||||
list: NodeTracing[],
|
||||
depth: number,
|
||||
belongParallelIndexInfo?: string,
|
||||
}, t: any) {
|
||||
let branchIndex = 0
|
||||
const hasMoreThanOneParallel = list.filter(node => node.parallelDetail?.isParallelStartNode).length > 1
|
||||
list.forEach((node) => {
|
||||
const parallel_id = node.parallel_id ?? node.execution_metadata?.parallel_id ?? null
|
||||
const parallel_start_node_id = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null
|
||||
@ -26,15 +29,20 @@ function addTitle({
|
||||
return
|
||||
|
||||
const isParallelStartNode = node.parallelDetail?.isParallelStartNode
|
||||
if (isParallelStartNode)
|
||||
parallelNumRecord.num++
|
||||
|
||||
const letter = parallelNumRecord.num > 1 ? String.fromCharCode(64 + level) : ''
|
||||
const parallelLevelInfo = `${parallelNumRecord.num}${letter}`
|
||||
const parallelIndexLetter = (() => {
|
||||
if (!isParallelStartNode || !hasMoreThanOneParallel)
|
||||
return ''
|
||||
|
||||
const index = 1 + list.filter(node => node.parallelDetail?.isParallelStartNode).findIndex(item => item.node_id === node.node_id)
|
||||
return String.fromCharCode(64 + index)
|
||||
})()
|
||||
|
||||
const parallelIndexInfo = `${depth}${parallelIndexLetter}`
|
||||
|
||||
if (isParallelStartNode) {
|
||||
node.parallelDetail!.isParallelStartNode = true
|
||||
node.parallelDetail!.parallelTitle = `${t('workflow.common.parallel')}-${parallelLevelInfo}`
|
||||
node.parallelDetail!.parallelTitle = `${t('workflow.common.parallel')}-${parallelIndexInfo}`
|
||||
}
|
||||
|
||||
const isBrachStartNode = parallel_start_node_id === node.node_id
|
||||
@ -47,14 +55,14 @@ function addTitle({
|
||||
}
|
||||
}
|
||||
|
||||
node.parallelDetail!.branchTitle = `${t('workflow.common.branch')}-${parallelLevelInfo}-${branchLetter}`
|
||||
node.parallelDetail!.branchTitle = `${t('workflow.common.branch')}-${belongParallelIndexInfo}-${branchLetter}`
|
||||
}
|
||||
|
||||
if (node.parallelDetail?.children && node.parallelDetail.children.length > 0) {
|
||||
addTitle({
|
||||
list: node.parallelDetail.children,
|
||||
level: level + 1,
|
||||
parallelNumRecord,
|
||||
depth: depth + 1,
|
||||
belongParallelIndexInfo: parallelIndexInfo,
|
||||
}, t)
|
||||
}
|
||||
})
|
||||
@ -70,7 +78,7 @@ const format = (list: NodeTracing[], t: any): NodeTracing[] => {
|
||||
const parallel_id = node.parallel_id ?? node.execution_metadata?.parallel_id ?? null
|
||||
const parent_parallel_id = node.parent_parallel_id ?? node.execution_metadata?.parent_parallel_id ?? null
|
||||
const branchStartNodeId = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null
|
||||
const parent_parallel_start_node_id = node.parent_parallel_start_node_id ?? node.execution_metadata?.parent_parallel_start_node_id ?? null
|
||||
const parentParallelBranchStartNodeId = node.parent_parallel_start_node_id ?? node.execution_metadata?.parent_parallel_start_node_id ?? null
|
||||
const isNotInParallel = !parallel_id || node.node_type === BlockEnum.End
|
||||
if (isNotInParallel)
|
||||
return
|
||||
@ -87,16 +95,24 @@ const format = (list: NodeTracing[], t: any): NodeTracing[] => {
|
||||
if (isRootLevel)
|
||||
return
|
||||
|
||||
const parentParallelStartNode = result.find(item => item.node_id === parent_parallel_start_node_id)
|
||||
// append to parent parallel start node
|
||||
const parentParallelStartNode = result.find(item => item.node_id === parentParallelBranchStartNodeId)
|
||||
// append to parent parallel start node and after the same branch
|
||||
if (parentParallelStartNode) {
|
||||
if (!parentParallelStartNode?.parallelDetail) {
|
||||
parentParallelStartNode!.parallelDetail = {
|
||||
children: [],
|
||||
}
|
||||
}
|
||||
if (parentParallelStartNode!.parallelDetail.children)
|
||||
parentParallelStartNode!.parallelDetail.children.push(node)
|
||||
if (parentParallelStartNode!.parallelDetail.children) {
|
||||
const sameBranchNodesLastIndex = parentParallelStartNode.parallelDetail.children.findLastIndex((node) => {
|
||||
const currStartNodeId = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null
|
||||
return currStartNodeId === parentParallelBranchStartNodeId
|
||||
})
|
||||
if (sameBranchNodesLastIndex !== -1)
|
||||
parentParallelStartNode!.parallelDetail.children.splice(sameBranchNodesLastIndex + 1, 0, node)
|
||||
else
|
||||
parentParallelStartNode!.parallelDetail.children.push(node)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -144,14 +160,9 @@ const format = (list: NodeTracing[], t: any): NodeTracing[] => {
|
||||
// console.log(`----- p: ${now} end -----`)
|
||||
// })
|
||||
|
||||
const parallelNumRecord: Record<string, number> = {
|
||||
num: 0,
|
||||
}
|
||||
|
||||
addTitle({
|
||||
list: filteredInParallelSubNodes,
|
||||
level: 1,
|
||||
parallelNumRecord,
|
||||
depth: 1,
|
||||
}, t)
|
||||
|
||||
return filteredInParallelSubNodes
|
||||
|
@ -73,6 +73,7 @@ const translation = {
|
||||
placeholder: 'Select a tool...',
|
||||
auth: 'AUTHORIZATION',
|
||||
settings: 'TOOL SETTINGS',
|
||||
empty: 'Click the \'+\' button to add tools. You can add multiple tools.',
|
||||
},
|
||||
configureApp: 'Configure App',
|
||||
configureModel: 'Configure model',
|
||||
|
@ -73,6 +73,7 @@ const translation = {
|
||||
placeholder: '选择工具',
|
||||
auth: '授权',
|
||||
settings: '工具设置',
|
||||
empty: '点击 "+" 按钮添加工具。您可以添加多个工具。',
|
||||
},
|
||||
configureApp: '应用设置',
|
||||
configureModel: '模型设置',
|
||||
|
Loading…
Reference in New Issue
Block a user