feat: implemented vision model support and ui enhancements.

- Updated color palette settings have been implemented.
- Added VisionIcon component utilizing Ant Design icons and styled components for visual customization.
- Updated vision model regex to include additional models.
- Added support for multiple file columns in i18n resources.
- Added translations to column titles.
- Added support for vision models in the Select Model Button component.
- Added functionality to display a vision model icon next to the model name on dropdown items.
- Implemented changes to add vision model support to the Edit Models Popup.
- Added icon to display vision models in provider settings.
This commit is contained in:
kangfenmao 2024-09-13 15:46:48 +08:00
parent 71876e6a70
commit 617af8b12a
9 changed files with 49 additions and 13 deletions

View File

@ -24,9 +24,9 @@
--color-background-soft: var(--color-black-soft); --color-background-soft: var(--color-black-soft);
--color-background-mute: var(--color-black-mute); --color-background-mute: var(--color-black-mute);
--color-primary: #135200; --color-primary: #00b96b;
--color-primary-soft: #13520099; --color-primary-soft: #00b96b99;
--color-primary-mute: #13520033; --color-primary-mute: #00b96b33;
--color-text: var(--color-text-1); --color-text: var(--color-text-1);
--color-icon: #ffffff99; --color-icon: #ffffff99;

View File

@ -0,0 +1,15 @@
import { EyeOutlined } from '@ant-design/icons'
import React, { FC } from 'react'
import styled from 'styled-components'
const VisionIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => {
return <Icon {...(props as any)} />
}
const Icon = styled(EyeOutlined)`
color: var(--color-primary);
font-size: 14px;
margin-left: 4px;
`
export default VisionIcon

View File

