From dcf57651fe09d03b864dc52617b335c4f694e72f Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Mon, 25 Nov 2024 13:24:36 +0800 Subject: [PATCH] feat: update input component and add translation features --- .../src/components/Popups/PromptPopup.tsx | 8 +-- src/renderer/src/config/prompts.ts | 3 ++ src/renderer/src/i18n/locales/en-us.json | 5 +- src/renderer/src/i18n/locales/ru-ru.json | 5 +- src/renderer/src/i18n/locales/zh-cn.json | 5 +- src/renderer/src/i18n/locales/zh-tw.json | 5 +- .../src/pages/settings/ModelSettings.tsx | 54 +++++++++++++++---- .../ProviderSettings/ProviderSetting.tsx | 8 ++- src/renderer/src/services/AssistantService.ts | 5 +- src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/migrate.ts | 5 ++ src/renderer/src/store/settings.ts | 11 +++- 12 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/components/Popups/PromptPopup.tsx b/src/renderer/src/components/Popups/PromptPopup.tsx index 0d4552d1..519bd10f 100644 --- a/src/renderer/src/components/Popups/PromptPopup.tsx +++ b/src/renderer/src/components/Popups/PromptPopup.tsx @@ -1,4 +1,5 @@ -import { Input, InputProps, Modal } from 'antd' +import { Input, Modal } from 'antd' +import { TextAreaProps } from 'antd/es/input' import { useState } from 'react' import { Box } from '../Layout' @@ -9,7 +10,7 @@ interface PromptPopupShowParams { message: string defaultValue?: string inputPlaceholder?: string - inputProps?: InputProps + inputProps?: TextAreaProps } interface Props extends PromptPopupShowParams { @@ -42,13 +43,14 @@ const PromptPopupContainer: React.FC = ({ return ( {message} - setValue(e.target.value)} allowClear autoFocus onPressEnter={onOk} + rows={1} {...inputProps} /> diff --git a/src/renderer/src/config/prompts.ts b/src/renderer/src/config/prompts.ts index 63157654..281ef01f 100644 --- a/src/renderer/src/config/prompts.ts +++ b/src/renderer/src/config/prompts.ts @@ -46,3 +46,6 @@ export const AGENT_PROMPT = ` export const SUMMARIZE_PROMPT = '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不要使用标点符号和其他特殊符号。' + +export const TRANSLATE_PROMPT = + 'Translate from input language to {{target_language}}, provide the translation result directly without any explanation, keep original format. If the target language is the same as the source language, do not translate. The text to be translated is as follows:\n\n{{text}}' diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 942cff14..1bd18baa 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -32,7 +32,8 @@ "chat": "Chat", "close": "Close", "cancel": "Cancel", - "download": "Download" + "download": "Download", + "reset": "Reset" }, "button": { "add": "Add", @@ -312,6 +313,8 @@ "models.add.group_name": "Group Name", "models.add.group_name.tooltip": "Optional e.g. ChatGPT", "models.add.group_name.placeholder": "Optional e.g. ChatGPT", + "models.translate_model_prompt_title": "Translate Model Prompt", + "models.translate_model_prompt_message": "Please enter the translate model prompt", "models.empty": "No models found", "assistant.title": "Default Assistant", "assistant.model_params": "Model Parameters", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index fd0859fa..f6ca46b6 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -32,7 +32,8 @@ "chat": "Чат", "close": "Закрыть", "cancel": "Отмена", - "download": "Скачать" + "download": "Скачать", + "reset": "Сбросить" }, "button": { "add": "Добавить", @@ -312,6 +313,8 @@ "models.add.group_name": "Имя группы", "models.add.group_name.tooltip": "Необязательно, например, ChatGPT", "models.add.group_name.placeholder": "Необязательно, например, ChatGPT", + "models.translate_model_prompt_title": "Модель перевода", + "models.translate_model_prompt_message": "Введите модель перевода", "models.empty": "Модели не найдены", "assistant.title": "Ассистент по умолчанию", "assistant.model_params": "Параметры модели", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 158e986e..6671aa6e 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -32,7 +32,8 @@ "chat": "聊天", "close": "关闭", "cancel": "取消", - "download": "下载" + "download": "下载", + "reset": "重置" }, "button": { "add": "添加", @@ -300,6 +301,8 @@ "models.add.group_name": "分组名称", "models.add.group_name.tooltip": "例如 ChatGPT", "models.add.group_name.placeholder": "例如 ChatGPT", + "models.translate_model_prompt_title": "翻译模型提示词", + "models.translate_model_prompt_message": "请输入翻译模型提示词", "models.empty": "没有模型", "assistant.title": "默认助手", "assistant.model_params": "模型参数", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 37b4cc76..32c2ba8b 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -32,7 +32,8 @@ "chat": "聊天", "close": "關閉", "cancel": "取消", - "download": "下載" + "download": "下載", + "reset": "重置" }, "button": { "add": "添加", @@ -300,6 +301,8 @@ "models.add.group_name": "群組名稱", "models.add.group_name.tooltip": "可選,例如 ChatGPT", "models.add.group_name.placeholder": "可選,例如 ChatGPT", + "models.translate_model_prompt_title": "翻譯模型提示詞", + "models.translate_model_prompt_message": "請輸入翻譯模型提示詞", "models.empty": "找不到模型", "assistant.title": "預設助手", "assistant.model_params": "模型參數", diff --git a/src/renderer/src/pages/settings/ModelSettings.tsx b/src/renderer/src/pages/settings/ModelSettings.tsx index 9d3fd636..c6631051 100644 --- a/src/renderer/src/pages/settings/ModelSettings.tsx +++ b/src/renderer/src/pages/settings/ModelSettings.tsx @@ -1,11 +1,16 @@ -import { EditOutlined, MessageOutlined, SettingOutlined, TranslationOutlined } from '@ant-design/icons' +import { EditOutlined, MessageOutlined, RedoOutlined, SettingOutlined, TranslationOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' +import PromptPopup from '@renderer/components/Popups/PromptPopup' +import { TRANSLATE_PROMPT } from '@renderer/config/prompts' import { useTheme } from '@renderer/context/ThemeProvider' import { useDefaultModel } from '@renderer/hooks/useAssistant' import { useProviders } from '@renderer/hooks/useProvider' +import { useSettings } from '@renderer/hooks/useSettings' import { getModelUniqId, hasModel } from '@renderer/services/ModelService' +import { useAppDispatch } from '@renderer/store' +import { setTranslateModelPrompt } from '@renderer/store/settings' import { Model } from '@renderer/types' -import { Button, Select } from 'antd' +import { Button, Select, Tooltip } from 'antd' import { find, sortBy } from 'lodash' import { FC, useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -20,6 +25,9 @@ const ModelSettings: FC = () => { const allModels = providers.map((p) => p.models).flat() const { theme } = useTheme() const { t } = useTranslation() + const { translateModelPrompt } = useSettings() + + const dispatch = useAppDispatch() const selectOptions = providers .filter((p) => p.models.length > 0) @@ -47,6 +55,24 @@ const ModelSettings: FC = () => { [translateModel] ) + const onUpdateTranslateModel = async () => { + const prompt = await PromptPopup.show({ + title: t('settings.models.translate_model_prompt_title'), + message: t('settings.models.translate_model_prompt_message'), + defaultValue: translateModelPrompt, + inputProps: { + rows: 10 + } + }) + if (prompt) { + dispatch(setTranslateModelPrompt(prompt)) + } + } + + const onResetTranslatePrompt = () => { + dispatch(setTranslateModelPrompt(TRANSLATE_PROMPT)) + } + return ( @@ -93,14 +119,22 @@ const ModelSettings: FC = () => { {t('settings.models.translate_model')} - setTranslateModel(find(allModels, JSON.parse(value)) as Model)} + options={selectOptions} + placeholder={t('settings.models.empty')} + /> + + + )} + {t('settings.models.translate_model_description')} diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index 24198b50..716a9e6c 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -11,7 +11,7 @@ import VisionIcon from '@renderer/components/Icons/VisionIcon' import { getModelLogo, isVisionModel, VISION_REGEX } from '@renderer/config/models' import { PROVIDER_CONFIG } from '@renderer/config/providers' import { useTheme } from '@renderer/context/ThemeProvider' -import { useAssistants } from '@renderer/hooks/useAssistant' +import { useAssistants, useDefaultModel } from '@renderer/hooks/useAssistant' import { useProvider } from '@renderer/hooks/useProvider' import i18n from '@renderer/i18n' import { isOpenAIProvider } from '@renderer/providers/ProviderFactory' @@ -57,6 +57,8 @@ const ProviderSetting: FC = ({ provider: _provider }) => { const { theme } = useTheme() const dispatch = useAppDispatch() + const { defaultModel, setDefaultModel } = useDefaultModel() + const modelGroups = groupBy(models, 'group') useEffect(() => { @@ -151,6 +153,10 @@ const ProviderSetting: FC = ({ provider: _provider }) => { ) } }) + + if (defaultModel?.id === model.id && defaultModel?.provider === provider.id) { + setDefaultModel({ ...defaultModel, type: types }) + } } const modelTypeContent = (model: Model) => ( diff --git a/src/renderer/src/services/AssistantService.ts b/src/renderer/src/services/AssistantService.ts index b9b02990..a386e173 100644 --- a/src/renderer/src/services/AssistantService.ts +++ b/src/renderer/src/services/AssistantService.ts @@ -23,7 +23,10 @@ export function getDefaultTranslateAssistant(targetLanguage: string, text: strin const translateModel = getTranslateModel() const assistant: Assistant = getDefaultAssistant() assistant.model = translateModel - assistant.prompt = `Translate from input language to ${targetLanguage}, provide the translation result directly without any explanation, keep original format. If the target language is the same as the source language, do not translate. The text to be translated is as follows:\n\n ${text}` + assistant.prompt = store + .getState() + .settings.translateModelPrompt.replace('{{target_language}}', targetLanguage) + .replace('{{text}}', text) return assistant } diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index b6862c79..b471d353 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -24,7 +24,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 43, + version: 44, blacklist: ['runtime'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 18449e19..b9bbad36 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -1,4 +1,5 @@ import { SYSTEM_MODELS } from '@renderer/config/models' +import { TRANSLATE_PROMPT } from '@renderer/config/prompts' import db from '@renderer/databases' import i18n from '@renderer/i18n' import { Assistant } from '@renderer/types' @@ -699,6 +700,10 @@ const migrateConfig = { state.settings.proxyMode = 'system' } return state + }, + '44': (state: RootState) => { + state.settings.translateModelPrompt = TRANSLATE_PROMPT + return state } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index b0838caf..36a98182 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -1,4 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { TRANSLATE_PROMPT } from '@renderer/config/prompts' import { CodeStyleVarious, LanguageVarious, ThemeMode } from '@renderer/types' export type SendMessageShortcut = 'Enter' | 'Shift+Enter' @@ -34,6 +35,7 @@ export interface SettingsState { webdavUser: string webdavPass: string webdavPath: string + translateModelPrompt: string } const initialState: SettingsState = { @@ -65,7 +67,8 @@ const initialState: SettingsState = { webdavHost: '', webdavUser: '', webdavPass: '', - webdavPath: '/cherry-studio' + webdavPath: '/cherry-studio', + translateModelPrompt: TRANSLATE_PROMPT } const settingsSlice = createSlice({ @@ -165,6 +168,9 @@ const settingsSlice = createSlice({ }, setCodeStyle: (state, action: PayloadAction) => { state.codeStyle = action.payload + }, + setTranslateModelPrompt: (state, action: PayloadAction) => { + state.translateModelPrompt = action.payload } } }) @@ -200,7 +206,8 @@ export const { setCodeCollapsible, setMathEngine, setMessageStyle, - setCodeStyle + setCodeStyle, + setTranslateModelPrompt } = settingsSlice.actions export default settingsSlice.reducer