chore: add warning ui for agentic stragey
parent
e2e2090e0c
commit
7c460eb6e7
@ -0,0 +1,86 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react'
|
||||||
|
import Input, { type InputProps } from '../input'
|
||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
|
||||||
|
export type InputNumberProps = {
|
||||||
|
unit?: string
|
||||||
|
value?: number
|
||||||
|
onChange: (value?: number) => void
|
||||||
|
amount?: number
|
||||||
|
size?: 'sm' | 'md'
|
||||||
|
max?: number
|
||||||
|
min?: number
|
||||||
|
defaultValue?: number
|
||||||
|
} & Omit<InputProps, 'value' | 'onChange' | 'size' | 'min' | 'max' | 'defaultValue'>
|
||||||
|
|
||||||
|
export const InputNumber: FC<InputNumberProps> = (props) => {
|
||||||
|
const { unit, className, onChange, amount = 1, value, size = 'md', max, min, defaultValue, ...rest } = props
|
||||||
|
|
||||||
|
const isValidValue = (v: number) => {
|
||||||
|
if (max && v > max)
|
||||||
|
return false
|
||||||
|
if (min && v < min)
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const inc = () => {
|
||||||
|
if (value === undefined) {
|
||||||
|
onChange(defaultValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newValue = value + amount
|
||||||
|
if (!isValidValue(newValue))
|
||||||
|
return
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
const dec = () => {
|
||||||
|
if (value === undefined) {
|
||||||
|
onChange(defaultValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newValue = value - amount
|
||||||
|
if (!isValidValue(newValue))
|
||||||
|
return
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className='flex'>
|
||||||
|
<Input {...rest}
|
||||||
|
// disable default controller
|
||||||
|
type='text'
|
||||||
|
className={classNames('rounded-r-none', className)}
|
||||||
|
value={value}
|
||||||
|
max={max}
|
||||||
|
min={min}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.value === '')
|
||||||
|
onChange(undefined)
|
||||||
|
|
||||||
|
const parsed = Number(e.target.value)
|
||||||
|
if (Number.isNaN(parsed))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!isValidValue(parsed))
|
||||||
|
return
|
||||||
|
onChange(parsed)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{unit && <div className='flex items-center bg-components-input-bg-normal text-[13px] text-text-placeholder pr-2'>{unit}</div>}
|
||||||
|
<div className='flex flex-col bg-components-input-bg-normal rounded-r-md border-l border-divider-subtle text-text-tertiary focus:shadow-xs'>
|
||||||
|
<button onClick={inc} className={classNames(
|
||||||
|
size === 'sm' ? 'pt-1' : 'pt-1.5',
|
||||||
|
'px-1.5 hover:bg-components-input-bg-hover',
|
||||||
|
)}>
|
||||||
|
<RiArrowUpSLine className='size-3' />
|
||||||
|
</button>
|
||||||
|
<button onClick={dec} className={classNames(
|
||||||
|
size === 'sm' ? 'pb-1' : 'pb-1.5',
|
||||||
|
'px-1.5 hover:bg-components-input-bg-hover',
|
||||||
|
)}>
|
||||||
|
<RiArrowDownSLine className='size-3' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import AllTools from '../../../block-selector/all-tools'
|
||||||
|
import type { Strategy } from './agent-strategy'
|
||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
import { RiArrowDownSLine, RiErrorWarningFill } from '@remixicon/react'
|
||||||
|
import { useAllBuiltInTools } from '@/service/use-tools'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { InstallPluginButton } from './install-plugin-button'
|
||||||
|
|
||||||
|
const ExternalNotInstallWarn = () => {
|
||||||
|
// TODO: add i18n label
|
||||||
|
return <Tooltip
|
||||||
|
popupContent={<div className='space-y-1 text-xs'>
|
||||||
|
<h3 className='text-text-primary font-semibold'>This plugin is not installed</h3>
|
||||||
|
<p className='text-text-secondary tracking-tight'>This plugin is installed from GitHub. Please go to Plugins to reinstall</p>
|
||||||
|
<p>
|
||||||
|
<Link href={'/plugins'} className='text-text-accent tracking-tight'>Link to Plugins</Link>
|
||||||
|
</p>
|
||||||
|
</div>}
|
||||||
|
needsDelay
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<RiErrorWarningFill className='text-text-destructive size-4' />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AgentStrategySelectorProps = {
|
||||||
|
value?: Strategy,
|
||||||
|
onChange: (value?: Strategy) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
|
||||||
|
const { value, onChange } = props
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const list = useAllBuiltInTools()
|
||||||
|
// TODO: should be replaced by real data
|
||||||
|
const isExternalInstalled = true
|
||||||
|
return <PortalToFollowElem open={open} onOpenChange={setOpen}>
|
||||||
|
<PortalToFollowElemTrigger className='w-full'>
|
||||||
|
<div className='py-2 pl-3 pr-2 flex items-center rounded-lg bg-components-input-bg-normal w-full hover:bg-state-base-hover-alt' onClick={() => setOpen(true)}>
|
||||||
|
{list.data && <img
|
||||||
|
src={list.data.find(
|
||||||
|
coll => coll,
|
||||||
|
)?.icon as string}
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
className='rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'
|
||||||
|
alt='icon'
|
||||||
|
/>}
|
||||||
|
<p
|
||||||
|
className={classNames(value ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder', 'text-xs px-1')}
|
||||||
|
>
|
||||||
|
{value?.agent_strategy_name || 'Select agentic strategy'}
|
||||||
|
</p>
|
||||||
|
<div className='ml-auto flex items-center gap-1'>
|
||||||
|
<InstallPluginButton onClick={e => e.preventDefault()} />
|
||||||
|
{isExternalInstalled ? <ExternalNotInstallWarn /> : <RiArrowDownSLine className='size-4 text-text-tertiary' />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent>
|
||||||
|
{list.data && <AllTools
|
||||||
|
className='border-components-panel-border bg-components-panel-bg-blur'
|
||||||
|
searchText=''
|
||||||
|
tags={[]}
|
||||||
|
buildInTools={list.data}
|
||||||
|
customTools={[]}
|
||||||
|
workflowTools={[]}
|
||||||
|
onSelect={(_e, tool) => {
|
||||||
|
if (!tool) {
|
||||||
|
// TODO: should not be called, try it
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onChange({
|
||||||
|
agent_strategy_name: tool.title,
|
||||||
|
agent_strategy_provider_name: tool.provider_name,
|
||||||
|
agent_parameters: {},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>}
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import type { ToolVarInputs } from '../../tool/types'
|
||||||
|
import ListEmpty from '@/app/components/base/list-empty'
|
||||||
|
import { AgentStrategySelector } from './agent-strategy-selector'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
export type Strategy = {
|
||||||
|
agent_strategy_provider_name: string
|
||||||
|
agent_strategy_name: string
|
||||||
|
agent_strategy_label?: string
|
||||||
|
agent_parameters?: ToolVarInputs
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AgentStrategyProps = {
|
||||||
|
strategy?: Strategy
|
||||||
|
onStrategyChange: (strategy?: Strategy) => void
|
||||||
|
formSchema: CredentialFormSchema[]
|
||||||
|
formValue: ToolVarInputs
|
||||||
|
onFormValueChange: (value: ToolVarInputs) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||||
|
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props
|
||||||
|
return <div className='space-y-2'>
|
||||||
|
<AgentStrategySelector value={strategy} onChange={onStrategyChange} />
|
||||||
|
{
|
||||||
|
strategy
|
||||||
|
? <div></div>
|
||||||
|
// TODO: list empty need a icon
|
||||||
|
: <ListEmpty
|
||||||
|
title='Please configure agentic strategy.'
|
||||||
|
description={<div className='text-text-tertiary text-xs'>
|
||||||
|
After configuring the agentic strategy, this node will automatically load the remaining configurations. The strategy will affect the mechanism of multi-step tool reasoning. <br />
|
||||||
|
<Link href={'/'} className='text-text-accent-secondary'>Learn more</Link>
|
||||||
|
</div>}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
import type { ComponentProps, FC, PropsWithChildren, ReactNode } from 'react'
|
||||||
|
|
||||||
|
export type GroupLabelProps = ComponentProps<'div'>
|
||||||
|
|
||||||
|
export const GroupLabel: FC<GroupLabelProps> = (props) => {
|
||||||
|
const { children, className, ...rest } = props
|
||||||
|
return <div {...rest} className={classNames('mb-1 system-2xs-medium-uppercase text-text-tertiary', className)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Group = PropsWithChildren<{
|
||||||
|
label: ReactNode
|
||||||
|
}>
|
||||||
|
|
||||||
|
export const Group: FC<Group> = (props) => {
|
||||||
|
const { children, label } = props
|
||||||
|
return <div className={classNames('py-1')}>
|
||||||
|
{label}
|
||||||
|
<div className='space-y-0.5'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { RiInstallLine, RiLoader2Line } from '@remixicon/react'
|
||||||
|
import type { ComponentProps } from 'react'
|
||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
|
||||||
|
type InstallPluginButtonProps = Omit<ComponentProps<typeof Button>, 'children'>
|
||||||
|
|
||||||
|
export const InstallPluginButton = (props: InstallPluginButtonProps) => {
|
||||||
|
const { loading, className, ...rest } = props
|
||||||
|
// TODO: add i18n label
|
||||||
|
return <Button variant={'secondary'} disabled={loading} className={classNames('flex items-center', className)} {...rest}>
|
||||||
|
{loading ? 'Installing' : 'Install'}
|
||||||
|
{!loading ? <RiInstallLine className='size-4 ml-1' /> : <RiLoader2Line className='size-4 ml-1 animate-spin' />}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
import type { ComponentProps, PropsWithChildren } from 'react'
|
||||||
|
|
||||||
|
export type SettingItemProps = PropsWithChildren<{
|
||||||
|
label: string
|
||||||
|
indicator?: ComponentProps<typeof Indicator>['color']
|
||||||
|
}>
|
||||||
|
|
||||||
|
export const SettingItem = ({ label, children, indicator }: SettingItemProps) => {
|
||||||
|
return <div className='flex items-center h-6 justify-between bg-gray-100 rounded-md px-1 space-x-1 text-xs font-normal relative'>
|
||||||
|
<div className='max-w-[100px] shrink-0 truncate text-xs font-medium text-text-tertiary uppercase'>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
<div className='grow w-0 shrink-0 truncate text-right text-xs font-normal text-text-secondary'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
{indicator && <Indicator color={indicator} className='absolute -right-0.5 -top-0.5' />}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
import { useRef } from 'react'
|
||||||
|
|
||||||
|
export type ToolIconProps = {
|
||||||
|
src: string
|
||||||
|
alt?: string
|
||||||
|
status?: 'error' | 'warning'
|
||||||
|
tooltip?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToolIcon = ({ src, status, tooltip, alt }: ToolIconProps) => {
|
||||||
|
const indicator = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const notSuccess = (['error', 'warning'] as Array<ToolIconProps['status']>).includes(status)
|
||||||
|
return <Tooltip triggerMethod='hover' popupContent={tooltip} disabled={!notSuccess}>
|
||||||
|
<div className={classNames(
|
||||||
|
'size-5 border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge relative',
|
||||||
|
)}
|
||||||
|
ref={containerRef}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
className={classNames(
|
||||||
|
'w-full h-full max-w-5 max-h-5 object-cover rounded-[6px]',
|
||||||
|
notSuccess && 'opacity-50',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{indicator && <Indicator color={indicator} className="absolute right-[-1px] top-[-1px]" />}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '../../constants'
|
||||||
|
import type { NodeDefault } from '../../types'
|
||||||
|
import type { AgentNodeType } from './types'
|
||||||
|
|
||||||
|
const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||||
|
defaultValue: {
|
||||||
|
max_iterations: 3,
|
||||||
|
},
|
||||||
|
getAvailablePrevNodes(isChatMode) {
|
||||||
|
return isChatMode
|
||||||
|
? ALL_CHAT_AVAILABLE_BLOCKS
|
||||||
|
: ALL_COMPLETION_AVAILABLE_BLOCKS
|
||||||
|
},
|
||||||
|
getAvailableNextNodes(isChatMode) {
|
||||||
|
return isChatMode
|
||||||
|
? ALL_CHAT_AVAILABLE_BLOCKS
|
||||||
|
: ALL_COMPLETION_AVAILABLE_BLOCKS
|
||||||
|
},
|
||||||
|
checkValid(payload) {
|
||||||
|
let isValid = true
|
||||||
|
let errorMessages = ''
|
||||||
|
if (payload.type) {
|
||||||
|
isValid = true
|
||||||
|
errorMessages = ''
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isValid,
|
||||||
|
errorMessage: errorMessages,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default nodeDefault
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import type { NodeProps } from '../../types'
|
||||||
|
import type { AgentNodeType } from './types'
|
||||||
|
import { SettingItem } from '../_base/components/setting-item'
|
||||||
|
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||||
|
import { Group, GroupLabel } from '../_base/components/group'
|
||||||
|
import { ToolIcon } from './components/tool-icon'
|
||||||
|
|
||||||
|
const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||||
|
const strategySelected = true
|
||||||
|
return <div className='mb-1 px-3 py-1 space-y-1'>
|
||||||
|
{strategySelected
|
||||||
|
// TODO: add tooltip for this
|
||||||
|
? <SettingItem label='Strategy' indicator='red'>
|
||||||
|
ReAct
|
||||||
|
</SettingItem>
|
||||||
|
: <SettingItem label='Agentic strategy Not Set' />}
|
||||||
|
<Group label={
|
||||||
|
<GroupLabel className='mt-1'>
|
||||||
|
Model
|
||||||
|
</GroupLabel>}>
|
||||||
|
<ModelSelector
|
||||||
|
modelList={[]}
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<ModelSelector
|
||||||
|
modelList={[]}
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<ModelSelector
|
||||||
|
modelList={[]}
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
<Group label={<GroupLabel className='mt-1'>
|
||||||
|
Toolbox
|
||||||
|
</GroupLabel>}>
|
||||||
|
<div className='grid grid-cols-10 gap-0.5'>
|
||||||
|
<ToolIcon src='/logo/logo.png' />
|
||||||
|
<ToolIcon src='/logo/logo.png' status='error' tooltip='Gmail Sender is not installed' />
|
||||||
|
<ToolIcon src='/logo/logo.png' status='warning' tooltip='DuckDuckGo AI Search Not Authorized' />
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AgentNode
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import type { NodePanelProps } from '../../types'
|
||||||
|
import type { AgentNodeType } from './types'
|
||||||
|
import Field from '../_base/components/field'
|
||||||
|
import { InputNumber } from '@/app/components/base/input-number'
|
||||||
|
import Slider from '@/app/components/base/slider'
|
||||||
|
import useNodeCrud from '../_base/hooks/use-node-crud'
|
||||||
|
import { AgentStrategy } from '../_base/components/agent-strategy'
|
||||||
|
|
||||||
|
const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
||||||
|
const { inputs, setInputs } = useNodeCrud(props.id, props.data)
|
||||||
|
const [iter, setIter] = [inputs.max_iterations, (value: number) => {
|
||||||
|
setInputs({
|
||||||
|
...inputs,
|
||||||
|
max_iterations: value,
|
||||||
|
})
|
||||||
|
}]
|
||||||
|
return <>
|
||||||
|
<Field title={'Strategy'} className='px-4' >
|
||||||
|
<AgentStrategy
|
||||||
|
strategy={inputs.agent_strategy_name ? {
|
||||||
|
agent_strategy_provider_name: inputs.agent_strategy_provider_name!,
|
||||||
|
agent_strategy_name: inputs.agent_strategy_name!,
|
||||||
|
agent_parameters: inputs.agent_parameters,
|
||||||
|
} : undefined}
|
||||||
|
onStrategyChange={(strategy) => {
|
||||||
|
setInputs({
|
||||||
|
...inputs,
|
||||||
|
agent_strategy_provider_name: strategy?.agent_strategy_provider_name,
|
||||||
|
agent_strategy_name: strategy?.agent_strategy_name,
|
||||||
|
agent_parameters: strategy?.agent_parameters,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
formSchema={[]}
|
||||||
|
formValue={{}}
|
||||||
|
onFormValueChange={console.error}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field title={'tools'} className='px-4'>
|
||||||
|
|
||||||
|
</Field>
|
||||||
|
<Field title={'max iterations'} tooltip={'max iter'} inline className='px-4'>
|
||||||
|
<div className='flex w-[200px] items-center gap-3'>
|
||||||
|
<Slider value={iter} onChange={setIter} className='w-full' min={1} max={10} />
|
||||||
|
<InputNumber
|
||||||
|
value={iter}
|
||||||
|
// TODO: maybe empty, handle this
|
||||||
|
onChange={setIter as any}
|
||||||
|
defaultValue={3}
|
||||||
|
size='sm'
|
||||||
|
min={1}
|
||||||
|
max={10}
|
||||||
|
className='w-12'
|
||||||
|
placeholder=''
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Field>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AgentPanel
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import type { CommonNodeType } from '@/app/components/workflow/types'
|
||||||
|
import type { ToolVarInputs } from '../tool/types'
|
||||||
|
|
||||||
|
export type AgentNodeType = CommonNodeType & {
|
||||||
|
max_iterations: number
|
||||||
|
agent_strategy_provider_name?: string
|
||||||
|
agent_strategy_name?: string
|
||||||
|
agent_strategy_label?: string
|
||||||
|
agent_parameters?: ToolVarInputs,
|
||||||
|
agent_configurations?: Record<string, ToolVarInputs>
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import useNodeCrud from '../_base/hooks/use-node-crud'
|
||||||
|
import useVarList from '../_base/hooks/use-var-list'
|
||||||
|
import type { AgentNodeType } from './types'
|
||||||
|
import {
|
||||||
|
useNodesReadOnly,
|
||||||
|
} from '@/app/components/workflow/hooks'
|
||||||
|
|
||||||
|
const useConfig = (id: string, payload: AgentNodeType) => {
|
||||||
|
const { nodesReadOnly: readOnly } = useNodesReadOnly()
|
||||||
|
const { inputs, setInputs } = useNodeCrud<AgentNodeType>(id, payload)
|
||||||
|
// variables
|
||||||
|
const { handleVarListChange, handleAddVariable } = useVarList<AgentNodeType>({
|
||||||
|
inputs,
|
||||||
|
setInputs,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
readOnly,
|
||||||
|
inputs,
|
||||||
|
handleVarListChange,
|
||||||
|
handleAddVariable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useConfig
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import type { AgentNodeType } from './types'
|
||||||
|
|
||||||
|
export const checkNodeValid = (payload: AgentNodeType) => {
|
||||||
|
return true
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue