diff --git a/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/audio-support-icon.svg b/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/audio-support-icon.svg new file mode 100644 index 0000000000..cad145c65f --- /dev/null +++ b/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/audio-support-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/document-support-icon.svg b/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/document-support-icon.svg new file mode 100644 index 0000000000..d7c09789fb --- /dev/null +++ b/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/document-support-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/video-support-icon.svg b/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/video-support-icon.svg new file mode 100644 index 0000000000..f87aa023b6 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/solid/mediaAndDevices/video-support-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/AudioSupportIcon.json b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/AudioSupportIcon.json new file mode 100644 index 0000000000..cd3006b76d --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/AudioSupportIcon.json @@ -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" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/AudioSupportIcon.tsx b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/AudioSupportIcon.tsx new file mode 100644 index 0000000000..c9477fc07f --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/AudioSupportIcon.tsx @@ -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, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'AudioSupportIcon' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/DocumentSupportIcon.json b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/DocumentSupportIcon.json new file mode 100644 index 0000000000..49cb6a521c --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/DocumentSupportIcon.json @@ -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" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/DocumentSupportIcon.tsx b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/DocumentSupportIcon.tsx new file mode 100644 index 0000000000..d848c5a156 --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/DocumentSupportIcon.tsx @@ -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, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'DocumentSupportIcon' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/VideoSupportIcon.json b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/VideoSupportIcon.json new file mode 100644 index 0000000000..4bc6881a5d --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/VideoSupportIcon.json @@ -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" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/VideoSupportIcon.tsx b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/VideoSupportIcon.tsx new file mode 100644 index 0000000000..6406af0746 --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/VideoSupportIcon.tsx @@ -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, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'VideoSupportIcon' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts index ab43a47ea1..7c313fecfb 100644 --- a/web/app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts +++ b/web/app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts @@ -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' diff --git a/web/app/components/base/switch/index.tsx b/web/app/components/base/switch/index.tsx index 8cda6e9390..3c3a4e6ca3 100644 --- a/web/app/components/base/switch/index.tsx +++ b/web/app/components/base/switch/index.tsx @@ -23,19 +23,19 @@ const Switch = React.forwardRef(({ 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 ( (({ onChange, size = 'md', def diff --git a/web/app/components/header/account-dropdown/workplace-selector/index.tsx b/web/app/components/header/account-dropdown/workplace-selector/index.tsx index 03e8ce2814..0d40cbdfc2 100644 --- a/web/app/components/header/account-dropdown/workplace-selector/index.tsx +++ b/web/app/components/header/account-dropdown/workplace-selector/index.tsx @@ -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 = () => { <>
{currentWorkspace?.name[0].toLocaleUpperCase()}
@@ -83,30 +68,18 @@ const WorkplaceSelector = () => { { workspaces.map(workspace => ( -
-
handleSwitchWorkspace(workspace.id)}> -
{workspace.name[0].toLocaleUpperCase()}
-
{workspace.name}
- { - -
- - {plan.type === 'professional' ? 'PRO' : plan.type.toUpperCase()} - -
-
- } -
- - {({ active }) =>
handleSwitchWorkspace(workspace.id)}> -
{workspace.name[0].toLocaleUpperCase()}
-
{workspace.name}
- {/* {workspace.current && } */} -
} - -
+
handleSwitchWorkspace(workspace.id)}> +
{workspace.name[0].toLocaleUpperCase()}
+
{workspace.name}
+ { + +
+ + {plan.type === 'professional' ? 'PRO' : plan.type.toUpperCase()} + +
+
+ }
)) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 47eb0ef0ba..eda380d2ae 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -22,14 +22,14 @@ const ModelIcon: FC = ({ }) => { const language = useLanguage() if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o')) - return
+ return
if (provider?.provider.includes('openai') && modelName?.startsWith('gpt-4')) - return
+ return
if (provider?.icon_small) { return ( -
+
model-icon = ({ return (
diff --git a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx index bf25c49256..a14a22bb35 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx @@ -37,7 +37,7 @@ const ModelName: FC = ({ if (!modelItem) return null return ( -
+
= ({ popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.vision })} >
- +
@@ -73,6 +76,48 @@ const FeatureIcon: FC = ({ ) } + if (feature === ModelFeatureEnum.document) { + return ( + +
+ + + +
+
+ ) + } + + if (feature === ModelFeatureEnum.audio) { + return ( + +
+ + + +
+
+ ) + } + + if (feature === ModelFeatureEnum.video) { + return ( + +
+ + + +
+
+ ) + } + return null } diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx index fa8abee9b0..d43f0a280d 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -34,41 +34,45 @@ const ModelTrigger: FC = ({ return (
- - - {!readonly && ( -
- { - model.status !== ModelStatusEnum.active - ? ( - - - - ) - : ( - - ) - } -
- )} +
+ +
+
+ + {!readonly && ( +
+ { + model.status !== ModelStatusEnum.active + ? ( + + + + ) + : ( + + ) + } +
+ )} +
) } diff --git a/web/app/components/plugins/card/index.tsx b/web/app/components/plugins/card/index.tsx index 09b4ca73cb..235f4d4953 100644 --- a/web/app/components/plugins/card/index.tsx +++ b/web/app/components/plugins/card/index.tsx @@ -65,7 +65,7 @@ const Card = ({ {/* Header */}
-
+
{verified && <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />} diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx index a5b96e4eea..c59a1799f3 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx @@ -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]) diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index 19432f4217..32c468cde8 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -49,6 +49,7 @@ export type Collection = { allow_delete: boolean labels: string[] plugin_id?: string + letter?: string } export type ToolParameter = { diff --git a/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx b/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx index 8e07eb5650..04622cabff 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx @@ -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> ) diff --git a/web/app/components/workflow/block-selector/tools.tsx b/web/app/components/workflow/block-selector/tools.tsx index d15a499dc3..060f6dfa2c 100644 --- a/web/app/components/workflow/block-selector/tools.tsx +++ b/web/app/components/workflow/block-selector/tools.tsx @@ -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} diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts index c02a74e3a4..787c3fbff1 100644 --- a/web/app/components/workflow/block-selector/types.ts +++ b/web/app/components/workflow/block-selector/types.ts @@ -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> } diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 402aefc364..454c84833b 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -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' /> } diff --git a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx index 7fc0362da1..e1d43e10e3 100644 --- a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx @@ -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'> diff --git a/web/app/components/workflow/nodes/agent/node.tsx b/web/app/components/workflow/nodes/agent/node.tsx index 948a9ef0d2..cef0a4330b 100644 --- a/web/app/components/workflow/nodes/agent/node.tsx +++ b/web/app/components/workflow/nodes/agent/node.tsx @@ -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 ? { diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx index 7ea501815b..af4ce6c863 100644 --- a/web/app/components/workflow/nodes/agent/panel.tsx +++ b/web/app/components/workflow/nodes/agent/panel.tsx @@ -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> } diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index fcd1bed5aa..a1f96e33a2 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -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, } } diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx index 4d27c9bb4c..9efd03df7a 100644 --- a/web/app/components/workflow/run/node.tsx +++ b/web/app/components/workflow/run/node.tsx @@ -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)}> diff --git a/web/app/components/workflow/run/result-panel.tsx b/web/app/components/workflow/run/result-panel.tsx index f977987785..a198b2ff6d 100644 --- a/web/app/components/workflow/run/result-panel.tsx +++ b/web/app/components/workflow/run/result-panel.tsx @@ -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'>