feat: add localization text and model type support #378

This commit is contained in:
kangfenmao 2024-11-25 10:22:20 +08:00
parent d377376bae
commit 1e2ebe5232
8 changed files with 73 additions and 16 deletions

View File

@ -1,15 +1,15 @@
:root { :root {
--color-scrollbar-thumb: rgba(255, 255, 255, 0.15); --color-scrollbar-thumb: rgba(255, 255, 255, 0.15);
--color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.2); --color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.2);
--color-scrollbar-thumb-right: rgba(255, 255, 255, 0.25); --color-scrollbar-thumb-right: rgba(255, 255, 255, 0.18);
--color-scrollbar-thumb-right-hover: rgba(255, 255, 255, 0.35); --color-scrollbar-thumb-right-hover: rgba(255, 255, 255, 0.25);
} }
body[theme-mode='light'] { body[theme-mode='light'] {
--color-scrollbar-thumb: rgba(0, 0, 0, 0.15); --color-scrollbar-thumb: rgba(0, 0, 0, 0.15);
--color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.2); --color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.2);
--color-scrollbar-thumb-right: rgba(0, 0, 0, 0.25); --color-scrollbar-thumb-right: rgba(0, 0, 0, 0.18);
--color-scrollbar-thumb-right-hover: rgba(0, 0, 0, 0.35); --color-scrollbar-thumb-right-hover: rgba(0, 0, 0, 0.25);
} }
/* 全局初始化滚动条样式 */ /* 全局初始化滚动条样式 */

View File

