merge feat/plugins

This commit is contained in:
JzoNg 2025-01-02 08:51:03 +08:00
commit 6f97eb5713
29 changed files with 427 additions and 142 deletions

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M10.3567 3.56405L10.2334 3.84689C10.1432 4.05396 9.8568 4.05396 9.76655 3.84689L9.6433 3.56405C9.42355 3.05973 9.02775 2.6582 8.53385 2.43854L8.154 2.26961C7.94865 2.17826 7.94865 1.8794 8.154 1.78806L8.5126 1.62857C9.0192 1.40325 9.4221 0.986865 9.63805 0.465414L9.76465 0.159767C9.8529 -0.0532556 10.1471 -0.0532556 10.2353 0.159767L10.3619 0.465414C10.5779 0.986865 10.9808 1.40325 11.4874 1.62857L11.846 1.78806C12.0514 1.8794 12.0514 2.17826 11.846 2.26961L11.4662 2.43854C10.9723 2.6582 10.5764 3.05973 10.3567 3.56405ZM4.25 3H3.25V9H4.25V3ZM2 5H1V7H2V5ZM6.5 1H5.5V11H6.5V1ZM8.75 4H7.75V9H8.75V4ZM11 5H10V7H11V5Z" fill="#676F83"/>
</svg>

After

Width:  |  Height:  |  Size: 750 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M10.5 4V10.4966C10.5 10.7751 10.2776 11 10.0033 11H1.9967C1.72248 11 1.5 10.778 1.5 10.5041V1.4959C1.5 1.22766 1.72435 1 2.00111 1H7.4984L10.5 4ZM9.5 4.5H7V2H2.5V10H9.5V4.5ZM4 3.5H5.5V4.5H4V3.5ZM4 5.5H8V6.5H4V5.5ZM4 7.5H8V8.5H4V7.5Z" fill="#676F83"/>
</svg>

After

Width:  |  Height:  |  Size: 364 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M10.2334 4.3469L10.3567 4.06406C10.5764 3.55974 10.9723 3.15821 11.4662 2.93854L11.846 2.76961C12.0514 2.67827 12.0514 2.37941 11.846 2.28806L11.4874 2.12857C10.9808 1.90326 10.5779 1.48687 10.3619 0.965415L10.2353 0.659765C10.1471 0.446745 9.8529 0.446745 9.76465 0.659765L9.63805 0.965415C9.4221 1.48687 9.0192 1.90326 8.5126 2.12857L8.154 2.28806C7.94865 2.37941 7.94865 2.67827 8.154 2.76961L8.53385 2.93854C9.02775 3.15821 9.42355 3.55974 9.6433 4.06406L9.76655 4.3469C9.8568 4.55396 10.1432 4.55396 10.2334 4.3469ZM1.4959 1.5H7V2.5H4V9.5H8V4.5H9V5.5H10H11V10.0033C11 10.2776 10.7723 10.5 10.5041 10.5H1.4959C1.22203 10.5 1 10.2775 1 10.0033V1.9967C1 1.72238 1.22766 1.5 1.4959 1.5ZM2 2.5V3.5H3V2.5H2ZM2 4.5V5.5H3V4.5H2ZM2 6.5V7.5H3V6.5H2ZM9 6.5V7.5H10V6.5H9ZM2 8.5V9.5H3V8.5H2ZM9 8.5V9.5H10V8.5H9Z" fill="#676F83"/>
</svg>

After

Width:  |  Height:  |  Size: 935 B

View File

@ -0,0 +1,26 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"xmlns": "http://www.w3.org/2000/svg",
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10.3567 3.56405L10.2334 3.84689C10.1432 4.05396 9.8568 4.05396 9.76655 3.84689L9.6433 3.56405C9.42355 3.05973 9.02775 2.6582 8.53385 2.43854L8.154 2.26961C7.94865 2.17826 7.94865 1.8794 8.154 1.78806L8.5126 1.62857C9.0192 1.40325 9.4221 0.986865 9.63805 0.465414L9.76465 0.159767C9.8529 -0.0532556 10.1471 -0.0532556 10.2353 0.159767L10.3619 0.465414C10.5779 0.986865 10.9808 1.40325 11.4874 1.62857L11.846 1.78806C12.0514 1.8794 12.0514 2.17826 11.846 2.26961L11.4662 2.43854C10.9723 2.6582 10.5764 3.05973 10.3567 3.56405ZM4.25 3H3.25V9H4.25V3ZM2 5H1V7H2V5ZM6.5 1H5.5V11H6.5V1ZM8.75 4H7.75V9H8.75V4ZM11 5H10V7H11V5Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "AudioSupportIcon"
}

