mirror of
https://github.com/langgenius/dify.git
synced 2026-01-08 07:14:14 +00:00
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
@@ -4,7 +4,7 @@ const Title = ({
|
||||
title: string
|
||||
}) => {
|
||||
return (
|
||||
<div className='max-w-[150px] truncate text-text-secondary system-md-semibold'>
|
||||
<div className='truncate text-text-secondary system-md-semibold'>
|
||||
{title}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ import cn from '@/utils/classnames'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { getLanguage } from '@/i18n/language'
|
||||
import { useCategories } from '../hooks'
|
||||
import { renderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
export type Props = {
|
||||
className?: string
|
||||
@@ -47,7 +48,7 @@ const Card = ({
|
||||
const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent_strategy'].includes(type)
|
||||
const cornerMark = isBundle ? categoriesMap.bundle?.label : categoriesMap[category]?.label
|
||||
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
||||
obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
|
||||
obj ? renderI18nObject(obj, locale) : ''
|
||||
|
||||
const wrapClassName = cn('relative p-4 pb-3 border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg hover-bg-components-panel-on-panel-item-bg rounded-xl shadow-xs', className)
|
||||
if (isLoading) {
|
||||
|
||||
@@ -94,7 +94,7 @@ const PluginItem: FC<Props> = ({
|
||||
<div className="flex items-center h-5">
|
||||
<Title title={label[locale]} />
|
||||
{verified && <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />}
|
||||
<Badge className='ml-1' text={source === PluginSource.github ? plugin.meta!.version : plugin.version} />
|
||||
<Badge className='shrink-0 ml-1' text={source === PluginSource.github ? plugin.meta!.version : plugin.version} />
|
||||
</div>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Description text={description[locale]} descriptionLineRows={1}></Description>
|
||||
|
||||
@@ -6,6 +6,7 @@ import classNames from '@/utils/classnames'
|
||||
|
||||
export const CUSTOM_GROUP_NAME = '@@@custom@@@'
|
||||
export const WORKFLOW_GROUP_NAME = '@@@workflow@@@'
|
||||
export const AGENT_GROUP_NAME = '@@@agent@@@'
|
||||
/*
|
||||
{
|
||||
A: {
|
||||
@@ -46,8 +47,10 @@ export const groupItems = (items: ToolWithProvider[], getFirstChar: (item: ToolW
|
||||
groupName = item.author
|
||||
else if (item.type === CollectionType.custom)
|
||||
groupName = CUSTOM_GROUP_NAME
|
||||
else
|
||||
else if (item.type === CollectionType.workflow)
|
||||
groupName = WORKFLOW_GROUP_NAME
|
||||
else
|
||||
groupName = AGENT_GROUP_NAME
|
||||
|
||||
if (!acc[letter][groupName])
|
||||
acc[letter][groupName] = []
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { BlockEnum } from '../../../types'
|
||||
import type { ToolDefaultValue } from '../../types'
|
||||
import Item from './item'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { CUSTOM_GROUP_NAME, WORKFLOW_GROUP_NAME } from '../../index-bar'
|
||||
import { AGENT_GROUP_NAME, CUSTOM_GROUP_NAME, WORKFLOW_GROUP_NAME } from '../../index-bar'
|
||||
|
||||
type Props = {
|
||||
payload: Record<string, ToolWithProvider[]>
|
||||
@@ -27,6 +27,9 @@ const ToolListTreeView: FC<Props> = ({
|
||||
if (name === WORKFLOW_GROUP_NAME)
|
||||
return t('workflow.tabs.workflowTool')
|
||||
|
||||
if (name === AGENT_GROUP_NAME)
|
||||
return t('workflow.tabs.agent')
|
||||
|
||||
return name
|
||||
}, [t])
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ const Blocks = ({
|
||||
Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => {
|
||||
if (!result[groupName])
|
||||
result[groupName] = []
|
||||
|
||||
result[groupName].push(...withLetterAndGroupViewToolsData[letter][groupName])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,6 +24,7 @@ import { useNodesExtraData } from './use-nodes-data'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import type { AgentNodeType } from '../nodes/agent/types'
|
||||
|
||||
export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
const { t } = useTranslation()
|
||||
@@ -33,6 +34,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
const buildInTools = useStore(s => s.buildInTools)
|
||||
const customTools = useStore(s => s.customTools)
|
||||
const workflowTools = useStore(s => s.workflowTools)
|
||||
const agentStrategies = useStore(s => s.agentStrategies)
|
||||
|
||||
const needWarningNodes = useMemo(() => {
|
||||
const list = []
|
||||
@@ -57,6 +59,18 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
toolIcon = workflowTools.find(tool => tool.id === node.data.provider_id)?.icon
|
||||
}
|
||||
|
||||
if (node.data.type === BlockEnum.Agent) {
|
||||
const data = node.data as AgentNodeType
|
||||
const provider = agentStrategies.find(s => s.plugin_unique_identifier === data.plugin_unique_identifier)
|
||||
const strategy = provider?.declaration.strategies.find(s => s.identity.name === data.agent_strategy_name)
|
||||
// debugger
|
||||
moreDataForCheckValid = {
|
||||
provider,
|
||||
strategy,
|
||||
language,
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type === CUSTOM_NODE) {
|
||||
const { errorMessage } = nodesExtraData[node.data.type].checkValid(node.data, t, moreDataForCheckValid)
|
||||
|
||||
@@ -92,7 +106,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
}
|
||||
|
||||
return list
|
||||
}, [t, nodes, edges, nodesExtraData, buildInTools, customTools, workflowTools, language, isChatMode])
|
||||
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, agentStrategies])
|
||||
|
||||
return needWarningNodes
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import I18n from '@/context/i18n'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
|
||||
import { useWorkflowConfig } from '@/service/use-workflow'
|
||||
import { fetchStrategyList } from '@/service/strategy'
|
||||
|
||||
export const useIsChatMode = () => {
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
@@ -459,6 +460,21 @@ export const useFetchToolsData = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const useFetchAgentStrategy = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const handleFetchAllAgentStrategies = useCallback(async () => {
|
||||
const agentStrategies = await fetchStrategyList()
|
||||
|
||||
workflowStore.setState({
|
||||
agentStrategies: agentStrategies || [],
|
||||
})
|
||||
}, [workflowStore])
|
||||
|
||||
return {
|
||||
handleFetchAllAgentStrategies,
|
||||
}
|
||||
}
|
||||
|
||||
export const useWorkflowInit = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const {
|
||||
@@ -466,6 +482,7 @@ export const useWorkflowInit = () => {
|
||||
edges: edgesTemplate,
|
||||
} = useWorkflowTemplate()
|
||||
const { handleFetchAllTools } = useFetchToolsData()
|
||||
const { handleFetchAllAgentStrategies } = useFetchAgentStrategy()
|
||||
const appDetail = useAppStore(state => state.appDetail)!
|
||||
const setSyncWorkflowDraftHash = useStore(s => s.setSyncWorkflowDraftHash)
|
||||
const [data, setData] = useState<FetchWorkflowDraftResponse>()
|
||||
@@ -545,7 +562,8 @@ export const useWorkflowInit = () => {
|
||||
handleFetchAllTools('builtin')
|
||||
handleFetchAllTools('custom')
|
||||
handleFetchAllTools('workflow')
|
||||
}, [handleFetchPreloadData, handleFetchAllTools])
|
||||
handleFetchAllAgentStrategies()
|
||||
}, [handleFetchPreloadData, handleFetchAllTools, handleFetchAllAgentStrategies])
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { memo, useMemo, useState } from 'react'
|
||||
import type { Strategy } from './agent-strategy'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { RiArrowDownSLine, RiArrowRightUpLine, RiErrorWarningFill } from '@remixicon/react'
|
||||
@@ -38,7 +38,7 @@ const ExternalNotInstallWarn = () => {
|
||||
function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => string): ToolWithProvider[] {
|
||||
return input.map((item) => {
|
||||
const res: ToolWithProvider = {
|
||||
id: item.provider,
|
||||
id: item.plugin_unique_identifier,
|
||||
author: item.declaration.identity.author,
|
||||
name: item.declaration.identity.name,
|
||||
description: item.declaration.identity.description as any,
|
||||
@@ -69,7 +69,7 @@ export type AgentStrategySelectorProps = {
|
||||
onChange: (value?: Strategy) => void,
|
||||
}
|
||||
|
||||
export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
|
||||
export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => {
|
||||
const { value, onChange } = props
|
||||
const [open, setOpen] = useState(false)
|
||||
const [viewType, setViewType] = useState<ViewType>(ViewType.flat)
|
||||
@@ -126,6 +126,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
|
||||
agent_strategy_provider_name: tool!.provider_name,
|
||||
agent_strategy_label: tool!.tool_label,
|
||||
agent_output_schema: tool!.output_schema,
|
||||
plugin_unique_identifier: tool!.provider_id,
|
||||
})
|
||||
setOpen(false)
|
||||
}}
|
||||
@@ -147,4 +148,6 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
|
||||
</div> */}
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
}
|
||||
})
|
||||
|
||||
AgentStrategySelector.displayName = 'AgentStrategySelector'
|
||||
|
||||
@@ -12,16 +12,20 @@ import Slider from '@/app/components/base/slider'
|
||||
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
import Field from './field'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useDefaultModel, useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { type ComponentProps, memo } from 'react'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import Editor from './prompt/editor'
|
||||
import { useWorkflowStore } from '../../../store'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import type { NodeOutPutVar } from '../../../types'
|
||||
import type { Node } from 'reactflow'
|
||||
|
||||
export type Strategy = {
|
||||
agent_strategy_provider_name: string
|
||||
agent_strategy_name: string
|
||||
agent_strategy_label: string
|
||||
agent_output_schema: Record<string, any>
|
||||
plugin_unique_identifier: string
|
||||
}
|
||||
|
||||
export type AgentStrategyProps = {
|
||||
@@ -30,6 +34,8 @@ export type AgentStrategyProps = {
|
||||
formSchema: CredentialFormSchema[]
|
||||
formValue: ToolVarInputs
|
||||
onFormValueChange: (value: ToolVarInputs) => void
|
||||
nodeOutputVars?: NodeOutPutVar[],
|
||||
availableNodes?: Node[],
|
||||
}
|
||||
|
||||
type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field
|
||||
@@ -47,11 +53,11 @@ type StringSchema = CustomSchema<'string', {
|
||||
|
||||
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema | StringSchema
|
||||
|
||||
export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props
|
||||
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes } = props
|
||||
const { t } = useTranslation()
|
||||
const language = useLanguage()
|
||||
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const {
|
||||
setControlPromptEditorRerenderKey,
|
||||
@@ -70,7 +76,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
const onChange = (value: number) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
}
|
||||
return <Field title={def.label[language]} tooltip={def.tooltip?.[language]} inline>
|
||||
return <Field title={renderI18nObject(def.label)} tooltip={def.tooltip && renderI18nObject(def.tooltip)} inline>
|
||||
<div className='flex w-[200px] items-center gap-3'>
|
||||
<Slider
|
||||
value={value}
|
||||
@@ -103,7 +109,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
props.onChange({ ...props.value, [schema.variable]: value })
|
||||
}
|
||||
return (
|
||||
<Field title={schema.label[language]} tooltip={schema.tooltip?.[language]}>
|
||||
<Field title={renderI18nObject(schema.label)} tooltip={schema.tooltip && renderI18nObject(schema.tooltip)}>
|
||||
<ToolSelector
|
||||
scope={schema.scope}
|
||||
value={value}
|
||||
@@ -122,8 +128,8 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
<MultipleToolSelector
|
||||
scope={schema.scope}
|
||||
value={value || []}
|
||||
label={schema.label[language]}
|
||||
tooltip={schema.tooltip?.[language]}
|
||||
label={renderI18nObject(schema.label)}
|
||||
tooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
|
||||
onChange={onChange}
|
||||
supportCollapse
|
||||
/>
|
||||
@@ -142,13 +148,15 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onGenerated={handleGenerated}
|
||||
title={schema.label[language]}
|
||||
title={renderI18nObject(schema.label)}
|
||||
headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase'
|
||||
containerClassName='bg-transparent'
|
||||
gradientBorder={false}
|
||||
isSupportPromptGenerator={!!schema.auto_generate?.type}
|
||||
titleTooltip={schema.tooltip?.[language]}
|
||||
titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
|
||||
editorContainerClassName='px-0'
|
||||
availableNodes={availableNodes}
|
||||
nodesOutputVars={nodeOutputVars}
|
||||
isSupportJinja={schema.template?.enabled}
|
||||
varList={[]}
|
||||
modelConfig={
|
||||
@@ -197,4 +205,6 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
})
|
||||
|
||||
AgentStrategy.displayName = 'AgentStrategy'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import classNames from '@/utils/classnames'
|
||||
import type { ComponentProps, PropsWithChildren, ReactNode } from 'react'
|
||||
import { type ComponentProps, type PropsWithChildren, type ReactNode, memo } from 'react'
|
||||
|
||||
export type SettingItemProps = PropsWithChildren<{
|
||||
label: string
|
||||
@@ -9,7 +9,7 @@ export type SettingItemProps = PropsWithChildren<{
|
||||
tooltip?: ReactNode
|
||||
}>
|
||||
|
||||
export const SettingItem = ({ label, children, status, tooltip }: SettingItemProps) => {
|
||||
export const SettingItem = memo(({ label, children, status, tooltip }: SettingItemProps) => {
|
||||
const indicator: ComponentProps<typeof Indicator>['color'] = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined
|
||||
const needTooltip = ['error', 'warning'].includes(status as any)
|
||||
return <div className='flex items-center justify-between bg-workflow-block-parma-bg rounded-md py-1 px-1.5 space-x-1 text-xs font-normal relative'>
|
||||
@@ -23,4 +23,6 @@ export const SettingItem = ({ label, children, status, tooltip }: SettingItemPro
|
||||
</Tooltip>
|
||||
{indicator && <Indicator color={indicator} className='absolute -right-0.5 -top-0.5' />}
|
||||
</div>
|
||||
}
|
||||
})
|
||||
|
||||
SettingItem.displayName = 'SettingItem'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { memo, useMemo, useRef } from 'react'
|
||||
import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools } from '@/service/use-tools'
|
||||
|
||||
export type ToolIconProps = {
|
||||
@@ -10,7 +10,7 @@ export type ToolIconProps = {
|
||||
providerName: string
|
||||
}
|
||||
|
||||
export const ToolIcon = ({ status, tooltip, providerName }: ToolIconProps) => {
|
||||
export const ToolIcon = memo(({ status, tooltip, providerName }: ToolIconProps) => {
|
||||
const indicator = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const notSuccess = (['error', 'warning'] as Array<ToolIconProps['status']>).includes(status)
|
||||
@@ -41,4 +41,6 @@ export const ToolIcon = ({ status, tooltip, providerName }: ToolIconProps) => {
|
||||
{indicator && <Indicator color={indicator} className="absolute right-[-1px] top-[-1px]" />}
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
})
|
||||
|
||||
ToolIcon.displayName = 'ToolIcon'
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plugins/types'
|
||||
import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '../../constants'
|
||||
import type { NodeDefault } from '../../types'
|
||||
import type { AgentNodeType } from './types'
|
||||
import { renderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||
defaultValue: {
|
||||
@@ -15,16 +17,29 @@ const nodeDefault: NodeDefault<AgentNodeType> = {
|
||||
? ALL_CHAT_AVAILABLE_BLOCKS
|
||||
: ALL_COMPLETION_AVAILABLE_BLOCKS
|
||||
},
|
||||
checkValid(payload, t, moreDataForCheckValid) {
|
||||
let isValid = true
|
||||
let errorMessages = ''
|
||||
if (payload.type) {
|
||||
isValid = true
|
||||
errorMessages = ''
|
||||
checkValid(payload, t, moreDataForCheckValid: {
|
||||
strategyProvider: StrategyPluginDetail | undefined,
|
||||
strategy: StrategyDetail | undefined
|
||||
language: string
|
||||
}) {
|
||||
const { strategy, language } = moreDataForCheckValid
|
||||
if (!strategy) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.checkList.strategyNotSelected'),
|
||||
}
|
||||
}
|
||||
for (const param of strategy.parameters) {
|
||||
if (param.required && !payload.agent_parameters?.[param.name]?.value) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
isValid,
|
||||
errorMessage: errorMessages,
|
||||
isValid: true,
|
||||
errorMessage: '',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC, useMemo } from 'react'
|
||||
import { type FC, memo, useMemo } from 'react'
|
||||
import type { NodeProps } from '../../types'
|
||||
import type { AgentNodeType } from './types'
|
||||
import { SettingItem } from '../_base/components/setting-item'
|
||||
@@ -10,6 +10,7 @@ import useConfig from './use-config'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
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'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
const useAllModel = () => {
|
||||
const { data: textGeneration } = useModelList(ModelTypeEnum.textGeneration)
|
||||
@@ -32,7 +33,8 @@ const useAllModel = () => {
|
||||
}
|
||||
|
||||
const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||
const { inputs, currentStrategy } = useConfig(props.id, props.data)
|
||||
const { inputs, currentStrategy, currentStrategyStatus, pluginDetail } = useConfig(props.id, props.data)
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const { t } = useTranslation()
|
||||
const modelList = useAllModel()
|
||||
const models = useMemo(() => {
|
||||
@@ -87,9 +89,16 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||
{inputs.agent_strategy_name
|
||||
? <SettingItem
|
||||
label={t('workflow.nodes.agent.strategy.shortLabel')}
|
||||
status='error'
|
||||
tooltip={t('workflow.nodes.agent.strategyNotInstallTooltip', {
|
||||
status={
|
||||
['plugin-not-found', 'strategy-not-found'].includes(currentStrategyStatus)
|
||||
? 'error'
|
||||
: undefined
|
||||
}
|
||||
tooltip={t(`workflow.nodes.agent.${currentStrategyStatus === 'plugin-not-found' ? 'strategyNotInstallTooltip' : 'strategyNotFoundInPlugin'}`, {
|
||||
strategy: inputs.agent_strategy_label,
|
||||
plugin: pluginDetail?.declaration.label
|
||||
? renderI18nObject(pluginDetail?.declaration.label)
|
||||
: undefined,
|
||||
})}
|
||||
>
|
||||
{inputs.agent_strategy_label}
|
||||
@@ -126,4 +135,6 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
export default AgentNode
|
||||
AgentNode.displayName = 'AgentNode'
|
||||
|
||||
export default memo(AgentNode)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { memo, useMemo } from 'react'
|
||||
import type { NodePanelProps } from '../../types'
|
||||
import type { AgentNodeType } from './types'
|
||||
import Field from '../_base/components/field'
|
||||
@@ -33,6 +33,9 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
||||
formData,
|
||||
onFormChange,
|
||||
|
||||
availableNodesWithParent,
|
||||
availableVars,
|
||||
|
||||
isShowSingleRun,
|
||||
hideSingleRun,
|
||||
runningStatus,
|
||||
@@ -75,6 +78,7 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
||||
agent_strategy_name: inputs.agent_strategy_name!,
|
||||
agent_strategy_label: inputs.agent_strategy_label!,
|
||||
agent_output_schema: inputs.output_schema,
|
||||
plugin_unique_identifier: inputs.plugin_unique_identifier!,
|
||||
} : undefined}
|
||||
onStrategyChange={(strategy) => {
|
||||
setInputs({
|
||||
@@ -83,11 +87,14 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
||||
agent_strategy_name: strategy?.agent_strategy_name,
|
||||
agent_strategy_label: strategy?.agent_strategy_label,
|
||||
output_schema: strategy!.agent_output_schema,
|
||||
plugin_unique_identifier: strategy!.plugin_unique_identifier,
|
||||
})
|
||||
}}
|
||||
formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []}
|
||||
formValue={formData}
|
||||
onFormValueChange={onFormChange}
|
||||
nodeOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
/>
|
||||
</Field>
|
||||
<div>
|
||||
@@ -135,4 +142,6 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
export default AgentPanel
|
||||
AgentPanel.displayName = 'AgentPanel'
|
||||
|
||||
export default memo(AgentPanel)
|
||||
|
||||
@@ -7,4 +7,5 @@ export type AgentNodeType = CommonNodeType & {
|
||||
agent_strategy_label?: string
|
||||
agent_parameters?: ToolVarInputs
|
||||
output_schema: Record<string, any>
|
||||
plugin_unique_identifier?: string
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@ import type { AgentNodeType } from './types'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
import { useMemo } from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { type ToolVarInputs, VarType } from '../tool/types'
|
||||
import { useCheckInstalled } from '@/service/use-plugins'
|
||||
import type { Var } from '../../types'
|
||||
import { VarType as VarKindType } from '../../types'
|
||||
import useAvailableVarList from '../_base/hooks/use-available-var-list'
|
||||
|
||||
const useConfig = (id: string, payload: AgentNodeType) => {
|
||||
const { nodesReadOnly: readOnly } = useNodesReadOnly()
|
||||
@@ -20,16 +24,20 @@ const useConfig = (id: string, payload: AgentNodeType) => {
|
||||
const strategyProvider = useStrategyProviderDetail(
|
||||
inputs.agent_strategy_provider_name || '',
|
||||
)
|
||||
|
||||
const currentStrategy = strategyProvider.data?.declaration.strategies.find(
|
||||
str => str.identity.name === inputs.agent_strategy_name,
|
||||
)
|
||||
const currentStrategyStatus = useMemo(() => {
|
||||
const currentStrategyStatus: 'loading' | 'plugin-not-found' | 'strategy-not-found' | 'success' = useMemo(() => {
|
||||
if (strategyProvider.isLoading) return 'loading'
|
||||
if (strategyProvider.isError) return 'plugin-not-found'
|
||||
if (!currentStrategy) return 'strategy-not-found'
|
||||
return 'success'
|
||||
}, [currentStrategy, strategyProvider])
|
||||
const pluginId = inputs.agent_strategy_provider_name?.split('/').splice(0, 2).join('/')
|
||||
const pluginDetail = useCheckInstalled({
|
||||
pluginIds: [pluginId || ''],
|
||||
enabled: Boolean(pluginId),
|
||||
})
|
||||
const formData = useMemo(() => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(inputs.agent_parameters || {}).map(([key, value]) => {
|
||||
@@ -51,6 +59,30 @@ const useConfig = (id: string, payload: AgentNodeType) => {
|
||||
})
|
||||
}
|
||||
|
||||
// vars
|
||||
|
||||
const filterMemoryPromptVar = useCallback((varPayload: Var) => {
|
||||
return [
|
||||
VarKindType.arrayObject,
|
||||
VarKindType.array,
|
||||
VarKindType.number,
|
||||
VarKindType.string,
|
||||
VarKindType.secret,
|
||||
VarKindType.arrayString,
|
||||
VarKindType.arrayNumber,
|
||||
VarKindType.file,
|
||||
VarKindType.arrayFile,
|
||||
].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const {
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
} = useAvailableVarList(id, {
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar: filterMemoryPromptVar,
|
||||
})
|
||||
|
||||
// single run
|
||||
const {
|
||||
isShowSingleRun,
|
||||
@@ -70,7 +102,9 @@ const useConfig = (id: string, payload: AgentNodeType) => {
|
||||
defaultRunInputData: {},
|
||||
})
|
||||
const allVarStrArr = (() => {
|
||||
const arr = ['']
|
||||
const arr = currentStrategy?.parameters.filter(item => item.type === 'string').map((item) => {
|
||||
return formData[item.name]
|
||||
}) || []
|
||||
|
||||
return arr
|
||||
})()
|
||||
@@ -91,6 +125,9 @@ const useConfig = (id: string, payload: AgentNodeType) => {
|
||||
onFormChange,
|
||||
currentStrategyStatus,
|
||||
strategyProvider: strategyProvider.data,
|
||||
pluginDetail: pluginDetail.data?.plugins.at(0),
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
|
||||
isShowSingleRun,
|
||||
showSingleRun,
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { AgentNodeType } from './types'
|
||||
|
||||
export const checkNodeValid = (payload: AgentNodeType) => {
|
||||
return true
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { has } from 'immer/dist/internal'
|
||||
|
||||
export const agentNodeData = (() => {
|
||||
const node = {
|
||||
@@ -120,7 +119,6 @@ export const oneStepCircle = (() => {
|
||||
],
|
||||
}],
|
||||
}
|
||||
|
||||
})()
|
||||
|
||||
export const multiStepsCircle = (() => {
|
||||
@@ -138,13 +136,13 @@ export const multiStepsCircle = (() => {
|
||||
{ id: '1', parent_id: '4', label: 'Node 1' },
|
||||
{ id: '2', parent_id: '1', label: 'Node 2' },
|
||||
{ id: '4', parent_id: '2', label: 'Node 4' },
|
||||
// { id: '1', parent_id: '4', label: 'Node 1' },
|
||||
// { id: '2', parent_id: '1', label: 'Node 2' },
|
||||
// { id: '4', parent_id: '2', label: 'Node 4' },
|
||||
{ id: '1', parent_id: '4', label: 'Node 1' },
|
||||
{ id: '2', parent_id: '1', label: 'Node 2' },
|
||||
{ id: '4', parent_id: '2', label: 'Node 4' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// 1 -> [2(4(1(2(4...)))), 3]
|
||||
return {
|
||||
in: [node],
|
||||
expect: [{
|
||||
@@ -165,7 +163,7 @@ export const multiStepsCircle = (() => {
|
||||
label: 'Node 4',
|
||||
children: [],
|
||||
hasCircle: true,
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,24 +5,26 @@ import { cloneDeep } from 'lodash-es'
|
||||
const supportedAgentLogNodes = [BlockEnum.Agent, BlockEnum.Tool]
|
||||
|
||||
const remove = (node: AgentLogItemWithChildren, removeId: string) => {
|
||||
const { children } = node
|
||||
if (!children || children.length === 0) {
|
||||
let { children } = node
|
||||
if (!children || children.length === 0)
|
||||
return
|
||||
|
||||
const hasCircle = !!children.find(c => c.id === removeId)
|
||||
if (hasCircle) {
|
||||
node.hasCircle = true
|
||||
node.children = node.children.filter(c => c.id !== removeId)
|
||||
children = node.children
|
||||
}
|
||||
children.forEach((child, index) => {
|
||||
if (child.id === removeId) {
|
||||
node.hasCircle = true
|
||||
children.splice(index, 1)
|
||||
return
|
||||
}
|
||||
|
||||
children.forEach((child) => {
|
||||
remove(child, removeId)
|
||||
})
|
||||
}
|
||||
|
||||
const removeRepeatedSiblings = (list: AgentLogItemWithChildren[]) => {
|
||||
if (!list || list.length === 0) {
|
||||
if (!list || list.length === 0)
|
||||
return []
|
||||
}
|
||||
|
||||
const result: AgentLogItemWithChildren[] = []
|
||||
const addedItemIds: string[] = []
|
||||
list.forEach((item) => {
|
||||
@@ -35,19 +37,18 @@ const removeRepeatedSiblings = (list: AgentLogItemWithChildren[]) => {
|
||||
}
|
||||
|
||||
const removeCircleLogItem = (log: AgentLogItemWithChildren) => {
|
||||
let newLog = cloneDeep(log)
|
||||
const newLog = cloneDeep(log)
|
||||
newLog.children = removeRepeatedSiblings(newLog.children)
|
||||
let { id, children } = newLog
|
||||
if (!children || children.length === 0) {
|
||||
if (!children || children.length === 0)
|
||||
return log
|
||||
}
|
||||
|
||||
// check one step circle
|
||||
const hasOneStepCircle = !!children.find(c => c.id === id)
|
||||
if (hasOneStepCircle) {
|
||||
newLog.hasCircle = true
|
||||
newLog.children = newLog.children.filter(c => c.id !== id)
|
||||
children = newLog.children
|
||||
|
||||
}
|
||||
|
||||
children.forEach((child, index) => {
|
||||
@@ -85,6 +86,7 @@ const format = (list: NodeTracing[]): NodeTracing[] => {
|
||||
let removedCircleTree: AgentLogItemWithChildren[] = []
|
||||
if (supportedAgentLogNodes.includes(item.node_type) && item.execution_metadata?.agent_log && item.execution_metadata?.agent_log.length > 0)
|
||||
treeList = listToTree(item.execution_metadata.agent_log)
|
||||
// console.log(JSON.stringify(treeList))
|
||||
removedCircleTree = treeList.length > 0 ? treeList.map(t => removeCircleLogItem(t)) : []
|
||||
item.agentLog = removedCircleTree
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ import type {
|
||||
} from './types'
|
||||
import { WorkflowContext } from './context'
|
||||
import type { NodeTracing, VersionHistory } from '@/types/workflow'
|
||||
import type {
|
||||
StrategyPluginDetail,
|
||||
} from '@/app/components/plugins/types'
|
||||
|
||||
// #TODO chatVar#
|
||||
// const MOCK_DATA = [
|
||||
@@ -98,6 +101,7 @@ type Shape = {
|
||||
setCustomTools: (tools: ToolWithProvider[]) => void
|
||||
workflowTools: ToolWithProvider[]
|
||||
setWorkflowTools: (tools: ToolWithProvider[]) => void
|
||||
agentStrategies: StrategyPluginDetail[],
|
||||
clipboardElements: Node[]
|
||||
setClipboardElements: (clipboardElements: Node[]) => void
|
||||
showDebugAndPreviewPanel: boolean
|
||||
@@ -230,6 +234,7 @@ export const createWorkflowStore = () => {
|
||||
setCustomTools: customTools => set(() => ({ customTools })),
|
||||
workflowTools: [],
|
||||
setWorkflowTools: workflowTools => set(() => ({ workflowTools })),
|
||||
agentStrategies: [],
|
||||
clipboardElements: [],
|
||||
setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
|
||||
showDebugAndPreviewPanel: false,
|
||||
|
||||
@@ -395,6 +395,7 @@ export const canRunBySingle = (nodeType: BlockEnum) => {
|
||||
|| nodeType === BlockEnum.Tool
|
||||
|| nodeType === BlockEnum.ParameterExtractor
|
||||
|| nodeType === BlockEnum.Iteration
|
||||
|| nodeType === BlockEnum.Agent
|
||||
}
|
||||
|
||||
type ConnectedSourceOrTargetNodesChange = {
|
||||
|
||||
14
web/hooks/use-i18n.ts
Normal file
14
web/hooks/use-i18n.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
export const renderI18nObject = (obj: Record<string, string>, language: string) => {
|
||||
if (obj?.[language]) return obj[language]
|
||||
if (obj?.en_US) return obj.en_US
|
||||
return Object.values(obj)[0]
|
||||
}
|
||||
|
||||
export const useRenderI18nObject = () => {
|
||||
const language = useLanguage()
|
||||
return (obj: Record<string, string>) => {
|
||||
return renderI18nObject(obj, language)
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,7 @@ const translation = {
|
||||
'transform': 'Transform',
|
||||
'utilities': 'Utilities',
|
||||
'noResult': 'No match found',
|
||||
'agent': 'Agent Strategy',
|
||||
},
|
||||
blocks: {
|
||||
'start': 'Start',
|
||||
@@ -732,6 +733,7 @@ const translation = {
|
||||
toolNotInstallTooltip: '{{tool}} is not installed',
|
||||
toolNotAuthorizedTooltip: '{{tool}} Not Authorized',
|
||||
strategyNotInstallTooltip: '{{strategy}} is not installed',
|
||||
strategyNotFoundInPlugin: '{{strategy}} is not found in {{plugin}}',
|
||||
modelSelectorTooltips: {
|
||||
deprecated: 'This model is deprecated',
|
||||
},
|
||||
@@ -747,6 +749,9 @@ const translation = {
|
||||
json: 'agent generated json',
|
||||
},
|
||||
},
|
||||
checkList: {
|
||||
strategyNotSelected: 'Strategy not selected',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
stopBy: 'Stop by {{user}}',
|
||||
|
||||
@@ -218,6 +218,7 @@ const translation = {
|
||||
'transform': '转换',
|
||||
'utilities': '工具',
|
||||
'noResult': '未找到匹配项',
|
||||
'agent': 'Agent 策略',
|
||||
},
|
||||
blocks: {
|
||||
'start': '开始',
|
||||
@@ -732,6 +733,7 @@ const translation = {
|
||||
toolNotInstallTooltip: '{{tool}} 未安装',
|
||||
toolNotAuthorizedTooltip: '{{tool}} 未授权',
|
||||
strategyNotInstallTooltip: '{{strategy}} 未安装',
|
||||
strategyNotFoundInPlugin: '在 {{plugin}} 中未找到 {{strategy}}',
|
||||
modelSelectorTooltips: {
|
||||
deprecated: '此模型已弃用',
|
||||
},
|
||||
@@ -746,6 +748,9 @@ const translation = {
|
||||
},
|
||||
json: 'agent 生成的json',
|
||||
},
|
||||
checkList: {
|
||||
strategyNotSelected: '未选择策略',
|
||||
},
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
||||
10
web/service/strategy.ts
Normal file
10
web/service/strategy.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { StrategyPluginDetail } from '@/app/components/plugins/types'
|
||||
import { get } from './base'
|
||||
|
||||
export const fetchStrategyList = () => {
|
||||
return get<StrategyPluginDetail[]>('/workspaces/current/agent-providers')
|
||||
}
|
||||
|
||||
export const fetchStrategyDetail = (agentProvider: string) => {
|
||||
return get<StrategyPluginDetail>(`/workspaces/current/agent-provider/${agentProvider}`)
|
||||
}
|
||||
@@ -300,7 +300,7 @@ export const useMutationPluginsFromMarketplace = () => {
|
||||
exclude,
|
||||
type,
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
pageSize = 40,
|
||||
} = pluginsSearchParams
|
||||
return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', {
|
||||
body: {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { get } from './base'
|
||||
import type {
|
||||
StrategyPluginDetail,
|
||||
} from '@/app/components/plugins/types'
|
||||
@@ -6,6 +5,7 @@ import { useInvalid } from './use-base'
|
||||
import {
|
||||
useQuery,
|
||||
} from '@tanstack/react-query'
|
||||
import { fetchStrategyDetail, fetchStrategyList } from './strategy'
|
||||
|
||||
const NAME_SPACE = 'agent_strategy'
|
||||
|
||||
@@ -13,7 +13,7 @@ const useStrategyListKey = [NAME_SPACE, 'strategyList']
|
||||
export const useStrategyProviders = () => {
|
||||
return useQuery<StrategyPluginDetail[]>({
|
||||
queryKey: useStrategyListKey,
|
||||
queryFn: () => get<StrategyPluginDetail[]>('/workspaces/current/agent-providers'),
|
||||
queryFn: fetchStrategyList,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useInvalidateStrategyProviders = () => {
|
||||
export const useStrategyProviderDetail = (agentProvider: string) => {
|
||||
return useQuery<StrategyPluginDetail>({
|
||||
queryKey: [NAME_SPACE, 'detail', agentProvider],
|
||||
queryFn: () => get<StrategyPluginDetail>(`/workspaces/current/agent-provider/${agentProvider}`),
|
||||
queryFn: () => fetchStrategyDetail(agentProvider),
|
||||
enabled: !!agentProvider,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user