@ -145,7 +145,7 @@ const visionAllowedModels = [
const visionExcludedModels = ['gpt-4-\\d+-preview', 'gpt-4-turbo-preview', 'gpt-4-32k', 'gpt-4-\\d+'] const visionExcludedModels = ['gpt-4-\\d+-preview', 'gpt-4-turbo-preview', 'gpt-4-32k', 'gpt-4-\\d+']
const VISION_REGEX = new RegExp( export const VISION_REGEX = new RegExp(
`\\b(?!(?:${visionExcludedModels.join('|')})\\b)(${visionAllowedModels.join('|')})\\b`, `\\b(?!(?:${visionExcludedModels.join('|')})\\b)(${visionAllowedModels.join('|')})\\b`,
'i' 'i'
) )
@ -1044,7 +1044,7 @@ export function isEmbeddingModel(model: Model): boolean {
} }
export function isVisionModel(model: Model): boolean { export function isVisionModel(model: Model): boolean {
return VISION_REGEX.test(model.id) return VISION_REGEX.test(model.id) || model.type?.includes('vision') || false
} }
export function isSupportedModel(model: OpenAI.Models.Model): boolean { export function isSupportedModel(model: OpenAI.Models.Model): boolean {

View File

@ -145,7 +145,12 @@
"model": { "model": {
"stream_output": "Stream Output", "stream_output": "Stream Output",
"search": "Search models...", "search": "Search models...",
"pinned": "Pinned" "pinned": "Pinned",
"type": {
"text": "Text",
"vision": "Vision",
"select": "Select Model Types"
}
}, },
"paintings": { "paintings": {
"title": "Images", "title": "Images",

View File

@ -145,7 +145,12 @@
"model": { "model": {
"stream_output": "Потоковый вывод", "stream_output": "Потоковый вывод",
"search": "Поиск моделей...", "search": "Поиск моделей...",
"pinned": "Закреплено" "pinned": "Закреплено",
"type": {
"text": "Текст",
"vision": "Изображение",
"select": "Выберите тип модели"
}
}, },
"paintings": { "paintings": {
"title": "Изображения", "title": "Изображения",

View File

@ -145,7 +145,12 @@
"model": { "model": {
"stream_output": "流式输出", "stream_output": "流式输出",
"search": "搜索模型...", "search": "搜索模型...",
"pinned": "已固定" "pinned": "已固定",
"type": {
"text": "文本",
"vision": "图像",
"select": "选择模型类型"
}
}, },
"paintings": { "paintings": {
"title": "图片", "title": "图片",
@ -316,7 +321,7 @@
"about.title": "关于我们", "about.title": "关于我们",
"about.releases.title": "更新日志", "about.releases.title": "更新日志",
"about.releases.button": "查看", "about.releases.button": "查看",
"about.website.title": "官方网", "about.website.title": "官方网<EFBFBD><EFBFBD><EFBFBD>",
"about.website.button": "查看", "about.website.button": "查看",
"about.feedback.title": "意见反馈", "about.feedback.title": "意见反馈",
"about.feedback.button": "反馈", "about.feedback.button": "反馈",

View File

@ -145,7 +145,12 @@
"model": { "model": {
"stream_output": "串流輸出", "stream_output": "串流輸出",
"search": "搜尋模型...", "search": "搜尋模型...",
"pinned": "已固定" "pinned": "已固定",
"type": {
"text": "文字",
"vision": "圖像",
"select": "選擇模型類型"
}
}, },
"paintings": { "paintings": {
"title": "繪圖", "title": "繪圖",
@ -431,7 +436,7 @@
"user": "用戶", "user": "用戶",
"assistant": "助手", "assistant": "助手",
"created": "創建時間", "created": "創建時間",
"last_updated": "最後新", "last_updated": "最後<EFBFBD><EFBFBD>新",
"messages": "訊息數", "messages": "訊息數",
"conversation_details": "會話詳情", "conversation_details": "會話詳情",
"conversation_history": "會話歷史" "conversation_history": "會話歷史"

View File

@ -4,18 +4,19 @@ import {
ExportOutlined, ExportOutlined,
LoadingOutlined, LoadingOutlined,
MinusCircleOutlined, MinusCircleOutlined,
PlusOutlined PlusOutlined,
SettingOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import VisionIcon from '@renderer/components/Icons/VisionIcon' import VisionIcon from '@renderer/components/Icons/VisionIcon'
import { getModelLogo, isVisionModel } from '@renderer/config/models' import { getModelLogo, isVisionModel, 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 { useProvider } from '@renderer/hooks/useProvider' import { useProvider } from '@renderer/hooks/useProvider'
import i18n from '@renderer/i18n' import i18n from '@renderer/i18n'
import { isOpenAIProvider } from '@renderer/providers/ProviderFactory' import { isOpenAIProvider } from '@renderer/providers/ProviderFactory'
import { checkApi } from '@renderer/services/ApiService' import { checkApi } from '@renderer/services/ApiService'
import { Provider } from '@renderer/types' import { Model, ModelType, Provider } from '@renderer/types'
import { Avatar, Button, Card, Divider, Flex, Input, Space, Switch } 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'
@ -125,6 +126,26 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
return (apiHost.endsWith('/') ? apiHost : `${apiHost}/v1/`) + 'chat/completions' return (apiHost.endsWith('/') ? apiHost : `${apiHost}/v1/`) + 'chat/completions'
} }
const onUpdateModelTypes = (model: Model, types: ModelType[]) => {
const updatedModels = models.map((m) => {
if (m.id === model.id) {
return { ...m, type: types }
}
return m
})
updateProvider({ ...provider, models: updatedModels })
}
const modelTypeContent = (model: Model) => (
<div>
<Checkbox.Group
value={model.type}
onChange={(types) => onUpdateModelTypes(model, types as ModelType[])}
options={[{ label: t('model.type.vision'), value: 'vision', disabled: VISION_REGEX.test(model.id) }]}
/>
</div>
)
return ( return (
<SettingContainer theme={theme}> <SettingContainer theme={theme}>
<SettingTitle> <SettingTitle>
@ -211,6 +232,9 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
{model.name[0].toUpperCase()} {model.name[0].toUpperCase()}
</Avatar> </Avatar>
{model.name} {isVisionModel(model) && <VisionIcon />} {model.name} {isVisionModel(model) && <VisionIcon />}
<Popover content={modelTypeContent(model)} title={t('model.type.select')} trigger="click">
<SettingIcon />
</Popover>
</ModelListHeader> </ModelListHeader>
<RemoveIcon onClick={() => removeModel(model)} /> <RemoveIcon onClick={() => removeModel(model)} />
</ModelListItem> </ModelListItem>
@ -265,4 +289,14 @@ const RemoveIcon = styled(MinusCircleOutlined)`
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
` `
const SettingIcon = styled(SettingOutlined)`
margin-left: 10px;
color: var(--color-text);
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
color: var(--color-text-2);
}
`
export default ProviderSetting export default ProviderSetting

View File

@ -78,6 +78,8 @@ export type Provider = {
export type ProviderType = 'openai' | 'anthropic' | 'gemini' export type ProviderType = 'openai' | 'anthropic' | 'gemini'
export type ModelType = 'text' | 'vision'
export type Model = { export type Model = {
id: string id: string
provider: string provider: string
@ -85,6 +87,7 @@ export type Model = {
group: string group: string
owned_by?: string owned_by?: string
description?: string description?: string
type?: ModelType[]
} }
export type Suggestion = { export type Suggestion = {