View File

@ -0,0 +1,16 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './AudioSupportIcon.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'AudioSupportIcon'
export default Icon

View File

@ -0,0 +1,26 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"xmlns": "http://www.w3.org/2000/svg",
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10.5 4V10.4966C10.5 10.7751 10.2776 11 10.0033 11H1.9967C1.72248 11 1.5 10.778 1.5 10.5041V1.4959C1.5 1.22766 1.72435 1 2.00111 1H7.4984L10.5 4ZM9.5 4.5H7V2H2.5V10H9.5V4.5ZM4 3.5H5.5V4.5H4V3.5ZM4 5.5H8V6.5H4V5.5ZM4 7.5H8V8.5H4V7.5Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "DocumentSupportIcon"
}

View File

@ -0,0 +1,16 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './DocumentSupportIcon.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'DocumentSupportIcon'
export default Icon

View File

@ -0,0 +1,26 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"xmlns": "http://www.w3.org/2000/svg",
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10.2334 4.3469L10.3567 4.06406C10.5764 3.55974 10.9723 3.15821 11.4662 2.93854L11.846 2.76961C12.0514 2.67827 12.0514 2.37941 11.846 2.28806L11.4874 2.12857C10.9808 1.90326 10.5779 1.48687 10.3619 0.965415L10.2353 0.659765C10.1471 0.446745 9.8529 0.446745 9.76465 0.659765L9.63805 0.965415C9.4221 1.48687 9.0192 1.90326 8.5126 2.12857L8.154 2.28806C7.94865 2.37941 7.94865 2.67827 8.154 2.76961L8.53385 2.93854C9.02775 3.15821 9.42355 3.55974 9.6433 4.06406L9.76655 4.3469C9.8568 4.55396 10.1432 4.55396 10.2334 4.3469ZM1.4959 1.5H7V2.5H4V9.5H8V4.5H9V5.5H10H11V10.0033C11 10.2776 10.7723 10.5 10.5041 10.5H1.4959C1.22203 10.5 1 10.2775 1 10.0033V1.9967C1 1.72238 1.22766 1.5 1.4959 1.5ZM2 2.5V3.5H3V2.5H2ZM2 4.5V5.5H3V4.5H2ZM2 6.5V7.5H3V6.5H2ZM9 6.5V7.5H10V6.5H9ZM2 8.5V9.5H3V8.5H2ZM9 8.5V9.5H10V8.5H9Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "VideoSupportIcon"
}

View File

@ -0,0 +1,16 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './VideoSupportIcon.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'VideoSupportIcon'
export default Icon

View File

@ -1,3 +1,5 @@
export { default as AudioSupportIcon } from './AudioSupportIcon'
export { default as DocumentSupportIcon } from './DocumentSupportIcon'
export { default as MagicBox } from './MagicBox'
export { default as MagicEyes } from './MagicEyes'
export { default as MagicWand } from './MagicWand'
@ -7,3 +9,4 @@ export { default as Robot } from './Robot'
export { default as Sliders02 } from './Sliders02'
export { default as Speaker } from './Speaker'
export { default as StopCircle } from './StopCircle'
export { default as VideoSupportIcon } from './VideoSupportIcon'

View File

