mirror of https://github.com/langgenius/dify.git
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
commit
a071d2cd1b
|
|
@ -1,14 +0,0 @@
|
|||
'use server'
|
||||
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
// Server Actions
|
||||
export async function handleDelete() {
|
||||
// revalidatePath only invalidates the cache when the included path is next visited.
|
||||
revalidatePath('/')
|
||||
}
|
||||
|
||||
export async function fetchPluginDetail(org: string, name: string) {
|
||||
// Fetch plugin detail TODO
|
||||
return { org, name }
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
'use client'
|
||||
import Card from '@/app/components/plugins/card'
|
||||
import { customTool, extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card/card-mock'
|
||||
// import PluginItem from '@/app/components/plugins/plugin-item'
|
||||
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
|
||||
// import ProviderCard from '@/app/components/plugins/provider-card'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import InstallBundle from '@/app/components/plugins/install-plugin/install-bundle'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import LoadingError from '@/app/components/plugins/install-plugin/base/loading-error'
|
||||
|
||||
const PluginList = () => {
|
||||
const pluginList = [toolNotion, extensionDallE, modelGPT4, customTool]
|
||||
const [isShow, {
|
||||
setFalse: hide,
|
||||
}] = useBoolean(true)
|
||||
|
||||
return (
|
||||
<div className='pb-3 bg-white'>
|
||||
<LoadingError />
|
||||
{isShow && (
|
||||
<InstallBundle
|
||||
onClose={hide}
|
||||
fromDSLPayload={[
|
||||
// {
|
||||
// type: 'marketplace',
|
||||
// value: {
|
||||
// plugin_unique_identifier: 'langgenius/google:0.0.2@dcb354c9d0fee60e6e9c9eb996e1e485bbef343ba8cd545c0cfb3ec80970f6f1',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
type: 'github',
|
||||
value: {
|
||||
repo: 'YIXIAO0/test',
|
||||
version: '1.11.5',
|
||||
package: 'test.difypkg',
|
||||
github_plugin_unique_identifier: 'yixiao0/test:0.0.1@3592166c87afcf944b4f13f27467a5c8f9e00bd349cb42033a072734a37431b4',
|
||||
},
|
||||
},
|
||||
// {
|
||||
// type: 'github',
|
||||
// value: {
|
||||
// package: 'dify-test.difypkg',
|
||||
// repo: 'WTW0313/dify-test',
|
||||
// release: '0.0.5-beta.2',
|
||||
// github_plugin_unique_identifier: 'wtw0313/dify-test:0.0.1@1633daa043b47155d4228e2db7734245fd6d3e20ba812e5c02ce69fc1e3038f4',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// type: 'marketplace',
|
||||
// value: {
|
||||
// plugin_unique_identifier: 'langgenius/openai:0.0.2@7baee9635a07573ea192621ebfdacb39db466fa691e75255beaf48bf41d44375',
|
||||
// },
|
||||
// },
|
||||
]} />
|
||||
)
|
||||
}
|
||||
<div className='mx-3 '>
|
||||
{/* <h2 className='my-3'>Dify Plugin list</h2> */}
|
||||
{/* <div className='grid grid-cols-2 gap-3'>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<PluginItem
|
||||
key={index}
|
||||
payload={plugin as any}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
))}
|
||||
</div> */}
|
||||
|
||||
<h2 className='my-3'>Install Plugin / Package under bundle</h2>
|
||||
<div className='w-[512px] rounded-2xl bg-background-section-burn p-2'>
|
||||
<Card
|
||||
payload={toolNotion as any}
|
||||
descriptionLineRows={1}
|
||||
titleLeft={
|
||||
<Badge className='ml-1' text={toolNotion.version} />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{/* <h3 className='my-1'>Installed</h3>
|
||||
<div className='w-[512px] rounded-2xl bg-background-section-burn p-2'>
|
||||
<Card
|
||||
payload={toolNotion as any}
|
||||
descriptionLineRows={1}
|
||||
installed
|
||||
/>
|
||||
</div> */}
|
||||
|
||||
{/* <h3 className='my-1'>Install model provide</h3>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<ProviderCard key={index} payload={plugin as any} />
|
||||
))}
|
||||
</div> */}
|
||||
|
||||
<div className='my-3 h-[px] bg-gray-50'></div>
|
||||
<h2 className='my-3'>Marketplace Plugin list</h2>
|
||||
<div className='grid grid-cols-4 gap-3'>
|
||||
{pluginList.map((plugin, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
payload={plugin as any}
|
||||
footer={
|
||||
<CardMoreInfo downloadCount={index % 2 === 0 ? 1234 : 6} tags={index % 2 === 0 ? ['Search', 'Tag that has very very long name', 'Productivity', 'Tag2'] : []} />
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// export const metadata = {
|
||||
// title: 'Plugins - Card',
|
||||
// }
|
||||
|
||||
export default PluginList
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
'use client'
|
||||
import React from 'react'
|
||||
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
|
||||
|
||||
const ToolsPicker = () => {
|
||||
const [show, setShow] = React.useState(true)
|
||||
return (
|
||||
<div className=' mt-10 ml-10'>
|
||||
<ToolPicker
|
||||
trigger={<div className='inline-block w-[70px]'>Click me</div>}
|
||||
isShow={show}
|
||||
onShowChange={setShow}
|
||||
disabled={false}
|
||||
supportAddCustomTool={true}
|
||||
onSelect={() => { }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolsPicker
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
'use client'
|
||||
import { toolNeko } from '@/app/components/plugins/card/card-mock'
|
||||
import { PluginSource } from '@/app/components/plugins/types'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import React from 'react'
|
||||
|
||||
const UpdatePlugin = () => {
|
||||
const { setShowUpdatePluginModal } = useModalContext()
|
||||
const handleUpdateFromMarketPlace = () => {
|
||||
setShowUpdatePluginModal({
|
||||
payload: {
|
||||
type: PluginSource.marketplace,
|
||||
marketPlace: {
|
||||
originalPackageInfo: {
|
||||
id: 'langgenius/neko:0.0.1@9e57d693739287c0efdc96847d7ed959ca93f70aa704471f2eb7ed3313821824',
|
||||
payload: toolNeko as any,
|
||||
},
|
||||
targetPackageInfo: {
|
||||
id: 'target_xxx',
|
||||
version: '1.2.3',
|
||||
},
|
||||
},
|
||||
},
|
||||
onCancelCallback: () => {
|
||||
console.log('canceled')
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
console.log('saved')
|
||||
},
|
||||
})
|
||||
}
|
||||
const handleUpdateFromGithub = () => {
|
||||
setShowUpdatePluginModal({
|
||||
payload: {
|
||||
type: PluginSource.github,
|
||||
github: {
|
||||
originalPackageInfo: {
|
||||
id: '111',
|
||||
repo: 'aaa/bbb',
|
||||
version: 'xxx',
|
||||
url: 'aaa/bbb',
|
||||
currVersion: '1.2.3',
|
||||
currPackage: 'pack1',
|
||||
} as any,
|
||||
},
|
||||
},
|
||||
onCancelCallback: () => {
|
||||
console.log('canceled')
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
console.log('saved')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>更新组件</div>
|
||||
<div className='flex space-x-1'>
|
||||
<div className='underline cursor-pointer' onClick={handleUpdateFromMarketPlace}>从 Marketplace</div>
|
||||
<div className='underline cursor-pointer' onClick={handleUpdateFromGithub}>从 GitHub</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(UpdatePlugin)
|
||||
|
|
@ -36,6 +36,10 @@ const iconClassName = `
|
|||
w-5 h-5 mr-2
|
||||
`
|
||||
|
||||
const scrolledClassName = `
|
||||
border-b shadow-xs bg-white/[.98]
|
||||
`
|
||||
|
||||
type IAccountSettingProps = {
|
||||
onCancel: () => void
|
||||
activeTab?: string
|
||||
|
|
|
|||
|
|
@ -5,32 +5,37 @@ import type {
|
|||
} from '../declarations'
|
||||
import { useLanguage } from '../hooks'
|
||||
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
|
||||
import { OpenaiViolet } from '@/app/components/base/icons/src/public/llm'
|
||||
import { OpenaiBlue, OpenaiViolet } from '@/app/components/base/icons/src/public/llm'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type ModelIconProps = {
|
||||
provider?: Model | ModelProvider
|
||||
modelName?: string
|
||||
className?: string
|
||||
isDeprecated?: boolean
|
||||
}
|
||||
const ModelIcon: FC<ModelIconProps> = ({
|
||||
provider,
|
||||
className,
|
||||
modelName,
|
||||
isDeprecated = false,
|
||||
}) => {
|
||||
const language = useLanguage()
|
||||
|
||||
if (provider?.provider.includes('openai') && (modelName?.startsWith('gpt-4') || modelName?.includes('4o')))
|
||||
return <OpenaiViolet className={cn('w-4 h-4', className)}/>
|
||||
if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o'))
|
||||
return <OpenaiBlue className={cn('w-5 h-5', className)}/>
|
||||
if (provider?.provider.includes('openai') && modelName?.startsWith('gpt-4'))
|
||||
return <OpenaiViolet className={cn('w-5 h-5', className)}/>
|
||||
|
||||
if (provider?.icon_small) {
|
||||
return (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
alt='model-icon'
|
||||
src={`${provider.icon_small[language] || provider.icon_small.en_US}`}
|
||||
className={cn('w-4 h-4', className)}
|
||||
/>
|
||||
|
||||
<div className={isDeprecated ? 'opacity-50' : ''}>
|
||||
<img
|
||||
alt='model-icon'
|
||||
src={`${provider.icon_small[language] || provider.icon_small.en_US}`}
|
||||
className={cn('w-4 h-4', className)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ type FormProps<
|
|||
validatedSuccess?: boolean
|
||||
showOnVariableMap: Record<string, string[]>
|
||||
isEditMode: boolean
|
||||
isAgentStrategy?: boolean
|
||||
readonly?: boolean
|
||||
inputClassName?: string
|
||||
isShowDefaultValue?: boolean
|
||||
|
|
@ -60,6 +61,7 @@ function Form<
|
|||
validatedSuccess,
|
||||
showOnVariableMap,
|
||||
isEditMode,
|
||||
isAgentStrategy = false,
|
||||
readonly,
|
||||
inputClassName,
|
||||
isShowDefaultValue = false,
|
||||
|
|
@ -294,6 +296,7 @@ function Form<
|
|||
popupClassName='!w-[387px]'
|
||||
isAdvancedMode
|
||||
isInWorkflow
|
||||
isAgentStrategy={isAgentStrategy}
|
||||
value={value[variable]}
|
||||
setModel={model => handleModelChanged(variable, model)}
|
||||
readonly={readonly}
|
||||
|
|
|
|||
|
|
@ -37,43 +37,45 @@ const ModelName: FC<ModelNameProps> = ({
|
|||
if (!modelItem)
|
||||
return null
|
||||
return (
|
||||
<div className={cn('flex items-center truncate text-components-input-text-filled system-sm-regular', className)}>
|
||||
<div className={cn('flex items-center overflow-hidden text-ellipsis truncate text-components-input-text-filled system-sm-regular', className)}>
|
||||
<div
|
||||
className='truncate'
|
||||
title={modelItem.label[language] || modelItem.label.en_US}
|
||||
>
|
||||
{modelItem.label[language] || modelItem.label.en_US}
|
||||
</div>
|
||||
{
|
||||
showModelType && modelItem.model_type && (
|
||||
<ModelBadge className={cn('ml-1', modelTypeClassName)}>
|
||||
{modelTypeFormat(modelItem.model_type)}
|
||||
</ModelBadge>
|
||||
)
|
||||
}
|
||||
{
|
||||
modelItem.model_properties.mode && showMode && (
|
||||
<ModelBadge className={cn('ml-1', modeClassName)}>
|
||||
{(modelItem.model_properties.mode as string).toLocaleUpperCase()}
|
||||
</ModelBadge>
|
||||
)
|
||||
}
|
||||
{
|
||||
showFeatures && modelItem.features?.map(feature => (
|
||||
<FeatureIcon
|
||||
key={feature}
|
||||
feature={feature}
|
||||
className={cn('ml-1', featuresClassName)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{
|
||||
showContextSize && modelItem.model_properties.context_size && (
|
||||
<ModelBadge className='ml-1'>
|
||||
{sizeFormat(modelItem.model_properties.context_size as number)}
|
||||
</ModelBadge>
|
||||
)
|
||||
}
|
||||
<div className='flex items-center gap-0.5'>
|
||||
{
|
||||
showModelType && modelItem.model_type && (
|
||||
<ModelBadge className={modelTypeClassName}>
|
||||
{modelTypeFormat(modelItem.model_type)}
|
||||
</ModelBadge>
|
||||
)
|
||||
}
|
||||
{
|
||||
modelItem.model_properties.mode && showMode && (
|
||||
<ModelBadge className={modeClassName}>
|
||||
{(modelItem.model_properties.mode as string).toLocaleUpperCase()}
|
||||
</ModelBadge>
|
||||
)
|
||||
}
|
||||
{
|
||||
showFeatures && modelItem.features?.map(feature => (
|
||||
<FeatureIcon
|
||||
key={feature}
|
||||
feature={feature}
|
||||
className={featuresClassName}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{
|
||||
showContextSize && modelItem.model_properties.context_size && (
|
||||
<ModelBadge>
|
||||
{sizeFormat(modelItem.model_properties.context_size as number)}
|
||||
</ModelBadge>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,180 @@
|
|||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type {
|
||||
CustomConfigurationModelFixedFields,
|
||||
ModelItem,
|
||||
ModelProvider,
|
||||
} from '../declarations'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationStatusEnum,
|
||||
} from '../declarations'
|
||||
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from '../provider-added-card'
|
||||
import { ModelStatusEnum } from '../declarations'
|
||||
import {
|
||||
useUpdateModelList,
|
||||
useUpdateModelProviders,
|
||||
} from '../hooks'
|
||||
import ModelIcon from '../model-icon'
|
||||
import ModelName from '../model-name'
|
||||
import Button from '@/app/components/base/button'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { RiEqualizer2Line, RiErrorWarningFill } from '@remixicon/react'
|
||||
|
||||
export type AgentModelTriggerProps = {
|
||||
open?: boolean
|
||||
disabled?: boolean
|
||||
currentProvider?: ModelProvider
|
||||
currentModel?: ModelItem
|
||||
providerName?: string
|
||||
modelId?: string
|
||||
hasDeprecated?: boolean
|
||||
}
|
||||
|
||||
const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
||||
disabled,
|
||||
currentProvider,
|
||||
currentModel,
|
||||
providerName,
|
||||
modelId,
|
||||
hasDeprecated,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { modelProviders } = useProviderContext()
|
||||
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
||||
const updateModelProviders = useUpdateModelProviders()
|
||||
const updateModelList = useUpdateModelList()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const modelProvider = modelProviders.find(item => item.provider === providerName)
|
||||
const needsConfiguration = modelProvider?.custom_configuration.status === CustomConfigurationStatusEnum.noConfigure && !(
|
||||
modelProvider.system_configuration.enabled === true
|
||||
&& modelProvider.system_configuration.quota_configurations.find(
|
||||
item => item.quota_type === modelProvider.system_configuration.current_quota_type,
|
||||
)
|
||||
)
|
||||
|
||||
const handleOpenModal = (
|
||||
provider: ModelProvider,
|
||||
configurationMethod: ConfigurationMethodEnum,
|
||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||
) => {
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider: provider,
|
||||
currentConfigurationMethod: configurationMethod,
|
||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
updateModelProviders()
|
||||
|
||||
provider.supported_model_types.forEach((type) => {
|
||||
updateModelList(type)
|
||||
})
|
||||
|
||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as any)
|
||||
|
||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
||||
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative group flex items-center p-1 gap-[2px] flex-grow rounded-lg bg-components-input-bg-normal cursor-pointer hover:bg-state-base-hover-alt',
|
||||
)}
|
||||
>
|
||||
{modelId ? (
|
||||
<>
|
||||
{currentProvider && (
|
||||
<ModelIcon
|
||||
className="m-0.5"
|
||||
provider={currentProvider}
|
||||
modelName={currentModel?.model}
|
||||
isDeprecated={hasDeprecated}
|
||||
/>
|
||||
)}
|
||||
{!currentProvider && (
|
||||
<ModelIcon
|
||||
className="m-0.5"
|
||||
provider={modelProvider}
|
||||
modelName={modelId}
|
||||
isDeprecated={hasDeprecated}
|
||||
/>
|
||||
)}
|
||||
{currentModel && (
|
||||
<ModelName
|
||||
className="flex px-1 py-[3px] items-center gap-1 grow"
|
||||
modelItem={currentModel}
|
||||
showMode
|
||||
showFeatures
|
||||
/>
|
||||
)}
|
||||
{!currentModel && (
|
||||
<div className="flex py-[3px] px-1 items-center gap-1 grow opacity-50 truncate">
|
||||
<div className="text-components-input-text-filled text-ellipsis overflow-hidden system-sm-regular">
|
||||
{modelId}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{needsConfiguration && (
|
||||
<Button
|
||||
size="small"
|
||||
className="z-[100]"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleOpenModal(modelProvider, ConfigurationMethodEnum.predefinedModel, undefined)
|
||||
}}
|
||||
>
|
||||
<div className="flex px-[3px] justify-center items-center gap-1">
|
||||
{t('workflow.nodes.agent.notAuthorized')}
|
||||
</div>
|
||||
<div className="flex w-[14px] h-[14px] justify-center items-center">
|
||||
<div className="w-2 h-2 shrink-0 rounded-[3px] border border-components-badge-status-light-warning-border-inner
|
||||
bg-components-badge-status-light-warning-bg shadow-components-badge-status-light-warning-halo" />
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
{!needsConfiguration && disabled && (
|
||||
<Tooltip
|
||||
popupContent={t('workflow.nodes.agent.modelSelectorTooltips.deprecated')}
|
||||
asChild={false}
|
||||
>
|
||||
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex p-1 pl-2 items-center gap-1 grow">
|
||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap system-sm-regular text-components-input-text-placeholder">
|
||||
{t('workflow.nodes.agent.configureModel')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex pr-1 items-center">
|
||||
<RiEqualizer2Line className="w-4 h-4 text-text-tertiary group-hover:text-text-secondary" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{currentProvider && currentModel && currentModel.status === ModelStatusEnum.active && (
|
||||
<div className="flex pr-1 items-center">
|
||||
<RiEqualizer2Line className="w-4 h-4 text-text-tertiary group-hover:text-text-secondary" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentModelTrigger
|
||||
|
|
@ -77,28 +77,30 @@ const PopupItem: FC<PopupItemProps> = ({
|
|||
<div
|
||||
key={modelItem.model}
|
||||
className={`
|
||||
group relative flex items-center px-3 py-1.5 h-8 rounded-lg
|
||||
group relative flex items-center px-3 py-1.5 h-8 rounded-lg gap-1
|
||||
${modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-not-allowed hover:bg-state-base-hover-alt'}
|
||||
`}
|
||||
onClick={() => handleSelect(model.provider, modelItem)}
|
||||
>
|
||||
<ModelIcon
|
||||
className={`
|
||||
shrink-0 mr-2 w-4 h-4
|
||||
${modelItem.status !== ModelStatusEnum.active && 'opacity-60'}
|
||||
`}
|
||||
provider={model}
|
||||
modelName={modelItem.model}
|
||||
/>
|
||||
<ModelName
|
||||
className={`
|
||||
grow text-sm font-normal text-text-primary
|
||||
${modelItem.status !== ModelStatusEnum.active && 'opacity-60'}
|
||||
`}
|
||||
modelItem={modelItem}
|
||||
showMode
|
||||
showFeatures
|
||||
/>
|
||||
<div className='flex items-center gap-2'>
|
||||
<ModelIcon
|
||||
className={`
|
||||
shrink-0 w-4 h-4
|
||||
${modelItem.status !== ModelStatusEnum.active && 'opacity-60'}
|
||||
`}
|
||||
provider={model}
|
||||
modelName={modelItem.model}
|
||||
/>
|
||||
<ModelName
|
||||
className={`
|
||||
text-text-secondary system-sm-medium
|
||||
${modelItem.status !== ModelStatusEnum.active && 'opacity-60'}
|
||||
`}
|
||||
modelItem={modelItem}
|
||||
showMode
|
||||
showFeatures
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
defaultModel?.model === modelItem.model && defaultModel.provider === currentProvider.provider && (
|
||||
<Check className='shrink-0 w-4 h-4 text-text-accent' />
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const Card = ({
|
|||
const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
|
||||
const { categoriesMap } = useCategories()
|
||||
const { category, type, name, org, label, brief, icon, verified } = payload
|
||||
const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent-strategy'].includes(type)
|
||||
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 || ''
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export const useCategories = (translateFromOut?: TFunction) => {
|
|||
const categories = categoryKeys.map((category) => {
|
||||
if (category === 'agent') {
|
||||
return {
|
||||
name: 'agent-strategy',
|
||||
name: 'agent_strategy',
|
||||
label: t(`plugin.category.${category}s`),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
|||
const updateModelProviders = useUpdateModelProviders()
|
||||
const invalidateAllToolProviders = useInvalidateAllToolProviders()
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
// TODO: check installed in beta version.
|
||||
|
||||
const getTitle = useCallback(() => {
|
||||
if (isBundle && step === InstallStep.installed)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export const getMarketplaceListCondition = (pluginType: string) => {
|
|||
return 'category=tool'
|
||||
|
||||
if (pluginType === PluginType.agent)
|
||||
return 'category=agent-strategy'
|
||||
return 'category=agent_strategy'
|
||||
|
||||
if (pluginType === PluginType.model)
|
||||
return 'category=model'
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ const PluginDetailPanel: FC<Props> = ({
|
|||
console.log('tool change', val)
|
||||
setValue(val)
|
||||
}
|
||||
const testDelete = () => {
|
||||
setValue(undefined)
|
||||
}
|
||||
|
||||
if (!detail)
|
||||
return null
|
||||
|
|
@ -63,6 +66,7 @@ const PluginDetailPanel: FC<Props> = ({
|
|||
<ToolSelector
|
||||
value={value}
|
||||
onSelect={item => testChange(item)}
|
||||
onDelete={testDelete}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import ModelSelector from '@/app/components/header/account-setting/model-provide
|
|||
import {
|
||||
useModelList,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import AgentModelTrigger from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger'
|
||||
import Trigger from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger'
|
||||
import type { TriggerProps } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger'
|
||||
import {
|
||||
|
|
@ -34,6 +35,7 @@ export type ModelParameterModalProps = {
|
|||
renderTrigger?: (v: TriggerProps) => ReactNode
|
||||
readonly?: boolean
|
||||
isInWorkflow?: boolean
|
||||
isAgentStrategy?: boolean
|
||||
scope?: string
|
||||
}
|
||||
|
||||
|
|
@ -46,13 +48,25 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
|||
renderTrigger,
|
||||
readonly,
|
||||
isInWorkflow,
|
||||
isAgentStrategy,
|
||||
scope = ModelTypeEnum.textGeneration,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { isAPIKeySet } = useProviderContext()
|
||||
const [open, setOpen] = useState(false)
|
||||
const scopeArray = scope.split('&')
|
||||
const scopeFeatures = scopeArray.slice(1) || []
|
||||
const scopeFeatures = useMemo(() => {
|
||||
if (scopeArray.includes('all'))
|
||||
return []
|
||||
return scopeArray.filter(item => ![
|
||||
ModelTypeEnum.textGeneration,
|
||||
ModelTypeEnum.textEmbedding,
|
||||
ModelTypeEnum.rerank,
|
||||
ModelTypeEnum.moderation,
|
||||
ModelTypeEnum.speech2text,
|
||||
ModelTypeEnum.tts,
|
||||
].includes(item as ModelTypeEnum))
|
||||
}, [scopeArray])
|
||||
|
||||
const { data: textGenerationList } = useModelList(ModelTypeEnum.textGeneration)
|
||||
const { data: textEmbeddingList } = useModelList(ModelTypeEnum.textEmbedding)
|
||||
|
|
@ -168,8 +182,16 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
|||
providerName: value?.provider,
|
||||
modelId: value?.model,
|
||||
})
|
||||
: (
|
||||
<Trigger
|
||||
: (isAgentStrategy
|
||||
? <AgentModelTrigger
|
||||
disabled={disabled}
|
||||
hasDeprecated={hasDeprecated}
|
||||
currentProvider={currentProvider}
|
||||
currentModel={currentModel}
|
||||
providerName={value?.provider}
|
||||
modelId={value?.model}
|
||||
/>
|
||||
: <Trigger
|
||||
disabled={disabled}
|
||||
isInWorkflow={isInWorkflow}
|
||||
modelDisabled={modelDisabled}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import type { FC } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
RiArrowLeftLine,
|
||||
RiArrowRightUpLine,
|
||||
|
|
@ -12,6 +13,7 @@ import {
|
|||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import ToolTrigger from '@/app/components/plugins/plugin-detail-panel/tool-selector/tool-trigger'
|
||||
import ToolItem from '@/app/components/plugins/plugin-detail-panel/tool-selector/tool-item'
|
||||
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
|
|
@ -54,6 +56,7 @@ type Props = {
|
|||
parameters?: Record<string, any>
|
||||
extra?: Record<string, any>
|
||||
}) => void
|
||||
onDelete?: () => void
|
||||
supportAddCustomTool?: boolean
|
||||
scope?: string
|
||||
}
|
||||
|
|
@ -63,6 +66,7 @@ const ToolSelector: FC<Props> = ({
|
|||
placement = 'left',
|
||||
offset = 4,
|
||||
onSelect,
|
||||
onDelete,
|
||||
scope,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
|
@ -155,12 +159,33 @@ const ToolSelector: FC<Props> = ({
|
|||
className='w-full'
|
||||
onClick={handleTriggerClick}
|
||||
>
|
||||
<ToolTrigger
|
||||
isConfigure
|
||||
open={isShow}
|
||||
value={value}
|
||||
provider={currentProvider}
|
||||
/>
|
||||
{!value?.provider_name && (
|
||||
<ToolTrigger
|
||||
isConfigure
|
||||
open={isShow}
|
||||
value={value}
|
||||
provider={currentProvider}
|
||||
/>
|
||||
)}
|
||||
{value?.provider_name && (
|
||||
<ToolItem
|
||||
open={isShow}
|
||||
icon={currentProvider?.icon}
|
||||
providerName={value.provider_name}
|
||||
toolName={value.tool_name}
|
||||
onDelete={onDelete}
|
||||
noAuth={currentProvider && !currentProvider.is_team_authorization}
|
||||
onAuth={() => setShowSettingAuth(true)}
|
||||
// uninstalled
|
||||
errorTip={<div className='space-y-1 text-xs'>
|
||||
<h3 className='text-text-primary font-semibold'>{t('workflow.nodes.agent.pluginNotInstalled')}</h3>
|
||||
<p className='text-text-secondary tracking-tight'>{t('workflow.nodes.agent.pluginNotInstalledDesc')}</p>
|
||||
<p>
|
||||
<Link href={'/plugins'} className='text-text-accent tracking-tight'>{t('workflow.nodes.agent.linkToPlugin')}</Link>
|
||||
</p>
|
||||
</div>}
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1000]'>
|
||||
<div className={cn('relative w-[361px] min-h-20 max-h-[642px] pb-4 rounded-xl backdrop-blur-sm bg-components-panel-bg-blur border-[0.5px] border-components-panel-border shadow-lg', !isShowSettingAuth && 'overflow-y-auto pb-2')}>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiEqualizer2Line,
|
||||
RiErrorWarningFill,
|
||||
} from '@remixicon/react'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
icon?: any
|
||||
providerName?: string
|
||||
toolName?: string
|
||||
showSwitch?: boolean
|
||||
switchValue?: boolean
|
||||
onSwitchChange?: (value: boolean) => void
|
||||
onDelete?: () => void
|
||||
noAuth?: boolean
|
||||
onAuth?: () => void
|
||||
isError?: boolean
|
||||
errorTip?: any
|
||||
uninstalled?: boolean
|
||||
isInstalling?: boolean
|
||||
onInstall?: () => void
|
||||
open: boolean
|
||||
}
|
||||
|
||||
const ToolItem = ({
|
||||
open,
|
||||
icon,
|
||||
providerName,
|
||||
toolName,
|
||||
showSwitch,
|
||||
switchValue,
|
||||
onSwitchChange,
|
||||
onDelete,
|
||||
noAuth,
|
||||
onAuth,
|
||||
uninstalled,
|
||||
isInstalling,
|
||||
onInstall,
|
||||
isError,
|
||||
errorTip,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const providerNameText = providerName?.split('/').pop()
|
||||
const isTransparent = uninstalled || isError
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'group p-1.5 pr-2 flex items-center gap-1 bg-components-panel-on-panel-item-bg border-[0.5px] border-components-panel-border-subtle rounded-lg shadow-xs cursor-default hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm',
|
||||
open && 'bg-components-panel-on-panel-item-bg-hover shadow-sm',
|
||||
isDeleting && 'hover:bg-state-destructive-hover border-state-destructive-border shadow-xs',
|
||||
)}>
|
||||
<div className={cn('shrink-0', isTransparent && 'opacity-50')}>
|
||||
{typeof icon === 'string' && <div className='w-7 h-7 bg-cover bg-center border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge rounded-lg' style={{ backgroundImage: `url(${icon})` }} />}
|
||||
{typeof icon !== 'string' && <AppIcon className='w-7 h-7 border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge rounded-lg' size='xs' icon={icon?.content} background={icon?.background} />}
|
||||
</div>
|
||||
<div className={cn('pl-0.5 grow truncate', isTransparent && 'opacity-50')}>
|
||||
<div className='text-text-tertiary system-2xs-medium-uppercase'>{providerNameText}</div>
|
||||
<div className='text-text-secondary system-xs-medium'>{toolName}</div>
|
||||
</div>
|
||||
<div className='hidden group-hover:flex items-center gap-1'>
|
||||
{!noAuth && !isError && !uninstalled && (
|
||||
<ActionButton>
|
||||
<RiEqualizer2Line className='w-4 h-4' />
|
||||
</ActionButton>
|
||||
)}
|
||||
<div
|
||||
className='p-1 rounded-md text-text-tertiary cursor-pointer hover:text-text-destructive'
|
||||
onClick={onDelete}
|
||||
onMouseOver={() => setIsDeleting(true)}
|
||||
onMouseLeave={() => setIsDeleting(false)}
|
||||
>
|
||||
<RiDeleteBinLine className='w-4 h-4' />
|
||||
</div>
|
||||
</div>
|
||||
{!isError && !uninstalled && !noAuth && showSwitch && (
|
||||
<Switch
|
||||
className='mr-1'
|
||||
size='md'
|
||||
defaultValue={switchValue}
|
||||
onChange={onSwitchChange} />
|
||||
)}
|
||||
{!isError && !uninstalled && noAuth && (
|
||||
<Button variant='secondary' size='small' onClick={onAuth}>
|
||||
{t('tools.notAuthorized')}
|
||||
<Indicator className='ml-2' color='orange' />
|
||||
</Button>
|
||||
)}
|
||||
{!isError && uninstalled && (
|
||||
<InstallPluginButton size={'small'} loading={isInstalling} onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onInstall?.()
|
||||
}} />
|
||||
)}
|
||||
{isError && (
|
||||
<Tooltip
|
||||
popupContent={errorTip}
|
||||
needsDelay
|
||||
>
|
||||
<div>
|
||||
<RiErrorWarningFill className='w-4 h-4 text-text-destructive' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolItem
|
||||
|
|
@ -6,7 +6,7 @@ export enum PluginType {
|
|||
tool = 'tool',
|
||||
model = 'model',
|
||||
extension = 'extension',
|
||||
agent = 'agent-strategy',
|
||||
agent = 'agent_strategy',
|
||||
}
|
||||
|
||||
export enum PluginSource {
|
||||
|
|
@ -109,7 +109,7 @@ export type PluginDetail = {
|
|||
}
|
||||
|
||||
export type Plugin = {
|
||||
type: 'plugin' | 'bundle' | 'model' | 'extension' | 'tool'
|
||||
type: 'plugin' | 'bundle' | 'model' | 'extension' | 'tool' | 'agent_strategy'
|
||||
org: string
|
||||
author?: string
|
||||
name: string
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => {
|
|||
validating={false}
|
||||
showOnVariableMap={{}}
|
||||
isEditMode={true}
|
||||
isAgentStrategy={true}
|
||||
fieldLabelClassName='uppercase'
|
||||
customRenderField={renderField}
|
||||
override={override}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const formatToTracingNodeList = (list: NodeTracing[], t: any) => {
|
|||
const formattedAgentList = formatAgentNode(allItems)
|
||||
const formattedRetryList = formatRetryNode(formattedAgentList) // retry one node
|
||||
// would change the structure of the list. Iteration and parallel can include each other.
|
||||
const formattedIterationList = formatIterationNode(formattedRetryList)
|
||||
const formattedIterationList = formatIterationNode(formattedRetryList, t)
|
||||
const formattedParallelList = formatParallelNode(formattedIterationList, t)
|
||||
|
||||
const result = formattedParallelList
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
|
||||
import formatParallelNode from '../parallel'
|
||||
function addChildrenToIterationNode(iterationNode: NodeTracing, childrenNodes: NodeTracing[]): NodeTracing {
|
||||
const details: NodeTracing[][] = []
|
||||
childrenNodes.forEach((item) => {
|
||||
|
|
@ -18,7 +18,7 @@ function addChildrenToIterationNode(iterationNode: NodeTracing, childrenNodes: N
|
|||
}
|
||||
}
|
||||
|
||||
const format = (list: NodeTracing[]): NodeTracing[] => {
|
||||
const format = (list: NodeTracing[], t: any): NodeTracing[] => {
|
||||
const iterationNodeIds = list
|
||||
.filter(item => item.node_type === BlockEnum.Iteration)
|
||||
.map(item => item.node_id)
|
||||
|
|
@ -36,7 +36,14 @@ const format = (list: NodeTracing[]): NodeTracing[] => {
|
|||
item.status = 'failed'
|
||||
item.error = error.error
|
||||
}
|
||||
return addChildrenToIterationNode(item, childrenNodes)
|
||||
const addedChildrenList = addChildrenToIterationNode(item, childrenNodes)
|
||||
// handle parallel node in iteration node
|
||||
if (addedChildrenList.details && addedChildrenList.details.length > 0) {
|
||||
addedChildrenList.details = addedChildrenList.details.map((row) => {
|
||||
return formatParallelNode(row, t)
|
||||
})
|
||||
}
|
||||
return addedChildrenList
|
||||
}
|
||||
|
||||
return item
|
||||
|
|
|
|||
|
|
@ -714,6 +714,8 @@ const translation = {
|
|||
install: 'Install',
|
||||
installing: 'Installing',
|
||||
},
|
||||
configureModel: 'Configure Model',
|
||||
notAuthorized: 'Not Authorized',
|
||||
model: 'model',
|
||||
toolbox: 'toolbox',
|
||||
strategyNotSet: 'Agentic strategy Not Set',
|
||||
|
|
@ -723,6 +725,9 @@ const translation = {
|
|||
toolNotInstallTooltip: '{{tool}} is not installed',
|
||||
toolNotAuthorizedTooltip: '{{tool}} Not Authorized',
|
||||
strategyNotInstallTooltip: '{{strategy}} is not installed',
|
||||
modelSelectorTooltips: {
|
||||
deprecated: 'This model is deprecated',
|
||||
},
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
|||
|
|
@ -717,12 +717,17 @@ const translation = {
|
|||
model: '模型',
|
||||
toolbox: '工具箱',
|
||||
strategyNotSet: '代理策略未设置',
|
||||
configureModel: '配置模型',
|
||||
notAuthorized: '未授权',
|
||||
tools: '工具',
|
||||
maxIterations: '最大迭代次数',
|
||||
modelNotInstallTooltip: '此模型未安装',
|
||||
toolNotInstallTooltip: '{{tool}} 未安装',
|
||||
toolNotAuthorizedTooltip: '{{tool}} 未授权',
|
||||
strategyNotInstallTooltip: '{{strategy}} 未安装',
|
||||
modelSelectorTooltips: {
|
||||
deprecated: '此模型已弃用',
|
||||
},
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
useQuery,
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
const NAME_SPACE = 'agent-strategy'
|
||||
const NAME_SPACE = 'agent_strategy'
|
||||
|
||||
const useStrategyListKey = [NAME_SPACE, 'strategyList']
|
||||
export const useStrategyProviders = () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue