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

This commit is contained in:
Yi 2024-11-01 14:56:11 +08:00
commit 2fd4b6e6d2
54 changed files with 402 additions and 635 deletions

View File

@ -3,11 +3,19 @@ import React from 'react'
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
const ToolsPicker = () => {
const [show, setShow] = React.useState(true)
return (
<ToolPicker
supportAddCustomTool={true}
onSelect={() => { }}
/>
<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>
)
}

View File

@ -27,7 +27,7 @@ import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import type { App } from '@/types/app'
type Props = {
interface Props {
appDetail: App
}
@ -283,7 +283,6 @@ const Annotation: FC<Props> = ({
if (
embeddingModel.embedding_model_name !== annotationConfig?.embedding_model?.embedding_model_name
|| embeddingModel.embedding_provider_name !== annotationConfig?.embedding_model?.embedding_provider_name
|| embeddingModel.plugin_id !== annotationConfig?.embedding_model?.plugin_id
) {
const { job_id: jobId }: any = await updateAnnotationStatus(appDetail.id, AnnotationEnableStatus.enable, embeddingModel, score)
await ensureJobCompleted(jobId, AnnotationEnableStatus.enable)

View File

@ -23,9 +23,8 @@ export type HitHistoryItem = {
}
export type EmbeddingModelConfig = {
plugin_id: string
embedding_model_name: string
embedding_provider_name: string
embedding_model_name: string
}
export enum AnnotationEnableStatus {

View File

@ -27,10 +27,12 @@ import { MAX_TOOLS_NUM } from '@/config'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import Tooltip from '@/app/components/base/tooltip'
import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other'
import AddToolModal from '@/app/components/tools/add-tool-modal'
// import AddToolModal from '@/app/components/tools/add-tool-modal'
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
import { updateBuiltInToolCredential } from '@/service/tools'
import cn from '@/utils/classnames'
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
type AgentToolWithMoreInfo = AgentTool & { icon: any; collection?: Collection } | null
const AgentTools: FC = () => {
@ -81,6 +83,21 @@ const AgentTools: FC = () => {
const [isDeleting, setIsDeleting] = useState<number>(-1)
const handleSelectTool = (tool: ToolDefaultValue) => {
const newModelConfig = produce(modelConfig, (draft) => {
draft.agentConfig.tools.push({
provider_id: tool.provider_id,
provider_type: tool.provider_type as CollectionType,
provider_name: tool.provider_name,
tool_name: tool.tool_name,
tool_label: tool.tool_label,
tool_parameters: {},
enabled: true,
})
})
setModelConfig(newModelConfig)
}
return (
<>
<Panel
@ -107,7 +124,14 @@ const AgentTools: FC = () => {
{tools.length < MAX_TOOLS_NUM && (
<>
<div className='ml-3 mr-1 h-3.5 w-px bg-gray-200'></div>
<OperationBtn type="add" onClick={() => setIsShowChooseTool(true)} />
<ToolPicker
trigger={<OperationBtn type="add" />}
isShow={isShowChooseTool}
onShowChange={setIsShowChooseTool}
disabled={false}
supportAddCustomTool
onSelect={handleSelectTool}
/>
</>
)}
</div>
@ -125,8 +149,8 @@ const AgentTools: FC = () => {
{item.isDeleted && <DefaultToolIcon className='w-5 h-5' />}
{!item.isDeleted && (
<div className={cn((item.notAuthor || !item.enabled) && 'opacity-50')}>
{typeof item.icon === 'string' && <div className='w-5 h-5 bg-cover bg-center rounded-md' style={{ backgroundImage: `url(${item.icon})` }}/>}
{typeof item.icon !== 'string' && <AppIcon className='rounded-md' size='xs' icon={item.icon?.content} background={item.icon?.background}/>}
{typeof item.icon === 'string' && <div className='w-5 h-5 bg-cover bg-center rounded-md' style={{ backgroundImage: `url(${item.icon})` }} />}
{typeof item.icon !== 'string' && <AppIcon className='rounded-md' size='xs' icon={item.icon?.content} background={item.icon?.background} />}
</div>
)}
<div
@ -245,9 +269,6 @@ const AgentTools: FC = () => {
))}
</div >
</Panel >
{isShowChooseTool && (
<AddToolModal onHide={() => setIsShowChooseTool(false)} />
)}
{isShowSettingTool && (
<SettingBuiltInTool
toolName={currentTool?.tool_name as string}

View File

@ -25,7 +25,7 @@ import { useSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowled
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
type Props = {
interface Props {
datasetConfigs: DatasetConfigs
onChange: (configs: DatasetConfigs, isRetrievalModeChange?: boolean) => void
isInWorkflow?: boolean
@ -71,7 +71,6 @@ const ConfigContent: FC<Props> = ({
? {
...rerankDefaultModel,
provider: rerankDefaultModel.provider.provider,
plugin_id: rerankDefaultModel.provider.plugin_id,
}
: undefined,
)
@ -81,14 +80,12 @@ const ConfigContent: FC<Props> = ({
return {
provider_name: datasetConfigs.reranking_model.reranking_provider_name,
model_name: datasetConfigs.reranking_model.reranking_model_name,
plugin_id: datasetConfigs.reranking_model.reranking_plugin_id,
}
}
else if (rerankDefaultModel) {
return {
provider_name: rerankDefaultModel.provider.provider,
model_name: rerankDefaultModel.model,
plugin_id: rerankDefaultModel.provider.plugin_id,
}
}
})()
@ -175,7 +172,7 @@ const ConfigContent: FC<Props> = ({
return false
return datasetConfigs.reranking_enable
}, [canManuallyToggleRerank, datasetConfigs.reranking_enable, isRerankDefaultModelValid])
}, [canManuallyToggleRerank, datasetConfigs.reranking_enable])
const handleDisabledSwitchClick = useCallback(() => {
if (!currentRerankModel && !showRerankModel)
@ -303,14 +300,13 @@ const ConfigContent: FC<Props> = ({
</div>
<div>
<ModelSelector
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name, plugin_id: rerankModel?.plugin_id }}
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name }}
onSelect={(v) => {
onChange({
...datasetConfigs,
reranking_model: {
reranking_provider_name: v.provider,
reranking_model_name: v.model,
reranking_plugin_id: v.plugin_id,
},
})
}}
@ -388,7 +384,6 @@ const ConfigContent: FC<Props> = ({
portalToFollowElemContentClassName='!z-[1002]'
isAdvancedMode={true}
mode={model?.mode}
pluginId={model?.plugin_id}
provider={model?.provider}
completionParams={model?.completion_params}
modelId={model?.name}

View File

@ -36,13 +36,12 @@ const ModelParameterTrigger: FC<ModelParameterTriggerProps> = ({
const language = useLanguage()
const index = multipleModelConfigs.findIndex(v => v.id === modelAndParameter.id)
const handleSelectModel = ({ modelId, provider, pluginId }: { modelId: string; provider: string; pluginId: string }) => {
const handleSelectModel = ({ modelId, provider }: { modelId: string; provider: string }) => {
const newModelConfigs = [...multipleModelConfigs]
newModelConfigs[index] = {
...newModelConfigs[index],
model: modelId,
provider,
plugin_id: pluginId,
}
onMultipleModelConfigsChange(true, newModelConfigs)
}
@ -59,7 +58,6 @@ const ModelParameterTrigger: FC<ModelParameterTriggerProps> = ({
<ModelParameterModal
mode={mode}
isAdvancedMode={isAdvancedMode}
pluginId={modelAndParameter.plugin_id}
provider={modelAndParameter.provider}
modelId={modelAndParameter.model}
completionParams={modelAndParameter.parameters}

View File

@ -1,7 +1,6 @@
export type ModelAndParameter = {
id: string
model: string
plugin_id: string
provider: string
parameters: Record<string, any>
}

View File

@ -72,7 +72,7 @@ import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import NewFeaturePanel from '@/app/components/base/features/new-feature-panel'
import { fetchFileUploadConfig } from '@/service/common'
type PublishConfig = {
interface PublishConfig {
modelConfig: ModelConfig
completionParams: FormValue
}
@ -156,7 +156,6 @@ const Configuration: FC = () => {
const setCompletionParams = (value: FormValue) => {
const params = { ...value }
// eslint-disable-next-line ts/no-use-before-define
if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) {
params.stop = getTempStop()
setTempStop([])
@ -165,7 +164,6 @@ const Configuration: FC = () => {
}
const [modelConfig, doSetModelConfig] = useState<ModelConfig>({
plugin_id: 'langgenius',
provider: 'openai',
model_id: 'gpt-3.5-turbo',
mode: ModelModeType.unset,
@ -200,7 +198,6 @@ const Configuration: FC = () => {
reranking_model: {
reranking_provider_name: '',
reranking_model_name: '',
reranking_plugin_id: '',
},
top_k: DATASET_DEFAULT.top_k,
score_threshold_enabled: false,
@ -282,7 +279,6 @@ const Configuration: FC = () => {
reranking_model: restConfigs.reranking_model && {
reranking_provider_name: restConfigs.reranking_model.reranking_provider_name,
reranking_model_name: restConfigs.reranking_model.reranking_model_name,
reranking_plugin_id: restConfigs.reranking_model.reranking_plugin_id,
},
retrieval_model,
score_threshold_enabled,
@ -324,7 +320,6 @@ const Configuration: FC = () => {
textGenerationModelList,
} = useTextGenerationCurrentProviderAndModelAndModelList(
{
plugin_id: modelConfig.plugin_id,
provider: modelConfig.provider,
model: modelConfig.model_id,
},
@ -355,7 +350,6 @@ const Configuration: FC = () => {
const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true)
const setPromptMode = async (mode: PromptMode) => {
if (mode === PromptMode.advanced) {
// eslint-disable-next-line ts/no-use-before-define
await migrateToDefaultPrompt()
setCanReturnToSimpleMode(true)
}
@ -551,7 +545,6 @@ const Configuration: FC = () => {
const config = {
modelConfig: {
plugin_id: model.plugin_id,
provider: model.provider,
model_id: model.name,
mode: model.mode,
@ -770,8 +763,8 @@ const Configuration: FC = () => {
handleMultipleModelConfigsChange(
true,
[
{ id: `${Date.now()}`, model: modelConfig.model_id, plugin_id: modelConfig.plugin_id, provider: modelConfig.provider, parameters: completionParams },
{ id: `${Date.now()}-no-repeat`, model: '', plugin_id: '', provider: '', parameters: {} },
{ id: `${Date.now()}`, model: modelConfig.model_id, provider: modelConfig.provider, parameters: completionParams },
{ id: `${Date.now()}-no-repeat`, model: '', provider: '', parameters: {} },
],
)
setAppSiderbarExpand('collapse')
@ -893,7 +886,6 @@ const Configuration: FC = () => {
<ModelParameterModal
isAdvancedMode={isAdvancedMode}
mode={mode}
pluginId={modelConfig.plugin_id}
provider={modelConfig.provider}
completionParams={completionParams}
modelId={modelConfig.model_id}

View File

@ -1,327 +0,0 @@
'use client'
import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import produce from 'immer'
import {
RiAddLine,
RiDeleteBinLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import { ReactSortable } from 'react-sortablejs'
import {
useFeatures,
useFeaturesStore,
} from '../../hooks'
import type { OnFeaturesChange } from '../../types'
import cn from '@/utils/classnames'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import Button from '@/app/components/base/button'
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
import { getInputKeys } from '@/app/components/base/block-input'
import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var'
import { getNewVar } from '@/utils/var'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
import type { PromptVariable } from '@/models/debug'
import type { InputVar } from '@/app/components/workflow/types'
const MAX_QUESTION_NUM = 5
export type OpeningStatementProps = {
onChange?: OnFeaturesChange
readonly?: boolean
promptVariables?: PromptVariable[]
onAutoAddPromptVariable: (variable: PromptVariable[]) => void
workflowVariables?: InputVar[]
}
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
const OpeningStatement: FC<OpeningStatementProps> = ({
onChange,
readonly,
promptVariables = [],
onAutoAddPromptVariable,
workflowVariables = [],
}) => {
const { t } = useTranslation()
const featureStore = useFeaturesStore()
const openingStatement = useFeatures(s => s.features.opening)
const value = openingStatement?.opening_statement || ''
const suggestedQuestions = openingStatement?.suggested_questions || []
const [notIncludeKeys, setNotIncludeKeys] = useState<string[]>([])
const hasValue = !!(value || '').trim()
const inputRef = useRef<HTMLTextAreaElement>(null)
const [isFocus, { setTrue: didSetFocus, setFalse: setBlur }] = useBoolean(false)
const setFocus = () => {
didSetFocus()
setTimeout(() => {
const input = inputRef.current
if (input) {
input.focus()
input.setSelectionRange(input.value.length, input.value.length)
}
}, 0)
}
const [tempValue, setTempValue] = useState(value)
useEffect(() => {
setTempValue(value || '')
}, [value])
const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(suggestedQuestions || [])
const notEmptyQuestions = tempSuggestedQuestions.filter(question => !!question && question.trim())
const coloredContent = (tempValue || '')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
.replace(/\n/g, '<br />')
const handleEdit = () => {
if (readonly)
return
setFocus()
}
const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
const handleCancel = () => {
setBlur()
setTempValue(value)
setTempSuggestedQuestions(suggestedQuestions)
}
const handleConfirm = () => {
const keys = getInputKeys(tempValue)
const promptKeys = promptVariables.map(item => item.key)
const workflowVariableKeys = workflowVariables.map(item => item.variable)
let notIncludeKeys: string[] = []
if (promptKeys.length === 0 && workflowVariables.length === 0) {
if (keys.length > 0)
notIncludeKeys = keys
}
else {
if (workflowVariables.length > 0)
notIncludeKeys = keys.filter(key => !workflowVariableKeys.includes(key))
else notIncludeKeys = keys.filter(key => !promptKeys.includes(key))
}
if (notIncludeKeys.length > 0) {
setNotIncludeKeys(notIncludeKeys)
showConfirmAddVar()
return
}
setBlur()
const { getState } = featureStore!
const {
features,
setFeatures,
} = getState()
const newFeatures = produce(features, (draft) => {
if (draft.opening) {
draft.opening.opening_statement = tempValue
draft.opening.suggested_questions = tempSuggestedQuestions
}
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}
const cancelAutoAddVar = () => {
const { getState } = featureStore!
const {
features,
setFeatures,
} = getState()
const newFeatures = produce(features, (draft) => {
if (draft.opening)
draft.opening.opening_statement = tempValue
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
hideConfirmAddVar()
setBlur()
}
const autoAddVar = () => {
const { getState } = featureStore!
const {
features,
setFeatures,
} = getState()
const newFeatures = produce(features, (draft) => {
if (draft.opening)
draft.opening.opening_statement = tempValue
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))])
hideConfirmAddVar()
setBlur()
}
const headerRight = !readonly ? (
isFocus ? (
<div className='flex items-center space-x-1'>
<Button
variant='ghost'
size='small'
onClick={handleCancel}
>
{t('common.operation.cancel')}
</Button>
<Button size='small' onClick={handleConfirm} variant="primary">{t('common.operation.save')}</Button>
</div>
) : (
<OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpener') as string} onClick={handleEdit} />
)
) : null
const renderQuestions = () => {
return isFocus ? (
<div>
<div className='flex items-center py-2'>
<div className='shrink-0 flex space-x-0.5 leading-[18px] text-xs font-medium text-gray-500'>
<div className='uppercase'>{t('appDebug.openingStatement.openingQuestion')}</div>
<div>·</div>
<div>{tempSuggestedQuestions.length}/{MAX_QUESTION_NUM}</div>
</div>
<div className='ml-3 grow w-0 h-px bg-[#243, 244, 246]'></div>
</div>
<ReactSortable
className="space-y-1"
list={tempSuggestedQuestions.map((name, index) => {
return {
id: index,
name,
}
})}
setList={list => setTempSuggestedQuestions(list.map(item => item.name))}
handle='.handle'
ghostClass="opacity-50"
animation={150}
>
{tempSuggestedQuestions.map((question, index) => {
return (
<div className='group relative rounded-lg border border-gray-200 flex items-center pl-2.5 hover:border-gray-300 hover:bg-white' key={index}>
<div className='handle flex items-center justify-center w-4 h-4 cursor-grab'>
<svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M1 2C1.55228 2 2 1.55228 2 1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1C0 1.55228 0.447715 2 1 2ZM1 6C1.55228 6 2 5.55228 2 5C2 4.44772 1.55228 4 1 4C0.447715 4 0 4.44772 0 5C0 5.55228 0.447715 6 1 6ZM6 1C6 1.55228 5.55228 2 5 2C4.44772 2 4 1.55228 4 1C4 0.447715 4.44772 0 5 0C5.55228 0 6 0.447715 6 1ZM5 6C5.55228 6 6 5.55228 6 5C6 4.44772 5.55228 4 5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6ZM2 9C2 9.55229 1.55228 10 1 10C0.447715 10 0 9.55229 0 9C0 8.44771 0.447715 8 1 8C1.55228 8 2 8.44771 2 9ZM5 10C5.55228 10 6 9.55229 6 9C6 8.44771 5.55228 8 5 8C4.44772 8 4 8.44771 4 9C4 9.55229 4.44772 10 5 10Z" fill="#98A2B3" />
</svg>
</div>
<input
type="input"
value={question || ''}
onChange={(e) => {
const value = e.target.value
setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => {
if (index === i)
return value
return item
}))
}}
className={'w-full overflow-x-auto pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer rounded-lg'}
/>
<div
className='block absolute top-1/2 translate-y-[-50%] right-1.5 p-1 rounded-md cursor-pointer hover:bg-[#FEE4E2] hover:text-[#D92D20]'
onClick={() => {
setTempSuggestedQuestions(tempSuggestedQuestions.filter((_, i) => index !== i))
}}
>
<RiDeleteBinLine className='w-3.5 h-3.5' />
</div>
</div>
)
})}</ReactSortable>
{tempSuggestedQuestions.length < MAX_QUESTION_NUM && (
<div
onClick={() => { setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }}
className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400 bg-gray-100 hover:bg-gray-200'>
<RiAddLine className='w-4 h-4' />
<div className='text-gray-500 text-[13px]'>{t('appDebug.variableConfig.addOption')}</div>
</div>
)}
</div>
) : (
<div className='mt-1.5 flex flex-wrap'>
{notEmptyQuestions.map((question, index) => {
return (
<div key={index} className='mt-1 mr-1 max-w-full truncate last:mr-0 shrink-0 leading-8 items-center px-2.5 rounded-lg border border-gray-200 shadow-xs bg-white text-[13px] font-normal text-gray-900 cursor-pointer'>
{question}
</div>
)
})}
</div>
)
}
return (
<Panel
className={cn(isShowConfirmAddVar && 'h-[220px]', 'relative !bg-gray-25')}
title={t('appDebug.openingStatement.title')}
headerIcon={
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M8.33353 1.33301C4.83572 1.33301 2.00019 4.16854 2.00019 7.66634C2.00019 8.37301 2.11619 9.05395 2.3307 9.69036C2.36843 9.80229 2.39063 9.86853 2.40507 9.91738L2.40979 9.93383L2.40729 9.93903C2.39015 9.97437 2.36469 10.0218 2.31705 10.11L1.2158 12.1484C1.14755 12.2746 1.07633 12.4064 1.02735 12.5209C0.978668 12.6348 0.899813 12.8437 0.938613 13.0914C0.984094 13.3817 1.15495 13.6373 1.40581 13.7903C1.61981 13.9208 1.843 13.9279 1.96683 13.9264C2.09141 13.925 2.24036 13.9095 2.38314 13.8947L5.81978 13.5395C5.87482 13.5338 5.9036 13.5309 5.92468 13.5292L5.92739 13.529L5.93564 13.532C5.96154 13.5413 5.99666 13.5548 6.0573 13.5781C6.76459 13.8506 7.53244 13.9997 8.33353 13.9997C11.8313 13.9997 14.6669 11.1641 14.6669 7.66634C14.6669 4.16854 11.8313 1.33301 8.33353 1.33301ZM5.9799 5.72116C6.73142 5.08698 7.73164 5.27327 8.33144 5.96584C8.93125 5.27327 9.91854 5.09365 10.683 5.72116C11.4474 6.34867 11.5403 7.41567 10.9501 8.16572C10.5845 8.6304 9.6668 9.47911 9.02142 10.0576C8.78435 10.2702 8.66582 10.3764 8.52357 10.4192C8.40154 10.456 8.26134 10.456 8.13931 10.4192C7.99706 10.3764 7.87853 10.2702 7.64147 10.0576C6.99609 9.47911 6.07839 8.6304 5.71276 8.16572C5.12259 7.41567 5.22839 6.35534 5.9799 5.72116Z" fill="#E74694" />
</svg>
}
headerRight={headerRight}
hasHeaderBottomBorder={!hasValue}
isFocus={isFocus}
>
<div className='text-gray-700 text-sm'>
{(hasValue || (!hasValue && isFocus)) ? (
<>
{isFocus
? (
<div>
<textarea
ref={inputRef}
value={tempValue}
rows={3}
onChange={e => setTempValue(e.target.value)}
className="w-full px-0 text-sm border-0 bg-transparent focus:outline-none "
placeholder={t('appDebug.openingStatement.placeholder') as string}
>
</textarea>
</div>
)
: (
<div dangerouslySetInnerHTML={{
__html: coloredContent,
}}></div>
)}
{renderQuestions()}
</>) : (
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.openingStatement.noDataPlaceHolder')}</div>
)}
{isShowConfirmAddVar && (
<ConfirmAddVar
varNameArr={notIncludeKeys}
onConfirm={autoAddVar}
onCancel={cancelAutoAddVar}
onHide={hideConfirmAddVar}
/>
)}
</div>
</Panel>
)
}
export default React.memo(OpeningStatement)

View File

@ -18,7 +18,6 @@ type Props = {
isShow: boolean
onHide: () => void
onSave: (embeddingModel: {
plugin_id: string
embedding_provider_name: string
embedding_model_name: string
}, score: number) => void
@ -44,13 +43,11 @@ const ConfigParamModal: FC<Props> = ({
const [isLoading, setLoading] = useState(false)
const [embeddingModel, setEmbeddingModel] = useState(oldAnnotationConfig.embedding_model
? {
plugin_id: oldAnnotationConfig.embedding_model.plugin_id,
providerName: oldAnnotationConfig.embedding_model.embedding_provider_name,
modelName: oldAnnotationConfig.embedding_model.embedding_model_name,
}
: (embeddingsDefaultModel
? {
plugin_id: embeddingsDefaultModel.provider.plugin_id,
providerName: embeddingsDefaultModel.provider.provider,
modelName: embeddingsDefaultModel.model,
}
@ -70,7 +67,6 @@ const ConfigParamModal: FC<Props> = ({
}
setLoading(true)
await onSave({
plugin_id: embeddingModel.plugin_id,
embedding_provider_name: embeddingModel.providerName,
embedding_model_name: embeddingModel.modelName,
}, annotationConfig.score_threshold)
@ -111,14 +107,12 @@ const ConfigParamModal: FC<Props> = ({
<div className='pt-1'>
<ModelSelector
defaultModel={embeddingModel && {
plugin_id: '',
provider: embeddingModel.providerName,
model: embeddingModel.modelName,
}}
modelList={embeddingsModelList}
onSelect={(val) => {
setEmbeddingModel({
plugin_id: val.plugin_id,
providerName: val.provider,
modelName: val.model,
})

View File

@ -199,13 +199,13 @@ export default function AccountSetting({
)}
</div>
<div className='px-4 sm:px-8 pt-2'>
{activeMenu === 'provider' && <ModelProviderPage searchText={searchValue} />}
{activeMenu === 'members' && <MembersPage />}
{activeMenu === 'billing' && <BillingPage />}
{activeMenu === 'language' && <LanguagePage />}
{activeMenu === 'provider' && <ModelProviderPage />}
{activeMenu === 'data-source' && <DataSourcePage />}
{activeMenu === 'api-based-extension' && <ApiBasedExtensionPage />}
{activeMenu === 'custom' && <CustomPage />}
{activeMenu === 'language' && <LanguagePage />}
</div>
</div>
</div>

View File

@ -1,6 +1,6 @@
export type FormValue = Record<string, any>
export type TypeWithI18N<T = string> = {
export interface TypeWithI18N<T = string> {
en_US: T
zh_Hans: T
[key: string]: T
@ -17,7 +17,7 @@ export enum FormTypeEnum {
file = 'file',
}
export type FormOption = {
export interface FormOption {
label: TypeWithI18N
value: string
show_on: FormShowOnObject[]
@ -89,12 +89,12 @@ export enum CustomConfigurationStatusEnum {
noConfigure = 'no-configure',
}
export type FormShowOnObject = {
export interface FormShowOnObject {
variable: string
value: string
}
export type CredentialFormSchemaBase = {
export interface CredentialFormSchemaBase {
variable: string
label: TypeWithI18N
type: FormTypeEnum
@ -112,7 +112,7 @@ export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: Fo
export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder?: TypeWithI18N }
export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput
export type ModelItem = {
export interface ModelItem {
model: string
label: TypeWithI18N
model_type: ModelTypeEnum
@ -141,7 +141,7 @@ export enum QuotaUnitEnum {
credits = 'credits',
}
export type QuotaConfiguration = {
export interface QuotaConfiguration {
quota_type: CurrentSystemQuotaTypeEnum
quota_unit: QuotaUnitEnum
quota_limit: number
@ -150,8 +150,7 @@ export type QuotaConfiguration = {
is_valid: boolean
}
export type ModelProvider = {
plugin_id: string
export interface ModelProvider {
provider: string
label: TypeWithI18N
description?: TypeWithI18N
@ -185,8 +184,7 @@ export type ModelProvider = {
}
}
export type Model = {
plugin_id: string
export interface Model {
provider: string
icon_large: TypeWithI18N
icon_small: TypeWithI18N
@ -195,29 +193,27 @@ export type Model = {
status: ModelStatusEnum
}
export type DefaultModelResponse = {
export interface DefaultModelResponse {
model: string
model_type: ModelTypeEnum
provider: {
plugin_id: string
provider: string
icon_large: TypeWithI18N
icon_small: TypeWithI18N
}
}
export type DefaultModel = {
plugin_id: string
export interface DefaultModel {
provider: string
model: string
}
export type CustomConfigurationModelFixedFields = {
export interface CustomConfigurationModelFixedFields {
__model_name: string
__model_type: ModelTypeEnum
}
export type ModelParameterRule = {
export interface ModelParameterRule {
default?: number | string | boolean | string[]
help?: TypeWithI18N
label: TypeWithI18N
@ -232,7 +228,7 @@ export type ModelParameterRule = {
tagPlaceholder?: TypeWithI18N
}
export type ModelLoadBalancingConfigEntry = {
export interface ModelLoadBalancingConfigEntry {
/** model balancing config entry id */
id?: string
/** is config entry enabled */
@ -247,7 +243,7 @@ export type ModelLoadBalancingConfigEntry = {
ttl?: number
}
export type ModelLoadBalancingConfig = {
export interface ModelLoadBalancingConfig {
enabled: boolean
configs: ModelLoadBalancingConfigEntry[]
}

View File

@ -11,7 +11,6 @@ import type {
DefaultModel,
DefaultModelResponse,
Model,
ModelProvider,
ModelTypeEnum,
} from './declarations'
import {
@ -37,12 +36,11 @@ export const useSystemDefaultModelAndModelList: UseDefaultModelAndModelList = (
modelList,
) => {
const currentDefaultModel = useMemo(() => {
const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider.provider && provider.plugin_id === defaultModel?.provider.plugin_id)
const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider.provider)
const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model)
const currentDefaultModel = currentProvider && currentModel && {
model: currentModel.model,
provider: currentProvider.provider,
plugin_id: currentProvider.plugin_id,
}
return currentDefaultModel
@ -64,20 +62,20 @@ export const useLanguage = () => {
}
export const useProviderCredentialsAndLoadBalancing = (
provider: ModelProvider,
provider: string,
configurationMethod: ConfigurationMethodEnum,
configured?: boolean,
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
) => {
const { data: predefinedFormSchemasValue, mutate: mutatePredefined } = useSWR(
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured)
? `/workspaces/current/model-providers/${provider.plugin_id}/${provider.provider}/credentials`
? `/workspaces/current/model-providers/${provider}/credentials`
: null,
fetchModelProviderCredentials,
)
const { data: customFormSchemasValue, mutate: mutateCustomized } = useSWR(
(configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields)
? `/workspaces/current/model-providers/${provider.plugin_id}/${provider.provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}`
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}`
: null,
fetchModelProviderCredentials,
)
@ -174,11 +172,7 @@ export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: Mode
const { modelList, defaultModel } = useModelListAndDefaultModel(type)
const { currentProvider, currentModel } = useCurrentProviderAndModel(
modelList,
{
plugin_id: defaultModel?.provider.plugin_id || '',
provider: defaultModel?.provider.provider || '',
model: defaultModel?.model || '',
},
{ provider: defaultModel?.provider.provider || '', model: defaultModel?.model || '' },
)
return {
@ -199,7 +193,6 @@ export const useUpdateModelList = () => {
return updateModelList
}
// deprecated ???
export const useAnthropicBuyQuota = () => {
const [loading, setLoading] = useState(false)

View File

@ -1,16 +1,15 @@
import { useMemo, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import { useDebounce } from 'ahooks'
import {
RiAlertFill,
RiArrowDownSLine,
RiArrowRightUpLine,
RiBrainLine,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
import SystemModelSelector from './system-model-selector'
import ProviderAddedCard, { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
// import ProviderCard from './provider-card'
import type {
CustomConfigurationModelFixedFields,
ModelProvider,
@ -26,16 +25,24 @@ import {
useUpdateModelProviders,
} from './hooks'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import ProviderCard from '@/app/components/plugins/provider-card'
import I18n from '@/context/i18n'
import { useProviderContext } from '@/context/provider-context'
import { useModalContextSelector } from '@/context/modal-context'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import {
useMarketplacePlugins,
} from '@/app/components/plugins/marketplace/hooks'
import { PluginType } from '@/app/components/plugins/types'
import { MARKETPLACE_URL_PREFIX } from '@/config'
import cn from '@/utils/classnames'
import { extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card/card-mock'
type Props = {
searchText: string
}
const ModelProviderPage = () => {
const ModelProviderPage = ({ searchText }: Props) => {
const debouncedSearchText = useDebounce(searchText, { wait: 500 })
const { t } = useTranslation()
const { eventEmitter } = useEventEmitterContextContext()
const updateModelProviders = useUpdateModelProviders()
@ -67,6 +74,18 @@ const ModelProviderPage = () => {
return [configuredProviders, notConfiguredProviders]
}, [providers])
const [filteredConfiguredProviders, filteredNotConfiguredProviders] = useMemo(() => {
const filteredConfiguredProviders = configuredProviders.filter(
provider => provider.provider.toLowerCase().includes(debouncedSearchText.toLowerCase())
|| Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())),
)
const filteredNotConfiguredProviders = notConfiguredProviders.filter(
provider => provider.provider.toLowerCase().includes(debouncedSearchText.toLowerCase())
|| Object.values(provider.label).some(text => text.toLowerCase().includes(debouncedSearchText.toLowerCase())),
)
return [filteredConfiguredProviders, filteredNotConfiguredProviders]
}, [configuredProviders, debouncedSearchText, notConfiguredProviders])
const handleOpenModal = (
provider: ModelProvider,
@ -91,7 +110,7 @@ const ModelProviderPage = () => {
if (configurationMethod === ConfigurationMethodEnum.customizableModel && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
eventEmitter?.emit({
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
payload: provider,
payload: provider.provider,
} as any)
if (CustomConfigurationModelFixedFields?.__model_type)
@ -102,10 +121,28 @@ const ModelProviderPage = () => {
}
const [collapse, setCollapse] = useState(false)
const { locale } = useContext(I18n)
// TODO #Plugin list API#
const pluginList = [toolNotion, extensionDallE, modelGPT4]
const {
plugins,
queryPlugins,
queryPluginsWithDebounced,
isLoading: isPluginsLoading,
} = useMarketplacePlugins()
useEffect(() => {
if (searchText) {
queryPluginsWithDebounced({
query: searchText,
category: PluginType.model,
})
}
else {
queryPlugins({
query: searchText,
category: PluginType.model,
})
}
}, [queryPlugins, queryPluginsWithDebounced, searchText])
return (
<div className='relative pt-1 -mt-2'>
@ -132,7 +169,7 @@ const ModelProviderPage = () => {
/>
</div>
</div>
{!configuredProviders?.length && (
{!filteredConfiguredProviders?.length && (
<div className='mb-2 p-4 rounded-[10px]' style={{ background: 'linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%)' }}>
<div className='w-10 h-10 flex items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur'>
<RiBrainLine className='w-5 h-5 text-text-primary' />
@ -141,9 +178,9 @@ const ModelProviderPage = () => {
<div className='mt-1 text-text-tertiary system-xs-regular'>{t('common.modelProvider.emptyProviderTip')}</div>
</div>
)}
{!!configuredProviders?.length && (
{!!filteredConfiguredProviders?.length && (
<div className='relative'>
{configuredProviders?.map(provider => (
{filteredConfiguredProviders?.map(provider => (
<ProviderAddedCard
key={provider.provider}
provider={provider}
@ -152,11 +189,11 @@ const ModelProviderPage = () => {
))}
</div>
)}
{false && !!notConfiguredProviders?.length && (
{!!filteredNotConfiguredProviders?.length && (
<>
<div className='flex items-center mb-2 pt-2 text-text-primary system-md-semibold'>{t('common.modelProvider.configureRequired')}</div>
<div className='relative'>
{notConfiguredProviders?.map(provider => (
{filteredNotConfiguredProviders?.map(provider => (
<ProviderAddedCard
notConfigured
key={provider.provider}
@ -176,19 +213,20 @@ const ModelProviderPage = () => {
</div>
<div className='flex items-center mb-2 pt-2'>
<span className='pr-1 text-text-tertiary system-sm-regular'>{t('common.modelProvider.discoverMore')}</span>
<Link target="_blank" href="/plugins" className='inline-flex items-center system-sm-medium text-text-accent'>
<Link target="_blank" href={`${MARKETPLACE_URL_PREFIX}`} className='inline-flex items-center system-sm-medium text-text-accent'>
Dify Marketplace
<RiArrowRightUpLine className='w-4 h-4' />
</Link>
</div>
</div>
{!collapse && (
{!collapse && !isPluginsLoading && (
<div className='grid grid-cols-2 gap-2'>
{pluginList.map((plugin, index) => (
<ProviderCard key={index} installed={false} payload={plugin as any} />
{plugins.map(plugin => (
<ProviderCard key={plugin.plugin_id} payload={plugin} />
))}
</div>
)}
{!collapse && isPluginsLoading && <Loading type='area' />}
</div>
</div>
)

View File

@ -72,7 +72,7 @@ const ModelModal: FC<ModelModalProps> = ({
loadBalancing: originalConfig,
mutate,
} = useProviderCredentialsAndLoadBalancing(
provider,
provider.provider,
configurateMethod,
providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active,
currentCustomConfigurationModelFixedFields,
@ -229,7 +229,6 @@ const ModelModal: FC<ModelModalProps> = ({
setLoading(true)
const res = await saveCredentials(
providerFormSchemaPredefined,
provider.plugin_id,
provider.provider,
encodeSecretValues(value),
{
@ -256,7 +255,6 @@ const ModelModal: FC<ModelModalProps> = ({
const res = await removeCredentials(
providerFormSchemaPredefined,
provider.plugin_id,
provider.provider,
value,
)

View File

@ -209,7 +209,6 @@ const ModelLoadBalancingEntryModal: FC<ModelModalProps> = ({
const res = await validateLoadBalancingCredentials(
providerFormSchemaPredefined,
provider.plugin_id,
provider.provider,
{
...value,

View File

@ -38,9 +38,8 @@ export type ModelParameterModalProps = {
isAdvancedMode: boolean
mode: string
modelId: string
pluginId: string
provider: string
setModel: (model: { modelId: string; provider: string; pluginId: string; mode?: string; features?: string[] }) => void
setModel: (model: { modelId: string; provider: string; mode?: string; features?: string[] }) => void
completionParams: FormValue
onCompletionParamsChange: (newParams: FormValue) => void
hideDebugWithMultipleModel?: boolean
@ -75,7 +74,6 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
portalToFollowElemContentClassName,
isAdvancedMode,
modelId,
pluginId,
provider,
setModel,
completionParams,
@ -90,17 +88,13 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
const { t } = useTranslation()
const { isAPIKeySet } = useProviderContext()
const [open, setOpen] = useState(false)
const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${pluginId}/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules)
const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules)
const {
currentProvider,
currentModel,
activeTextGenerationModelList,
} = useTextGenerationCurrentProviderAndModelAndModelList(
{
plugin_id: pluginId,
provider,
model: modelId,
},
{ provider, model: modelId },
)
const hasDeprecated = !currentProvider || !currentModel
@ -118,12 +112,11 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
})
}
const handleChangeModel = ({ provider, model, plugin_id }: DefaultModel) => {
const handleChangeModel = ({ provider, model }: DefaultModel) => {
const targetProvider = activeTextGenerationModelList.find(modelItem => modelItem.provider === provider)
const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model)
setModel({
modelId: model,
pluginId: plugin_id,
provider,
mode: targetModelItem?.model_properties.mode as string,
features: targetModelItem?.features || [],
@ -208,7 +201,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
{t('common.modelProvider.model').toLocaleUpperCase()}
</div>
<ModelSelector
defaultModel={(provider || modelId) ? { plugin_id: pluginId, provider, model: modelId } : undefined}
defaultModel={(provider || modelId) ? { provider, model: modelId } : undefined}
modelList={activeTextGenerationModelList}
onSelect={handleChangeModel}
triggerClassName='max-w-[295px]'

View File

@ -41,11 +41,11 @@ const ModelSelector: FC<ModelSelectorProps> = ({
defaultModel,
)
const handleSelect = (pluginId: string, provider: string, model: ModelItem) => {
const handleSelect = (provider: string, model: ModelItem) => {
setOpen(false)
if (onSelect)
onSelect({ plugin_id: pluginId, provider, model: model.model })
onSelect({ provider, model: model.model })
}
const handleToggle = () => {

View File

@ -25,7 +25,7 @@ import Tooltip from '@/app/components/base/tooltip'
type PopupItemProps = {
defaultModel?: DefaultModel
model: Model
onSelect: (pluginId: string, provider: string, model: ModelItem) => void
onSelect: (provider: string, model: ModelItem) => void
}
const PopupItem: FC<PopupItemProps> = ({
defaultModel,
@ -39,11 +39,11 @@ const PopupItem: FC<PopupItemProps> = ({
const updateModelList = useUpdateModelList()
const updateModelProviders = useUpdateModelProviders()
const currentProvider = modelProviders.find(provider => provider.provider === model.provider)!
const handleSelect = (pluginId: string, provider: string, modelItem: ModelItem) => {
const handleSelect = (provider: string, modelItem: ModelItem) => {
if (modelItem.status !== ModelStatusEnum.active)
return
onSelect(pluginId, provider, modelItem)
onSelect(provider, modelItem)
}
const handleOpenModelModal = () => {
setShowModelModal({
@ -80,7 +80,7 @@ const PopupItem: FC<PopupItemProps> = ({
group relative flex items-center px-3 py-1.5 h-8 rounded-lg
${modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-gray-50' : 'cursor-not-allowed hover:bg-gray-50/60'}
`}
onClick={() => handleSelect(model.plugin_id, model.provider, modelItem)}
onClick={() => handleSelect(model.provider, modelItem)}
>
<ModelIcon
className={`

View File

@ -15,7 +15,7 @@ import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
type PopupProps = {
defaultModel?: DefaultModel
modelList: Model[]
onSelect: (pluginId: string, provider: string, model: ModelItem) => void
onSelect: (provider: string, model: ModelItem) => void
}
const Popup: FC<PopupProps> = ({
defaultModel,

View File

@ -41,7 +41,7 @@ const CredentialPanel: FC<CredentialPanelProps> = ({
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
const res = await changeModelProviderPriority({
url: `/workspaces/current/model-providers/${provider.plugin_id}/${provider.provider}/preferred-provider-type`,
url: `/workspaces/current/model-providers/${provider.provider}/preferred-provider-type`,
body: {
preferred_provider_type: key,
},
@ -57,7 +57,7 @@ const CredentialPanel: FC<CredentialPanelProps> = ({
eventEmitter?.emit({
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
payload: provider,
payload: provider.provider,
} as any)
}
}

View File

@ -52,12 +52,12 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
const showQuota = systemConfig.enabled && [...MODEL_PROVIDER_QUOTA_GET_PAID].includes(provider.provider) && !IS_CE_EDITION
const showCredential = configurationMethods.includes(ConfigurationMethodEnum.predefinedModel) && isCurrentWorkspaceManager
const getModelList = async (pluginID: string, providerName: string) => {
const getModelList = async (providerName: string) => {
if (loading)
return
try {
setLoading(true)
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${pluginID}/${providerName}/models`)
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${providerName}/models`)
setModelList(modelsData.data)
setCollapsed(false)
setFetched(true)
@ -72,12 +72,12 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
return
}
getModelList(provider.plugin_id, provider.provider)
getModelList(provider.provider)
}
eventEmitter?.useSubscription((v: any) => {
if (v?.type === UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST && v.payload.provider === provider.provider)
getModelList(v.payload.plugin_id, v.payload.provider)
if (v?.type === UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST && v.payload === provider.provider)
getModelList(v.payload)
})
return (
@ -172,7 +172,7 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
models={modelList}
onCollapse={() => setCollapsed(true)}
onConfig={currentCustomConfigurationModelFixedFields => onOpenModal(ConfigurationMethodEnum.customizableModel, currentCustomConfigurationModelFixedFields)}
onChange={(provider: ModelProvider) => getModelList(provider.plugin_id, provider.provider)}
onChange={(provider: string) => getModelList(provider)}
/>
)
}

View File

@ -33,10 +33,10 @@ const ModelListItem = ({ model, provider, isConfigurable, onConfig, onModifyLoad
const toggleModelEnablingStatus = useCallback(async (enabled: boolean) => {
if (enabled)
await enableModel(`/workspaces/current/model-providers/${provider.plugin_id}/${provider.provider}/models/enable`, { model: model.model, model_type: model.model_type })
await enableModel(`/workspaces/current/model-providers/${provider.provider}/models/enable`, { model: model.model, model_type: model.model_type })
else
await disableModel(`/workspaces/current/model-providers/${provider.plugin_id}/${provider.provider}/models/disable`, { model: model.model, model_type: model.model_type })
}, [model.model, model.model_type, provider.plugin_id, provider.provider])
await disableModel(`/workspaces/current/model-providers/${provider.provider}/models/disable`, { model: model.model, model_type: model.model_type })
}, [model.model, model.model_type, provider.provider])
const { run: debouncedToggleModelEnablingStatus } = useDebounceFn(toggleModelEnablingStatus, { wait: 500 })

View File

@ -23,7 +23,7 @@ type ModelListProps = {
models: ModelItem[]
onCollapse: () => void
onConfig: (currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => void
onChange?: (provider: ModelProvider) => void
onChange?: (provider: string) => void
}
const ModelList: FC<ModelListProps> = ({
provider,

View File

@ -19,7 +19,7 @@ export type ModelLoadBalancingModalProps = {
model: ModelItem
open?: boolean
onClose?: () => void
onSave?: (provider: ModelProvider) => void
onSave?: (provider: string) => void
}
// model balancing config modal
@ -30,7 +30,7 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
const [loading, setLoading] = useState(false)
const { data, mutate } = useSWR(
`/workspaces/current/model-providers/${provider.plugin_id}/${provider.provider}/models/credentials?model=${model.model}&model_type=${model.model_type}`,
`/workspaces/current/model-providers/${provider.provider}/models/credentials?model=${model.model}&model_type=${model.model_type}`,
fetchModelLoadBalancingConfig,
)
@ -94,7 +94,7 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
if (res.result === 'success') {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
mutate()
onSave?.(provider)
onSave?.(provider.provider)
onClose?.()
}
}

View File

@ -94,7 +94,6 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
model_settings: [ModelTypeEnum.textGeneration, ModelTypeEnum.textEmbedding, ModelTypeEnum.rerank, ModelTypeEnum.speech2text, ModelTypeEnum.tts].map((modelType) => {
return {
model_type: modelType,
plugin_id: getCurrentDefaultModelByModelType(modelType)?.plugin_id,
provider: getCurrentDefaultModelByModelType(modelType)?.provider,
model: getCurrentDefaultModelByModelType(modelType)?.model,
}
@ -132,6 +131,7 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
>
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
<Button
className='relative'
variant={notConfigured ? 'primary' : 'secondary'}
size='small'
>

View File

@ -26,15 +26,14 @@ export const isNullOrUndefined = (value: any) => {
return value === undefined || value === null
}
// deprecated ???
export const validateCredentials = async (predefined: boolean, pluginID: string, provider: string, v: FormValue) => {
export const validateCredentials = async (predefined: boolean, provider: string, v: FormValue) => {
let body, url
if (predefined) {
body = {
credentials: v,
}
url = `/workspaces/current/model-providers/${pluginID}/${provider}/credentials/validate`
url = `/workspaces/current/model-providers/${provider}/credentials/validate`
}
else {
const { __model_name, __model_type, ...credentials } = v
@ -43,7 +42,7 @@ export const validateCredentials = async (predefined: boolean, pluginID: string,
model_type: __model_type,
credentials,
}
url = `/workspaces/current/model-providers/${pluginID}/${provider}/models/credentials/validate`
url = `/workspaces/current/model-providers/${provider}/models/credentials/validate`
}
try {
const res = await validateModelProvider({ url, body })
@ -57,14 +56,14 @@ export const validateCredentials = async (predefined: boolean, pluginID: string,
}
}
export const validateLoadBalancingCredentials = async (predefined: boolean, pluginID: string, provider: string, v: FormValue, id?: string): Promise<{
export const validateLoadBalancingCredentials = async (predefined: boolean, provider: string, v: FormValue, id?: string): Promise<{
status: ValidatedStatus
message?: string
}> => {
const { __model_name, __model_type, ...credentials } = v
try {
const res = await validateModelLoadBalancingCredentials({
url: `/workspaces/current/model-providers/${pluginID}/${provider}/models/load-balancing-configs/${id ? `${id}/` : ''}credentials-validate`,
url: `/workspaces/current/model-providers/${provider}/models/load-balancing-configs/${id ? `${id}/` : ''}credentials-validate`,
body: {
model: __model_name,
model_type: __model_type,
@ -81,7 +80,7 @@ export const validateLoadBalancingCredentials = async (predefined: boolean, plug
}
}
export const saveCredentials = async (predefined: boolean, pluginID: string, provider: string, v: FormValue, loadBalancing?: ModelLoadBalancingConfig) => {
export const saveCredentials = async (predefined: boolean, provider: string, v: FormValue, loadBalancing?: ModelLoadBalancingConfig) => {
let body, url
if (predefined) {
@ -90,7 +89,7 @@ export const saveCredentials = async (predefined: boolean, pluginID: string, pro
credentials: v,
load_balancing: loadBalancing,
}
url = `/workspaces/current/model-providers/${pluginID}/${provider}`
url = `/workspaces/current/model-providers/${provider}`
}
else {
const { __model_name, __model_type, ...credentials } = v
@ -100,7 +99,7 @@ export const saveCredentials = async (predefined: boolean, pluginID: string, pro
credentials,
load_balancing: loadBalancing,
}
url = `/workspaces/current/model-providers/${pluginID}/${provider}/models`
url = `/workspaces/current/model-providers/${provider}/models`
}
return setModelProvider({ url, body })
@ -120,12 +119,12 @@ export const savePredefinedLoadBalancingConfig = async (provider: string, v: For
return setModelProvider({ url, body })
}
export const removeCredentials = async (predefined: boolean, pluginID: string, provider: string, v: FormValue) => {
export const removeCredentials = async (predefined: boolean, provider: string, v: FormValue) => {
let url = ''
let body
if (predefined) {
url = `/workspaces/current/model-providers/${pluginID}/${provider}`
url = `/workspaces/current/model-providers/${provider}`
}
else {
if (v) {
@ -134,7 +133,7 @@ export const removeCredentials = async (predefined: boolean, pluginID: string, p
model: __model_name,
model_type: __model_type,
}
url = `/workspaces/current/model-providers/${pluginID}/${provider}/models`
url = `/workspaces/current/model-providers/${provider}/models`
}
}

View File

@ -13,10 +13,11 @@ import checkTaskStatus from '../../base/check-task-status'
const i18nPrefix = 'plugin.installModal'
interface Props {
type Props = {
uniqueIdentifier: string
payload: PluginDeclaration
onCancel: () => void
onStartToInstall?: () => void
onInstalled: () => void
onFailed: (message?: string) => void
}
@ -25,6 +26,7 @@ const Installed: FC<Props> = ({
uniqueIdentifier,
payload,
onCancel,
onStartToInstall,
onInstalled,
onFailed,
}) => {
@ -43,6 +45,8 @@ const Installed: FC<Props> = ({
const handleInstall = async () => {
if (isInstalling) return
setIsInstalling(true)
onStartToInstall?.()
try {
const {
all_installed: isInstalled,

View File

@ -18,6 +18,7 @@ type Props = {
uniqueIdentifier: string
payload: PluginManifestInMarket
onCancel: () => void
onStartToInstall?: () => void
onInstalled: () => void
onFailed: (message?: string) => void
}
@ -26,6 +27,7 @@ const Installed: FC<Props> = ({
uniqueIdentifier,
payload,
onCancel,
onStartToInstall,
onInstalled,
onFailed,
}) => {
@ -43,6 +45,7 @@ const Installed: FC<Props> = ({
const handleInstall = async () => {
if (isInstalling) return
onStartToInstall?.()
setIsInstalling(true)
try {
@ -90,7 +93,7 @@ const Installed: FC<Props> = ({
</>
)
}</>)
}, [payload])
}, [payload.latest_version, supportCheckInstalled])
return (
<>

View File

@ -130,7 +130,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type,
})
setPlugins(undefined)
setPlugins([])
return
}

View File

@ -40,7 +40,7 @@ export const useMarketplaceCollectionsAndPlugins = () => {
export const useMarketplacePlugins = () => {
const [isLoading, setIsLoading] = useState(false)
const [plugins, setPlugins] = useState<Plugin[]>()
const [plugins, setPlugins] = useState<Plugin[]>([])
const queryPlugins = useCallback(async (query: PluginsSearchParams) => {
setIsLoading(true)

View File

@ -1,8 +1,10 @@
'use client'
import { RiArrowRightUpLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import Card from '@/app/components/plugins/card'
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
import type { Plugin } from '@/app/components/plugins/types'
import { MARKETPLACE_URL_PREFIX } from '@/config'
import Button from '@/app/components/base/button'
type CardWrapperProps = {
@ -13,6 +15,7 @@ const CardWrapper = ({
plugin,
showInstallButton,
}: CardWrapperProps) => {
const { t } = useTranslation()
return (
<div className='group relative rounded-xl cursor-pointer'>
<Card
@ -32,13 +35,15 @@ const CardWrapper = ({
variant='primary'
className='flex-1'
>
Install
{t('plugin.detailPanel.operation.install')}
</Button>
<Button
className='flex-1'
>
Details
<RiArrowRightUpLine className='ml-1 w-4 h-4' />
<a href={`${MARKETPLACE_URL_PREFIX}/plugin/${plugin.org}/${plugin.name}`} target='_blank' className='flex items-center gap-0.5'>
{t('plugin.detailPanel.operation.detail')}
<RiArrowRightUpLine className='ml-1 w-4 h-4' />
</a>
</Button>
</div>
)

View File

@ -1,6 +1,5 @@
'use client'
import { RiCloseLine } from '@remixicon/react'
import { useMarketplaceContext } from '../context'
import TagsFilter from './tags-filter'
import ActionButton from '@/app/components/base/action-button'
import cn from '@/utils/classnames'
@ -11,6 +10,8 @@ type SearchBoxProps = {
inputClassName?: string
tags: string[]
onTagsChange: (tags: string[]) => void
size?: 'small' | 'large'
placeholder?: string
}
const SearchBox = ({
search,
@ -18,36 +19,39 @@ const SearchBox = ({
inputClassName,
tags,
onTagsChange,
size = 'small',
placeholder = 'Search tools...',
}: SearchBoxProps) => {
const intersected = useMarketplaceContext(v => v.intersected)
const searchPluginText = useMarketplaceContext(v => v.searchPluginText)
const handleSearchPluginTextChange = useMarketplaceContext(v => v.handleSearchPluginTextChange)
return (
<div
className={cn(
'sticky top-3 flex items-center mx-auto p-1.5 w-[640px] h-11 border border-components-chat-input-border bg-components-panel-bg-blur rounded-xl shadow-md z-[11]',
'flex items-center z-[11]',
size === 'large' && 'p-1.5 bg-components-panel-bg-blur rounded-xl shadow-md border border-components-chat-input-border',
size === 'small' && 'p-0.5 bg-components-input-bg-normal rounded-lg',
inputClassName,
!intersected && 'w-[508px] transition-[width] duration-300',
)}
>
<TagsFilter
tags={tags}
onTagsChange={onTagsChange}
size={size}
/>
<div className='mx-1 w-[1px] h-3.5 bg-divider-regular'></div>
<div className='grow flex items-center p-1 pl-2'>
<div className='flex items-center mr-2 py-0.5 w-full'>
<input
className='grow block outline-none appearance-none body-md-medium text-text-secondary'
className={cn(
'grow block outline-none appearance-none body-md-medium text-text-secondary bg-transparent',
)}
value={search}
onChange={(e) => {
onSearchChange(e.target.value)
}}
placeholder={placeholder}
/>
{
searchPluginText && (
<ActionButton onClick={() => handleSearchPluginTextChange('')}>
search && (
<ActionButton onClick={() => onSearchChange('')}>
<RiCloseLine className='w-4 h-4' />
</ActionButton>
)

View File

@ -12,11 +12,15 @@ const SearchBoxWrapper = () => {
return (
<SearchBox
inputClassName={cn(!intersected && 'w-[508px] transition-[width] duration-300')}
inputClassName={cn(
'sticky top-3 mx-auto w-[640px]',
!intersected && 'w-[508px] transition-[width] duration-300',
)}
search={searchPluginText}
onSearchChange={handleSearchPluginTextChange}
tags={filterPluginTags}
onTagsChange={handleFilterPluginTagsChange}
size='large'
/>
)
}

View File

@ -18,10 +18,12 @@ import Input from '@/app/components/base/input'
type TagsFilterProps = {
tags: string[]
onTagsChange: (tags: string[]) => void
size: 'small' | 'large'
}
const TagsFilter = ({
tags,
onTagsChange,
size,
}: TagsFilterProps) => {
const [open, setOpen] = useState(false)
const [searchText, setSearchText] = useState('')
@ -56,7 +58,9 @@ const TagsFilter = ({
>
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
<div className={cn(
'flex items-center px-2 py-1 h-8 text-text-tertiary rounded-lg hover:bg-state-base-hover cursor-pointer',
'flex items-center text-text-tertiary rounded-lg hover:bg-state-base-hover cursor-pointer',
size === 'large' && 'px-2 py-1 h-8',
size === 'small' && 'pr-1.5 py-0.5 h-7 pl-1 ',
selectedTagsLength && 'text-text-secondary',
open && 'bg-state-base-hover',
)}>
@ -65,6 +69,8 @@ const TagsFilter = ({
</div>
<div className={cn(
'flex items-center p-1 system-sm-medium',
size === 'large' && 'p-1',
size === 'small' && 'px-0.5 py-1',
)}>
{
!selectedTagsLength && 'All Tags'
@ -95,7 +101,7 @@ const TagsFilter = ({
}
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-10'>
<PortalToFollowElemContent className='z-[1000]'>
<div className='w-[240px] border-[0.5px] border-components-panel-border bg-components-panel-bg-blur rounded-xl shadow-lg'>
<div className='p-2 pb-1'>
<Input

View File

@ -1,8 +1,7 @@
'use client'
import React from 'react'
import { useContext } from 'use-context-selector'
import type { FC } from 'react'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import { RiArrowRightUpLine, RiVerifiedBadgeLine } from '@remixicon/react'
import Badge from '../base/badge'
import type { Plugin } from './types'
@ -11,70 +10,67 @@ import Icon from './card/base/card-icon'
import Title from './card/base/title'
import DownloadCount from './card/base/download-count'
import Button from '@/app/components/base/button'
import { useGetLanguage } from '@/context/i18n'
import { MARKETPLACE_URL_PREFIX } from '@/config'
import cn from '@/utils/classnames'
import I18n from '@/context/i18n'
type Props = {
className?: string
payload: Plugin
installed?: boolean
}
const ProviderCard: FC<Props> = ({
className,
payload,
installed = true,
}) => {
const { locale } = useContext(I18n)
const { t } = useTranslation()
const language = useGetLanguage()
const { org, label } = payload
return (
<div className={cn('group 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)}>
<Link href={`/plugins/test/card?org=${payload.org}&name=${payload.name}`}>
{/* Header */}
<div className="flex">
<Icon src={payload.icon} />
<div className="ml-3 w-0 grow">
<div className="flex items-center h-5">
<Title title={label[locale]} />
<RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />
</div>
<div className='mb-1 flex justify-between items-center h-4'>
<div className='flex items-center'>
<div className='text-text-tertiary system-xs-regular'>{org}</div>
<div className='mx-2 text-text-quaternary system-xs-regular'>·</div>
<DownloadCount downloadCount={payload.install_count || 0} />
</div>
{/* Header */}
<div className="flex">
<Icon src={payload.icon} />
<div className="ml-3 w-0 grow">
<div className="flex items-center h-5">
<Title title={label[language]} />
<RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" />
</div>
<div className='mb-1 flex justify-between items-center h-4'>
<div className='flex items-center'>
<div className='text-text-tertiary system-xs-regular'>{org}</div>
<div className='mx-2 text-text-quaternary system-xs-regular'>·</div>
<DownloadCount downloadCount={payload.install_count || 0} />
</div>
</div>
</div>
<Description className='mt-3' text={payload.brief[locale]} descriptionLineRows={2}></Description>
<div className='mt-3 flex space-x-0.5'>
{['LLM', 'text embedding', 'speech2text'].map(tag => (
<Badge key={tag} text={tag} />
))}
</div>
{!installed && (
<div
className='hidden group-hover:flex items-center gap-2 absolute bottom-0 left-0 right-0 p-4 pt-8'
style={{ background: 'linear-gradient(0deg, #F9FAFB 60.27%, rgba(249, 250, 251, 0.00) 100%)' }}
>
<Button
className='flex-grow'
variant='primary'
>
Install
</Button>
<Button
className='flex-grow'
variant='secondary'
>
Details
<RiArrowRightUpLine className='w-4 h-4' />
</Button>
</div>
)}
</Link>
</div>
<Description className='mt-3' text={payload.brief[language]} descriptionLineRows={2}></Description>
<div className='mt-3 flex space-x-0.5'>
{payload.tags.map(tag => (
<Badge key={tag.name} text={tag.name} />
))}
</div>
<div
className='hidden group-hover:flex items-center gap-2 absolute bottom-0 left-0 right-0 p-4 pt-8 rounded-xl bg-gradient-to-tr from-[#f9fafb] to-[rgba(249,250,251,0)]'
>
<Button
className='flex-grow'
variant='primary'
>
{t('plugin.detailPanel.operation.install')}
</Button>
<Button
className='flex-grow'
variant='secondary'
>
<a href={`${MARKETPLACE_URL_PREFIX}/plugin/${payload.org}/${payload.name}`} target='_blank' className='flex items-center gap-0.5'>
{t('plugin.detailPanel.operation.detail')}
<RiArrowRightUpLine className='w-4 h-4' />
</a>
</Button>
</div>
</div>
)
}

View File

@ -103,6 +103,7 @@ export type Plugin = {
type: PluginType
org: string
name: string
plugin_id: string
version: string
latest_version: string
icon: string

View File

@ -37,7 +37,7 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin
}
else {
queryMarketplaceCollectionsAndPlugins()
setPlugins(undefined)
setPlugins([])
}
}, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, setPlugins])

View File

@ -84,9 +84,12 @@ const AllTools = ({
</div>
<ViewTypeSelect viewType={activeView} onChange={setActiveView} />
{supportAddCustomTool && (
<ActionButton>
<RiAddLine className='w-4 h-4' />
</ActionButton>
<div className='flex items-center'>
<div className='mr-1.5 w-px h-3.5 bg-divider-regular'></div>
<ActionButton className='bg-components-button-primary-bg hover:bg-components-button-primary-bg text-components-button-primary-text hover:text-components-button-primary-text'>
<RiAddLine className='w-4 h-4' />
</ActionButton>
</div>
)}
</div>
<div

View File

@ -1,5 +1,5 @@
'use client'
import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll'
import Item from './item'
@ -30,7 +30,6 @@ const List = ({
wrapElemRef,
nextToStickyELemRef,
})
const stickyClassName = useMemo(() => {
switch (scrollPosition) {
case ScrollPosition.aboveTheWrap:
@ -38,7 +37,7 @@ const List = ({
case ScrollPosition.showing:
return 'bottom-0 pt-3 pb-1'
case ScrollPosition.belowTheWrap:
return 'bottom-0 items-center rounded-b-xl border-t border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg cursor-pointer'
return 'bottom-0 items-center rounded-b-xl border-t border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg rounded-b-lg cursor-pointer'
}
}, [scrollPosition])
@ -46,6 +45,11 @@ const List = ({
handleScroll,
}))
useEffect(() => {
handleScroll()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [list])
const handleHeadClick = () => {
if (scrollPosition === ScrollPosition.belowTheWrap) {
nextToStickyELemRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
@ -57,7 +61,7 @@ const List = ({
if (hasSearchText) {
return (
<Link
className='sticky bottom-0 z-10 flex h-8 px-4 py-1 system-sm-medium items-center rounded-b-xl border-t border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg text-text-accent-light-mode-only cursor-pointer'
className='sticky bottom-0 z-10 flex h-8 px-4 py-1 system-sm-medium items-center border-t border-[0.5px] border-components-panel-border bg-components-panel-bg-blur rounded-b-lg shadow-lg text-text-accent-light-mode-only cursor-pointer'
href={`${marketplaceUrlPrefix}/plugins`}
target='_blank'
>

View File

@ -2,6 +2,15 @@
import type { FC } from 'react'
import React from 'react'
import { useEffect, useState } from 'react'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import type {
OffsetOptions,
Placement,
} from '@floating-ui/react'
import AllTools from '@/app/components/workflow/block-selector/all-tools'
import type { ToolDefaultValue } from './types'
import {
@ -10,16 +19,31 @@ import {
fetchAllWorkflowTools,
} from '@/service/tools'
import type { BlockEnum, ToolWithProvider } from '@/app/components/workflow/types'
import SearchBox from '@/app/components/plugins/marketplace/search-box'
import { useTranslation } from 'react-i18next'
type Props = {
disabled: boolean
trigger: React.ReactNode
placement?: Placement
offset?: OffsetOptions
isShow: boolean
onShowChange: (isShow: boolean) => void
onSelect: (tool: ToolDefaultValue) => void
supportAddCustomTool?: boolean
}
const ToolPicker: FC<Props> = ({
disabled,
trigger,
placement = 'right-start',
offset = 0,
isShow,
onShowChange,
onSelect,
supportAddCustomTool,
}) => {
const { t } = useTranslation()
const [searchText, setSearchText] = useState('')
const [buildInTools, setBuildInTools] = useState<ToolWithProvider[]>([])
@ -37,23 +61,52 @@ const ToolPicker: FC<Props> = ({
})()
}, [])
const handleTriggerClick = () => {
if (disabled) return
onShowChange(true)
}
const handleSelect = (_type: BlockEnum, tool?: ToolDefaultValue) => {
onSelect(tool!)
}
return (
<div className="relative mt-5 mx-auto w-[320px] bg-white">
<input placeholder='search holder' value={searchText} onChange={e => setSearchText(e.target.value)} />
<AllTools
className='mt-1'
searchText={searchText}
onSelect={handleSelect}
buildInTools={buildInTools}
customTools={customTools}
workflowTools={workflowTools}
supportAddCustomTool={supportAddCustomTool}
/>
</div>
<PortalToFollowElem
placement={placement}
offset={offset}
open={isShow}
onOpenChange={onShowChange}
>
<PortalToFollowElemTrigger
onClick={handleTriggerClick}
>
{trigger}
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[1000]'>
<div className="relative w-[320px] min-h-20 rounded-xl bg-components-panel-bg-blur border-[0.5px] border-components-panel-border shadow-lg">
<div className='p-2 pb-1'>
<SearchBox
search={searchText}
onSearchChange={setSearchText}
tags={[]}
onTagsChange={() => { }}
size='small'
placeholder={t('plugin.searchTools')!}
/>
</div>
<AllTools
className='mt-1'
searchText={searchText}
onSelect={handleSelect}
buildInTools={buildInTools}
customTools={customTools}
workflowTools={workflowTools}
supportAddCustomTool={supportAddCustomTool}
/>
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}

View File

@ -132,7 +132,6 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
isInWorkflow
isAdvancedMode={true}
mode={model?.mode}
pluginId={model?.plugin_id}
provider={model?.provider}
completionParams={model?.completion_params}
modelId={model?.name}

View File

@ -123,7 +123,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
},
})
const handleModelChanged = useCallback((model: { provider: string; modelId: string; pluginId: string; mode?: string }) => {
const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => {
const newInputs = produce(inputRef.current, (draft) => {
draft.model.provider = model.provider
draft.model.name = model.modelId
@ -139,7 +139,6 @@ const useConfig = (id: string, payload: LLMNodeType) => {
useEffect(() => {
if (currentProvider?.provider && currentModel?.model && !model.provider) {
handleModelChanged({
pluginId: currentProvider?.plugin_id,
provider: currentProvider?.provider,
modelId: currentModel?.model,
mode: currentModel?.model_properties?.mode as string,

View File

@ -77,7 +77,6 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({
isInWorkflow
isAdvancedMode={true}
mode={model?.mode}
pluginId={model?.plugin_id}
provider={model?.provider}
completionParams={model?.completion_params}
modelId={model?.name}

View File

@ -65,7 +65,6 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({
isInWorkflow
isAdvancedMode={true}
mode={model?.mode}
pluginId={model?.plugin_id}
provider={model?.provider}
completionParams={model.completion_params}
modelId={model.name}

View File

@ -1,6 +1,6 @@
import type { CommonNodeType, Memory, ModelConfig, ValueSelector, VisionSetting } from '@/app/components/workflow/types'
export type Topic = {
export interface Topic {
id: string
name: string
}

View File

@ -37,7 +37,7 @@ export enum ControlMode {
Hand = 'hand',
}
export type Branch = {
export interface Branch {
id: string
name: string
}
@ -68,7 +68,7 @@ export type CommonNodeType<T = {}> = {
height?: number
} & T & Partial<Pick<ToolDefaultValue, 'provider_id' | 'provider_type' | 'provider_name' | 'tool_name'>>
export type CommonEdgeType = {
export interface CommonEdgeType {
_hovering?: boolean
_connectedNodeIsHovering?: boolean
_connectedNodeIsSelected?: boolean
@ -82,14 +82,14 @@ export type CommonEdgeType = {
export type Node<T = {}> = ReactFlowNode<CommonNodeType<T>>
export type SelectedNode = Pick<Node, 'id' | 'data'>
export type NodeProps<T = unknown> = { id: string; data: CommonNodeType<T> }
export type NodePanelProps<T> = {
export interface NodeProps<T = unknown> { id: string; data: CommonNodeType<T> }
export interface NodePanelProps<T> {
id: string
data: CommonNodeType<T>
}
export type Edge = ReactFlowEdge<CommonEdgeType>
export type WorkflowDataUpdater = {
export interface WorkflowDataUpdater {
nodes: Node[]
edges: Edge[]
viewport: Viewport
@ -97,7 +97,7 @@ export type WorkflowDataUpdater = {
export type ValueSelector = string[] // [nodeId, key | obj key path]
export type Variable = {
export interface Variable {
variable: string
label?: string | {
nodeType: BlockEnum
@ -112,14 +112,14 @@ export type Variable = {
isParagraph?: boolean
}
export type EnvironmentVariable = {
export interface EnvironmentVariable {
id: string
name: string
value: any
value_type: 'string' | 'number' | 'secret'
}
export type ConversationVariable = {
export interface ConversationVariable {
id: string
name: string
value_type: ChatVarType
@ -127,13 +127,13 @@ export type ConversationVariable = {
description: string
}
export type GlobalVariable = {
export interface GlobalVariable {
name: string
value_type: 'string' | 'number'
description: string
}
export type VariableWithValue = {
export interface VariableWithValue {
key: string
value: string
}
@ -169,8 +169,7 @@ export type InputVar = {
value_selector?: ValueSelector
} & Partial<UploadFileSetting>
export type ModelConfig = {
plugin_id: string
export interface ModelConfig {
provider: string
name: string
mode: string
@ -188,7 +187,7 @@ export enum EditionType {
jinja2 = 'jinja2',
}
export type PromptItem = {
export interface PromptItem {
id?: string
role?: PromptRole
text: string
@ -201,12 +200,12 @@ export enum MemoryRole {
assistant = 'assistant',
}
export type RolePrefix = {
export interface RolePrefix {
user: string
assistant: string
}
export type Memory = {
export interface Memory {
role_prefix?: RolePrefix
window: {
enabled: boolean
@ -230,7 +229,7 @@ export enum VarType {
any = 'any',
}
export type Var = {
export interface Var {
variable: string
type: VarType
children?: Var[] // if type is obj, has the children struct
@ -241,21 +240,21 @@ export type Var = {
des?: string
}
export type NodeOutPutVar = {
export interface NodeOutPutVar {
nodeId: string
title: string
vars: Var[]
isStartNode?: boolean
}
export type Block = {
export interface Block {
classification?: string
type: BlockEnum
title: string
description?: string
}
export type NodeDefault<T> = {
export interface NodeDefault<T> {
defaultValue: Partial<T>
getAvailablePrevNodes: (isChatMode: boolean) => BlockEnum[]
getAvailableNextNodes: (isChatMode: boolean) => BlockEnum[]
@ -295,19 +294,19 @@ export type OnNodeAdd = (
}
) => void
export type CheckValidRes = {
export interface CheckValidRes {
isValid: boolean
errorMessage?: string
}
export type RunFile = {
export interface RunFile {
type: string
transfer_method: TransferMethod[]
url?: string
upload_file_id?: string
}
export type WorkflowRunningData = {
export interface WorkflowRunningData {
task_id?: string
message_id?: string
conversation_id?: string
@ -332,7 +331,7 @@ export type WorkflowRunningData = {
tracing?: NodeTracing[]
}
export type HistoryWorkflowData = {
export interface HistoryWorkflowData {
id: string
sequence_number: number
status: string
@ -344,7 +343,7 @@ export enum ChangeType {
remove = 'remove',
}
export type MoreInfo = {
export interface MoreInfo {
type: ChangeType
payload?: {
beforeKey: string
@ -364,7 +363,7 @@ export enum SupportUploadFileTypes {
custom = 'custom',
}
export type UploadFileSetting = {
export interface UploadFileSetting {
allowed_file_upload_methods: TransferMethod[]
allowed_file_types: SupportUploadFileTypes[]
allowed_file_extensions?: string[]
@ -372,7 +371,7 @@ export type UploadFileSetting = {
number_limits?: number
}
export type VisionSetting = {
export interface VisionSetting {
variable_selector: ValueSelector
detail: Resolution
}

View File

@ -32,7 +32,7 @@ import OpeningSettingModal from '@/app/components/base/features/new-feature-pane
import type { OpeningStatement } from '@/app/components/base/features/types'
import type { InputVar } from '@/app/components/workflow/types'
export type ModalState<T> = {
export interface ModalState<T> {
payload: T
onCancelCallback?: () => void
onSaveCallback?: (newPayload: T) => void
@ -43,7 +43,7 @@ export type ModalState<T> = {
datasetBindings?: { id: string; name: string }[]
}
export type ModelModalType = {
export interface ModelModalType {
currentProvider: ModelProvider
currentConfigurationMethod: ConfigurationMethodEnum
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
@ -52,7 +52,7 @@ export type LoadBalancingEntryModalType = ModelModalType & {
entry?: ModelLoadBalancingConfigEntry
index?: number
}
export type ModalContextState = {
export interface ModalContextState {
setShowAccountSettingModal: Dispatch<SetStateAction<ModalState<string> | null>>
setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
@ -90,7 +90,7 @@ export const useModalContext = () => useContext(ModalContext)
export const useModalContextSelector = <T,>(selector: (state: ModalContextState) => T): T =>
useContextSelector(ModalContext, selector)
type ModalContextProviderProps = {
interface ModalContextProviderProps {
children: React.ReactNode
}
export const ModalContextProvider = ({

View File

@ -4,6 +4,7 @@ const translation = {
searchInMarketplace: 'Search in Marketplace',
fromMarketplace: 'From Marketplace',
endpointsEnabled: '{{num}} sets of endpoints enabled',
searchTools: 'Search tools...',
detailPanel: {
categoryTip: {
marketplace: 'Installed from Marketplace',
@ -13,7 +14,7 @@ const translation = {
},
operation: {
install: 'Install',
detail: 'Detail',
detail: 'Details',
update: 'Update',
info: 'Plugin Info',
checkUpdate: 'Check Update',

View File

@ -4,6 +4,7 @@ const translation = {
searchInMarketplace: '在 Marketplace 中搜索',
fromMarketplace: '来自市场',
endpointsEnabled: '{{num}} 组端点已启用',
searchTools: '搜索工具...',
detailPanel: {
categoryTip: {
marketplace: '从 Marketplace 安装',

View File

@ -10,25 +10,25 @@ export enum PromptMode {
advanced = 'advanced',
}
export type PromptItem = {
export interface PromptItem {
role?: PromptRole
text: string
}
export type ChatPromptConfig = {
export interface ChatPromptConfig {
prompt: PromptItem[]
}
export type ConversationHistoriesRole = {
export interface ConversationHistoriesRole {
user_prefix: string
assistant_prefix: string
}
export type CompletionPromptConfig = {
export interface CompletionPromptConfig {
prompt: PromptItem
conversation_histories_role: ConversationHistoriesRole
}
export type BlockStatus = {
export interface BlockStatus {
context: boolean
history: boolean
query: boolean
@ -40,7 +40,7 @@ export enum PromptRole {
assistant = 'assistant',
}
export type PromptVariable = {
export interface PromptVariable {
key: string
name: string
type: string // "string" | "number" | "select",
@ -55,7 +55,7 @@ export type PromptVariable = {
icon_background?: string
}
export type CompletionParams = {
export interface CompletionParams {
max_tokens: number
temperature: number
top_p: number
@ -66,12 +66,12 @@ export type CompletionParams = {
export type ModelId = 'gpt-3.5-turbo' | 'text-davinci-003'
export type PromptConfig = {
export interface PromptConfig {
prompt_template: string
prompt_variables: PromptVariable[]
}
export type MoreLikeThisConfig = {
export interface MoreLikeThisConfig {
enabled: boolean
}
@ -79,7 +79,7 @@ export type SuggestedQuestionsAfterAnswerConfig = MoreLikeThisConfig
export type SpeechToTextConfig = MoreLikeThisConfig
export type TextToSpeechConfig = {
export interface TextToSpeechConfig {
enabled: boolean
voice?: string
language?: string
@ -88,18 +88,17 @@ export type TextToSpeechConfig = {
export type CitationConfig = MoreLikeThisConfig
export type AnnotationReplyConfig = {
export interface AnnotationReplyConfig {
id: string
enabled: boolean
score_threshold: number
embedding_model: {
plugin_id: string
embedding_provider_name: string
embedding_model_name: string
}
}
export type ModerationContentConfig = {
export interface ModerationContentConfig {
enabled: boolean
preset_response?: string
}
@ -114,15 +113,14 @@ export type ModerationConfig = MoreLikeThisConfig & {
}
export type RetrieverResourceConfig = MoreLikeThisConfig
export type AgentConfig = {
export interface AgentConfig {
enabled: boolean
strategy: AgentStrategy
max_iteration: number
tools: ToolItem[]
}
// frontend use. Not the same as backend
export type ModelConfig = {
plugin_id: string
export interface ModelConfig {
provider: string // LLM Provider: for example "OPENAI"
model_id: string
mode: ModelModeType
@ -140,17 +138,16 @@ export type ModelConfig = {
dataSets: any[]
agentConfig: AgentConfig
}
export type DatasetConfigItem = {
export interface DatasetConfigItem {
enable: boolean
value: number
}
export type DatasetConfigs = {
export interface DatasetConfigs {
retrieval_model: RETRIEVE_TYPE
reranking_model: {
reranking_provider_name: string
reranking_model_name: string
reranking_plugin_id: string
}
top_k: number
score_threshold_enabled: boolean
@ -175,39 +172,39 @@ export type DatasetConfigs = {
reranking_enable?: boolean
}
export type DebugRequestBody = {
export interface DebugRequestBody {
inputs: Inputs
query: string
completion_params: CompletionParams
model_config: ModelConfig
}
export type DebugResponse = {
export interface DebugResponse {
id: string
answer: string
created_at: string
}
export type DebugResponseStream = {
export interface DebugResponseStream {
id: string
data: string
created_at: string
}
export type FeedBackRequestBody = {
export interface FeedBackRequestBody {
message_id: string
rating: 'like' | 'dislike'
content?: string
from_source: 'api' | 'log'
}
export type FeedBackResponse = {
export interface FeedBackResponse {
message_id: string
rating: 'like' | 'dislike'
}
// Log session list
export type LogSessionListQuery = {
export interface LogSessionListQuery {
keyword?: string
start?: string // format datetime(YYYY-mm-dd HH:ii)
end?: string // format datetime(YYYY-mm-dd HH:ii)
@ -215,7 +212,7 @@ export type LogSessionListQuery = {
limit: number // default 20. 1-100
}
export type LogSessionListResponse = {
export interface LogSessionListResponse {
data: {
id: string
conversation_id: string
@ -229,7 +226,7 @@ export type LogSessionListResponse = {
}
// log session detail and debug
export type LogSessionDetailResponse = {
export interface LogSessionDetailResponse {
id: string
conversation_id: string
model_provider: string
@ -243,7 +240,7 @@ export type LogSessionDetailResponse = {
from_source: 'api' | 'log'
}
export type SavedMessage = {
export interface SavedMessage {
id: string
answer: string
}

View File

@ -107,7 +107,6 @@ export type MessageContent = {
agent_thoughts: any[] // TODO
workflow_run_id: string
parent_message_id: string | null
plugin_id: string
}
export type CompletionConversationGeneralDetail = {
@ -130,7 +129,6 @@ export type CompletionConversationGeneralDetail = {
dislike: number
}
model_config: {
plugin_id: string
provider: string
model_id: string
configs: Pick<ModelConfigDetail, 'prompt_template'>

View File

@ -13,9 +13,7 @@ export const updateAnnotationStatus = (appId: string, action: AnnotationEnableSt
if (embeddingModel) {
body = {
...body,
embedding_model_plugin_id: embeddingModel.plugin_id,
embedding_provider_name: embeddingModel.embedding_provider_name,
embedding_model_name: embeddingModel.embedding_model_name,
...embeddingModel,
}
}

View File

@ -3,13 +3,13 @@ import type { IOnCompleted, IOnData, IOnError, IOnFile, IOnMessageEnd, IOnMessag
import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug'
import type { ModelModeType } from '@/types/app'
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
export type AutomaticRes = {
export interface AutomaticRes {
prompt: string
variables: string[]
opening_statement: string
error?: string
}
export type CodeGenRes = {
export interface CodeGenRes {
code: string
language: string[]
error?: string
@ -82,8 +82,8 @@ export const generateRuleCode = (body: Record<string, any>) => {
})
}
export const fetchModelParams = (pluginID: string, providerName: string, modelId: string) => {
return get(`workspaces/current/model-providers/${pluginID}/${providerName}/models/parameter-rules`, {
export const fetchModelParams = (providerName: string, modelId: string) => {
return get(`workspaces/current/model-providers/${providerName}/models/parameter-rules`, {
params: {
model: modelId,
},