Merge branch 'feat/plugins' into dev/plugin-deploy

This commit is contained in:
zxhlyh
2025-01-02 15:51:21 +08:00
28 changed files with 242 additions and 79 deletions

View File

@@ -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>
)

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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] = []

View File

@@ -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])

View File

@@ -60,7 +60,6 @@ const Blocks = ({
Object.keys(withLetterAndGroupViewToolsData[letter]).forEach((groupName) => {
if (!result[groupName])
result[groupName] = []
result[groupName].push(...withLetterAndGroupViewToolsData[letter][groupName])
})
})

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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: '',
}
},
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -7,4 +7,5 @@ export type AgentNodeType = CommonNodeType & {
agent_strategy_label?: string
agent_parameters?: ToolVarInputs
output_schema: Record<string, any>
plugin_unique_identifier?: string
}

View File

@@ -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,

View File

@@ -1,5 +0,0 @@
import type { AgentNodeType } from './types'
export const checkNodeValid = (payload: AgentNodeType) => {
return true
}

View File

@@ -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,
}
},
],
},
{

View File

@@ -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

View File

@@ -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,

View File

@@ -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
View 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)
}
}

View File

@@ -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}}',

View File

@@ -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
View 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}`)
}

View File

@@ -300,7 +300,7 @@ export const useMutationPluginsFromMarketplace = () => {
exclude,
type,
page = 1,
pageSize = 20,
pageSize = 40,
} = pluginsSearchParams
return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', {
body: {

View File

@@ -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,
})
}