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

This commit is contained in:
Yi 2024-12-25 14:54:22 +08:00
commit f42cfe8075
12 changed files with 82 additions and 19 deletions

View File

@ -1,3 +1,4 @@
import type { ReactNode } from 'react'
import React from 'react'
import { Variable02 } from '../icons/src/vender/solid/development'
import VerticalLine from './vertical-line'
@ -5,19 +6,21 @@ import HorizontalLine from './horizontal-line'
type ListEmptyProps = {
title?: string
description?: React.ReactNode
description?: ReactNode
icon?: ReactNode
}
const ListEmpty = ({
title,
description,
icon,
}: ListEmptyProps) => {
return (
<div className='flex w-[320px] p-4 flex-col items-start gap-2 rounded-[10px] bg-workflow-process-bg'>
<div className='flex w-10 h-10 justify-center items-center gap-2 rounded-[10px]'>
<div className='flex relative p-1 justify-center items-center gap-2 grow self-stretch rounded-[10px]
border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg'>
<Variable02 className='w-5 h-5 shrink-0 text-text-accent' />
{icon || <Variable02 className='w-5 h-5 shrink-0 text-text-accent' />}
<VerticalLine className='absolute -right-[1px] top-1/2 -translate-y-1/4'/>
<VerticalLine className='absolute -left-[1px] top-1/2 -translate-y-1/4'/>
<HorizontalLine className='absolute top-0 left-3/4 -translate-x-1/4 -translate-y-1/2'/>

View File

@ -2,6 +2,7 @@ import type { FC } from 'react'
import { memo } from 'react'
import { BlockEnum } from './types'
import {
Agent,
Answer,
Assigner,
Code,
@ -53,8 +54,7 @@ const getIcon = (type: BlockEnum, className: string) => {
[BlockEnum.ParameterExtractor]: <ParameterExtractor className={className} />,
[BlockEnum.DocExtractor]: <DocsExtractor className={className} />,
[BlockEnum.ListFilter]: <ListFilter className={className} />,
// TODO: add icon for Agent
[BlockEnum.Agent]: <VariableX className={className} />,
[BlockEnum.Agent]: <Agent className={className} />,
}[type]
}
const ICON_CONTAINER_BG_COLOR_MAP: Record<string, string> = {

View File

@ -48,6 +48,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
}, [query, list.data])
// TODO: should be replaced by real data
const isExternalInstalled = true
const { t } = useTranslation()
return <PortalToFollowElem open={open} onOpenChange={setOpen} placement='bottom'>
<PortalToFollowElemTrigger className='w-full'>
<div className='py-2 pl-3 pr-2 flex items-center rounded-lg bg-components-input-bg-normal w-full hover:bg-state-base-hover-alt select-none' onClick={() => setOpen(o => !o)}>
@ -64,7 +65,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => {
<p
className={classNames(value ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder', 'text-xs px-1')}
>
{value?.agent_strategy_name || 'Select agentic strategy'}
{value?.agent_strategy_name || t('workflow.nodes.agent.strategy.selectTip')}
</p>
{value && <div className='ml-auto flex items-center gap-1'>
<InstallPluginButton onClick={e => e.stopPropagation()} size={'small'} />

View File

@ -5,6 +5,7 @@ import { AgentStrategySelector } from './agent-strategy-selector'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { Agent } from '@/app/components/base/icons/src/vender/workflow'
export type Strategy = {
agent_strategy_provider_name: string
@ -39,8 +40,8 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
fieldLabelClassName='uppercase'
/>
</div>
// TODO: list empty need a icon
: <ListEmpty
icon={<Agent className='w-5 h-5 shrink-0 text-text-accent' />}
title={t('workflow.nodes.agent.strategy.configureTip')}
description={<div className='text-text-tertiary text-xs'>
{t('workflow.nodes.agent.strategy.configureTipDesc')} <br />

View File

@ -6,18 +6,26 @@ import ModelSelector from '@/app/components/header/account-setting/model-provide
import { Group, GroupLabel } from '../_base/components/group'
import { ToolIcon } from './components/tool-icon'
import useConfig from './use-config'
import { useTranslation } from 'react-i18next'
const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
const { inputs } = useConfig(props.id, props.data)
const { t } = useTranslation()
return <div className='mb-1 px-3 py-1 space-y-1'>
{inputs.agent_strategy_name
? <SettingItem label='Strategy' status='error' tooltip='ReAct is not installed'>
? <SettingItem
label={t('workflow.nodes.agent.strategy.shortLabel')}
status='error'
tooltip={t('workflow.nodes.agent.strategyNotInstallTooltip', {
strategy: inputs.agent_strategy_name,
})}
>
{inputs.agent_strategy_name}
</SettingItem>
: <SettingItem label='Agentic strategy Not Set' />}
: <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />}
<Group
label={<GroupLabel className='mt-1'>
Model
{t('workflow.nodes.agent.model')}
</GroupLabel>}
>
<ModelSelector
@ -34,12 +42,22 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
/>
</Group>
<Group label={<GroupLabel className='mt-1'>
Toolbox
{t('workflow.nodes.agent.toolbox')}
</GroupLabel>}>
<div className='grid grid-cols-10 gap-0.5'>
<ToolIcon src='/logo/logo.png' />
<ToolIcon src='/logo/logo.png' status='error' tooltip='Gmail Sender is not installed' />
<ToolIcon src='/logo/logo.png' status='warning' tooltip='DuckDuckGo AI Search Not Authorized' />
<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>
</Group>
</div>

View File

@ -4,8 +4,9 @@ import type { AgentNodeType } from './types'
import Field from '../_base/components/field'
import { InputNumber } from '@/app/components/base/input-number'
import Slider from '@/app/components/base/slider'
import useNodeCrud from '../_base/hooks/use-node-crud'
import { AgentStrategy } from '../_base/components/agent-strategy'
import useConfig from './use-config'
import { useTranslation } from 'react-i18next'
const mockSchema = [
{
@ -260,7 +261,8 @@ const mockSchema = [
] as const
const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
const { inputs, setInputs } = useNodeCrud(props.id, props.data)
const { inputs, setInputs } = useConfig(props.id, props.data)
const { t } = useTranslation()
const [iter, setIter] = [inputs.max_iterations, (value: number) => {
setInputs({
...inputs,
@ -268,7 +270,7 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
})
}]
return <div className='space-y-2 my-2'>
<Field title={'Strategy'} className='px-4' >
<Field title={t('workflow.nodes.agent.strategy.label')} className='px-4' >
<AgentStrategy
strategy={inputs.agent_strategy_name ? {
agent_strategy_provider_name: inputs.agent_strategy_provider_name!,
@ -291,10 +293,10 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
})}
/>
</Field>
<Field title={'tools'} className='px-4'>
<Field title={t('workflow.nodes.agent.tools')} className='px-4'>
</Field>
<Field title={'max iterations'} tooltip={'max iter'} inline className='px-4'>
<Field title={t('workflow.nodes.agent.maxIterations')} tooltip={'max iter'} inline className='px-4'>
<div className='flex w-[200px] items-center gap-3'>
<Slider value={iter} onChange={setIter} className='w-full' min={1} max={10} />
<InputNumber

View File

@ -17,6 +17,7 @@ const useConfig = (id: string, payload: AgentNodeType) => {
return {
readOnly,
inputs,
setInputs,
handleVarListChange,
handleAddVariable,
}

View File

@ -2,8 +2,9 @@ import format from '.'
import { simpleIterationData } from './data'
describe('format api data to tracing panel data', () => {
// test('result should have no nodes in iteration node', () => {
// }
test('result should have no nodes in iteration node', () => {
expect(format(simpleIterationData.in as any).find(item => !!(item as any).execution_metadata?.iteration_id)).toBeUndefined()
})
test('iteration should put nodes in details', () => {
expect(format(simpleIterationData.in as any)).toEqual(simpleIterationData.output)
})

View File

@ -31,6 +31,11 @@ const format = (list: NodeTracing[]): NodeTracing[] => {
.map((item) => {
if (item.node_type === BlockEnum.Iteration) {
const childrenNodes = list.filter(child => child.execution_metadata?.iteration_id === item.node_id)
const error = childrenNodes.find(child => child.status === 'failed')
if (error) {
item.status = 'failed'
item.error = error.error
}
return addChildrenToIterationNode(item, childrenNodes)
}

View File

@ -0,0 +1,7 @@
import type { NodeTracing } from '@/types/workflow'
const format = (list: NodeTracing[]): NodeTracing[] => {
return list
}
export default format

View File

@ -699,8 +699,11 @@ const translation = {
},
agent: {
strategy: {
label: 'Agentic Strategy',
shortLabel: 'Strategy',
configureTip: 'Please configure agentic strategy.',
configureTipDesc: 'After configuring the agentic strategy, this node will automatically load the remaining configurations. The strategy will affect the mechanism of multi-step tool reasoning. ',
selectTip: 'Select agentic strategy',
},
learnMore: 'Learn more',
pluginNotInstalled: 'This plugin is not installed',
@ -710,6 +713,15 @@ const translation = {
install: 'Install',
installing: 'Installing',
},
model: 'model',
toolbox: 'toolbox',
strategyNotSet: 'Agentic strategy Not Set',
tools: 'Tools',
maxIterations: 'Max Iterations',
modelNotInstallTooltip: 'This model is not installed',
toolNotInstallTooltip: '{{tool}} is not installed',
toolNotAuthorizedTooltip: '{{tool}} Not Authorized',
strategyNotInstallTooltip: '{{strategy}} is not installed',
},
},
tracing: {

View File

@ -699,8 +699,11 @@ const translation = {
},
agent: {
strategy: {
label: 'Agent 策略',
shortLabel: '策略',
configureTip: '请配置 Agent 策略。',
configureTipDesc: '配置完成后,此节点将自动加载剩余配置。策略将影响多步工具推理的机制。',
selectTip: '选择 Agent 策略',
},
learnMore: '了解更多',
pluginNotInstalled: '插件未安装',
@ -710,6 +713,15 @@ const translation = {
install: '安装',
installing: '安装中',
},
model: '模型',
toolbox: '工具箱',
strategyNotSet: '代理策略未设置',
tools: '工具',
maxIterations: '最大迭代次数',
modelNotInstallTooltip: '此模型未安装',
toolNotInstallTooltip: '{{tool}} 未安装',
toolNotAuthorizedTooltip: '{{tool}} 未授权',
strategyNotInstallTooltip: '{{strategy}} 未安装',
},
},
tracing: {