@ -1,7 +1,7 @@
import { Model } from '@renderer/types' import { Model } from '@renderer/types'
const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-turbo|dall|cogview/i const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-turbo|dall|cogview/i
const VISION_REGEX = /llava|moondream|minicpm|gemini|claude|vision|glm-4v/i const VISION_REGEX = /llava|moondream|minicpm|gemini-1.5|claude-3|vision|glm-4v|gpt-4|qwen-vl/i
const EMBEDDING_REGEX = /embedding/i const EMBEDDING_REGEX = /embedding/i
export const SYSTEM_MODELS: Record<string, Model[]> = { export const SYSTEM_MODELS: Record<string, Model[]> = {

View File

@ -103,7 +103,11 @@ const resources = {
'assistant.search.placeholder': 'Search' 'assistant.search.placeholder': 'Search'
}, },
files: { files: {
title: 'Files' title: 'Files',
file: 'File',
name: 'Name',
size: 'Size',
created_at: 'Created At'
}, },
agents: { agents: {
title: 'Assistants', title: 'Assistants',
@ -362,7 +366,11 @@ const resources = {
'assistant.search.placeholder': '搜索' 'assistant.search.placeholder': '搜索'
}, },
files: { files: {
title: '文件' title: '文件',
file: '文件',
name: '文件名',
size: '大小',
created_at: '创建时间'
}, },
agents: { agents: {
title: '智能体', title: '智能体',

View File

@ -24,23 +24,23 @@ const FilesPage: FC = () => {
const columns = [ const columns = [
{ {
title: 'File', title: t('files.file'),
dataIndex: 'file', dataIndex: 'file',
key: 'file' key: 'file'
}, },
{ {
title: 'Name', title: t('files.name'),
dataIndex: 'name', dataIndex: 'name',
key: 'name' key: 'name'
}, },
{ {
title: 'Size', title: t('files.size'),
dataIndex: 'size', dataIndex: 'size',
key: 'size', key: 'size',
width: '100px' width: '100px'
}, },
{ {
title: 'Created At', title: t('files.created_at'),
dataIndex: 'created_at', dataIndex: 'created_at',
key: 'created_at', key: 'created_at',
width: '120px' width: '120px'

View File

@ -1,5 +1,7 @@
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import VisionIcon from '@renderer/components/Icons/VisionIcon'
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 { Assistant } from '@renderer/types' import { Assistant } from '@renderer/types'
import { Button } from 'antd' import { Button } from 'antd'
@ -27,6 +29,7 @@ const SelectModelButton: FC<Props> = ({ assistant }) => {
<DropdownButton size="small" type="default"> <DropdownButton size="small" type="default">
<ModelAvatar model={model} size={20} /> <ModelAvatar model={model} size={20} />
<ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName> <ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName>
{isVisionModel(model) && <VisionIcon style={{ marginLeft: 0 }} />}
</DropdownButton> </DropdownButton>
</SelectModelDropdown> </SelectModelDropdown>
) )

View File

@ -1,3 +1,5 @@
import VisionIcon from '@renderer/components/Icons/VisionIcon'
import { isVisionModel } from '@renderer/config/models'
import { getModelLogo } from '@renderer/config/provider' import { getModelLogo } from '@renderer/config/provider'
import { useProviders } from '@renderer/hooks/useProvider' import { useProviders } from '@renderer/hooks/useProvider'
import { getModelUniqId } from '@renderer/services/model' import { getModelUniqId } from '@renderer/services/model'
@ -25,7 +27,11 @@ const SelectModelDropdown: FC<Props & PropsWithChildren> = ({ children, model, o
type: 'group', type: 'group',
children: reverse(sortBy(p.models, 'name')).map((m) => ({ children: reverse(sortBy(p.models, 'name')).map((m) => ({
key: getModelUniqId(m), key: getModelUniqId(m),
label: upperFirst(m?.name), label: (
<div>
{upperFirst(m?.name)} {isVisionModel(m) && <VisionIcon />}
</div>
),
defaultSelectedKeys: model ? [getModelUniqId(model)] : [], defaultSelectedKeys: model ? [getModelUniqId(model)] : [],
icon: ( icon: (
<Avatar src={getModelLogo(m?.id || '')} size={24}> <Avatar src={getModelLogo(m?.id || '')} size={24}>

View File

@ -1,5 +1,6 @@
import { LoadingOutlined, MinusOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons' import { LoadingOutlined, MinusOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'
import { SYSTEM_MODELS } from '@renderer/config/models' import VisionIcon from '@renderer/components/Icons/VisionIcon'
import { isVisionModel, SYSTEM_MODELS } from '@renderer/config/models'
import { getModelLogo } from '@renderer/config/provider' import { getModelLogo } from '@renderer/config/provider'
import { useProvider } from '@renderer/hooks/useProvider' import { useProvider } from '@renderer/hooks/useProvider'
import { fetchModels } from '@renderer/services/api' import { fetchModels } from '@renderer/services/api'
@ -126,6 +127,7 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
</Avatar> </Avatar>
<ListItemName> <ListItemName>
{model.name} {model.name}
{isVisionModel(model) && <VisionIcon />}
{isFreeModel(model) && ( {isFreeModel(model) && (
<Tag style={{ marginLeft: 10 }} color="green"> <Tag style={{ marginLeft: 10 }} color="green">
Free Free

View File

@ -6,6 +6,8 @@ import {
MinusCircleOutlined, MinusCircleOutlined,
PlusOutlined PlusOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import VisionIcon from '@renderer/components/Icons/VisionIcon'
import { isVisionModel } from '@renderer/config/models'
import { getModelLogo } from '@renderer/config/provider' import { getModelLogo } from '@renderer/config/provider'
import { PROVIDER_CONFIG } from '@renderer/config/provider' import { PROVIDER_CONFIG } from '@renderer/config/provider'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
@ -148,7 +150,7 @@ 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} {model.name} {isVisionModel(model) && <VisionIcon />}
</ModelListHeader> </ModelListHeader>
<RemoveIcon onClick={() => removeModel(model)} /> <RemoveIcon onClick={() => removeModel(model)} />
</ModelListItem> </ModelListItem>