diff --git a/src/renderer/src/assets/styles/scrollbar.scss b/src/renderer/src/assets/styles/scrollbar.scss index f0334c9762..4a2b258292 100644 --- a/src/renderer/src/assets/styles/scrollbar.scss +++ b/src/renderer/src/assets/styles/scrollbar.scss @@ -1,15 +1,15 @@ :root { --color-scrollbar-thumb: rgba(255, 255, 255, 0.15); --color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.2); - --color-scrollbar-thumb-right: rgba(255, 255, 255, 0.25); - --color-scrollbar-thumb-right-hover: rgba(255, 255, 255, 0.35); + --color-scrollbar-thumb-right: rgba(255, 255, 255, 0.18); + --color-scrollbar-thumb-right-hover: rgba(255, 255, 255, 0.25); } body[theme-mode='light'] { --color-scrollbar-thumb: rgba(0, 0, 0, 0.15); --color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.2); - --color-scrollbar-thumb-right: rgba(0, 0, 0, 0.25); - --color-scrollbar-thumb-right-hover: rgba(0, 0, 0, 0.35); + --color-scrollbar-thumb-right: rgba(0, 0, 0, 0.18); + --color-scrollbar-thumb-right-hover: rgba(0, 0, 0, 0.25); } /* 全局初始化滚动条样式 */ diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index a95c6a53da..790efdfcbb 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -145,7 +145,7 @@ const visionAllowedModels = [ 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`, 'i' ) @@ -1044,7 +1044,7 @@ export function isEmbeddingModel(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 { diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 5ffea3c99d..9fb93d9caa 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -145,7 +145,12 @@ "model": { "stream_output": "Stream Output", "search": "Search models...", - "pinned": "Pinned" + "pinned": "Pinned", + "type": { + "text": "Text", + "vision": "Vision", + "select": "Select Model Types" + } }, "paintings": { "title": "Images", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index c7854735ce..7c38310386 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -145,7 +145,12 @@ "model": { "stream_output": "Потоковый вывод", "search": "Поиск моделей...", - "pinned": "Закреплено" + "pinned": "Закреплено", + "type": { + "text": "Текст", + "vision": "Изображение", + "select": "Выберите тип модели" + } }, "paintings": { "title": "Изображения", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 11d8ec9a84..87041ea66f 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -145,7 +145,12 @@ "model": { "stream_output": "流式输出", "search": "搜索模型...", - "pinned": "已固定" + "pinned": "已固定", + "type": { + "text": "文本", + "vision": "图像", + "select": "选择模型类型" + } }, "paintings": { "title": "图片", @@ -316,7 +321,7 @@ "about.title": "关于我们", "about.releases.title": "更新日志", "about.releases.button": "查看", - "about.website.title": "官方网站", + "about.website.title": "官方网���", "about.website.button": "查看", "about.feedback.title": "意见反馈", "about.feedback.button": "反馈", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 441aa3874c..7e7d2331de 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -145,7 +145,12 @@ "model": { "stream_output": "串流輸出", "search": "搜尋模型...", - "pinned": "已固定" + "pinned": "已固定", + "type": { + "text": "文字", + "vision": "圖像", + "select": "選擇模型類型" + } }, "paintings": { "title": "繪圖", @@ -431,7 +436,7 @@ "user": "用戶", "assistant": "助手", "created": "創建時間", - "last_updated": "最後更新", + "last_updated": "最後��新", "messages": "訊息數", "conversation_details": "會話詳情", "conversation_history": "會話歷史" diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index 5d0f032741..3b3f35c0bc 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -4,18 +4,19 @@ import { ExportOutlined, LoadingOutlined, MinusCircleOutlined, - PlusOutlined + PlusOutlined, + SettingOutlined } from '@ant-design/icons' 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 { useTheme } from '@renderer/context/ThemeProvider' import { useProvider } from '@renderer/hooks/useProvider' import i18n from '@renderer/i18n' import { isOpenAIProvider } from '@renderer/providers/ProviderFactory' import { checkApi } from '@renderer/services/ApiService' -import { Provider } from '@renderer/types' -import { Avatar, Button, Card, Divider, Flex, Input, Space, Switch } from 'antd' +import { Model, ModelType, Provider } from '@renderer/types' +import { Avatar, Button, Card, Checkbox, Divider, Flex, Input, Popover, Space, Switch } from 'antd' import Link from 'antd/es/typography/Link' import { groupBy, isEmpty } from 'lodash' import { FC, useEffect, useState } from 'react' @@ -125,6 +126,26 @@ const ProviderSetting: FC = ({ provider: _provider }) => { 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) => ( +
+ onUpdateModelTypes(model, types as ModelType[])} + options={[{ label: t('model.type.vision'), value: 'vision', disabled: VISION_REGEX.test(model.id) }]} + /> +
+ ) + return ( @@ -211,6 +232,9 @@ const ProviderSetting: FC = ({ provider: _provider }) => { {model.name[0].toUpperCase()} {model.name} {isVisionModel(model) && } + + + removeModel(model)} /> @@ -265,4 +289,14 @@ const RemoveIcon = styled(MinusCircleOutlined)` 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 diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index ce69b2fcb7..2ce5ff2837 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -78,6 +78,8 @@ export type Provider = { export type ProviderType = 'openai' | 'anthropic' | 'gemini' +export type ModelType = 'text' | 'vision' + export type Model = { id: string provider: string @@ -85,6 +87,7 @@ export type Model = { group: string owned_by?: string description?: string + type?: ModelType[] } export type Suggestion = {