feat: add ModelTags component

This commit is contained in:
kangfenmao 2025-01-07 09:54:22 +08:00
parent f68bd4d8d8
commit 2e7ecbc753
6 changed files with 52 additions and 43 deletions

View File

@ -0,0 +1,35 @@
import { isEmbeddingModel, isVisionModel, isWebSearchModel } from '@renderer/config/models'
import { Model } from '@renderer/types'
import { isFreeModel } from '@renderer/utils'
import { Tag } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import VisionIcon from './Icons/VisionIcon'
import WebSearchIcon from './Icons/WebSearchIcon'
interface ModelTagsProps {
model: Model
}
const ModelTags: FC<ModelTagsProps> = ({ model }) => {
const { t } = useTranslation()
return (
<>
{isVisionModel(model) && <VisionIcon />}
{isWebSearchModel(model) && <WebSearchIcon />}
{isFreeModel(model) && (
<Tag style={{ marginLeft: 10 }} color="green">
{t('models.free')}
</Tag>
)}
{isEmbeddingModel(model) && (
<Tag style={{ marginLeft: 10 }} color="orange">
{t('models.embedding')}
</Tag>
)}
</>
)
}
export default ModelTags

View File

@ -1,7 +1,7 @@
import { PushpinOutlined, SearchOutlined } from '@ant-design/icons' import { PushpinOutlined, SearchOutlined } from '@ant-design/icons'
import VisionIcon from '@renderer/components/Icons/VisionIcon' import VisionIcon from '@renderer/components/Icons/VisionIcon'
import { TopView } from '@renderer/components/TopView' import { TopView } from '@renderer/components/TopView'
import { getModelLogo, isEmbeddingModel, isVisionModel, isWebSearchModel } from '@renderer/config/models' import { getModelLogo, isEmbeddingModel, isVisionModel } from '@renderer/config/models'
import db from '@renderer/databases' import db from '@renderer/databases'
import { useProviders } from '@renderer/hooks/useProvider' import { useProviders } from '@renderer/hooks/useProvider'
import { getModelUniqId } from '@renderer/services/ModelService' import { getModelUniqId } from '@renderer/services/ModelService'
@ -12,8 +12,8 @@ import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import WebSearchIcon from '../Icons/WebSearchIcon'
import { HStack } from '../Layout' import { HStack } from '../Layout'
import ModelTags from '../ModelTags'
import Scrollbar from '../Scrollbar' import Scrollbar from '../Scrollbar'
type MenuItem = Required<MenuProps>['items'][number] type MenuItem = Required<MenuProps>['items'][number]
@ -75,7 +75,7 @@ const PopupContainer: React.FC<PopupContainerProps> = ({ model, resolve }) => {
label: ( label: (
<ModelItem> <ModelItem>
<span> <span>
{m?.name} {isVisionModel(m) && <VisionIcon />} {isWebSearchModel(m) && <WebSearchIcon />} {m?.name} <ModelTags model={m} />
</span> </span>
<PinIcon <PinIcon
onClick={(e) => { onClick={(e) => {

View File

@ -1,8 +1,7 @@
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import VisionIcon from '@renderer/components/Icons/VisionIcon' import ModelTags from '@renderer/components/ModelTags'
import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup' import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup'
import { isLocalAi } from '@renderer/config/env' import { isLocalAi } from '@renderer/config/env'
import { isVisionModel } from '@renderer/config/models'
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { getProviderName } from '@renderer/services/ProviderService' import { getProviderName } from '@renderer/services/ProviderService'
import { Assistant } from '@renderer/types' import { Assistant } from '@renderer/types'
@ -40,7 +39,7 @@ const SelectModelButton: FC<Props> = ({ assistant }) => {
<ModelName> <ModelName>
{model ? model.name : t('button.select_model')} {providerName ? '| ' + providerName : ''} {model ? model.name : t('button.select_model')} {providerName ? '| ' + providerName : ''}
</ModelName> </ModelName>
{isVisionModel(model) && <VisionIcon style={{ marginLeft: 0 }} />} <ModelTags model={model} />
</ButtonContent> </ButtonContent>
</DropdownButton> </DropdownButton>
) )
@ -63,7 +62,6 @@ const ButtonContent = styled.div`
` `
const ModelName = styled.span` const ModelName = styled.span`
margin-left: -2px;
font-weight: 500; font-weight: 500;
` `

View File

@ -1,13 +1,12 @@
import { LoadingOutlined, MinusOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons' import { LoadingOutlined, MinusOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'
import VisionIcon from '@renderer/components/Icons/VisionIcon'
import WebSearchIcon from '@renderer/components/Icons/WebSearchIcon'
import { Center } from '@renderer/components/Layout' import { Center } from '@renderer/components/Layout'
import ModelTags from '@renderer/components/ModelTags'
import { getModelLogo, isEmbeddingModel, isVisionModel, isWebSearchModel, SYSTEM_MODELS } from '@renderer/config/models' import { getModelLogo, isEmbeddingModel, isVisionModel, isWebSearchModel, SYSTEM_MODELS } from '@renderer/config/models'
import { useProvider } from '@renderer/hooks/useProvider' import { useProvider } from '@renderer/hooks/useProvider'
import { fetchModels } from '@renderer/services/ApiService' import { fetchModels } from '@renderer/services/ApiService'
import { Model, Provider } from '@renderer/types' import { Model, Provider } from '@renderer/types'
import { getDefaultGroupName, isFreeModel, runAsyncFunction } from '@renderer/utils' import { getDefaultGroupName, isFreeModel, runAsyncFunction } from '@renderer/utils'
import { Avatar, Button, Empty, Flex, Modal, Popover, Radio, Tag, Tooltip } from 'antd' import { Avatar, Button, Empty, Flex, Modal, Popover, Radio, Tooltip } from 'antd'
import Search from 'antd/es/input/Search' import Search from 'antd/es/input/Search'
import { groupBy, isEmpty, uniqBy } from 'lodash' import { groupBy, isEmpty, uniqBy } from 'lodash'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
@ -156,18 +155,7 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
<Tooltip title={model.id} placement="top"> <Tooltip title={model.id} placement="top">
<span style={{ cursor: 'help' }}>{model.name}</span> <span style={{ cursor: 'help' }}>{model.name}</span>
</Tooltip> </Tooltip>
{isVisionModel(model) && <VisionIcon />} <ModelTags model={model} />
{isWebSearchModel(model) && <WebSearchIcon />}
{isFreeModel(model) && (
<Tag style={{ marginLeft: 10 }} color="green">
{t('models.free')}
</Tag>
)}
{isEmbeddingModel(model) && (
<Tag style={{ marginLeft: 10 }} color="orange">
{t('models.embedding')}
</Tag>
)}
{!isEmpty(model.description) && ( {!isEmpty(model.description) && (
<Popover <Popover
trigger="click" trigger="click"

View File

@ -7,16 +7,8 @@ import {
PlusOutlined, PlusOutlined,
SettingOutlined SettingOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import VisionIcon from '@renderer/components/Icons/VisionIcon' import ModelTags from '@renderer/components/ModelTags'
import WebSearchIcon from '@renderer/components/Icons/WebSearchIcon' import { EMBEDDING_REGEX, getModelLogo, VISION_REGEX } from '@renderer/config/models'
import {
EMBEDDING_REGEX,
getModelLogo,
isEmbeddingModel,
isVisionModel,
isWebSearchModel,
VISION_REGEX
} from '@renderer/config/models'
import { PROVIDER_CONFIG } from '@renderer/config/providers' import { PROVIDER_CONFIG } from '@renderer/config/providers'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
import { useAssistants, useDefaultModel } from '@renderer/hooks/useAssistant' import { useAssistants, useDefaultModel } from '@renderer/hooks/useAssistant'
@ -27,7 +19,7 @@ import { checkApi } from '@renderer/services/ApiService'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
import { setModel } from '@renderer/store/assistants' import { setModel } from '@renderer/store/assistants'
import { Model, ModelType, Provider } from '@renderer/types' import { Model, ModelType, Provider } from '@renderer/types'
import { Avatar, Button, Card, Checkbox, Divider, Flex, Input, Popover, Space, Switch, Tag } from 'antd' import { Avatar, Button, Card, Checkbox, Divider, Flex, Input, Popover, Space, Switch } from 'antd'
import Link from 'antd/es/typography/Link' import Link from 'antd/es/typography/Link'
import { groupBy, isEmpty } from 'lodash' import { groupBy, isEmpty } from 'lodash'
import { FC, useEffect, useState } from 'react' import { FC, useEffect, useState } from 'react'
@ -278,13 +270,8 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
<Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }}> <Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }}>
{model.name[0].toUpperCase()} {model.name[0].toUpperCase()}
</Avatar> </Avatar>
{model.name} {isVisionModel(model) && <VisionIcon />} {model.name}
{isWebSearchModel(model) && <WebSearchIcon />} <ModelTags model={model} />
{isEmbeddingModel(model) && (
<Tag style={{ marginLeft: 10 }} color="orange">
{t('models.embedding')}
</Tag>
)}
<Popover content={modelTypeContent(model)} title={t('models.type.select')} trigger="click"> <Popover content={modelTypeContent(model)} title={t('models.type.select')} trigger="click">
<SettingIcon /> <SettingIcon />
</Popover> </Popover>

View File

@ -124,7 +124,7 @@ const ProvidersList: FC = () => {
{provider.isSystem ? t(`provider.${provider.id}`) : provider.name} {provider.isSystem ? t(`provider.${provider.id}`) : provider.name}
</ProviderItemName> </ProviderItemName>
{provider.enabled && ( {provider.enabled && (
<Tag color="green" style={{ marginLeft: 'auto', borderRadius: 16 }}> <Tag color="green" style={{ marginLeft: 'auto', marginRight: 0, borderRadius: 16 }}>
ON ON
</Tag> </Tag>
)} )}
@ -163,7 +163,7 @@ const Container = styled.div`
const ProviderListContainer = styled.div` const ProviderListContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: var(--assistants-width); min-width: calc(var(--settings-width) + 10px);
height: calc(100vh - var(--navbar-height)); height: calc(100vh - var(--navbar-height));
border-right: 0.5px solid var(--color-border); border-right: 0.5px solid var(--color-border);
` `
@ -173,13 +173,14 @@ const ProviderList = styled.div`
flex: 1; flex: 1;
flex-direction: column; flex-direction: column;
padding: 8px; padding: 8px;
padding-right: 5px;
` `
const ProviderListItem = styled.div` const ProviderListItem = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
padding: 5px 8px; padding: 5px 10px;
width: 100%; width: 100%;
cursor: grab; cursor: grab;
border-radius: var(--list-item-border-radius); border-radius: var(--list-item-border-radius);