refactor: enhance model provider management with new icons, improved UI elements, and marketplace integration

This commit is contained in:
CodingOnStar
2026-03-06 14:18:29 +08:00
parent dd9c526447
commit e371bfd676
8 changed files with 246 additions and 57 deletions

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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" />
)
}
{

View File

@ -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>
)

View File

@ -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

View File

@ -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',

View File

@ -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",