mirror of
https://github.com/langgenius/dify.git
synced 2026-01-06 06:26:00 +00:00
feat: agent node checklist
This commit is contained in:
@@ -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,17 @@ 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,
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type === CUSTOM_NODE) {
|
||||
const { errorMessage } = nodesExtraData[node.data.type].checkValid(node.data, t, moreDataForCheckValid)
|
||||
|
||||
@@ -92,7 +105,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,7 +12,7 @@ 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 { type ComponentProps, memo } from 'react'
|
||||
import { useDefaultModel, useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import Editor from './prompt/editor'
|
||||
import { useWorkflowStore } from '../../../store'
|
||||
@@ -22,6 +22,7 @@ export type Strategy = {
|
||||
agent_strategy_name: string
|
||||
agent_strategy_label: string
|
||||
agent_output_schema: Record<string, any>
|
||||
plugin_unique_identifier: string
|
||||
}
|
||||
|
||||
export type AgentStrategyProps = {
|
||||
@@ -47,7 +48,7 @@ type StringSchema = CustomSchema<'string', {
|
||||
|
||||
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema | StringSchema
|
||||
|
||||
export const AgentStrategy = (props: AgentStrategyProps) => {
|
||||
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props
|
||||
const { t } = useTranslation()
|
||||
const language = useLanguage()
|
||||
@@ -197,4 +198,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,3 +1,4 @@
|
||||
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'
|
||||
@@ -15,16 +16,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
|
||||
}) {
|
||||
const { strategy } = moreDataForCheckValid
|
||||
if (!strategy) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: 'Please select a strategy',
|
||||
}
|
||||
}
|
||||
for (const param of strategy.parameters) {
|
||||
if (param.required && !payload.agent_parameters?.[param.name]?.value) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: `Please select ${param.name}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: tool selector valid?
|
||||
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'
|
||||
@@ -126,4 +126,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'
|
||||
@@ -75,6 +75,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,6 +84,7 @@ 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) || []}
|
||||
@@ -135,4 +137,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
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { AgentNodeType } from './types'
|
||||
|
||||
export const checkNodeValid = (payload: AgentNodeType) => {
|
||||
return true
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
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}`)
|
||||
}
|
||||
@@ -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