mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 04:26:30 +08:00
Feat: tool setting support variable (#13465)
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
This commit is contained in:
parent
824f8d8994
commit
2ea3b64a45
@ -5,7 +5,7 @@ import classNames from '@/utils/classnames'
|
|||||||
|
|
||||||
type SwitchProps = {
|
type SwitchProps = {
|
||||||
onChange?: (value: boolean) => void
|
onChange?: (value: boolean) => void
|
||||||
size?: 'sm' | 'md' | 'lg' | 'l'
|
size?: 'xs' | 'sm' | 'md' | 'lg' | 'l'
|
||||||
defaultValue?: boolean
|
defaultValue?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
@ -23,6 +23,7 @@ const Switch = React.forwardRef(
|
|||||||
l: 'h-5 w-9',
|
l: 'h-5 w-9',
|
||||||
md: 'h-4 w-7',
|
md: 'h-4 w-7',
|
||||||
sm: 'h-3 w-5',
|
sm: 'h-3 w-5',
|
||||||
|
xs: 'h-2.5 w-3.5',
|
||||||
}
|
}
|
||||||
|
|
||||||
const circleStyle = {
|
const circleStyle = {
|
||||||
@ -30,6 +31,7 @@ const Switch = React.forwardRef(
|
|||||||
l: 'h-4 w-4',
|
l: 'h-4 w-4',
|
||||||
md: 'h-3 w-3',
|
md: 'h-3 w-3',
|
||||||
sm: 'h-2 w-2',
|
sm: 'h-2 w-2',
|
||||||
|
xs: 'h-1.5 w-1',
|
||||||
}
|
}
|
||||||
|
|
||||||
const translateLeft = {
|
const translateLeft = {
|
||||||
@ -37,6 +39,7 @@ const Switch = React.forwardRef(
|
|||||||
l: 'translate-x-4',
|
l: 'translate-x-4',
|
||||||
md: 'translate-x-3',
|
md: 'translate-x-3',
|
||||||
sm: 'translate-x-2',
|
sm: 'translate-x-2',
|
||||||
|
xs: 'translate-x-1.5',
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<OriginalSwitch
|
<OriginalSwitch
|
||||||
@ -53,6 +56,7 @@ const Switch = React.forwardRef(
|
|||||||
enabled ? 'bg-components-toggle-bg' : 'bg-components-toggle-bg-unchecked',
|
enabled ? 'bg-components-toggle-bg' : 'bg-components-toggle-bg-unchecked',
|
||||||
'relative inline-flex flex-shrink-0 cursor-pointer rounded-[5px] border-2 border-transparent transition-colors duration-200 ease-in-out',
|
'relative inline-flex flex-shrink-0 cursor-pointer rounded-[5px] border-2 border-transparent transition-colors duration-200 ease-in-out',
|
||||||
disabled ? '!opacity-50 !cursor-not-allowed' : '',
|
disabled ? '!opacity-50 !cursor-not-allowed' : '',
|
||||||
|
size === 'xs' && 'rounded-sm',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -61,6 +65,7 @@ const Switch = React.forwardRef(
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
circleStyle[size],
|
circleStyle[size],
|
||||||
enabled ? translateLeft[size] : 'translate-x-0',
|
enabled ? translateLeft[size] : 'translate-x-0',
|
||||||
|
size === 'xs' && 'rounded-[1px]',
|
||||||
'pointer-events-none inline-block transform rounded-[3px] bg-components-toggle-knob shadow ring-0 transition duration-200 ease-in-out',
|
'pointer-events-none inline-block transform rounded-[3px] bg-components-toggle-knob shadow ring-0 transition duration-200 ease-in-out',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -3,47 +3,51 @@ import type { FC } from 'react'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
interface Option {
|
type Option = {
|
||||||
value: string
|
value: string
|
||||||
text: string | JSX.Element
|
text: string | JSX.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ItemProps {
|
type ItemProps = {
|
||||||
className?: string
|
className?: string
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
onClick: (v: string) => void
|
onClick: (v: string) => void
|
||||||
option: Option
|
option: Option
|
||||||
|
smallItem?: boolean
|
||||||
}
|
}
|
||||||
const Item: FC<ItemProps> = ({
|
const Item: FC<ItemProps> = ({
|
||||||
className,
|
className,
|
||||||
isActive,
|
isActive,
|
||||||
onClick,
|
onClick,
|
||||||
option,
|
option,
|
||||||
|
smallItem,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={option.value}
|
key={option.value}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative pb-2.5 system-xl-semibold',
|
'relative pb-2.5 ',
|
||||||
!isActive && 'cursor-pointer',
|
!isActive && 'cursor-pointer',
|
||||||
|
smallItem ? 'system-sm-semibold-uppercase' : 'system-xl-semibold',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={() => !isActive && onClick(option.value)}
|
onClick={() => !isActive && onClick(option.value)}
|
||||||
>
|
>
|
||||||
<div className={cn(isActive ? 'text-text-primary' : 'text-text-tertiary')}>{option.text}</div>
|
<div className={cn(isActive ? 'text-text-primary' : 'text-text-tertiary')}>{option.text}</div>
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<div className='absolute bottom-0 left-0 right-0 h-0.5 bg-util-colors-blue-blue-500'></div>
|
<div className='absolute bottom-0 left-0 right-0 h-0.5 bg-util-colors-blue-brand-blue-brand-600'></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
className?: string
|
className?: string
|
||||||
value: string
|
value: string
|
||||||
onChange: (v: string) => void
|
onChange: (v: string) => void
|
||||||
options: Option[]
|
options: Option[]
|
||||||
noBorderBottom?: boolean
|
noBorderBottom?: boolean
|
||||||
|
smallItem?: boolean
|
||||||
itemClassName?: string
|
itemClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +58,7 @@ const TabSlider: FC<Props> = ({
|
|||||||
options,
|
options,
|
||||||
noBorderBottom,
|
noBorderBottom,
|
||||||
itemClassName,
|
itemClassName,
|
||||||
|
smallItem,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, !noBorderBottom && 'border-b border-divider-subtle', 'flex space-x-6')}>
|
<div className={cn(className, !noBorderBottom && 'border-b border-divider-subtle', 'flex space-x-6')}>
|
||||||
@ -64,6 +69,7 @@ const TabSlider: FC<Props> = ({
|
|||||||
onClick={onChange}
|
onClick={onChange}
|
||||||
key={option.value}
|
key={option.value}
|
||||||
className={itemClassName}
|
className={itemClassName}
|
||||||
|
smallItem={smallItem}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,6 +22,10 @@ import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-sele
|
|||||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-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 AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||||
import RadioE from '@/app/components/base/radio/ui'
|
import RadioE from '@/app/components/base/radio/ui'
|
||||||
|
import type {
|
||||||
|
NodeOutPutVar,
|
||||||
|
} from '@/app/components/workflow/types'
|
||||||
|
import type { Node } from 'reactflow'
|
||||||
|
|
||||||
type FormProps<
|
type FormProps<
|
||||||
CustomFormSchema extends Omit<CredentialFormSchema, 'type'> & { type: string } = never,
|
CustomFormSchema extends Omit<CredentialFormSchema, 'type'> & { type: string } = never,
|
||||||
@ -47,6 +51,9 @@ type FormProps<
|
|||||||
) => ReactNode
|
) => ReactNode
|
||||||
// If return falsy value, this field will fallback to default render
|
// If return falsy value, this field will fallback to default render
|
||||||
override?: [Array<FormTypeEnum>, (formSchema: CredentialFormSchema, props: Omit<FormProps<CustomFormSchema>, 'override' | 'customRenderField'>) => ReactNode]
|
override?: [Array<FormTypeEnum>, (formSchema: CredentialFormSchema, props: Omit<FormProps<CustomFormSchema>, 'override' | 'customRenderField'>) => ReactNode]
|
||||||
|
nodeId?: string
|
||||||
|
nodeOutputVars?: NodeOutPutVar[],
|
||||||
|
availableNodes?: Node[],
|
||||||
}
|
}
|
||||||
|
|
||||||
function Form<
|
function Form<
|
||||||
@ -69,6 +76,9 @@ function Form<
|
|||||||
fieldMoreInfo,
|
fieldMoreInfo,
|
||||||
customRenderField,
|
customRenderField,
|
||||||
override,
|
override,
|
||||||
|
nodeId,
|
||||||
|
nodeOutputVars,
|
||||||
|
availableNodes,
|
||||||
}: FormProps<CustomFormSchema>) {
|
}: FormProps<CustomFormSchema>) {
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const [changeKey, setChangeKey] = useState('')
|
const [changeKey, setChangeKey] = useState('')
|
||||||
@ -326,6 +336,9 @@ function Form<
|
|||||||
</div>
|
</div>
|
||||||
<ToolSelector
|
<ToolSelector
|
||||||
scope={scope}
|
scope={scope}
|
||||||
|
nodeId={nodeId}
|
||||||
|
nodeOutputVars={nodeOutputVars || []}
|
||||||
|
availableNodes={availableNodes || []}
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
value={value[variable]}
|
value={value[variable]}
|
||||||
// selectedTools={value[variable] ? [value[variable]] : []}
|
// selectedTools={value[variable] ? [value[variable]] : []}
|
||||||
@ -351,6 +364,9 @@ function Form<
|
|||||||
<div key={variable} className={cn(itemClassName, 'py-3')}>
|
<div key={variable} className={cn(itemClassName, 'py-3')}>
|
||||||
<MultipleToolSelector
|
<MultipleToolSelector
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
|
nodeId={nodeId}
|
||||||
|
nodeOutputVars={nodeOutputVars || []}
|
||||||
|
availableNodes={availableNodes || []}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
label={label[language] || label.en_US}
|
label={label[language] || label.en_US}
|
||||||
required={required}
|
required={required}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import ActionButton from '@/app/components/base/action-button'
|
|||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
|
import type { ToolValue } from '@/app/components/workflow/block-selector/types'
|
||||||
|
import type { Node } from 'reactflow'
|
||||||
|
import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -21,6 +23,9 @@ type Props = {
|
|||||||
supportCollapse?: boolean
|
supportCollapse?: boolean
|
||||||
scope?: string
|
scope?: string
|
||||||
onChange: (value: ToolValue[]) => void
|
onChange: (value: ToolValue[]) => void
|
||||||
|
nodeOutputVars: NodeOutPutVar[],
|
||||||
|
availableNodes: Node[],
|
||||||
|
nodeId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const MultipleToolSelector = ({
|
const MultipleToolSelector = ({
|
||||||
@ -32,6 +37,9 @@ const MultipleToolSelector = ({
|
|||||||
supportCollapse,
|
supportCollapse,
|
||||||
scope,
|
scope,
|
||||||
onChange,
|
onChange,
|
||||||
|
nodeOutputVars,
|
||||||
|
availableNodes,
|
||||||
|
nodeId,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const enabledCount = value.filter(item => item.enabled).length
|
const enabledCount = value.filter(item => item.enabled).length
|
||||||
@ -121,6 +129,9 @@ const MultipleToolSelector = ({
|
|||||||
{!collapse && (
|
{!collapse && (
|
||||||
<>
|
<>
|
||||||
<ToolSelector
|
<ToolSelector
|
||||||
|
nodeId={nodeId}
|
||||||
|
nodeOutputVars={nodeOutputVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
value={undefined}
|
value={undefined}
|
||||||
selectedTools={value}
|
selectedTools={value}
|
||||||
@ -140,6 +151,9 @@ const MultipleToolSelector = ({
|
|||||||
{value.length > 0 && value.map((item, index) => (
|
{value.length > 0 && value.map((item, index) => (
|
||||||
<div className='mb-1' key={index}>
|
<div className='mb-1' key={index}>
|
||||||
<ToolSelector
|
<ToolSelector
|
||||||
|
nodeId={nodeId}
|
||||||
|
nodeOutputVars={nodeOutputVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
value={item}
|
value={item}
|
||||||
selectedTools={value}
|
selectedTools={value}
|
||||||
|
|||||||
@ -21,8 +21,10 @@ import ToolCredentialForm from '@/app/components/plugins/plugin-detail-panel/too
|
|||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Textarea from '@/app/components/base/textarea'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
|
import TabSlider from '@/app/components/base/tab-slider-plain'
|
||||||
|
import ReasoningConfigForm from '@/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form'
|
||||||
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
|
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
|
||||||
import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
import { generateFormValue, getPlainValue, getStructureValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||||
|
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import {
|
import {
|
||||||
@ -41,6 +43,8 @@ import type {
|
|||||||
Placement,
|
Placement,
|
||||||
} from '@floating-ui/react'
|
} from '@floating-ui/react'
|
||||||
import { MARKETPLACE_API_PREFIX } from '@/config'
|
import { MARKETPLACE_API_PREFIX } from '@/config'
|
||||||
|
import type { Node } from 'reactflow'
|
||||||
|
import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -54,6 +58,7 @@ type Props = {
|
|||||||
provider_name: string
|
provider_name: string
|
||||||
tool_name: string
|
tool_name: string
|
||||||
tool_label: string
|
tool_label: string
|
||||||
|
settings?: Record<string, any>
|
||||||
parameters?: Record<string, any>
|
parameters?: Record<string, any>
|
||||||
extra?: Record<string, any>
|
extra?: Record<string, any>
|
||||||
}) => void
|
}) => void
|
||||||
@ -65,6 +70,9 @@ type Props = {
|
|||||||
onControlledStateChange?: (state: boolean) => void
|
onControlledStateChange?: (state: boolean) => void
|
||||||
panelShowState?: boolean
|
panelShowState?: boolean
|
||||||
onPanelShowStateChange?: (state: boolean) => void
|
onPanelShowStateChange?: (state: boolean) => void
|
||||||
|
nodeOutputVars: NodeOutPutVar[],
|
||||||
|
availableNodes: Node[],
|
||||||
|
nodeId?: string,
|
||||||
}
|
}
|
||||||
const ToolSelector: FC<Props> = ({
|
const ToolSelector: FC<Props> = ({
|
||||||
value,
|
value,
|
||||||
@ -81,6 +89,9 @@ const ToolSelector: FC<Props> = ({
|
|||||||
onControlledStateChange,
|
onControlledStateChange,
|
||||||
panelShowState,
|
panelShowState,
|
||||||
onPanelShowStateChange,
|
onPanelShowStateChange,
|
||||||
|
nodeOutputVars,
|
||||||
|
availableNodes,
|
||||||
|
nodeId = '',
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [isShow, onShowChange] = useState(false)
|
const [isShow, onShowChange] = useState(false)
|
||||||
@ -107,17 +118,20 @@ const ToolSelector: FC<Props> = ({
|
|||||||
|
|
||||||
const [isShowChooseTool, setIsShowChooseTool] = useState(false)
|
const [isShowChooseTool, setIsShowChooseTool] = useState(false)
|
||||||
const handleSelectTool = (tool: ToolDefaultValue) => {
|
const handleSelectTool = (tool: ToolDefaultValue) => {
|
||||||
const paramValues = addDefaultValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form !== 'llm') as any))
|
const settingValues = generateFormValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form !== 'llm') as any))
|
||||||
|
const paramValues = generateFormValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form === 'llm') as any), true)
|
||||||
const toolValue = {
|
const toolValue = {
|
||||||
provider_name: tool.provider_id,
|
provider_name: tool.provider_id,
|
||||||
type: tool.provider_type,
|
type: tool.provider_type,
|
||||||
tool_name: tool.tool_name,
|
tool_name: tool.tool_name,
|
||||||
tool_label: tool.tool_label,
|
tool_label: tool.tool_label,
|
||||||
|
settings: settingValues,
|
||||||
parameters: paramValues,
|
parameters: paramValues,
|
||||||
enabled: tool.is_team_authorization,
|
enabled: tool.is_team_authorization,
|
||||||
extra: {
|
extra: {
|
||||||
description: '',
|
description: '',
|
||||||
},
|
},
|
||||||
|
schemas: tool.paramSchemas,
|
||||||
}
|
}
|
||||||
onSelect(toolValue)
|
onSelect(toolValue)
|
||||||
// setIsShowChooseTool(false)
|
// setIsShowChooseTool(false)
|
||||||
@ -133,14 +147,33 @@ const ToolSelector: FC<Props> = ({
|
|||||||
} as any)
|
} as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentToolParams = useMemo(() => {
|
// tool settings & params
|
||||||
|
const currentToolSettings = useMemo(() => {
|
||||||
if (!currentProvider) return []
|
if (!currentProvider) return []
|
||||||
return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters.filter(param => param.form !== 'llm') || []
|
return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters.filter(param => param.form !== 'llm') || []
|
||||||
}, [currentProvider, value])
|
}, [currentProvider, value])
|
||||||
|
const currentToolParams = useMemo(() => {
|
||||||
|
if (!currentProvider) return []
|
||||||
|
return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters.filter(param => param.form === 'llm') || []
|
||||||
|
}, [currentProvider, value])
|
||||||
|
const [currType, setCurrType] = useState('settings')
|
||||||
|
const showTabSlider = currentToolSettings.length > 0 && currentToolParams.length > 0
|
||||||
|
const userSettingsOnly = currentToolSettings.length > 0 && !currentToolParams.length
|
||||||
|
const reasoningConfigOnly = currentToolParams.length > 0 && !currentToolSettings.length
|
||||||
|
|
||||||
const formSchemas = useMemo(() => toolParametersToFormSchemas(currentToolParams), [currentToolParams])
|
const settingsFormSchemas = useMemo(() => toolParametersToFormSchemas(currentToolSettings), [currentToolSettings])
|
||||||
|
const paramsFormSchemas = useMemo(() => toolParametersToFormSchemas(currentToolParams), [currentToolParams])
|
||||||
|
|
||||||
const handleFormChange = (v: Record<string, any>) => {
|
const handleSettingsFormChange = (v: Record<string, any>) => {
|
||||||
|
const newValue = getStructureValue(v)
|
||||||
|
|
||||||
|
const toolValue = {
|
||||||
|
...value,
|
||||||
|
settings: newValue,
|
||||||
|
}
|
||||||
|
onSelect(toolValue as any)
|
||||||
|
}
|
||||||
|
const handleParamsFormChange = (v: Record<string, any>) => {
|
||||||
const toolValue = {
|
const toolValue = {
|
||||||
...value,
|
...value,
|
||||||
parameters: v,
|
parameters: v,
|
||||||
@ -281,12 +314,9 @@ const ToolSelector: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
{/* authorization */}
|
{/* authorization */}
|
||||||
{currentProvider && currentProvider.type === CollectionType.builtIn && currentProvider.allow_delete && (
|
{currentProvider && currentProvider.type === CollectionType.builtIn && currentProvider.allow_delete && (
|
||||||
<div className='px-4 pt-3 flex flex-col'>
|
<>
|
||||||
<div className='flex items-center gap-2'>
|
<Divider className='my-1 w-full' />
|
||||||
<div className='shrink-0 text-text-tertiary system-xs-medium-uppercase'>{t('plugin.detailPanel.toolSelector.auth')}</div>
|
<div className='px-4 py-2'>
|
||||||
<Divider bgStyle='gradient' className='grow' />
|
|
||||||
</div>
|
|
||||||
<div className='py-2'>
|
|
||||||
{!currentProvider.is_team_authorization && (
|
{!currentProvider.is_team_authorization && (
|
||||||
<Button
|
<Button
|
||||||
variant='primary'
|
variant='primary'
|
||||||
@ -309,37 +339,87 @@ const ToolSelector: FC<Props> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* tool settings */}
|
{/* tool settings */}
|
||||||
{currentToolParams.length > 0 && currentProvider?.is_team_authorization && (
|
{(currentToolSettings.length > 0 || currentToolParams.length > 0) && currentProvider?.is_team_authorization && (
|
||||||
<div className='px-4 pt-3'>
|
<>
|
||||||
<div className='flex items-center gap-2'>
|
<Divider className='my-1 w-full' />
|
||||||
<div className='shrink-0 text-text-tertiary system-xs-medium-uppercase'>{t('plugin.detailPanel.toolSelector.settings')}</div>
|
{/* tabs */}
|
||||||
<Divider bgStyle='gradient' className='grow' />
|
{nodeId && showTabSlider && (
|
||||||
</div>
|
<TabSlider
|
||||||
<div className='py-2'>
|
className='shrink-0 mt-1 px-4'
|
||||||
<Form
|
itemClassName='py-3'
|
||||||
value={value?.parameters || {}}
|
noBorderBottom
|
||||||
onChange={handleFormChange}
|
smallItem
|
||||||
formSchemas={formSchemas as any}
|
value={currType}
|
||||||
isEditMode={true}
|
onChange={(value) => {
|
||||||
showOnVariableMap={{}}
|
setCurrType(value)
|
||||||
validating={false}
|
}}
|
||||||
inputClassName='bg-components-input-bg-normal hover:bg-components-input-bg-hover'
|
options={[
|
||||||
fieldMoreInfo={item => item.url
|
{ value: 'settings', text: t('plugin.detailPanel.toolSelector.settings')! },
|
||||||
? (<a
|
{ value: 'params', text: t('plugin.detailPanel.toolSelector.params')! },
|
||||||
href={item.url}
|
]}
|
||||||
target='_blank' rel='noopener noreferrer'
|
|
||||||
className='inline-flex items-center text-xs text-text-accent'
|
|
||||||
>
|
|
||||||
{t('tools.howToGet')}
|
|
||||||
<RiArrowRightUpLine className='ml-1 w-3 h-3' />
|
|
||||||
</a>)
|
|
||||||
: null}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
</div>
|
{nodeId && showTabSlider && currType === 'params' && (
|
||||||
|
<div className='px-4 py-2'>
|
||||||
|
<div className='text-text-tertiary system-xs-regular'>{t('plugin.detailPanel.toolSelector.paramsTip1')}</div>
|
||||||
|
<div className='text-text-tertiary system-xs-regular'>{t('plugin.detailPanel.toolSelector.paramsTip2')}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* user settings only */}
|
||||||
|
{userSettingsOnly && (
|
||||||
|
<div className='p-4 pb-1'>
|
||||||
|
<div className='text-text-primary system-sm-semibold-uppercase'>{t('plugin.detailPanel.toolSelector.settings')}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* reasoning config only */}
|
||||||
|
{nodeId && reasoningConfigOnly && (
|
||||||
|
<div className='mb-1 p-4 pb-1'>
|
||||||
|
<div className='text-text-primary system-sm-semibold-uppercase'>{t('plugin.detailPanel.toolSelector.params')}</div>
|
||||||
|
<div className='pb-1'>
|
||||||
|
<div className='text-text-tertiary system-xs-regular'>{t('plugin.detailPanel.toolSelector.paramsTip1')}</div>
|
||||||
|
<div className='text-text-tertiary system-xs-regular'>{t('plugin.detailPanel.toolSelector.paramsTip2')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* user settings form */}
|
||||||
|
{(currType === 'settings' || userSettingsOnly) && (
|
||||||
|
<div className='px-4 py-2'>
|
||||||
|
<Form
|
||||||
|
value={getPlainValue(value?.settings || {})}
|
||||||
|
onChange={handleSettingsFormChange}
|
||||||
|
formSchemas={settingsFormSchemas as any}
|
||||||
|
isEditMode={true}
|
||||||
|
showOnVariableMap={{}}
|
||||||
|
validating={false}
|
||||||
|
inputClassName='bg-components-input-bg-normal hover:bg-components-input-bg-hover'
|
||||||
|
fieldMoreInfo={item => item.url
|
||||||
|
? (<a
|
||||||
|
href={item.url}
|
||||||
|
target='_blank' rel='noopener noreferrer'
|
||||||
|
className='inline-flex items-center text-xs text-text-accent'
|
||||||
|
>
|
||||||
|
{t('tools.howToGet')}
|
||||||
|
<RiArrowRightUpLine className='ml-1 w-3 h-3' />
|
||||||
|
</a>)
|
||||||
|
: null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* reasoning config form */}
|
||||||
|
{nodeId && (currType === 'params' || reasoningConfigOnly) && (
|
||||||
|
<ReasoningConfigForm
|
||||||
|
value={value?.parameters || {}}
|
||||||
|
onChange={handleParamsFormChange}
|
||||||
|
schemas={paramsFormSchemas as any}
|
||||||
|
nodeOutputVars={nodeOutputVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
nodeId={nodeId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -0,0 +1,275 @@
|
|||||||
|
import { useCallback, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import produce from 'immer'
|
||||||
|
import {
|
||||||
|
RiArrowRightUpLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import Switch from '@/app/components/base/switch'
|
||||||
|
import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var'
|
||||||
|
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
|
||||||
|
import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||||
|
import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector'
|
||||||
|
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
|
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import type { Node } from 'reactflow'
|
||||||
|
import type {
|
||||||
|
NodeOutPutVar,
|
||||||
|
ValueSelector,
|
||||||
|
Var,
|
||||||
|
} from '@/app/components/workflow/types'
|
||||||
|
import type { ToolVarInputs } from '@/app/components/workflow/nodes/tool/types'
|
||||||
|
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||||
|
import { VarType } from '@/app/components/workflow/types'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: Record<string, any>
|
||||||
|
onChange: (val: Record<string, any>) => void
|
||||||
|
schemas: any[]
|
||||||
|
nodeOutputVars: NodeOutPutVar[],
|
||||||
|
availableNodes: Node[],
|
||||||
|
nodeId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReasoningConfigForm: React.FC<Props> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
schemas,
|
||||||
|
nodeOutputVars,
|
||||||
|
availableNodes,
|
||||||
|
nodeId,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const language = useLanguage()
|
||||||
|
const handleAutomatic = (key: string, val: any) => {
|
||||||
|
onChange({
|
||||||
|
...value,
|
||||||
|
[key]: {
|
||||||
|
value: val ? null : value[key]?.value,
|
||||||
|
auto: val ? 1 : 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [inputsIsFocus, setInputsIsFocus] = useState<Record<string, boolean>>({})
|
||||||
|
const handleInputFocus = useCallback((variable: string) => {
|
||||||
|
return (value: boolean) => {
|
||||||
|
setInputsIsFocus((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
[variable]: value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
const handleNotMixedTypeChange = useCallback((variable: string) => {
|
||||||
|
return (varValue: ValueSelector | string, varKindType: VarKindType) => {
|
||||||
|
const newValue = produce(value, (draft: ToolVarInputs) => {
|
||||||
|
const target = draft[variable].value
|
||||||
|
if (target) {
|
||||||
|
target.type = varKindType
|
||||||
|
target.value = varValue
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
draft[variable].value = {
|
||||||
|
type: varKindType,
|
||||||
|
value: varValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
}, [value, onChange])
|
||||||
|
const handleMixedTypeChange = useCallback((variable: string) => {
|
||||||
|
return (itemValue: string) => {
|
||||||
|
const newValue = produce(value, (draft: ToolVarInputs) => {
|
||||||
|
const target = draft[variable].value
|
||||||
|
if (target) {
|
||||||
|
target.value = itemValue
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
draft[variable].value = {
|
||||||
|
type: VarKindType.mixed,
|
||||||
|
value: itemValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
}, [value, onChange])
|
||||||
|
const handleFileChange = useCallback((variable: string) => {
|
||||||
|
return (varValue: ValueSelector | string) => {
|
||||||
|
const newValue = produce(value, (draft: ToolVarInputs) => {
|
||||||
|
draft[variable].value = {
|
||||||
|
type: VarKindType.variable,
|
||||||
|
value: varValue,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
}, [value, onChange])
|
||||||
|
const handleAppChange = useCallback((variable: string) => {
|
||||||
|
return (app: {
|
||||||
|
app_id: string
|
||||||
|
inputs: Record<string, any>
|
||||||
|
files?: any[]
|
||||||
|
}) => {
|
||||||
|
const newValue = produce(value, (draft: ToolVarInputs) => {
|
||||||
|
draft[variable].value = app as any
|
||||||
|
})
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
}, [onChange, value])
|
||||||
|
const handleModelChange = useCallback((variable: string) => {
|
||||||
|
return (model: any) => {
|
||||||
|
const newValue = produce(value, (draft: ToolVarInputs) => {
|
||||||
|
draft[variable].value = {
|
||||||
|
...draft[variable].value,
|
||||||
|
...model,
|
||||||
|
} as any
|
||||||
|
})
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
}, [onChange, value])
|
||||||
|
|
||||||
|
const renderField = (schema: any) => {
|
||||||
|
const {
|
||||||
|
variable,
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
tooltip,
|
||||||
|
type,
|
||||||
|
scope,
|
||||||
|
url,
|
||||||
|
} = schema
|
||||||
|
const auto = value[variable]?.auto
|
||||||
|
const tooltipContent = (tooltip && (
|
||||||
|
<Tooltip
|
||||||
|
popupContent={<div className='w-[200px]'>
|
||||||
|
{tooltip[language] || tooltip.en_US}
|
||||||
|
</div>}
|
||||||
|
triggerClassName='ml-1 w-4 h-4'
|
||||||
|
asChild={false} />
|
||||||
|
))
|
||||||
|
const varInput = value[variable].value
|
||||||
|
const isNumber = type === FormTypeEnum.textNumber
|
||||||
|
const isSelect = type === FormTypeEnum.select
|
||||||
|
const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files
|
||||||
|
const isAppSelector = type === FormTypeEnum.appSelector
|
||||||
|
const isModelSelector = type === FormTypeEnum.modelSelector
|
||||||
|
// const isToolSelector = type === FormTypeEnum.toolSelector
|
||||||
|
const isString = !isNumber && !isSelect && !isFile && !isAppSelector && !isModelSelector
|
||||||
|
return (
|
||||||
|
<div key={variable} className='space-y-1'>
|
||||||
|
<div className='flex items-center justify-between py-2 system-sm-semibold text-text-secondary'>
|
||||||
|
<div className='flex items-center space-x-2'>
|
||||||
|
<span className={cn('text-text-secondary code-sm-semibold')}>{label[language] || label.en_US}</span>
|
||||||
|
{required && (
|
||||||
|
<span className='ml-1 text-red-500'>*</span>
|
||||||
|
)}
|
||||||
|
{tooltipContent}
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center gap-1 px-2 py-1 rounded-[6px] border border-divider-subtle bg-background-default-lighter cursor-pointer hover:bg-state-base-hover' onClick={() => handleAutomatic(variable, !auto)}>
|
||||||
|
<span className='text-text-secondary system-xs-medium'>{t('plugin.detailPanel.toolSelector.auto')}</span>
|
||||||
|
<Switch
|
||||||
|
size='xs'
|
||||||
|
defaultValue={!!auto}
|
||||||
|
onChange={val => handleAutomatic(variable, val)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{auto === 0 && (
|
||||||
|
<>
|
||||||
|
{isString && (
|
||||||
|
<Input
|
||||||
|
className={cn(inputsIsFocus[variable] ? 'shadow-xs bg-gray-50 border-gray-300' : 'bg-gray-100 border-gray-100', 'rounded-lg px-3 py-[6px] border')}
|
||||||
|
value={varInput?.value as string || ''}
|
||||||
|
onChange={handleMixedTypeChange(variable)}
|
||||||
|
nodesOutputVars={nodeOutputVars}
|
||||||
|
availableNodes={availableNodes}
|
||||||
|
onFocusChange={handleInputFocus(variable)}
|
||||||
|
placeholder={t('workflow.nodes.http.insertVarPlaceholder')!}
|
||||||
|
placeholderClassName='!leading-[21px]'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* {isString && (
|
||||||
|
<VarReferencePicker
|
||||||
|
zIndex={1001}
|
||||||
|
readonly={false}
|
||||||
|
isShowNodeName
|
||||||
|
nodeId={nodeId}
|
||||||
|
value={varInput?.value || ''}
|
||||||
|
onChange={handleNotMixedTypeChange(variable)}
|
||||||
|
defaultVarKindType={VarKindType.variable}
|
||||||
|
filterVar={(varPayload: Var) => varPayload.type === VarType.number || varPayload.type === VarType.secret || varPayload.type === VarType.string}
|
||||||
|
/>
|
||||||
|
)} */}
|
||||||
|
{(isNumber || isSelect) && (
|
||||||
|
<VarReferencePicker
|
||||||
|
zIndex={1001}
|
||||||
|
readonly={false}
|
||||||
|
isShowNodeName
|
||||||
|
nodeId={nodeId}
|
||||||
|
value={varInput?.type === VarKindType.constant ? (varInput?.value ?? '') : (varInput?.value ?? [])}
|
||||||
|
onChange={handleNotMixedTypeChange(variable)}
|
||||||
|
defaultVarKindType={varInput?.type || (isNumber ? VarKindType.constant : VarKindType.variable)}
|
||||||
|
isSupportConstantValue
|
||||||
|
filterVar={isNumber ? (varPayload: Var) => varPayload.type === schema._type : undefined}
|
||||||
|
availableVars={isSelect ? nodeOutputVars : undefined}
|
||||||
|
schema={schema}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isFile && (
|
||||||
|
<VarReferencePicker
|
||||||
|
zIndex={1001}
|
||||||
|
readonly={false}
|
||||||
|
isShowNodeName
|
||||||
|
nodeId={nodeId}
|
||||||
|
value={varInput?.value || []}
|
||||||
|
onChange={handleFileChange(variable)}
|
||||||
|
defaultVarKindType={VarKindType.variable}
|
||||||
|
filterVar={(varPayload: Var) => varPayload.type === VarType.file || varPayload.type === VarType.arrayFile}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isAppSelector && (
|
||||||
|
<AppSelector
|
||||||
|
disabled={false}
|
||||||
|
scope={scope || 'all'}
|
||||||
|
value={varInput as any}
|
||||||
|
onSelect={handleAppChange(variable)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isModelSelector && (
|
||||||
|
<ModelParameterModal
|
||||||
|
popupClassName='!w-[387px]'
|
||||||
|
isAdvancedMode
|
||||||
|
isInWorkflow
|
||||||
|
value={varInput as any}
|
||||||
|
setModel={handleModelChange(variable)}
|
||||||
|
scope={scope}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{url && (
|
||||||
|
<a
|
||||||
|
href={url}
|
||||||
|
target='_blank' rel='noopener noreferrer'
|
||||||
|
className='inline-flex items-center text-xs text-text-accent'
|
||||||
|
>
|
||||||
|
{t('tools.howToGet')}
|
||||||
|
<RiArrowRightUpLine className='ml-1 w-3 h-3' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className='px-4 py-2 space-y-3'>
|
||||||
|
{schemas.map(schema => renderField(schema))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReasoningConfigForm
|
||||||
@ -63,3 +63,34 @@ export const addDefaultValue = (value: Record<string, any>, formSchemas: { varia
|
|||||||
})
|
})
|
||||||
return newValues
|
return newValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const generateFormValue = (value: Record<string, any>, formSchemas: { variable: string; default?: any }[], isReasoning = false) => {
|
||||||
|
const newValues = {} as any
|
||||||
|
formSchemas.forEach((formSchema) => {
|
||||||
|
const itemValue = value[formSchema.variable]
|
||||||
|
if ((formSchema.default !== undefined) && (value === undefined || itemValue === null || itemValue === '' || itemValue === undefined)) {
|
||||||
|
newValues[formSchema.variable] = {
|
||||||
|
...(isReasoning ? { value: null, auto: 1 } : { value: formSchema.default }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return newValues
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPlainValue = (value: Record<string, any>) => {
|
||||||
|
const plainValue = { ...value } as any
|
||||||
|
Object.keys(plainValue).forEach((key) => {
|
||||||
|
plainValue[key] = value[key].value
|
||||||
|
})
|
||||||
|
return plainValue
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStructureValue = (value: Record<string, any>) => {
|
||||||
|
const newValue = { ...value } as any
|
||||||
|
Object.keys(newValue).forEach((key) => {
|
||||||
|
newValue[key] = {
|
||||||
|
value: value[key],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export type ToolValue = {
|
|||||||
provider_name: string
|
provider_name: string
|
||||||
tool_name: string
|
tool_name: string
|
||||||
tool_label: string
|
tool_label: string
|
||||||
|
settings?: Record<string, any>
|
||||||
parameters?: Record<string, any>
|
parameters?: Record<string, any>
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
extra?: Record<string, any>
|
extra?: Record<string, any>
|
||||||
|
|||||||
@ -184,7 +184,7 @@ export const useChecklistBeforePublish = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, [nodesExtraData, notify, t, store, isChatMode, buildInTools, customTools, workflowTools, language])
|
}, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleCheckBeforePublish,
|
handleCheckBeforePublish,
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export type AgentStrategyProps = {
|
|||||||
onFormValueChange: (value: ToolVarInputs) => void
|
onFormValueChange: (value: ToolVarInputs) => void
|
||||||
nodeOutputVars?: NodeOutPutVar[],
|
nodeOutputVars?: NodeOutPutVar[],
|
||||||
availableNodes?: Node[],
|
availableNodes?: Node[],
|
||||||
|
nodeId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field
|
type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field
|
||||||
@ -46,7 +47,7 @@ type MultipleToolSelectorSchema = CustomSchema<'array[tools]'>
|
|||||||
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema
|
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema
|
||||||
|
|
||||||
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes } = props
|
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId } = props
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
|
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
|
||||||
const renderI18nObject = useRenderI18nObject()
|
const renderI18nObject = useRenderI18nObject()
|
||||||
@ -141,7 +142,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
|||||||
]
|
]
|
||||||
const renderField: ComponentProps<typeof Form<CustomField>>['customRenderField'] = (schema, props) => {
|
const renderField: ComponentProps<typeof Form<CustomField>>['customRenderField'] = (schema, props) => {
|
||||||
switch (schema.type) {
|
switch (schema.type) {
|
||||||
case 'tool-selector': {
|
case FormTypeEnum.toolSelector: {
|
||||||
const value = props.value[schema.variable]
|
const value = props.value[schema.variable]
|
||||||
const onChange = (value: any) => {
|
const onChange = (value: any) => {
|
||||||
props.onChange({ ...props.value, [schema.variable]: value })
|
props.onChange({ ...props.value, [schema.variable]: value })
|
||||||
@ -154,6 +155,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
|||||||
tooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
|
tooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
|
||||||
>
|
>
|
||||||
<ToolSelector
|
<ToolSelector
|
||||||
|
nodeId={props.nodeId || ''}
|
||||||
|
nodeOutputVars={props.nodeOutputVars || []}
|
||||||
|
availableNodes={props.availableNodes || []}
|
||||||
scope={schema.scope}
|
scope={schema.scope}
|
||||||
value={value}
|
value={value}
|
||||||
onSelect={item => onChange(item)}
|
onSelect={item => onChange(item)}
|
||||||
@ -162,13 +166,16 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case 'array[tools]': {
|
case FormTypeEnum.multiToolSelector: {
|
||||||
const value = props.value[schema.variable]
|
const value = props.value[schema.variable]
|
||||||
const onChange = (value: any) => {
|
const onChange = (value: any) => {
|
||||||
props.onChange({ ...props.value, [schema.variable]: value })
|
props.onChange({ ...props.value, [schema.variable]: value })
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<MultipleToolSelector
|
<MultipleToolSelector
|
||||||
|
nodeId={props.nodeId || ''}
|
||||||
|
nodeOutputVars={props.nodeOutputVars || []}
|
||||||
|
availableNodes={props.availableNodes || []}
|
||||||
scope={schema.scope}
|
scope={schema.scope}
|
||||||
value={value || []}
|
value={value || []}
|
||||||
label={renderI18nObject(schema.label)}
|
label={renderI18nObject(schema.label)}
|
||||||
@ -199,6 +206,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
|||||||
fieldLabelClassName='uppercase'
|
fieldLabelClassName='uppercase'
|
||||||
customRenderField={renderField}
|
customRenderField={renderField}
|
||||||
override={override}
|
override={override}
|
||||||
|
nodeId={nodeId}
|
||||||
|
nodeOutputVars={nodeOutputVars || []}
|
||||||
|
availableNodes={availableNodes || []}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
: <ListEmpty
|
: <ListEmpty
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import type { ToolNodeType } from '../../../tool/types'
|
|||||||
import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types'
|
import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types'
|
||||||
import type { IterationNodeType } from '../../../iteration/types'
|
import type { IterationNodeType } from '../../../iteration/types'
|
||||||
import type { ListFilterNodeType } from '../../../list-operator/types'
|
import type { ListFilterNodeType } from '../../../list-operator/types'
|
||||||
import { OUTPUT_FILE_SUB_VARIABLES } from '../../../if-else/default'
|
import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants'
|
||||||
import type { DocExtractorNodeType } from '../../../document-extractor/types'
|
import type { DocExtractorNodeType } from '../../../document-extractor/types'
|
||||||
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
|
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
|
||||||
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
|
import type { StartNodeType } from '@/app/components/workflow/nodes/start/types'
|
||||||
|
|||||||
@ -64,6 +64,7 @@ type Props = {
|
|||||||
placeholder?: string
|
placeholder?: string
|
||||||
minWidth?: number
|
minWidth?: number
|
||||||
popupFor?: 'assigned' | 'toAssigned'
|
popupFor?: 'assigned' | 'toAssigned'
|
||||||
|
zIndex?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const VarReferencePicker: FC<Props> = ({
|
const VarReferencePicker: FC<Props> = ({
|
||||||
@ -90,6 +91,7 @@ const VarReferencePicker: FC<Props> = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
minWidth,
|
minWidth,
|
||||||
popupFor,
|
popupFor,
|
||||||
|
zIndex,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const store = useStoreApi()
|
const store = useStoreApi()
|
||||||
@ -386,7 +388,7 @@ const VarReferencePicker: FC<Props> = ({
|
|||||||
</>
|
</>
|
||||||
</WrapElem>
|
</WrapElem>
|
||||||
<PortalToFollowElemContent style={{
|
<PortalToFollowElemContent style={{
|
||||||
zIndex: 100,
|
zIndex: zIndex || 100,
|
||||||
}} className='mt-1'>
|
}} className='mt-1'>
|
||||||
{!isConstant && (
|
{!isConstant && (
|
||||||
<VarReferencePopup
|
<VarReferencePopup
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plug
|
|||||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
||||||
import type { NodeDefault } from '../../types'
|
import type { NodeDefault } from '../../types'
|
||||||
import type { AgentNodeType } from './types'
|
import type { AgentNodeType } from './types'
|
||||||
|
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import { renderI18nObject } from '@/hooks/use-i18n'
|
import { renderI18nObject } from '@/hooks/use-i18n'
|
||||||
|
|
||||||
const nodeDefault: NodeDefault<AgentNodeType> = {
|
const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||||
@ -37,6 +38,94 @@ const nodeDefault: NodeDefault<AgentNodeType> = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const param of strategy.parameters) {
|
for (const param of strategy.parameters) {
|
||||||
|
// single tool
|
||||||
|
if (param.required && param.type === FormTypeEnum.toolSelector) {
|
||||||
|
// no value
|
||||||
|
const toolValue = payload.agent_parameters?.[param.name]?.value
|
||||||
|
if (!toolValue) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// not enabled
|
||||||
|
else if (!toolValue.enabled) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.noValidTool', { field: renderI18nObject(param.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check form of tool
|
||||||
|
else {
|
||||||
|
const schemas = toolValue.schemas || []
|
||||||
|
const userSettings = toolValue.settings
|
||||||
|
const reasoningConfig = toolValue.parameters
|
||||||
|
schemas.forEach((schema: any) => {
|
||||||
|
if (schema?.required) {
|
||||||
|
if (schema.form === 'form' && !userSettings[schema.name]?.value) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (schema.form === 'llm' && reasoningConfig[schema.name].auto === 0 && !userSettings[schema.name]?.value) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// multiple tools
|
||||||
|
if (param.required && param.type === FormTypeEnum.multiToolSelector) {
|
||||||
|
const tools = payload.agent_parameters?.[param.name]?.value || []
|
||||||
|
// no value
|
||||||
|
if (!tools.length) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// not enabled
|
||||||
|
else if (tools.every((tool: any) => !tool.enabled)) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.noValidTool', { field: renderI18nObject(param.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check form of tools
|
||||||
|
else {
|
||||||
|
let validState = {
|
||||||
|
isValid: true,
|
||||||
|
errorMessage: '',
|
||||||
|
}
|
||||||
|
for (const tool of tools) {
|
||||||
|
const schemas = tool.schemas || []
|
||||||
|
const userSettings = tool.settings
|
||||||
|
const reasoningConfig = tool.parameters
|
||||||
|
schemas.forEach((schema: any) => {
|
||||||
|
if (schema?.required) {
|
||||||
|
if (schema.form === 'form' && !userSettings[schema.name]?.value) {
|
||||||
|
return validState = {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (schema.form === 'llm' && reasoningConfig[schema.name]?.auto === 0 && !reasoningConfig[schema.name]?.value) {
|
||||||
|
return validState = {
|
||||||
|
isValid: false,
|
||||||
|
errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return validState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// common params
|
||||||
if (param.required && !payload.agent_parameters?.[param.name]?.value) {
|
if (param.required && !payload.agent_parameters?.[param.name]?.value) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
|
|||||||
@ -103,6 +103,7 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
|||||||
onFormValueChange={onFormChange}
|
onFormValueChange={onFormChange}
|
||||||
nodeOutputVars={availableVars}
|
nodeOutputVars={availableVars}
|
||||||
availableNodes={availableNodesWithParent}
|
availableNodes={availableNodesWithParent}
|
||||||
|
nodeId={props.id}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import ListFilterNode from './list-operator/node'
|
|||||||
import ListFilterPanel from './list-operator/panel'
|
import ListFilterPanel from './list-operator/panel'
|
||||||
import AgentNode from './agent/node'
|
import AgentNode from './agent/node'
|
||||||
import AgentPanel from './agent/panel'
|
import AgentPanel from './agent/panel'
|
||||||
|
import { TransferMethod } from '@/types/app'
|
||||||
|
|
||||||
export const NodeComponentMap: Record<string, ComponentType<any>> = {
|
export const NodeComponentMap: Record<string, ComponentType<any>> = {
|
||||||
[BlockEnum.Start]: StartNode,
|
[BlockEnum.Start]: StartNode,
|
||||||
@ -82,3 +83,18 @@ export const PanelComponentMap: Record<string, ComponentType<any>> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CUSTOM_NODE_TYPE = 'custom'
|
export const CUSTOM_NODE_TYPE = 'custom'
|
||||||
|
|
||||||
|
export const FILE_TYPE_OPTIONS = [
|
||||||
|
{ value: 'image', i18nKey: 'image' },
|
||||||
|
{ value: 'document', i18nKey: 'doc' },
|
||||||
|
{ value: 'audio', i18nKey: 'audio' },
|
||||||
|
{ value: 'video', i18nKey: 'video' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const TRANSFER_METHOD = [
|
||||||
|
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
|
||||||
|
{ value: TransferMethod.remote_url, i18nKey: 'url' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method']
|
||||||
|
export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method')
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
isComparisonOperatorNeedTranslate,
|
isComparisonOperatorNeedTranslate,
|
||||||
isEmptyRelatedOperator,
|
isEmptyRelatedOperator,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../default'
|
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants'
|
||||||
import type { ValueSelector } from '../../../types'
|
import type { ValueSelector } from '../../../types'
|
||||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import {
|
|||||||
} from '../../types'
|
} from '../../types'
|
||||||
import { comparisonOperatorNotRequireValue, getOperators } from '../../utils'
|
import { comparisonOperatorNotRequireValue, getOperators } from '../../utils'
|
||||||
import ConditionNumberInput from '../condition-number-input'
|
import ConditionNumberInput from '../condition-number-input'
|
||||||
import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../default'
|
import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../../constants'
|
||||||
import ConditionWrap from '../condition-wrap'
|
import ConditionWrap from '../condition-wrap'
|
||||||
import ConditionOperator from './condition-operator'
|
import ConditionOperator from './condition-operator'
|
||||||
import ConditionInput from './condition-input'
|
import ConditionInput from './condition-input'
|
||||||
@ -39,7 +39,7 @@ import { SimpleSelect as Select } from '@/app/components/base/select'
|
|||||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
||||||
|
|
||||||
interface ConditionItemProps {
|
type ConditionItemProps = {
|
||||||
className?: string
|
className?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
caseId: string
|
caseId: string
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
comparisonOperatorNotRequireValue,
|
comparisonOperatorNotRequireValue,
|
||||||
isComparisonOperatorNeedTranslate,
|
isComparisonOperatorNeedTranslate,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../default'
|
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants'
|
||||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
@ -20,7 +20,7 @@ import type {
|
|||||||
Node,
|
Node,
|
||||||
} from '@/app/components/workflow/types'
|
} from '@/app/components/workflow/types'
|
||||||
|
|
||||||
interface ConditionValueProps {
|
type ConditionValueProps = {
|
||||||
variableSelector: string[]
|
variableSelector: string[]
|
||||||
labelName?: string
|
labelName?: string
|
||||||
operator: ComparisonOperator
|
operator: ComparisonOperator
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, Handl
|
|||||||
import type { Node, NodeOutPutVar, Var } from '../../../types'
|
import type { Node, NodeOutPutVar, Var } from '../../../types'
|
||||||
import { VarType } from '../../../types'
|
import { VarType } from '../../../types'
|
||||||
import { useGetAvailableVars } from '../../variable-assigner/hooks'
|
import { useGetAvailableVars } from '../../variable-assigner/hooks'
|
||||||
import { SUB_VARIABLES } from '../default'
|
import { SUB_VARIABLES } from '../../constants'
|
||||||
import ConditionList from './condition-list'
|
import ConditionList from './condition-list'
|
||||||
import ConditionAdd from './condition-add'
|
import ConditionAdd from './condition-add'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { BlockEnum, type NodeDefault } from '../../types'
|
import { BlockEnum, type NodeDefault } from '../../types'
|
||||||
import { type IfElseNodeType, LogicalOperator } from './types'
|
import { type IfElseNodeType, LogicalOperator } from './types'
|
||||||
import { isEmptyRelatedOperator } from './utils'
|
import { isEmptyRelatedOperator } from './utils'
|
||||||
import { TransferMethod } from '@/types/app'
|
|
||||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
|
||||||
const i18nPrefix = 'workflow.errorMsg'
|
const i18nPrefix = 'workflow.errorMsg'
|
||||||
|
|
||||||
@ -79,18 +78,3 @@ const nodeDefault: NodeDefault<IfElseNodeType> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default nodeDefault
|
export default nodeDefault
|
||||||
|
|
||||||
export const FILE_TYPE_OPTIONS = [
|
|
||||||
{ value: 'image', i18nKey: 'image' },
|
|
||||||
{ value: 'document', i18nKey: 'doc' },
|
|
||||||
{ value: 'audio', i18nKey: 'audio' },
|
|
||||||
{ value: 'video', i18nKey: 'video' },
|
|
||||||
]
|
|
||||||
|
|
||||||
export const TRANSFER_METHOD = [
|
|
||||||
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
|
|
||||||
{ value: TransferMethod.remote_url, i18nKey: 'url' },
|
|
||||||
]
|
|
||||||
|
|
||||||
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method']
|
|
||||||
export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method')
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { ComparisonOperator } from '../../if-else/types'
|
|||||||
import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils'
|
import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils'
|
||||||
import SubVariablePicker from './sub-variable-picker'
|
import SubVariablePicker from './sub-variable-picker'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/if-else/default'
|
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/constants'
|
||||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||||
|
|
||||||
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { SUB_VARIABLES } from '../../if-else/default'
|
import { SUB_VARIABLES } from '../../constants'
|
||||||
import type { Item } from '@/app/components/base/select'
|
import type { Item } from '@/app/components/base/select'
|
||||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
|
|||||||
@ -78,8 +78,11 @@ const translation = {
|
|||||||
descriptionLabel: 'Tool description',
|
descriptionLabel: 'Tool description',
|
||||||
descriptionPlaceholder: 'Brief description of the tool\'s purpose, e.g., get the temperature for a specific location.',
|
descriptionPlaceholder: 'Brief description of the tool\'s purpose, e.g., get the temperature for a specific location.',
|
||||||
placeholder: 'Select a tool...',
|
placeholder: 'Select a tool...',
|
||||||
auth: 'AUTHORIZATION',
|
settings: 'USER SETTINGS',
|
||||||
settings: 'TOOL SETTINGS',
|
params: 'REASONING CONFIG',
|
||||||
|
paramsTip1: 'Controls LLM inference parameters.',
|
||||||
|
paramsTip2: 'When \'Automatic\' is off, the default value is used.',
|
||||||
|
auto: 'Automatic',
|
||||||
empty: 'Click the \'+\' button to add tools. You can add multiple tools.',
|
empty: 'Click the \'+\' button to add tools. You can add multiple tools.',
|
||||||
uninstalledTitle: 'Tool not installed',
|
uninstalledTitle: 'Tool not installed',
|
||||||
uninstalledContent: 'This plugin is installed from the local/GitHub repository. Please use after installation.',
|
uninstalledContent: 'This plugin is installed from the local/GitHub repository. Please use after installation.',
|
||||||
|
|||||||
@ -195,6 +195,8 @@ const translation = {
|
|||||||
visionVariable: 'Vision Variable',
|
visionVariable: 'Vision Variable',
|
||||||
},
|
},
|
||||||
invalidVariable: 'Invalid variable',
|
invalidVariable: 'Invalid variable',
|
||||||
|
noValidTool: '{{field}} no valid tool selected',
|
||||||
|
toolParameterRequired: '{{field}}: parameter [{{param}}] is required',
|
||||||
},
|
},
|
||||||
singleRun: {
|
singleRun: {
|
||||||
testRun: 'Test Run ',
|
testRun: 'Test Run ',
|
||||||
|
|||||||
@ -78,8 +78,11 @@ const translation = {
|
|||||||
descriptionLabel: '工具描述',
|
descriptionLabel: '工具描述',
|
||||||
descriptionPlaceholder: '简要描述工具目的,例如,获取特定位置的温度。',
|
descriptionPlaceholder: '简要描述工具目的,例如,获取特定位置的温度。',
|
||||||
placeholder: '选择工具',
|
placeholder: '选择工具',
|
||||||
auth: '授权',
|
settings: '用户设置',
|
||||||
settings: '工具设置',
|
params: '推理配置',
|
||||||
|
paramsTip1: '控制 LLM 推理参数。',
|
||||||
|
paramsTip2: '当“自动”关闭时,使用默认值。',
|
||||||
|
auto: '自动',
|
||||||
empty: '点击 "+" 按钮添加工具。您可以添加多个工具。',
|
empty: '点击 "+" 按钮添加工具。您可以添加多个工具。',
|
||||||
uninstalledTitle: '工具未安装',
|
uninstalledTitle: '工具未安装',
|
||||||
uninstalledContent: '此插件安装自 本地 / GitHub 仓库,请安装后使用。',
|
uninstalledContent: '此插件安装自 本地 / GitHub 仓库,请安装后使用。',
|
||||||
|
|||||||
@ -195,6 +195,9 @@ const translation = {
|
|||||||
visionVariable: '视觉变量',
|
visionVariable: '视觉变量',
|
||||||
},
|
},
|
||||||
invalidVariable: '无效的变量',
|
invalidVariable: '无效的变量',
|
||||||
|
noValidTool: '{{field}} 无可用工具',
|
||||||
|
toolParameterRequired: '{{field}}: 参数 [{{param}}] 不能为空',
|
||||||
|
|
||||||
},
|
},
|
||||||
singleRun: {
|
singleRun: {
|
||||||
testRun: '测试运行 ',
|
testRun: '测试运行 ',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user