Merge branch 'feat/plugins' of https://github.com/langgenius/dify into feat/plugins

This commit is contained in:
Yi 2024-12-30 12:55:42 +08:00
commit f461f56886
11 changed files with 104 additions and 65 deletions

View File

@ -1,30 +1,40 @@
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import Indicator from '@/app/components/header/indicator' import Indicator from '@/app/components/header/indicator'
import classNames from '@/utils/classnames' import classNames from '@/utils/classnames'
import { useRef } from 'react' import { useMemo, useRef } from 'react'
import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools } from '@/service/use-tools'
export type ToolIconProps = { export type ToolIconProps = {
src: string
alt?: string
status?: 'error' | 'warning' status?: 'error' | 'warning'
tooltip?: string tooltip?: string
providerName: string
} }
export const ToolIcon = ({ src, status, tooltip, alt }: ToolIconProps) => { export const ToolIcon = ({ status, tooltip, providerName }: ToolIconProps) => {
const indicator = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined const indicator = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined
const containerRef = useRef<HTMLDivElement>(null) const containerRef = useRef<HTMLDivElement>(null)
const notSuccess = (['error', 'warning'] as Array<ToolIconProps['status']>).includes(status) const notSuccess = (['error', 'warning'] as Array<ToolIconProps['status']>).includes(status)
const { data: buildInTools } = useAllBuiltInTools()
const { data: customTools } = useAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools()
const currentProvider = useMemo(() => {
const mergedTools = [...(buildInTools || []), ...(customTools || []), ...(workflowTools || [])]
return mergedTools.find((toolWithProvider) => {
return toolWithProvider.name === providerName
})
}, [providerName, buildInTools, customTools, workflowTools])
return <Tooltip triggerMethod='hover' popupContent={tooltip} disabled={!notSuccess}> return <Tooltip triggerMethod='hover' popupContent={tooltip} disabled={!notSuccess}>
<div className={classNames( <div className={classNames(
'size-5 border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge relative', 'size-5 border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge relative flex items-center justify-center rounded-[6px]',
)} )}
ref={containerRef} ref={containerRef}
> >
{/* eslint-disable-next-line @next/next/no-img-element */}
<img <img
src={src} src={currentProvider?.icon as string}
alt={alt} alt='tool icon'
className={classNames( className={classNames(
'w-full h-full max-w-5 max-h-5 object-cover rounded-[6px]', 'w-full h-full size-3.5 object-cover',
notSuccess && 'opacity-50', notSuccess && 'opacity-50',
)} )}
/> />

View File

@ -8,13 +8,11 @@ import type { ToolIconProps } from './components/tool-icon'
import { ToolIcon } from './components/tool-icon' import { ToolIcon } from './components/tool-icon'
import useConfig from './use-config' import useConfig from './use-config'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useInstalledPluginList } from '@/service/use-plugins'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => { const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
const { inputs, currentStrategy } = useConfig(props.id, props.data) const { inputs, currentStrategy } = useConfig(props.id, props.data)
const { t } = useTranslation() const { t } = useTranslation()
const pluginList = useInstalledPluginList()
// TODO: Implement models // TODO: Implement models
const models = useMemo(() => { const models = useMemo(() => {
if (!inputs) return [] if (!inputs) return []
@ -41,17 +39,28 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
const tools = useMemo(() => { const tools = useMemo(() => {
const tools: Array<ToolIconProps> = [] const tools: Array<ToolIconProps> = []
currentStrategy?.parameters.forEach((param) => { currentStrategy?.parameters.forEach((param) => {
if (['array[tool]', 'tool'].includes(param.type)) { if (param.type === FormTypeEnum.toolSelector) {
const vari = inputs.agent_parameters?.[param.name] const field = param.name
if (!vari) return const value = inputs.agent_parameters?.[field]
if (Array.isArray(vari.value)) { if (value) {
// TODO: Implement array of tools tools.push({
providerName: value.provider_name,
})
} }
else { }
// TODO: Implement single tool if (param.type === FormTypeEnum.multiToolSelector) {
const field = param.name
const value = inputs.agent_parameters?.[field]
if (value) {
(value as any[]).forEach((item) => {
tools.push({
providerName: item.provider_name,
})
})
} }
} }
}) })
return tools
}, [currentStrategy, inputs.agent_parameters]) }, [currentStrategy, inputs.agent_parameters])
return <div className='mb-1 px-3 py-1 space-y-1'> return <div className='mb-1 px-3 py-1 space-y-1'>
{inputs.agent_strategy_name {inputs.agent_strategy_name
@ -89,19 +98,7 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
{t('workflow.nodes.agent.toolbox')} {t('workflow.nodes.agent.toolbox')}
</GroupLabel>}> </GroupLabel>}>
<div className='grid grid-cols-10 gap-0.5'> <div className='grid grid-cols-10 gap-0.5'>
<ToolIcon src='/logo/logo.png' /> {tools.map(tool => <ToolIcon {...tool} key={Math.random()} />)}
<ToolIcon
src='/logo/logo.png'
status='error'
tooltip={t('workflow.nodes.agent.toolNotInstallTooltip', {
tool: 'Gmail Sender',
})} />
<ToolIcon
src='/logo/logo.png'
status='warning'
tooltip={t('workflow.nodes.agent.toolNotAuthorizedTooltip', {
tool: 'DuckDuckGo AI Search',
})} />
</div> </div>
</Group> </Group>
</div> </div>

View File

@ -6,12 +6,15 @@ import {
PortalToFollowElemTrigger, PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem' } from '@/app/components/base/portal-to-follow-elem'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentLogNavMoreProps = { type AgentLogNavMoreProps = {
options: { id: string; label: string }[] options: { id: string; label: string }[]
onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
} }
const AgentLogNavMore = ({ const AgentLogNavMore = ({
options, options,
onShowAgentOrToolLog,
}: AgentLogNavMoreProps) => { }: AgentLogNavMoreProps) => {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
@ -40,6 +43,10 @@ const AgentLogNavMore = ({
<div <div
key={option.id} key={option.id}
className='flex items-center px-2 h-8 rounded-lg system-md-regular text-text-secondary hover:bg-state-base-hover cursor-pointer' className='flex items-center px-2 h-8 rounded-lg system-md-regular text-text-secondary hover:bg-state-base-hover cursor-pointer'
onClick={() => {
onShowAgentOrToolLog(option as AgentLogItemWithChildren)
setOpen(false)
}}
> >
{option.label} {option.label}
</div> </div>

View File

@ -1,15 +1,25 @@
import { RiArrowLeftLine } from '@remixicon/react' import { RiArrowLeftLine } from '@remixicon/react'
import AgentLogNavMore from './agent-log-nav-more' import AgentLogNavMore from './agent-log-nav-more'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentLogNavProps = {
agentOrToolLogItemStack: { id: string; label: string }[]
onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
}
const AgentLogNav = ({
agentOrToolLogItemStack,
onShowAgentOrToolLog,
}: AgentLogNavProps) => {
const options = agentOrToolLogItemStack.slice(2)
const AgentLogNav = () => {
return ( return (
<div className='flex items-center p-1 pr-3 h-8'> <div className='flex items-center p-1 pr-3 h-8'>
<Button <Button
className='shrink-0 px-[5px]' className='shrink-0 px-[5px]'
size='small' size='small'
variant='ghost-accent' variant='ghost-accent'
onClick={() => {}} onClick={() => onShowAgentOrToolLog()}
> >
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' /> <RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
Agent Agent
@ -24,10 +34,17 @@ const AgentLogNav = () => {
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' /> <RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
Agent strategy Agent strategy
</Button> </Button>
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div> {
<AgentLogNavMore !!options.length && (
options={[]} <>
/> <div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
<AgentLogNavMore
options={options}
onShowAgentOrToolLog={onShowAgentOrToolLog}
/>
</>
)
}
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div> <div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
<div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'> <div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'>
Run Actions Run Actions

View File

@ -6,7 +6,7 @@ import type {
type AgentLogTriggerProps = { type AgentLogTriggerProps = {
nodeInfo: NodeTracing nodeInfo: NodeTracing
onShowAgentOrToolLog: (detail: AgentLogItemWithChildren) => void onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
} }
const AgentLogTrigger = ({ const AgentLogTrigger = ({
nodeInfo, nodeInfo,

View File

@ -3,21 +3,24 @@ import AgentLogNav from './agent-log-nav'
import type { AgentLogItemWithChildren } from '@/types/workflow' import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentResultPanelProps = { type AgentResultPanelProps = {
agentOrToolLogIdStack: string[] agentOrToolLogItemStack: { id: string; label: string }[]
agentOrToolLogListMap: Record<string, AgentLogItemWithChildren[]> agentOrToolLogListMap: Record<string, AgentLogItemWithChildren[]>
onShowAgentOrToolLog: (detail: AgentLogItemWithChildren) => void onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void
} }
const AgentResultPanel = ({ const AgentResultPanel = ({
agentOrToolLogIdStack, agentOrToolLogItemStack,
agentOrToolLogListMap, agentOrToolLogListMap,
onShowAgentOrToolLog, onShowAgentOrToolLog,
}: AgentResultPanelProps) => { }: AgentResultPanelProps) => {
const top = agentOrToolLogIdStack[agentOrToolLogIdStack.length - 1] const top = agentOrToolLogItemStack[agentOrToolLogItemStack.length - 1]
const list = agentOrToolLogListMap[top] const list = agentOrToolLogListMap[top.id]
return ( return (
<div className='overflow-y-auto'> <div className='overflow-y-auto'>
<AgentLogNav /> <AgentLogNav
agentOrToolLogItemStack={agentOrToolLogItemStack}
onShowAgentOrToolLog={onShowAgentOrToolLog}
/>
{ {
<div className='p-2'> <div className='p-2'>
{ {

View File

@ -33,22 +33,27 @@ export const useLogs = () => {
setIterationResultDurationMap(iterDurationMap) setIterationResultDurationMap(iterDurationMap)
}, [setShowIteratingDetailTrue, setIterationResultList, setIterationResultDurationMap]) }, [setShowIteratingDetailTrue, setIterationResultList, setIterationResultDurationMap])
const [agentOrToolLogIdStack, setAgentOrToolLogIdStack] = useState<string[]>([]) const [agentOrToolLogItemStack, setAgentOrToolLogItemStack] = useState<{ id: string; label: string }[]>([])
const agentOrToolLogIdStackRef = useRef(agentOrToolLogIdStack) const agentOrToolLogItemStackRef = useRef(agentOrToolLogItemStack)
const [agentOrToolLogListMap, setAgentOrToolLogListMap] = useState<Record<string, AgentLogItemWithChildren[]>>({}) const [agentOrToolLogListMap, setAgentOrToolLogListMap] = useState<Record<string, AgentLogItemWithChildren[]>>({})
const agentOrToolLogListMapRef = useRef(agentOrToolLogListMap) const agentOrToolLogListMapRef = useRef(agentOrToolLogListMap)
const handleShowAgentOrToolLog = useCallback((detail: AgentLogItemWithChildren) => { const handleShowAgentOrToolLog = useCallback((detail?: AgentLogItemWithChildren) => {
const { id, children } = detail if (!detail) {
let currentAgentOrToolLogIdStack = agentOrToolLogIdStackRef.current.slice() setAgentOrToolLogItemStack([])
const index = currentAgentOrToolLogIdStack.findIndex(logId => logId === id) agentOrToolLogItemStackRef.current = []
return
}
const { id, label, children } = detail
let currentAgentOrToolLogItemStack = agentOrToolLogItemStackRef.current.slice()
const index = currentAgentOrToolLogItemStack.findIndex(logItem => logItem.id === id)
if (index > -1) if (index > -1)
currentAgentOrToolLogIdStack = currentAgentOrToolLogIdStack.slice(0, index + 1) currentAgentOrToolLogItemStack = currentAgentOrToolLogItemStack.slice(0, index + 1)
else else
currentAgentOrToolLogIdStack = [...currentAgentOrToolLogIdStack.slice(), id] currentAgentOrToolLogItemStack = [...currentAgentOrToolLogItemStack.slice(), { id, label }]
setAgentOrToolLogIdStack(currentAgentOrToolLogIdStack) setAgentOrToolLogItemStack(currentAgentOrToolLogItemStack)
agentOrToolLogIdStackRef.current = currentAgentOrToolLogIdStack agentOrToolLogItemStackRef.current = currentAgentOrToolLogItemStack
if (children) { if (children) {
setAgentOrToolLogListMap({ setAgentOrToolLogListMap({
@ -56,10 +61,10 @@ export const useLogs = () => {
[id]: children, [id]: children,
}) })
} }
}, [setAgentOrToolLogIdStack, setAgentOrToolLogListMap]) }, [setAgentOrToolLogItemStack, setAgentOrToolLogListMap])
return { return {
showSpecialResultPanel: showRetryDetail || showIteratingDetail || !!agentOrToolLogIdStack.length, showSpecialResultPanel: showRetryDetail || showIteratingDetail || !!agentOrToolLogItemStack.length,
showRetryDetail, showRetryDetail,
setShowRetryDetailTrue, setShowRetryDetailTrue,
setShowRetryDetailFalse, setShowRetryDetailFalse,
@ -76,7 +81,7 @@ export const useLogs = () => {
setIterationResultDurationMap, setIterationResultDurationMap,
handleShowIterationResultList, handleShowIterationResultList,
agentOrToolLogIdStack, agentOrToolLogItemStack,
agentOrToolLogListMap, agentOrToolLogListMap,
handleShowAgentOrToolLog, handleShowAgentOrToolLog,
} }

View File

@ -34,7 +34,7 @@ type Props = {
hideProcessDetail?: boolean hideProcessDetail?: boolean
onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void
onShowRetryDetail?: (detail: NodeTracing[]) => void onShowRetryDetail?: (detail: NodeTracing[]) => void
onShowAgentOrToolLog?: (detail: AgentLogItemWithChildren) => void onShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
notShowIterationNav?: boolean notShowIterationNav?: boolean
} }

View File

@ -34,7 +34,7 @@ type ResultPanelProps = {
execution_metadata?: any execution_metadata?: any
handleShowIterationResultList?: (detail: NodeTracing[][], iterDurationMap: any) => void handleShowIterationResultList?: (detail: NodeTracing[][], iterDurationMap: any) => void
onShowRetryDetail?: (detail: NodeTracing[]) => void onShowRetryDetail?: (detail: NodeTracing[]) => void
handleShowAgentOrToolLog?: (detail: AgentLogItemWithChildren) => void handleShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
} }
const ResultPanel: FC<ResultPanelProps> = ({ const ResultPanel: FC<ResultPanelProps> = ({

View File

@ -17,9 +17,9 @@ export type SpecialResultPanelProps = {
iterationResultList?: NodeTracing[][] iterationResultList?: NodeTracing[][]
iterationResultDurationMap?: IterationDurationMap iterationResultDurationMap?: IterationDurationMap
agentOrToolLogIdStack?: string[] agentOrToolLogItemStack?: { id: string; label: string }[]
agentOrToolLogListMap?: Record<string, AgentLogItemWithChildren[]> agentOrToolLogListMap?: Record<string, AgentLogItemWithChildren[]>
handleShowAgentOrToolLog?: (detail: AgentLogItemWithChildren) => void handleShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
} }
const SpecialResultPanel = ({ const SpecialResultPanel = ({
showRetryDetail, showRetryDetail,
@ -31,7 +31,7 @@ const SpecialResultPanel = ({
iterationResultList, iterationResultList,
iterationResultDurationMap, iterationResultDurationMap,
agentOrToolLogIdStack, agentOrToolLogItemStack,
agentOrToolLogListMap, agentOrToolLogListMap,
handleShowAgentOrToolLog, handleShowAgentOrToolLog,
}: SpecialResultPanelProps) => { }: SpecialResultPanelProps) => {
@ -55,9 +55,9 @@ const SpecialResultPanel = ({
) )
} }
{ {
!!agentOrToolLogIdStack?.length && agentOrToolLogListMap && handleShowAgentOrToolLog && ( !!agentOrToolLogItemStack?.length && agentOrToolLogListMap && handleShowAgentOrToolLog && (
<AgentResultPanel <AgentResultPanel
agentOrToolLogIdStack={agentOrToolLogIdStack} agentOrToolLogItemStack={agentOrToolLogItemStack}
agentOrToolLogListMap={agentOrToolLogListMap} agentOrToolLogListMap={agentOrToolLogListMap}
onShowAgentOrToolLog={handleShowAgentOrToolLog} onShowAgentOrToolLog={handleShowAgentOrToolLog}
/> />

View File

@ -79,7 +79,7 @@ const TracingPanel: FC<TracingPanelProps> = ({
iterationResultDurationMap, iterationResultDurationMap,
handleShowIterationResultList, handleShowIterationResultList,
agentOrToolLogIdStack, agentOrToolLogItemStack,
agentOrToolLogListMap, agentOrToolLogListMap,
handleShowAgentOrToolLog, handleShowAgentOrToolLog,
} = useLogs() } = useLogs()
@ -158,7 +158,7 @@ const TracingPanel: FC<TracingPanelProps> = ({
iterationResultList={iterationResultList} iterationResultList={iterationResultList}
iterationResultDurationMap={iterationResultDurationMap} iterationResultDurationMap={iterationResultDurationMap}
agentOrToolLogIdStack={agentOrToolLogIdStack} agentOrToolLogItemStack={agentOrToolLogItemStack}
agentOrToolLogListMap={agentOrToolLogListMap} agentOrToolLogListMap={agentOrToolLogListMap}
handleShowAgentOrToolLog={handleShowAgentOrToolLog} handleShowAgentOrToolLog={handleShowAgentOrToolLog}
/> />