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 { useInvalidateInstalledPluginList } from '@/service/use-plugins'
import ConfigurationButton from './configuration-button'
import Loading from '@/app/components/base/loading'
import { PluginType } from '@/app/components/plugins/types'
import {
useModelModalHandler,
@ -104,7 +105,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
}, [providerName, modelId, currentProvider])
if (modelId && !isPluginChecked)
return null
return <Loading />
return (
<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 from github/local and model is not in model list */}
{!needsConfiguration && modelProvider && disabled && (
<Tooltip
popupContent={inModelList ? t('workflow.nodes.agent.modelSelectorTooltips.deprecated')
: renderTooltipContent(
t('workflow.nodes.agent.modelNotSupport.title'),
!pluginInfo ? t('workflow.nodes.agent.modelNotSupport.desc') : t('workflow.nodes.agent.modelNotSupport.descForVersionSwitch'),
!pluginInfo ? t('workflow.nodes.agent.linkToPlugin') : '',
!pluginInfo ? '/plugins' : '',
)
}
asChild={false}
needsDelay={!inModelList}
>
{!pluginInfo ? <RiErrorWarningFill className='w-4 h-4 text-text-destructive' /> : <SwitchPluginVersion uniqueIdentifier={pluginList?.plugins.find(plugin => plugin.name === pluginInfo.name)?.plugin_unique_identifier ?? ''} />}
</Tooltip>
<>
{inModelList ? (
<Tooltip
popupContent={t('workflow.nodes.agent.modelSelectorTooltips.deprecated')}
asChild={false}
needsDelay={false}
>
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
</Tooltip>
) : !pluginInfo ? (
<Tooltip
popupContent={renderTooltipContent(
t('workflow.nodes.agent.modelNotSupport.title'),
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 && (
<Tooltip

View File

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

View File

@ -42,10 +42,10 @@ export const useCategories = (translateFromOut?: TFunction) => {
const t = translateFromOut || translation
const categories = categoryKeys.map((category) => {
if (category === 'agent') {
if (category === 'agent-strategy') {
return {
name: 'agent_strategy',
label: t(`plugin.category.${category}s`),
name: 'agent-strategy',
label: t('plugin.category.agents'),
}
}
return {
@ -70,10 +70,10 @@ export const useSingleCategories = (translateFromOut?: TFunction) => {
const t = translateFromOut || translation
const categories = categoryKeys.map((category) => {
if (category === 'agent') {
if (category === 'agent-strategy') {
return {
name: 'agent_strategy',
label: t(`plugin.categorySingle.${category}`),
name: 'agent-strategy',
label: t('plugin.categorySingle.agent'),
}
}
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'
import React, { useCallback, useState } from 'react'
import React, { useCallback, useRef, useState } from 'react'
import Modal from '@/app/components/base/modal'
import type { Dependency, Plugin, PluginManifestInMarket } from '../../types'
import { InstallStep } from '../../types'
@ -9,6 +9,8 @@ import Installed from '../base/installed'
import { useTranslation } from 'react-i18next'
import useRefreshPluginList from '../hooks/use-refresh-plugin-list'
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'
@ -35,6 +37,9 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
const [errorMsg, setErrorMsg] = useState<string | null>(null)
const { refreshPluginList } = useRefreshPluginList()
const modalRef = useRef<HTMLElement>(null)
const foldAnimInto = useFoldAnimInto(onClose)
const getTitle = useCallback(() => {
if (isBundle && step === InstallStep.installed)
return t(`${i18nPrefix}.installComplete`)
@ -56,12 +61,14 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
setErrorMsg(errorMsg)
}, [])
const modalClassName = 'install-modal'
return (
<Modal
isShow={true}
onClose={onClose}
onClose={() => step === InstallStep.readyToInstall ? foldAnimInto(modalClassName) : onClose()}
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
>
<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
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'>

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',
(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) && (

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import { useWorkflowStore } from '../../../store'
import { useRenderI18nObject } from '@/hooks/use-i18n'
import type { NodeOutPutVar } from '../../../types'
import type { Node } from 'reactflow'
import { toType } from '@/app/components/tools/utils/to-form-schema'
export type Strategy = {
agent_strategy_provider_name: string
@ -150,7 +151,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
onGenerated={handleGenerated}
title={renderI18nObject(schema.label)}
headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase'
containerClassName='bg-transparent'
containerBackgroundClassName='bg-transparent'
gradientBorder={false}
isSupportPromptGenerator={!!schema.auto_generate?.type}
titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)}
@ -181,7 +182,18 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
strategy
? <div>
<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}
onChange={onFormValueChange}
validating={false}

View File

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

View File

@ -61,7 +61,7 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
}
const { t } = useTranslation()
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
onCancel={hideUpdateModal}
plugin={pluginManifestToCardPluginProps({

View File

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

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 { useLogs } from '@/app/components/workflow/run/hooks'
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'
@ -22,6 +23,7 @@ export function strategyParamToCredientialForm(param: StrategyParamItem): Creden
...param as any,
variable: param.name,
show_on: [],
type: toType(param.type),
}
}