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

This commit is contained in:
zxhlyh 2025-01-08 17:49:11 +08:00
commit 9ab39c3feb
15 changed files with 122 additions and 37 deletions

View File

@ -12,6 +12,7 @@ import {
import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types' import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types'
import { useInvalidateInstalledPluginList } from '@/service/use-plugins' import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
import ConfigurationButton from './configuration-button' import ConfigurationButton from './configuration-button'
import Loading from '@/app/components/base/loading'
import { PluginType } from '@/app/components/plugins/types' import { PluginType } from '@/app/components/plugins/types'
import { import {
useModelModalHandler, useModelModalHandler,
@ -104,7 +105,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
}, [providerName, modelId, currentProvider]) }, [providerName, modelId, currentProvider])
if (modelId && !isPluginChecked) if (modelId && !isPluginChecked)
return null return <Loading />
return ( return (
<div <div

View File

@ -45,20 +45,38 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, inModelList, disa
{/* plugin installed and model is in model list but disabled */} {/* plugin installed and model is in model list but disabled */}
{/* plugin installed from github/local and model is not in model list */} {/* plugin installed from github/local and model is not in model list */}
{!needsConfiguration && modelProvider && disabled && ( {!needsConfiguration && modelProvider && disabled && (
<Tooltip <>
popupContent={inModelList ? t('workflow.nodes.agent.modelSelectorTooltips.deprecated') {inModelList ? (
: renderTooltipContent( <Tooltip
t('workflow.nodes.agent.modelNotSupport.title'), popupContent={t('workflow.nodes.agent.modelSelectorTooltips.deprecated')}
!pluginInfo ? t('workflow.nodes.agent.modelNotSupport.desc') : t('workflow.nodes.agent.modelNotSupport.descForVersionSwitch'), asChild={false}
!pluginInfo ? t('workflow.nodes.agent.linkToPlugin') : '', needsDelay={false}
!pluginInfo ? '/plugins' : '', >
) <RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
} </Tooltip>
asChild={false} ) : !pluginInfo ? (
needsDelay={!inModelList} <Tooltip
> popupContent={renderTooltipContent(
{!pluginInfo ? <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> : <SwitchPluginVersion uniqueIdentifier={pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier ?? ''} />} t('workflow.nodes.agent.modelNotSupport.title'),
</Tooltip> t('workflow.nodes.agent.modelNotSupport.desc'),
t('workflow.nodes.agent.linkToPlugin'),
'/plugins',
)}
asChild={false}
needsDelay={true}
>
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
</Tooltip>
) : (
<SwitchPluginVersion
tooltip={renderTooltipContent(
t('workflow.nodes.agent.modelNotSupport.title'),
t('workflow.nodes.agent.modelNotSupport.descForVersionSwitch'),
)}
uniqueIdentifier={pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier ?? ''}
/>
)}
</>
)} )}
{!modelProvider && !pluginInfo && ( {!modelProvider && !pluginInfo && (
<Tooltip <Tooltip

View File

@ -21,7 +21,7 @@ export const tagKeys = [
export const categoryKeys = [ export const categoryKeys = [
'model', 'model',
'tool', 'tool',
'agent', 'agent-strategy',
'extension', 'extension',
'bundle', 'bundle',
] ]

View File

@ -42,10 +42,10 @@ export const useCategories = (translateFromOut?: TFunction) => {
const t = translateFromOut || translation const t = translateFromOut || translation
const categories = categoryKeys.map((category) => { const categories = categoryKeys.map((category) => {
if (category === 'agent') { if (category === 'agent-strategy') {
return { return {
name: 'agent_strategy', name: 'agent-strategy',
label: t(`plugin.category.${category}s`), label: t('plugin.category.agents'),
} }
} }
return { return {
@ -70,10 +70,10 @@ export const useSingleCategories = (translateFromOut?: TFunction) => {
const t = translateFromOut || translation const t = translateFromOut || translation
const categories = categoryKeys.map((category) => { const categories = categoryKeys.map((category) => {
if (category === 'agent') { if (category === 'agent-strategy') {
return { return {
name: 'agent_strategy', name: 'agent-strategy',
label: t(`plugin.categorySingle.${category}`), label: t('plugin.categorySingle.agent'),
} }
} }
return { return {

View File

@ -0,0 +1,37 @@
import { sleep } from '@/utils'
// modalElem fold into plugin install task btn
const animTime = 2000
function getElemCenter(elem: HTMLElement) {
const rect = elem.getBoundingClientRect()
return {
x: rect.left + rect.width / 2 + window.scrollX,
y: rect.top + rect.height / 2 + window.scrollY,
}
}
const useFoldAnimInto = (onClose: () => void) => {
return async function foldIntoAnim(modalClassName: string) {
const modalElem = document.querySelector(`.${modalClassName}`) as HTMLElement
const pluginTaskTriggerElem = document.getElementById('plugin-task-trigger')
if (!modalElem || !pluginTaskTriggerElem) {
onClose()
return
}
const modelCenter = getElemCenter(modalElem)
const modalElemRect = modalElem.getBoundingClientRect()
const pluginTaskTriggerCenter = getElemCenter(pluginTaskTriggerElem)
const xDiff = pluginTaskTriggerCenter.x - modelCenter.x
const yDiff = pluginTaskTriggerCenter.y - modelCenter.y
const scale = 1 / Math.max(modalElemRect.width, modalElemRect.height)
modalElem.style.transition = `all cubic-bezier(0.4, 0, 0.2, 1) ${animTime}ms`
modalElem.style.transform = `translate(${xDiff}px, ${yDiff}px) scale(${scale})`
await sleep(animTime)
onClose()
}
}
export default useFoldAnimInto

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import React, { useCallback, useState } from 'react' import React, { useCallback, useRef, useState } from 'react'
import Modal from '@/app/components/base/modal' import Modal from '@/app/components/base/modal'
import type { Dependency, Plugin, PluginManifestInMarket } from '../../types' import type { Dependency, Plugin, PluginManifestInMarket } from '../../types'
import { InstallStep } from '../../types' import { InstallStep } from '../../types'
@ -9,6 +9,8 @@ import Installed from '../base/installed'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useRefreshPluginList from '../hooks/use-refresh-plugin-list' import useRefreshPluginList from '../hooks/use-refresh-plugin-list'
import ReadyToInstallBundle from '../install-bundle/ready-to-install' import ReadyToInstallBundle from '../install-bundle/ready-to-install'
import useFoldAnimInto from '../hooks/use-fold-anim-into'
import cn from '@/utils/classnames'
const i18nPrefix = 'plugin.installModal' const i18nPrefix = 'plugin.installModal'
@ -35,6 +37,9 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
const [errorMsg, setErrorMsg] = useState<string | null>(null) const [errorMsg, setErrorMsg] = useState<string | null>(null)
const { refreshPluginList } = useRefreshPluginList() const { refreshPluginList } = useRefreshPluginList()
const modalRef = useRef<HTMLElement>(null)
const foldAnimInto = useFoldAnimInto(onClose)
const getTitle = useCallback(() => { const getTitle = useCallback(() => {
if (isBundle && step === InstallStep.installed) if (isBundle && step === InstallStep.installed)
return t(`${i18nPrefix}.installComplete`) return t(`${i18nPrefix}.installComplete`)
@ -56,12 +61,14 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
setErrorMsg(errorMsg) setErrorMsg(errorMsg)
}, []) }, [])
const modalClassName = 'install-modal'
return ( return (
<Modal <Modal
isShow={true} isShow={true}
onClose={onClose} onClose={() => step === InstallStep.readyToInstall ? foldAnimInto(modalClassName) : onClose()}
wrapperClassName='z-[9999]' wrapperClassName='z-[9999]'
className='flex min-w-[560px] p-0 flex-col items-start rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadows-shadow-xl' className={cn(modalClassName, 'flex min-w-[560px] p-0 flex-col items-start rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadows-shadow-xl')}
closable closable
> >
<div className='flex pt-6 pl-6 pb-3 pr-14 items-start gap-2 self-stretch'> <div className='flex pt-6 pl-6 pb-3 pr-14 items-start gap-2 self-stretch'>

View File

@ -146,7 +146,7 @@ const PluginPage = ({
> >
<div <div
className={cn( className={cn(
'sticky top-0 flex min-h-[60px] px-12 pt-4 pb-2 items-center self-stretch gap-1 z-10', activeTab === 'discover' && 'bg-background-body', 'sticky top-0 flex min-h-[60px] px-12 pt-4 pb-2 items-center self-stretch gap-1 z-10 bg-components-panel-bg', activeTab === 'discover' && 'bg-background-body',
)} )}
> >
<div className='flex justify-between items-center w-full'> <div className='flex justify-between items-center w-full'>

View File

@ -87,6 +87,7 @@ const PluginTasks = () => {
'relative flex items-center justify-center w-8 h-8 rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs hover:bg-components-button-secondary-bg-hover', 'relative flex items-center justify-center w-8 h-8 rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs hover:bg-components-button-secondary-bg-hover',
(isInstallingWithError || isFailed) && 'border-components-button-destructive-secondary-border-hover bg-state-destructive-hover hover:bg-state-destructive-hover-alt cursor-pointer', (isInstallingWithError || isFailed) && 'border-components-button-destructive-secondary-border-hover bg-state-destructive-hover hover:bg-state-destructive-hover-alt cursor-pointer',
)} )}
id="plugin-task-trigger"
> >
{ {
(isInstalling || isInstallingWithError) && ( (isInstalling || isInstallingWithError) && (

View File

@ -6,7 +6,7 @@ export enum PluginType {
tool = 'tool', tool = 'tool',
model = 'model', model = 'model',
extension = 'extension', extension = 'extension',
agent = 'agent_strategy', agent = 'agent-strategy',
} }
export enum PluginSource { export enum PluginSource {

View File

@ -1,5 +1,5 @@
import type { ToolCredential, ToolParameter } from '../types' import type { ToolCredential, ToolParameter } from '../types'
const toType = (type: string) => { export const toType = (type: string) => {
switch (type) { switch (type) {
case 'string': case 'string':
return 'text-input' return 'text-input'

View File

@ -19,6 +19,7 @@ import { useWorkflowStore } from '../../../store'
import { useRenderI18nObject } from '@/hooks/use-i18n' import { useRenderI18nObject } from '@/hooks/use-i18n'
import type { NodeOutPutVar } from '../../../types' import type { NodeOutPutVar } from '../../../types'
import type { Node } from 'reactflow' import type { Node } from 'reactflow'
import { toType } from '@/app/components/tools/utils/to-form-schema'
export type Strategy = { export type Strategy = {
agent_strategy_provider_name: string agent_strategy_provider_name: string
@ -150,7 +151,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
onGenerated={handleGenerated} onGenerated={handleGenerated}
title={renderI18nObject(schema.label)} title={renderI18nObject(schema.label)}
headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase' headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase'
containerClassName='bg-transparent' containerBackgroundClassName='bg-transparent'
gradientBorder={false} gradientBorder={false}
isSupportPromptGenerator={!!schema.auto_generate?.type} isSupportPromptGenerator={!!schema.auto_generate?.type}
titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)} titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
@ -181,7 +182,18 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
strategy strategy
? <div> ? <div>
<Form<CustomField> <Form<CustomField>
formSchemas={formSchema} formSchemas={[...formSchema, {
name: 'max_iteration',
type: toType('number'),
required: true,
label: {
en_US: 'Max Iteration',
zh_Hans: '最大迭代次数',
pt_BR: 'Max Iteration',
},
show_on: [],
variable: 'max-ite',
}]}
value={formValue} value={formValue}
onChange={onFormValueChange} onChange={onFormValueChange}
validating={false} validating={false}

View File

@ -68,7 +68,7 @@ type Props = {
onEditionTypeChange?: (editionType: EditionType) => void onEditionTypeChange?: (editionType: EditionType) => void
varList?: Variable[] varList?: Variable[]
handleAddVariable?: (payload: any) => void handleAddVariable?: (payload: any) => void
containerClassName?: string containerBackgroundClassName?: string
gradientBorder?: boolean gradientBorder?: boolean
titleTooltip?: ReactNode titleTooltip?: ReactNode
inputClassName?: string inputClassName?: string
@ -103,7 +103,7 @@ const Editor: FC<Props> = ({
handleAddVariable, handleAddVariable,
onGenerated, onGenerated,
modelConfig, modelConfig,
containerClassName, containerBackgroundClassName: containerClassName,
gradientBorder = true, gradientBorder = true,
titleTooltip, titleTooltip,
inputClassName, inputClassName,

View File

@ -61,7 +61,7 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
} }
const { t } = useTranslation() const { t } = useTranslation()
return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'> return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'>
<div className={cn('w-fit flex items-center justify-center', className)}> <div className={cn('w-fit flex items-center justify-center', className)} onClick={e => e.stopPropagation()}>
{isShowUpdateModal && pluginDetail && <PluginMutationModel {isShowUpdateModal && pluginDetail && <PluginMutationModel
onCancel={hideUpdateModal} onCancel={hideUpdateModal}
plugin={pluginManifestToCardPluginProps({ plugin={pluginManifestToCardPluginProps({

View File

@ -807,10 +807,17 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => {
case BlockEnum.Agent: { case BlockEnum.Agent: {
const payload = data as AgentNodeType const payload = data as AgentNodeType
const params = payload.agent_parameters || {} const valueSelectors: ValueSelector[] = []
const mixVars = matchNotSystemVars(Object.keys(params)?.filter(key => params[key].type === ToolVarType.mixed).map(key => params[key].value) as string[]) if (!payload.agent_parameters)
const vars = Object.keys(params).filter(key => params[key].type === ToolVarType.variable).map(key => params[key].value as string) || [] break
res = [...(mixVars as ValueSelector[]), ...(vars as any)]
Object.keys(payload.agent_parameters || {}).forEach((key) => {
const { value } = payload.agent_parameters![key]
if (typeof value === 'string')
valueSelectors.push(...matchNotSystemVars([value]))
})
res = valueSelectors
break
} }
} }
return res || [] return res || []

View File

@ -14,6 +14,7 @@ import ResultPanel from '@/app/components/workflow/run/result-panel'
import formatTracing from '@/app/components/workflow/run/utils/format-log' import formatTracing from '@/app/components/workflow/run/utils/format-log'
import { useLogs } from '@/app/components/workflow/run/hooks' import { useLogs } from '@/app/components/workflow/run/hooks'
import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form'
import { toType } from '@/app/components/tools/utils/to-form-schema'
const i18nPrefix = 'workflow.nodes.agent' const i18nPrefix = 'workflow.nodes.agent'
@ -22,6 +23,7 @@ export function strategyParamToCredientialForm(param: StrategyParamItem): Creden
...param as any, ...param as any,
variable: param.name, variable: param.name,
show_on: [], show_on: [],
type: toType(param.type),
} }
} }