mirror of
https://github.com/langgenius/dify.git
synced 2026-03-10 18:06:14 +08:00
refactor: enhance model provider management with new icons, improved UI elements, and marketplace integration
This commit is contained in:
@ -1,3 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M8.00008 0.666016C3.94999 0.666016 0.666748 3.94926 0.666748 7.99935C0.666748 12.0494 3.94999 15.3327 8.00008 15.3327C12.0502 15.3327 15.3334 12.0494 15.3334 7.99935C15.3334 3.94926 12.0502 0.666016 8.00008 0.666016ZM10.4715 5.52794C10.7318 5.78829 10.7318 6.2104 10.4715 6.47075L8.94289 7.99935L10.4715 9.52794C10.7318 9.78829 10.7318 10.2104 10.4715 10.4708C10.2111 10.7311 9.78903 10.7311 9.52868 10.4708L8.00008 8.94216L6.47149 10.4708C6.21114 10.7311 5.78903 10.7311 5.52868 10.4708C5.26833 10.2104 5.26833 9.78829 5.52868 9.52794L7.05727 7.99935L5.52868 6.47075C5.26833 6.2104 5.26833 5.78829 5.52868 5.52794C5.78903 5.26759 6.21114 5.26759 6.47149 5.52794L8.00008 7.05654L9.52868 5.52794C9.78903 5.26759 10.2111 5.26759 10.4715 5.52794Z" fill="#98A2B3"/>
|
||||
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M8.00008 0.666016C3.94999 0.666016 0.666748 3.94926 0.666748 7.99935C0.666748 12.0494 3.94999 15.3327 8.00008 15.3327C12.0502 15.3327 15.3334 12.0494 15.3334 7.99935C15.3334 3.94926 12.0502 0.666016 8.00008 0.666016ZM10.4715 5.52794C10.7318 5.78829 10.7318 6.2104 10.4715 6.47075L8.94289 7.99935L10.4715 9.52794C10.7318 9.78829 10.7318 10.2104 10.4715 10.4708C10.2111 10.7311 9.78903 10.7311 9.52868 10.4708L8.00008 8.94216L6.47149 10.4708C6.21114 10.7311 5.78903 10.7311 5.52868 10.4708C5.26833 10.2104 5.26833 9.78829 5.52868 9.52794L7.05727 7.99935L5.52868 6.47075C5.26833 6.2104 5.26833 5.78829 5.52868 5.52794C5.78903 5.26759 6.21114 5.26759 6.47149 5.52794L8.00008 7.05654L9.52868 5.52794C9.78903 5.26759 10.2111 5.26759 10.4715 5.52794Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 930 B |
@ -1,10 +1,14 @@
|
||||
import type { ChangeEvent, FC, KeyboardEvent } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import AutosizeInput from 'react-18-input-autosize'
|
||||
import _AutosizeInput from 'react-18-input-autosize'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
// CJS/ESM interop: Turbopack may resolve the module namespace object instead of the default export
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
const AutosizeInput = ('default' in (_AutosizeInput as any) ? (_AutosizeInput as any).default : _AutosizeInput) as typeof _AutosizeInput
|
||||
|
||||
type TagInputProps = {
|
||||
items: string[]
|
||||
onChange: (items: string[]) => void
|
||||
|
||||
@ -5,7 +5,7 @@ import type {
|
||||
ModelFeatureEnum,
|
||||
ModelItem,
|
||||
} from '../declarations'
|
||||
import { useState } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
@ -41,6 +41,7 @@ const ModelSelector: FC<ModelSelectorProps> = ({
|
||||
showDeprecatedWarnIcon = false,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const triggerRef = useRef<HTMLDivElement>(null)
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
@ -70,7 +71,7 @@ const ModelSelector: FC<ModelSelectorProps> = ({
|
||||
placement="bottom-start"
|
||||
offset={4}
|
||||
>
|
||||
<div className={cn('relative')}>
|
||||
<div ref={triggerRef} className={cn('relative')}>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={handleToggle}
|
||||
className="block"
|
||||
@ -113,6 +114,7 @@ const ModelSelector: FC<ModelSelectorProps> = ({
|
||||
onSelect={handleSelect}
|
||||
scopeFeatures={scopeFeatures}
|
||||
onHide={() => setOpen(false)}
|
||||
triggerRef={triggerRef}
|
||||
/>
|
||||
</PortalToFollowElemContent>
|
||||
</div>
|
||||
|
||||
@ -7,16 +7,18 @@ import type {
|
||||
import { useState } from 'react'
|
||||
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationStatusEnum,
|
||||
ModelFeatureEnum,
|
||||
ModelStatusEnum,
|
||||
ModelTypeEnum,
|
||||
PreferredProviderTypeEnum,
|
||||
} from '../declarations'
|
||||
import {
|
||||
useLanguage,
|
||||
@ -45,6 +47,7 @@ const PopupItem: FC<PopupItemProps> = ({
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
const language = useLanguage()
|
||||
const { currentWorkspace } = useAppContext()
|
||||
const { setShowModelModal } = useModalContext()
|
||||
const { modelProviders } = useProviderContext()
|
||||
const updateModelList = useUpdateModelList()
|
||||
@ -73,14 +76,55 @@ const PopupItem: FC<PopupItemProps> = ({
|
||||
})
|
||||
}
|
||||
|
||||
const isUsingCredits = currentProvider?.preferred_provider_type === PreferredProviderTypeEnum.system
|
||||
const credits = Math.max((currentWorkspace.trial_credits - currentWorkspace.trial_credits_used) || 0, 0)
|
||||
const hasCredits = credits > 0
|
||||
const isApiKeyActive = currentProvider?.custom_configuration.status === CustomConfigurationStatusEnum.active
|
||||
const credentialName = currentProvider?.custom_configuration.current_credential_name
|
||||
|
||||
return (
|
||||
<div className="mb-1">
|
||||
<div
|
||||
className="flex h-[22px] cursor-pointer items-center px-3 text-xs font-medium text-text-tertiary"
|
||||
onClick={() => setCollapsed(prev => !prev)}
|
||||
>
|
||||
{model.label[language] || model.label.en_US}
|
||||
<span className={cn('i-custom-vender-solid-general-arrow-down-round-fill h-4 w-4 text-text-quaternary', collapsed && '-rotate-90')} />
|
||||
<div className="flex h-[22px] items-center justify-between px-3 text-xs font-medium text-text-tertiary">
|
||||
<div
|
||||
className="flex cursor-pointer items-center"
|
||||
onClick={() => setCollapsed(prev => !prev)}
|
||||
>
|
||||
{model.label[language] || model.label.en_US}
|
||||
<span className={cn('i-custom-vender-solid-general-arrow-down-round-fill h-4 w-4 text-text-quaternary', collapsed && '-rotate-90')} />
|
||||
</div>
|
||||
<div className="flex items-center text-text-tertiary system-xs-medium">
|
||||
{isUsingCredits
|
||||
? (
|
||||
hasCredits
|
||||
? (
|
||||
<>
|
||||
<span className="i-ri-globe-line h-3 w-3" />
|
||||
<span className="ml-1">{t('modelProvider.selector.aiCredits', { ns: 'common' })}</span>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<span className="i-ri-alert-fill h-3 w-3 text-text-warning-secondary" />
|
||||
<span className="ml-1 text-text-warning">{t('modelProvider.selector.creditsExhausted', { ns: 'common' })}</span>
|
||||
</>
|
||||
)
|
||||
)
|
||||
: credentialName
|
||||
? (
|
||||
<>
|
||||
<span className={cn('h-1.5 w-1.5 shrink-0 rounded-[2px] border', isApiKeyActive ? 'border-components-badge-status-light-success-border-inner bg-components-badge-status-light-success-bg' : 'border-components-badge-status-light-error-border-inner bg-components-badge-status-light-error-bg')} />
|
||||
<span className="ml-1 text-text-tertiary">{credentialName}</span>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<span className="h-1.5 w-1.5 shrink-0 rounded-[2px] border border-components-badge-status-light-disabled-border-inner bg-components-badge-status-light-disabled-bg" />
|
||||
<span className="ml-1 text-text-tertiary">{t('modelProvider.selector.configureRequired', { ns: 'common' })}</span>
|
||||
</>
|
||||
)}
|
||||
<span className={cn('i-ri-arrow-down-s-line !h-[14px] !w-[14px] translate-y-px text-text-tertiary', collapsed && '-rotate-90')} />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{!collapsed && model.models.map(modelItem => (
|
||||
<Tooltip
|
||||
@ -154,7 +198,7 @@ const PopupItem: FC<PopupItemProps> = ({
|
||||
</div>
|
||||
{
|
||||
defaultModel?.model === modelItem.model && defaultModel.provider === currentProvider.provider && (
|
||||
<Check className="h-4 w-4 shrink-0 text-text-accent" />
|
||||
<span className="i-custom-vender-line-general-check h-4 w-4 shrink-0 text-text-accent" />
|
||||
)
|
||||
}
|
||||
{
|
||||
|
||||
@ -1,22 +1,27 @@
|
||||
import type { FC } from 'react'
|
||||
import type { FC, RefObject } from 'react'
|
||||
import type {
|
||||
DefaultModel,
|
||||
Model,
|
||||
ModelItem,
|
||||
} from '../declarations'
|
||||
import {
|
||||
RiArrowRightUpLine,
|
||||
RiSearchLine,
|
||||
} from '@remixicon/react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { tooltipManager } from '@/app/components/base/tooltip/TooltipManager'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
|
||||
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { supportFunctionCall } from '@/utils/tool-call'
|
||||
import { getMarketplaceUrl } from '@/utils/var'
|
||||
import { ModelFeatureEnum } from '../declarations'
|
||||
import { useLanguage } from '../hooks'
|
||||
import { useLanguage, useMarketplaceAllPlugins } from '../hooks'
|
||||
import { MODEL_PROVIDER_QUOTA_GET_PAID, modelNameMap, providerIconMap, providerKeyToPluginId } from '../utils'
|
||||
import PopupItem from './popup-item'
|
||||
|
||||
type PopupProps = {
|
||||
@ -25,6 +30,7 @@ type PopupProps = {
|
||||
onSelect: (provider: string, model: ModelItem) => void
|
||||
scopeFeatures?: ModelFeatureEnum[]
|
||||
onHide: () => void
|
||||
triggerRef?: RefObject<HTMLDivElement | null>
|
||||
}
|
||||
const Popup: FC<PopupProps> = ({
|
||||
defaultModel,
|
||||
@ -32,12 +38,48 @@ const Popup: FC<PopupProps> = ({
|
||||
onSelect,
|
||||
scopeFeatures = [],
|
||||
onHide,
|
||||
triggerRef,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const language = useLanguage()
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const [marketplaceCollapsed, setMarketplaceCollapsed] = useState(false)
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const { modelProviders } = useProviderContext()
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const triggerWidth = triggerRef?.current?.offsetWidth
|
||||
|
||||
const {
|
||||
plugins: allPlugins,
|
||||
} = useMarketplaceAllPlugins(modelProviders, '')
|
||||
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
|
||||
const { refreshPluginList } = useRefreshPluginList()
|
||||
const [installingProvider, setInstallingProvider] = useState<ModelProviderQuotaGetPaid | null>(null)
|
||||
|
||||
const handleInstallPlugin = useCallback(async (key: ModelProviderQuotaGetPaid) => {
|
||||
if (!allPlugins || installingProvider)
|
||||
return
|
||||
const pluginId = providerKeyToPluginId[key]
|
||||
const plugin = allPlugins.find(p => p.plugin_id === pluginId)
|
||||
if (!plugin)
|
||||
return
|
||||
|
||||
const uniqueIdentifier = plugin.latest_package_identifier
|
||||
setInstallingProvider(key)
|
||||
try {
|
||||
const { all_installed, task_id } = await installPackageFromMarketPlace(uniqueIdentifier)
|
||||
if (!all_installed) {
|
||||
const { check } = checkTaskStatus()
|
||||
await check({ taskId: task_id, pluginUniqueIdentifier: uniqueIdentifier })
|
||||
}
|
||||
refreshPluginList(plugin)
|
||||
}
|
||||
catch { }
|
||||
finally {
|
||||
setInstallingProvider(null)
|
||||
}
|
||||
}, [allPlugins, installingProvider, installPackageFromMarketPlace, refreshPluginList])
|
||||
|
||||
// Close any open tooltips when the user scrolls to prevent them from appearing
|
||||
// in incorrect positions or becoming detached from their trigger elements
|
||||
@ -81,17 +123,22 @@ const Popup: FC<PopupProps> = ({
|
||||
}).filter(model => model.models.length > 0)
|
||||
}, [language, modelList, scopeFeatures, searchText])
|
||||
|
||||
const marketplaceProviders = useMemo(() => {
|
||||
const installedProviders = new Set(modelList.map(m => m.provider))
|
||||
return MODEL_PROVIDER_QUOTA_GET_PAID.filter(key => !installedProviders.has(key))
|
||||
}, [modelList])
|
||||
|
||||
return (
|
||||
<div ref={scrollRef} className="max-h-[480px] w-[320px] overflow-y-auto rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg">
|
||||
<div ref={scrollRef} className="max-h-[480px] min-w-[320px] overflow-y-auto rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg no-scrollbar" style={triggerWidth ? { width: triggerWidth } : undefined}>
|
||||
<div className="sticky top-0 z-10 bg-components-panel-bg pb-1 pl-3 pr-2 pt-3">
|
||||
<div className={`
|
||||
flex h-8 items-center rounded-lg border pl-[9px] pr-[10px]
|
||||
${searchText ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-transparent bg-components-input-bg-normal'}
|
||||
`}
|
||||
>
|
||||
<RiSearchLine
|
||||
<span
|
||||
className={`
|
||||
mr-[7px] h-[14px] w-[14px] shrink-0
|
||||
i-ri-search-line mr-[7px] h-[14px] w-[14px] shrink-0
|
||||
${searchText ? 'text-text-tertiary' : 'text-text-quaternary'}
|
||||
`}
|
||||
/>
|
||||
@ -103,8 +150,8 @@ const Popup: FC<PopupProps> = ({
|
||||
/>
|
||||
{
|
||||
searchText && (
|
||||
<XCircle
|
||||
className="ml-1.5 h-[14px] w-[14px] shrink-0 cursor-pointer text-text-quaternary"
|
||||
<span
|
||||
className="i-custom-vender-solid-general-x-circle ml-1.5 h-[14px] w-[14px] shrink-0 cursor-pointer text-text-quaternary"
|
||||
onClick={() => setSearchText('')}
|
||||
/>
|
||||
)
|
||||
@ -122,13 +169,98 @@ const Popup: FC<PopupProps> = ({
|
||||
/>
|
||||
))
|
||||
}
|
||||
{
|
||||
!filteredModelList.length && (
|
||||
<div className="break-all px-3 py-1.5 text-center text-xs leading-[18px] text-text-tertiary">
|
||||
{`No model found for “${searchText}”`}
|
||||
{!filteredModelList.length && !modelList.length && (
|
||||
<div className="flex flex-col gap-2 rounded-[10px] bg-gradient-to-r from-state-base-hover to-background-gradient-mask-transparent p-4">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur-[5px]">
|
||||
<span className="i-ri-brain-2-line h-5 w-5 text-text-tertiary" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-text-secondary system-sm-medium">
|
||||
{t('modelProvider.selector.noProviderConfigured', { ns: 'common' })}
|
||||
</p>
|
||||
<p className="text-text-tertiary system-xs-regular">
|
||||
{t('modelProvider.selector.noProviderConfiguredDesc', { ns: 'common' })}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="w-[108px]"
|
||||
onClick={() => {
|
||||
onHide()
|
||||
setShowAccountSettingModal({ payload: ACCOUNT_SETTING_TAB.PROVIDER })
|
||||
}}
|
||||
>
|
||||
{t('modelProvider.selector.configure', { ns: 'common' })}
|
||||
<span className="i-ri-arrow-right-line h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{!filteredModelList.length && modelList.length > 0 && (
|
||||
<div className="break-all px-3 py-1.5 text-center text-xs leading-[18px] text-text-tertiary">
|
||||
{`No model found for \u201C${searchText}\u201D`}
|
||||
</div>
|
||||
)}
|
||||
{marketplaceProviders.length > 0 && (
|
||||
<>
|
||||
<div className="mx-2 my-1 border-t border-divider-subtle" />
|
||||
<div className="mb-1">
|
||||
<div className="flex h-[22px] items-center px-3">
|
||||
<div
|
||||
className="flex flex-1 cursor-pointer items-center text-text-primary system-sm-medium"
|
||||
onClick={() => setMarketplaceCollapsed(prev => !prev)}
|
||||
>
|
||||
{t('modelProvider.selector.fromMarketplace', { ns: 'common' })}
|
||||
<span className={cn('i-custom-vender-solid-general-arrow-down-round-fill h-4 w-4 text-text-quaternary', marketplaceCollapsed && '-rotate-90')} />
|
||||
</div>
|
||||
</div>
|
||||
{!marketplaceCollapsed && (
|
||||
<>
|
||||
{marketplaceProviders.map((key) => {
|
||||
const Icon = providerIconMap[key]
|
||||
const isInstalling = installingProvider === key
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className="group flex cursor-pointer items-center gap-1 rounded-lg py-0.5 pl-3 pr-0.5 hover:bg-state-base-hover"
|
||||
>
|
||||
<div className="flex flex-1 items-center gap-2 py-0.5">
|
||||
<Icon className="h-5 w-5 shrink-0 rounded-md" />
|
||||
<span className="text-text-secondary system-sm-regular">{modelNameMap[key]}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
className={cn(
|
||||
'shrink-0 backdrop-blur-[5px]',
|
||||
!isInstalling && 'hidden group-hover:flex',
|
||||
)}
|
||||
disabled={isInstalling}
|
||||
onClick={() => handleInstallPlugin(key)}
|
||||
>
|
||||
{isInstalling && <span className="i-ri-loader-2-line h-3.5 w-3.5 animate-spin" />}
|
||||
{isInstalling
|
||||
? t('installModal.installing', { ns: 'plugin' })
|
||||
: t('modelProvider.selector.install', { ns: 'common' })}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<a
|
||||
className="flex cursor-pointer items-center gap-0.5 px-3 pt-1.5"
|
||||
href={getMarketplaceUrl('', { theme })}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="flex-1 text-text-accent system-xs-regular">
|
||||
{t('modelProvider.selector.discoverMoreInMarketplace', { ns: 'common' })}
|
||||
</span>
|
||||
<span className="i-ri-arrow-right-up-line !h-3 !w-3 text-text-accent" />
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="sticky bottom-0 flex cursor-pointer items-center rounded-b-lg border-t border-divider-subtle bg-components-panel-bg px-4 py-2 text-text-accent-light-mode-only"
|
||||
@ -138,7 +270,7 @@ const Popup: FC<PopupProps> = ({
|
||||
}}
|
||||
>
|
||||
<span className="system-xs-medium">{t('model.settingsLink', { ns: 'common' })}</span>
|
||||
<RiArrowRightUpLine className="ml-0.5 h-3 w-3" />
|
||||
<span className="i-ri-arrow-right-up-line ml-0.5 h-3 w-3" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,33 +1,22 @@
|
||||
import type { ComponentType, FC } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import type { ModelProvider } from '../declarations'
|
||||
import type { Plugin } from '@/app/components/plugins/types'
|
||||
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AnthropicShortLight, Deepseek, Gemini, Grok, OpenaiSmall, Tongyi } from '@/app/components/base/icons/src/public/llm'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import { ModelProviderQuotaGetPaid } from '@/types/model-provider'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import { PreferredProviderTypeEnum } from '../declarations'
|
||||
import { useMarketplaceAllPlugins } from '../hooks'
|
||||
import { MODEL_PROVIDER_QUOTA_GET_PAID, modelNameMap } from '../utils'
|
||||
|
||||
// Icon map for each provider - single source of truth for provider icons
|
||||
const providerIconMap: Record<ModelProviderQuotaGetPaid, ComponentType<{ className?: string }>> = {
|
||||
[ModelProviderQuotaGetPaid.OPENAI]: OpenaiSmall,
|
||||
[ModelProviderQuotaGetPaid.ANTHROPIC]: AnthropicShortLight,
|
||||
[ModelProviderQuotaGetPaid.GEMINI]: Gemini,
|
||||
[ModelProviderQuotaGetPaid.X]: Grok,
|
||||
[ModelProviderQuotaGetPaid.DEEPSEEK]: Deepseek,
|
||||
[ModelProviderQuotaGetPaid.TONGYI]: Tongyi,
|
||||
}
|
||||
import { MODEL_PROVIDER_QUOTA_GET_PAID, modelNameMap, providerIconMap, providerKeyToPluginId } from '../utils'
|
||||
|
||||
// Derive allProviders from the shared constant
|
||||
const allProviders = MODEL_PROVIDER_QUOTA_GET_PAID.map(key => ({
|
||||
@ -35,17 +24,6 @@ const allProviders = MODEL_PROVIDER_QUOTA_GET_PAID.map(key => ({
|
||||
Icon: providerIconMap[key],
|
||||
}))
|
||||
|
||||
// Map provider key to plugin ID
|
||||
// provider key format: langgenius/provider/model, plugin ID format: langgenius/provider
|
||||
const providerKeyToPluginId: Record<ModelProviderQuotaGetPaid, string> = {
|
||||
[ModelProviderQuotaGetPaid.OPENAI]: 'langgenius/openai',
|
||||
[ModelProviderQuotaGetPaid.ANTHROPIC]: 'langgenius/anthropic',
|
||||
[ModelProviderQuotaGetPaid.GEMINI]: 'langgenius/gemini',
|
||||
[ModelProviderQuotaGetPaid.X]: 'langgenius/x',
|
||||
[ModelProviderQuotaGetPaid.DEEPSEEK]: 'langgenius/deepseek',
|
||||
[ModelProviderQuotaGetPaid.TONGYI]: 'langgenius/tongyi',
|
||||
}
|
||||
|
||||
type QuotaPanelProps = {
|
||||
providers: ModelProvider[]
|
||||
isLoading?: boolean
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import type { ComponentType } from 'react'
|
||||
import type {
|
||||
CredentialFormSchemaSelect,
|
||||
CredentialFormSchemaTextInput,
|
||||
FormValue,
|
||||
ModelLoadBalancingConfig,
|
||||
} from './declarations'
|
||||
import { AnthropicShortLight, Deepseek, Gemini, Grok, OpenaiSmall, Tongyi } from '@/app/components/base/icons/src/public/llm'
|
||||
import {
|
||||
deleteModelProvider,
|
||||
setModelProvider,
|
||||
@ -23,6 +25,24 @@ export { ModelProviderQuotaGetPaid } from '@/types/model-provider'
|
||||
|
||||
export const MODEL_PROVIDER_QUOTA_GET_PAID = [ModelProviderQuotaGetPaid.OPENAI, ModelProviderQuotaGetPaid.ANTHROPIC, ModelProviderQuotaGetPaid.GEMINI, ModelProviderQuotaGetPaid.X, ModelProviderQuotaGetPaid.DEEPSEEK, ModelProviderQuotaGetPaid.TONGYI]
|
||||
|
||||
export const providerIconMap: Record<ModelProviderQuotaGetPaid, ComponentType<{ className?: string }>> = {
|
||||
[ModelProviderQuotaGetPaid.OPENAI]: OpenaiSmall,
|
||||
[ModelProviderQuotaGetPaid.ANTHROPIC]: AnthropicShortLight,
|
||||
[ModelProviderQuotaGetPaid.GEMINI]: Gemini,
|
||||
[ModelProviderQuotaGetPaid.X]: Grok,
|
||||
[ModelProviderQuotaGetPaid.DEEPSEEK]: Deepseek,
|
||||
[ModelProviderQuotaGetPaid.TONGYI]: Tongyi,
|
||||
}
|
||||
|
||||
export const providerKeyToPluginId: Record<ModelProviderQuotaGetPaid, string> = {
|
||||
[ModelProviderQuotaGetPaid.OPENAI]: 'langgenius/openai',
|
||||
[ModelProviderQuotaGetPaid.ANTHROPIC]: 'langgenius/anthropic',
|
||||
[ModelProviderQuotaGetPaid.GEMINI]: 'langgenius/gemini',
|
||||
[ModelProviderQuotaGetPaid.X]: 'langgenius/x',
|
||||
[ModelProviderQuotaGetPaid.DEEPSEEK]: 'langgenius/deepseek',
|
||||
[ModelProviderQuotaGetPaid.TONGYI]: 'langgenius/tongyi',
|
||||
}
|
||||
|
||||
export const modelNameMap = {
|
||||
[ModelProviderQuotaGetPaid.OPENAI]: 'OpenAI',
|
||||
[ModelProviderQuotaGetPaid.ANTHROPIC]: 'Anthropic',
|
||||
|
||||
@ -403,8 +403,17 @@
|
||||
"modelProvider.resetDate": "Reset on {{date}}",
|
||||
"modelProvider.searchModel": "Search model",
|
||||
"modelProvider.selectModel": "Select your model",
|
||||
"modelProvider.selector.aiCredits": "AI credits",
|
||||
"modelProvider.selector.configure": "Configure",
|
||||
"modelProvider.selector.configureRequired": "Configure required",
|
||||
"modelProvider.selector.creditsExhausted": "Credits exhausted",
|
||||
"modelProvider.selector.discoverMoreInMarketplace": "Discover more in Marketplace",
|
||||
"modelProvider.selector.emptySetting": "Please go to settings to configure",
|
||||
"modelProvider.selector.emptyTip": "No available models",
|
||||
"modelProvider.selector.fromMarketplace": "From Marketplace",
|
||||
"modelProvider.selector.install": "Install",
|
||||
"modelProvider.selector.noProviderConfigured": "No model provider configured",
|
||||
"modelProvider.selector.noProviderConfiguredDesc": "Browse Marketplace to install one, or configure providers in settings.",
|
||||
"modelProvider.selector.rerankTip": "Please set up the Rerank model",
|
||||
"modelProvider.selector.tip": "This model has been removed. Please add a model or select another model.",
|
||||
"modelProvider.setupModelFirst": "Please set up your model first",
|
||||
|
||||
Reference in New Issue
Block a user