@ -23,19 +23,19 @@ const Switch = React.forwardRef<HTMLButtonElement>(({ onChange, size = 'md', def
sm: 'h-3 w-5',
}
// const circleStyle = {
// lg: 'h-5 w-5',
// l: 'h-4 w-4',
// md: 'h-3 w-3',
// sm: 'h-2 w-2',
// }
const circleStyle = {
lg: 'h-5 w-5',
l: 'h-4 w-4',
md: 'h-3 w-3',
sm: 'h-2 w-2',
}
// const translateLeft = {
// lg: 'translate-x-5',
// l: 'translate-x-4',
// md: 'translate-x-3',
// sm: 'translate-x-2',
// }
const translateLeft = {
lg: 'translate-x-5',
l: 'translate-x-4',
md: 'translate-x-3',
sm: 'translate-x-2',
}
return (
<OriginalSwitch
ref={ref}
@ -57,11 +57,9 @@ const Switch = React.forwardRef<HTMLButtonElement>(({ onChange, size = 'md', def
<span
aria-hidden="true"
className={classNames(
wrapStyle[size],
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',
disabled ? '!opacity-50 !cursor-not-allowed' : '',
className,
circleStyle[size],
enabled ? translateLeft[size] : 'translate-x-0',
'pointer-events-none inline-block transform rounded-[3px] bg-components-toggle-knob shadow ring-0 transition duration-200 ease-in-out',
)}
/>
</OriginalSwitch>

View File

@ -9,20 +9,6 @@ import { useWorkspacesContext } from '@/context/workspace-context'
import { useProviderContext } from '@/context/provider-context'
import { ToastContext } from '@/app/components/base/toast'
import PremiumBadge from '@/app/components/base/premium-badge'
import classNames from '@/utils/classnames'
const itemClassName = `
flex items-center px-3 py-2 h-10 cursor-pointer
`
const itemIconClassName = `
shrink-0 mr-2 flex items-center justify-center w-6 h-6 bg-[#EFF4FF] rounded-md text-xs font-medium text-primary-600
`
const itemNameClassName = `
grow mr-2 text-sm text-gray-700 text-left
`
const itemCheckClassName = `
shrink-0 w-4 h-4 text-primary-600
`
const WorkplaceSelector = () => {
const { t } = useTranslation()
@ -30,7 +16,6 @@ const WorkplaceSelector = () => {
const { notify } = useContext(ToastContext)
const { workspaces } = useWorkspacesContext()
const currentWorkspace = workspaces.find(v => v.current)
const isFreePlan = plan.type === 'sandbox'
const handleSwitchWorkspace = async (tenant_id: string) => {
try {
if (currentWorkspace?.id === tenant_id)
@ -51,8 +36,8 @@ const WorkplaceSelector = () => {
<>
<Menu.Button className={cn(
`
${itemClassName} w-full
group hover:bg-state-base-hover cursor-pointer ${open && 'bg-state-base-hover'} rounded-lg
flex items-center p-0.5 gap-1.5 w-full
group hover:bg-state-base-hover cursor-pointer ${open && 'bg-state-base-hover'} rounded-[10px]
`,
)}>
<div className='flex items-center justify-center w-7 h-7 bg-[#EFF4FF] rounded-lg text-xs font-medium text-primary-600'>{currentWorkspace?.name[0].toLocaleUpperCase()}</div>
@ -83,30 +68,18 @@ const WorkplaceSelector = () => {
</div>
{
workspaces.map(workspace => (
<div>
<div className='flex py-1 pl-3 pr-2 items-center gap-2 self-stretch hover:bg-state-base-hover rounded-lg' key={workspace.id} onClick={() => handleSwitchWorkspace(workspace.id)}>
<div className='flex items-center justify-center w-6 h-6 bg-[#EFF4FF] rounded-md text-xs font-medium text-primary-600'>{workspace.name[0].toLocaleUpperCase()}</div>
<div className='line-clamp-1 grow overflow-hidden text-text-secondary text-ellipsis system-md-regular cursor-pointer'>{workspace.name}</div>
{
<PremiumBadge size='s' color='gray' allowHover={false}>
<div className='system-2xs-medium'>
<span className='p-[2px]'>
{plan.type === 'professional' ? 'PRO' : plan.type.toUpperCase()}
</span>
</div>
</PremiumBadge>
}
</div>
<Menu.Item key={workspace.id}>
{({ active }) => <div className={classNames(itemClassName,
active && 'bg-state-base-hover',
)} key={workspace.id} onClick={() => handleSwitchWorkspace(workspace.id)}>
<div className={itemIconClassName}>{workspace.name[0].toLocaleUpperCase()}</div>
<div className={itemNameClassName}>{workspace.name}</div>
{/* {workspace.current && <Check className={itemCheckClassName} />} */}
</div>}
</Menu.Item>
<div className='flex py-1 pl-3 pr-2 items-center gap-2 self-stretch hover:bg-state-base-hover rounded-lg' key={workspace.id} onClick={() => handleSwitchWorkspace(workspace.id)}>
<div className='flex items-center justify-center w-6 h-6 bg-[#EFF4FF] rounded-md text-xs font-medium text-primary-600'>{workspace.name[0].toLocaleUpperCase()}</div>
<div className='line-clamp-1 grow overflow-hidden text-text-secondary text-ellipsis system-md-regular cursor-pointer'>{workspace.name}</div>
{
<PremiumBadge size='s' color='gray' allowHover={false}>
<div className='system-2xs-medium'>
<span className='p-[2px]'>
{plan.type === 'professional' ? 'PRO' : plan.type.toUpperCase()}
</span>
</div>
</PremiumBadge>
}
</div>
))
}

View File

@ -22,14 +22,14 @@ const ModelIcon: FC<ModelIconProps> = ({
}) => {
const language = useLanguage()
if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o'))
return <div className='flex w-6 h-6 items-center justify-center'><OpenaiBlue className={cn('w-5 h-5', className)}/></div>
return <div className='flex items-center justify-center'><OpenaiBlue className={cn('w-5 h-5', className)}/></div>
if (provider?.provider.includes('openai') && modelName?.startsWith('gpt-4'))
return <div className='flex w-6 h-6 items-center justify-center'><OpenaiViolet className={cn('w-5 h-5', className)}/></div>
return <div className='flex items-center justify-center'><OpenaiViolet className={cn('w-5 h-5', className)}/></div>
if (provider?.icon_small) {
return (
<div className={`flex w-6 h-6 items-center justify-center ${isDeprecated ? 'opacity-50' : ''}`}>
<div className={`flex items-center justify-center ${isDeprecated ? 'opacity-50' : ''}`}>
<img
alt='model-icon'
src={`${provider.icon_small[language] || provider.icon_small.en_US}`}
@ -41,7 +41,7 @@ const ModelIcon: FC<ModelIconProps> = ({
return (
<div className={cn(
'flex items-center justify-center w-6 h-6 rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle',
'flex items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle',
className,
)}>
<div className='flex w-5 h5 items-center justify-center opacity-35'>

View File

@ -37,7 +37,7 @@ const ModelName: FC<ModelNameProps> = ({
if (!modelItem)
return null
return (
<div className={cn('flex items-center overflow-hidden text-ellipsis truncate text-components-input-text-filled system-sm-regular', className)}>
<div className={cn('flex gap-0.5 items-center overflow-hidden text-ellipsis truncate text-components-input-text-filled system-sm-regular', className)}>
<div
className='truncate'
title={modelItem.label[language] || modelItem.label.en_US}

View File

@ -6,10 +6,13 @@ import {
ModelFeatureTextEnum,
} from '../declarations'
import {
AudioSupportIcon,
DocumentSupportIcon,
// MagicBox,
MagicEyes,
// MagicWand,
// Robot,
VideoSupportIcon,
} from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import Tooltip from '@/app/components/base/tooltip'
@ -65,7 +68,7 @@ const FeatureIcon: FC<FeatureIconProps> = ({
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.vision })}
>
<div className='inline-block cursor-help'>
<ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-text-tertiary ${className}`}>
<ModelBadge className={`!px-0 w-[18px] justify-center text-text-tertiary ${className}`}>
<MagicEyes className='w-3 h-3' />
</ModelBadge>
</div>
@ -73,6 +76,48 @@ const FeatureIcon: FC<FeatureIconProps> = ({
)
}
if (feature === ModelFeatureEnum.document) {
return (
<Tooltip
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.document })}
>
<div className='inline-block cursor-help'>
<ModelBadge className={`!px-0 w-[18px] justify-center text-text-tertiary ${className}`}>
<DocumentSupportIcon className='w-3 h-3' />
</ModelBadge>
</div>
</Tooltip>
)
}
if (feature === ModelFeatureEnum.audio) {
return (
<Tooltip
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.audio })}
>
<div className='inline-block cursor-help'>
<ModelBadge className={`!px-0 w-[18px] justify-center text-text-tertiary ${className}`}>
<AudioSupportIcon className='w-3 h-3' />
</ModelBadge>
</div>
</Tooltip>
)
}
if (feature === ModelFeatureEnum.video) {
return (
<Tooltip
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.video })}
>
<div className='inline-block cursor-help'>
<ModelBadge className={`!px-0 w-[18px] justify-center text-text-tertiary ${className}`}>
<VideoSupportIcon className='w-3 h-3' />
</ModelBadge>
</div>
</Tooltip>
)
}
return null
}

View File

@ -34,41 +34,45 @@ const ModelTrigger: FC<ModelTriggerProps> = ({
return (
<div
className={cn(
'group flex items-center p-1 gap-0.5 h-8 rounded-lg bg-components-input-bg-normal',
'group flex items-center p-1 gap-0.5 rounded-lg bg-components-input-bg-normal',
!readonly && 'hover:bg-components-input-bg-hover cursor-pointer',
open && 'bg-components-input-bg-hover',
model.status !== ModelStatusEnum.active && 'bg-components-input-bg-disabled hover:bg-components-input-bg-disabled',
className,
)}
>
<ModelIcon
className='shrink-0 mr-1.5'
provider={provider}
modelName={model.model}
/>
<ModelName
className='grow'
modelItem={model}
showMode
showFeatures
/>
{!readonly && (
<div className='shrink-0 flex items-center justify-center w-4 h-4'>
{
model.status !== ModelStatusEnum.active
? (
<Tooltip popupContent={MODEL_STATUS_TEXT[model.status][language]}>
<AlertTriangle className='w-4 h-4 text-[#F79009]' />
</Tooltip>
)
: (
<RiArrowDownSLine
className='w-3.5 h-3.5 text-text-tertiary'
/>
)
}
</div>
)}
<div className='flex items-center justify-center w-6 h-6'>
<ModelIcon
className='w-5 h-5 m-0.5'
provider={provider}
modelName={model.model}
/>
</div>
<div className='flex px-1 py-[3px] items-center gap-1 grow truncate'>
<ModelName
className='grow'
modelItem={model}
showMode
showFeatures
/>
{!readonly && (
<div className='shrink-0 flex items-center justify-center w-4 h-4'>
{
model.status !== ModelStatusEnum.active
? (
<Tooltip popupContent={MODEL_STATUS_TEXT[model.status][language]}>
<AlertTriangle className='w-4 h-4 text-text-warning-secondary' />
</Tooltip>
)
: (
<RiArrowDownSLine
className='w-3.5 h-3.5 text-text-tertiary'
/>
)
}
</div>
)}
</div>
</div>
)
}

View File

@ -65,7 +65,7 @@ const Card = ({
{/* Header */}
<div className="flex">
<Icon src={icon} installed={installed} installFailed={installFailed} />
<div className="ml-3 grow">
<div className="ml-3 w-0 grow">
<div className="flex items-center h-5">
<Title title={getLocalizedText(label)} />
{verified && <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />}

View File

@ -107,7 +107,7 @@ const ToolSelector: FC<Props> = ({
const [isShowChooseTool, setIsShowChooseTool] = useState(false)
const handleSelectTool = (tool: ToolDefaultValue) => {
const paramValues = addDefaultValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas as any))
const paramValues = addDefaultValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form !== 'llm') as any))
const toolValue = {
provider_name: tool.provider_id,
tool_name: tool.tool_name,
@ -133,7 +133,7 @@ const ToolSelector: FC<Props> = ({
const currentToolParams = useMemo(() => {
if (!currentProvider) return []
return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters || []
return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters.filter(param => param.form !== 'llm') || []
}, [currentProvider, value])
const formSchemas = useMemo(() => toolParametersToFormSchemas(currentToolParams), [currentToolParams])

View File

@ -49,6 +49,7 @@ export type Collection = {
allow_delete: boolean
labels: string[]
plugin_id?: string
letter?: string
}
export type ToolParameter = {

View File

@ -6,31 +6,53 @@ import type { BlockEnum } from '../../../types'
import type { ToolDefaultValue } from '../../types'
import Tool from '../tool'
import { ViewType } from '../../view-type-select'
import { useMemo } from 'react'
type Props = {
payload: ToolWithProvider[]
isShowLetterIndex: boolean
hasSearchText: boolean
onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void
letters: string[]
toolRefs: any
}
const ToolViewFlatView: FC<Props> = ({
letters,
payload,
isShowLetterIndex,
hasSearchText,
onSelect,
toolRefs,
}) => {
const firstLetterToolIds = useMemo(() => {
const res: Record<string, string> = {}
letters.forEach((letter) => {
const firstToolId = payload.find(tool => tool.letter === letter)?.id
if (firstToolId)
res[firstToolId] = letter
})
return res
}, [payload, letters])
return (
<div>
{payload.map(tool => (
<Tool
<div
key={tool.id}
payload={tool}
viewType={ViewType.flat}
isShowLetterIndex={isShowLetterIndex}
hasSearchText={hasSearchText}
onSelect={onSelect}
/>
ref={(el) => {
const letter = firstLetterToolIds[tool.id]
if (letter)
toolRefs.current[letter] = el
}}
>
<Tool
payload={tool}
viewType={ViewType.flat}
isShowLetterIndex={isShowLetterIndex}
hasSearchText={hasSearchText}
onSelect={onSelect}
/>
</div>
))}
</div>
)

View File

@ -69,13 +69,19 @@ const Blocks = ({
const listViewToolData = useMemo(() => {
const result: ToolWithProvider[] = []
Object.keys(withLetterAndGroupViewToolsData).forEach((letter) => {
letters.forEach((letter) => {
Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => {
result.push(...withLetterAndGroupViewToolsData[letter][groupName])
result.push(...withLetterAndGroupViewToolsData[letter][groupName].map((item) => {
return {
...item,
letter,
}
}))
})
})
return result
}, [withLetterAndGroupViewToolsData])
}, [withLetterAndGroupViewToolsData, letters])
const toolRefs = useRef({})
@ -94,6 +100,8 @@ const Blocks = ({
{!!tools.length && (
isFlatView ? (
<ToolListFlatView
toolRefs={toolRefs}
letters={letters}
payload={listViewToolData}
isShowLetterIndex={isShowLetterIndex}
hasSearchText={hasSearchText}

View File

@ -27,6 +27,6 @@ export type ToolDefaultValue = {
title: string
is_team_authorization: boolean
params: Record<string, any>
paramSchemas: Record<string, any>
paramSchemas: Record<string, any>[]
output_schema: Record<string, any>
}

View File

@ -161,6 +161,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
} : undefined
}
placeholderClassName='px-2 py-1'
titleClassName='system-sm-semibold-uppercase text-text-secondary text-[13px]'
inputClassName='px-2 py-1 bg-components-input-bg-normal focus:bg-components-input-bg-active focus:border-components-input-border-active focus:border rounded-lg'
/>
}

View File

@ -74,6 +74,7 @@ type Props = {
inputClassName?: string
editorContainerClassName?: string
placeholderClassName?: string
titleClassName?: string
}
const Editor: FC<Props> = ({
@ -107,6 +108,7 @@ const Editor: FC<Props> = ({
titleTooltip,
inputClassName,
placeholderClassName,
titleClassName,
editorContainerClassName,
}) => {
const { t } = useTranslation()
@ -145,7 +147,7 @@ const Editor: FC<Props> = ({
<div className={cn(isFocus ? 'bg-gray-50' : 'bg-gray-100', isExpand && 'h-full flex flex-col', 'rounded-lg', containerClassName)}>
<div className={cn('pt-1 pl-3 pr-2 flex justify-between items-center', headerClassName)}>
<div className='flex gap-2'>
<div className='leading-4 text-xs font-semibold text-gray-700 uppercase'>{title}</div>
<div className={cn('leading-4 text-xs font-semibold text-gray-700 uppercase', titleClassName)}>{title}</div>
{titleTooltip && <Tooltip popupContent={titleTooltip} />}
</div>
<div className='flex items-center'>

View File

@ -8,11 +8,33 @@ import type { ToolIconProps } from './components/tool-icon'
import { ToolIcon } from './components/tool-icon'
import useConfig from './use-config'
import { useTranslation } from 'react-i18next'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { FormTypeEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
const useAllModel = () => {
const { data: textGeneration } = useModelList(ModelTypeEnum.textGeneration)
const { data: moderation } = useModelList(ModelTypeEnum.moderation)
const { data: rerank } = useModelList(ModelTypeEnum.rerank)
const { data: speech2text } = useModelList(ModelTypeEnum.speech2text)
const { data: textEmbedding } = useModelList(ModelTypeEnum.textEmbedding)
const { data: tts } = useModelList(ModelTypeEnum.tts)
const models = useMemo(() => {
return textGeneration
.concat(moderation)
.concat(rerank)
.concat(speech2text)
.concat(textEmbedding)
.concat(tts)
}, [textGeneration, moderation, rerank, speech2text, textEmbedding, tts])
if (!textGeneration || !moderation || !rerank || !speech2text || !textEmbedding || !tts)
return undefined
return models
}
const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
const { inputs, currentStrategy } = useConfig(props.id, props.data)
const { t } = useTranslation()
const modelList = useAllModel()
const models = useMemo(() => {
if (!inputs) return []
// if selected, show in node
@ -73,7 +95,7 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
{inputs.agent_strategy_label}
</SettingItem>
: <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />}
{models.length > 0 && <Group
{models.length > 0 && modelList && <Group
label={<GroupLabel className='mt-1'>
{t('workflow.nodes.agent.model')}
</GroupLabel>}
@ -81,7 +103,8 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
{models.map((model) => {
return <ModelSelector
key={model.param}
modelList={[]}
modelList={modelList}
triggerClassName='bg-workflow-block-parma-bg'
defaultModel={
'provider' in model
? {

View File

@ -1,4 +1,5 @@
import type { FC } from 'react'
import { useMemo } from 'react'
import type { NodePanelProps } from '../../types'
import type { AgentNodeType } from './types'
import Field from '../_base/components/field'
@ -8,6 +9,11 @@ import { useTranslation } from 'react-i18next'
import OutputVars, { VarItem } from '../_base/components/output-vars'
import type { StrategyParamItem } from '@/app/components/plugins/types'
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form'
import ResultPanel from '@/app/components/workflow/run/result-panel'
import formatTracing from '@/app/components/workflow/run/utils/format-log'
import { useLogs } from '@/app/components/workflow/run/hooks'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
const i18nPrefix = 'workflow.nodes.agent'
@ -20,10 +26,49 @@ export function strategyParamToCredientialForm(param: StrategyParamItem): Creden
}
const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
const { inputs, setInputs, currentStrategy, formData, onFormChange } = useConfig(props.id, props.data)
const {
inputs,
setInputs,
currentStrategy,
formData,
onFormChange,
isShowSingleRun,
hideSingleRun,
runningStatus,
handleRun,
handleStop,
runResult,
runInputData,
setRunInputData,
varInputs,
} = useConfig(props.id, props.data)
const { t } = useTranslation()
const nodeInfo = useMemo(() => {
if (!runResult)
return
return formatTracing([runResult], t)[0]
}, [runResult, t])
const logsParams = useLogs()
const singleRunForms = (() => {
const forms: FormProps[] = []
if (varInputs.length > 0) {
forms.push(
{
label: t(`${i18nPrefix}.singleRun.variable`)!,
inputs: varInputs,
values: runInputData,
onChange: setRunInputData,
},
)
}
return forms
})()
return <div className='my-2'>
<Field title={t('workflow.nodes.agent.strategy.label')} className='px-4' >
<Field title={t('workflow.nodes.agent.strategy.label')} className='px-4 py-2' >
<AgentStrategy
strategy={inputs.agent_strategy_name ? {
agent_strategy_provider_name: inputs.agent_strategy_provider_name!,
@ -72,6 +117,21 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
))}
</OutputVars>
</div>
{
isShowSingleRun && (
<BeforeRunForm
nodeName={inputs.title}
nodeType={inputs.type}
onHide={hideSingleRun}
forms={singleRunForms}
runningStatus={runningStatus}
onRun={handleRun}
onStop={handleStop}
{...logsParams}
result={<ResultPanel {...runResult} nodeInfo={nodeInfo} showSteps={false} {...logsParams} />}
/>
)
}
</div>
}

View File

@ -21,26 +21,6 @@ const useConfig = (id: string, payload: AgentNodeType) => {
inputs.agent_strategy_provider_name || '',
)
// single run
const agentInputKey = `${id}.input_selector`
const {
isShowSingleRun,
showSingleRun,
hideSingleRun,
toVarInputs,
runningStatus,
handleRun,
handleStop,
runInputData,
setRunInputData,
runResult,
} = useOneStepRun<AgentNodeType>({
id,
data: inputs,
defaultRunInputData: {
[agentInputKey]: [''],
},
})
const currentStrategy = strategyProvider.data?.declaration.strategies.find(
str => str.identity.name === inputs.agent_strategy_name,
)
@ -70,6 +50,36 @@ const useConfig = (id: string, payload: AgentNodeType) => {
agent_parameters: res,
})
}
// single run
const {
isShowSingleRun,
showSingleRun,
hideSingleRun,
toVarInputs,
runningStatus,
handleRun,
handleStop,
runInputData,
setRunInputData,
runResult,
getInputVars,
} = useOneStepRun<AgentNodeType>({
id,
data: inputs,
defaultRunInputData: {},
})
const allVarStrArr = (() => {
const arr = ['']
return arr
})()
const varInputs = (() => {
const vars = getInputVars(allVarStrArr)
return vars
})()
return {
readOnly,
inputs,
@ -92,7 +102,7 @@ const useConfig = (id: string, payload: AgentNodeType) => {
runInputData,
setRunInputData,
runResult,
agentInputKey,
varInputs,
}
}

View File

@ -78,10 +78,10 @@ const NodePanel: FC<Props> = ({
setCollapseState(!nodeInfo.expand)
}, [nodeInfo.expand, setCollapseState])
const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration
const isRetryNode = hasRetryNode(nodeInfo.node_type) && nodeInfo.retryDetail
const isAgentNode = nodeInfo.node_type === BlockEnum.Agent
const isToolNode = nodeInfo.node_type === BlockEnum.Tool
const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration && nodeInfo.details?.length
const isRetryNode = hasRetryNode(nodeInfo.node_type) && nodeInfo.retryDetail?.length
const isAgentNode = nodeInfo.node_type === BlockEnum.Agent && nodeInfo.agentLog?.length
const isToolNode = nodeInfo.node_type === BlockEnum.Tool && nodeInfo.agentLog?.length
return (
<div className={cn('px-2 py-1', className)}>

View File

@ -57,10 +57,10 @@ const ResultPanel: FC<ResultPanelProps> = ({
handleShowAgentOrToolLog,
}) => {
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
const isToolNode = nodeInfo?.node_type === BlockEnum.Tool
const isIterationNode = nodeInfo?.node_type === BlockEnum.Iteration && nodeInfo?.details?.length
const isRetryNode = hasRetryNode(nodeInfo?.node_type) && nodeInfo?.retryDetail?.length
const isAgentNode = nodeInfo?.node_type === BlockEnum.Agent && nodeInfo?.agentLog?.length
const isToolNode = nodeInfo?.node_type === BlockEnum.Tool && nodeInfo?.agentLog?.length
return (
<div className='bg-components-panel-bg py-2'>