feat: update input component and add translation features
This commit is contained in:
parent
603b867a5f
commit
dcf57651fe
@ -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 { useState } from 'react'
|
||||||
|
|
||||||
import { Box } from '../Layout'
|
import { Box } from '../Layout'
|
||||||
@ -9,7 +10,7 @@ interface PromptPopupShowParams {
|
|||||||
message: string
|
message: string
|
||||||
defaultValue?: string
|
defaultValue?: string
|
||||||
inputPlaceholder?: string
|
inputPlaceholder?: string
|
||||||
inputProps?: InputProps
|
inputProps?: TextAreaProps
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props extends PromptPopupShowParams {
|
interface Props extends PromptPopupShowParams {
|
||||||
@ -42,13 +43,14 @@ const PromptPopupContainer: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<Modal title={title} open={open} onOk={onOk} onCancel={handleCancel} afterClose={onClose} centered>
|
<Modal title={title} open={open} onOk={onOk} onCancel={handleCancel} afterClose={onClose} centered>
|
||||||
<Box mb={8}>{message}</Box>
|
<Box mb={8}>{message}</Box>
|
||||||
<Input
|
<Input.TextArea
|
||||||
placeholder={inputPlaceholder}
|
placeholder={inputPlaceholder}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
allowClear
|
allowClear
|
||||||
autoFocus
|
autoFocus
|
||||||
onPressEnter={onOk}
|
onPressEnter={onOk}
|
||||||
|
rows={1}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -46,3 +46,6 @@ export const AGENT_PROMPT = `
|
|||||||
|
|
||||||
export const SUMMARIZE_PROMPT =
|
export const SUMMARIZE_PROMPT =
|
||||||
'你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不要使用标点符号和其他特殊符号。'
|
'你是一名擅长会话的助理,你需要将用户的会话总结为 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}}'
|
||||||
|
|||||||
@ -32,7 +32,8 @@
|
|||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"download": "Download"
|
"download": "Download",
|
||||||
|
"reset": "Reset"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
@ -312,6 +313,8 @@
|
|||||||
"models.add.group_name": "Group Name",
|
"models.add.group_name": "Group Name",
|
||||||
"models.add.group_name.tooltip": "Optional e.g. ChatGPT",
|
"models.add.group_name.tooltip": "Optional e.g. ChatGPT",
|
||||||
"models.add.group_name.placeholder": "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",
|
"models.empty": "No models found",
|
||||||
"assistant.title": "Default Assistant",
|
"assistant.title": "Default Assistant",
|
||||||
"assistant.model_params": "Model Parameters",
|
"assistant.model_params": "Model Parameters",
|
||||||
|
|||||||
@ -32,7 +32,8 @@
|
|||||||
"chat": "Чат",
|
"chat": "Чат",
|
||||||
"close": "Закрыть",
|
"close": "Закрыть",
|
||||||
"cancel": "Отмена",
|
"cancel": "Отмена",
|
||||||
"download": "Скачать"
|
"download": "Скачать",
|
||||||
|
"reset": "Сбросить"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "Добавить",
|
"add": "Добавить",
|
||||||
@ -312,6 +313,8 @@
|
|||||||
"models.add.group_name": "Имя группы",
|
"models.add.group_name": "Имя группы",
|
||||||
"models.add.group_name.tooltip": "Необязательно, например, ChatGPT",
|
"models.add.group_name.tooltip": "Необязательно, например, ChatGPT",
|
||||||
"models.add.group_name.placeholder": "Необязательно, например, ChatGPT",
|
"models.add.group_name.placeholder": "Необязательно, например, ChatGPT",
|
||||||
|
"models.translate_model_prompt_title": "Модель перевода",
|
||||||
|
"models.translate_model_prompt_message": "Введите модель перевода",
|
||||||
"models.empty": "Модели не найдены",
|
"models.empty": "Модели не найдены",
|
||||||
"assistant.title": "Ассистент по умолчанию",
|
"assistant.title": "Ассистент по умолчанию",
|
||||||
"assistant.model_params": "Параметры модели",
|
"assistant.model_params": "Параметры модели",
|
||||||
|
|||||||
@ -32,7 +32,8 @@
|
|||||||
"chat": "聊天",
|
"chat": "聊天",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"download": "下载"
|
"download": "下载",
|
||||||
|
"reset": "重置"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
@ -300,6 +301,8 @@
|
|||||||
"models.add.group_name": "分组名称",
|
"models.add.group_name": "分组名称",
|
||||||
"models.add.group_name.tooltip": "例如 ChatGPT",
|
"models.add.group_name.tooltip": "例如 ChatGPT",
|
||||||
"models.add.group_name.placeholder": "例如 ChatGPT",
|
"models.add.group_name.placeholder": "例如 ChatGPT",
|
||||||
|
"models.translate_model_prompt_title": "翻译模型提示词",
|
||||||
|
"models.translate_model_prompt_message": "请输入翻译模型提示词",
|
||||||
"models.empty": "没有模型",
|
"models.empty": "没有模型",
|
||||||
"assistant.title": "默认助手",
|
"assistant.title": "默认助手",
|
||||||
"assistant.model_params": "模型参数",
|
"assistant.model_params": "模型参数",
|
||||||
|
|||||||
@ -32,7 +32,8 @@
|
|||||||
"chat": "聊天",
|
"chat": "聊天",
|
||||||
"close": "關閉",
|
"close": "關閉",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"download": "下載"
|
"download": "下載",
|
||||||
|
"reset": "重置"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
@ -300,6 +301,8 @@
|
|||||||
"models.add.group_name": "群組名稱",
|
"models.add.group_name": "群組名稱",
|
||||||
"models.add.group_name.tooltip": "可選,例如 ChatGPT",
|
"models.add.group_name.tooltip": "可選,例如 ChatGPT",
|
||||||
"models.add.group_name.placeholder": "可選,例如 ChatGPT",
|
"models.add.group_name.placeholder": "可選,例如 ChatGPT",
|
||||||
|
"models.translate_model_prompt_title": "翻譯模型提示詞",
|
||||||
|
"models.translate_model_prompt_message": "請輸入翻譯模型提示詞",
|
||||||
"models.empty": "找不到模型",
|
"models.empty": "找不到模型",
|
||||||
"assistant.title": "預設助手",
|
"assistant.title": "預設助手",
|
||||||
"assistant.model_params": "模型參數",
|
"assistant.model_params": "模型參數",
|
||||||
|
|||||||
@ -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 { 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 { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { useDefaultModel } from '@renderer/hooks/useAssistant'
|
import { useDefaultModel } from '@renderer/hooks/useAssistant'
|
||||||
import { useProviders } from '@renderer/hooks/useProvider'
|
import { useProviders } from '@renderer/hooks/useProvider'
|
||||||
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { getModelUniqId, hasModel } from '@renderer/services/ModelService'
|
import { getModelUniqId, hasModel } from '@renderer/services/ModelService'
|
||||||
|
import { useAppDispatch } from '@renderer/store'
|
||||||
|
import { setTranslateModelPrompt } from '@renderer/store/settings'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
import { Button, Select } from 'antd'
|
import { Button, Select, Tooltip } from 'antd'
|
||||||
import { find, sortBy } from 'lodash'
|
import { find, sortBy } from 'lodash'
|
||||||
import { FC, useMemo } from 'react'
|
import { FC, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -20,6 +25,9 @@ const ModelSettings: FC = () => {
|
|||||||
const allModels = providers.map((p) => p.models).flat()
|
const allModels = providers.map((p) => p.models).flat()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { translateModelPrompt } = useSettings()
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const selectOptions = providers
|
const selectOptions = providers
|
||||||
.filter((p) => p.models.length > 0)
|
.filter((p) => p.models.length > 0)
|
||||||
@ -47,6 +55,24 @@ const ModelSettings: FC = () => {
|
|||||||
[translateModel]
|
[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 (
|
return (
|
||||||
<SettingContainer theme={theme}>
|
<SettingContainer theme={theme}>
|
||||||
<SettingGroup theme={theme}>
|
<SettingGroup theme={theme}>
|
||||||
@ -93,14 +119,22 @@ const ModelSettings: FC = () => {
|
|||||||
{t('settings.models.translate_model')}
|
{t('settings.models.translate_model')}
|
||||||
</div>
|
</div>
|
||||||
</SettingTitle>
|
</SettingTitle>
|
||||||
<Select
|
<HStack alignItems="center">
|
||||||
value={defaultTranslateModel}
|
<Select
|
||||||
defaultValue={defaultTranslateModel}
|
value={defaultTranslateModel}
|
||||||
style={{ width: 360 }}
|
defaultValue={defaultTranslateModel}
|
||||||
onChange={(value) => setTranslateModel(find(allModels, JSON.parse(value)) as Model)}
|
style={{ width: 360 }}
|
||||||
options={selectOptions}
|
onChange={(value) => setTranslateModel(find(allModels, JSON.parse(value)) as Model)}
|
||||||
placeholder={t('settings.models.empty')}
|
options={selectOptions}
|
||||||
/>
|
placeholder={t('settings.models.empty')}
|
||||||
|
/>
|
||||||
|
<Button icon={<SettingOutlined />} style={{ marginLeft: 8 }} onClick={onUpdateTranslateModel} />
|
||||||
|
{translateModelPrompt !== TRANSLATE_PROMPT && (
|
||||||
|
<Tooltip title={t('common.reset')}>
|
||||||
|
<Button icon={<RedoOutlined />} style={{ marginLeft: 8 }} onClick={onResetTranslatePrompt}></Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
<SettingDescription>{t('settings.models.translate_model_description')}</SettingDescription>
|
<SettingDescription>{t('settings.models.translate_model_description')}</SettingDescription>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import VisionIcon from '@renderer/components/Icons/VisionIcon'
|
|||||||
import { getModelLogo, isVisionModel, VISION_REGEX } 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 { useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAssistants, useDefaultModel } from '@renderer/hooks/useAssistant'
|
||||||
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'
|
||||||
@ -57,6 +57,8 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
const { defaultModel, setDefaultModel } = useDefaultModel()
|
||||||
|
|
||||||
const modelGroups = groupBy(models, 'group')
|
const modelGroups = groupBy(models, 'group')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -151,6 +153,10 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (defaultModel?.id === model.id && defaultModel?.provider === provider.id) {
|
||||||
|
setDefaultModel({ ...defaultModel, type: types })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelTypeContent = (model: Model) => (
|
const modelTypeContent = (model: Model) => (
|
||||||
|
|||||||
@ -23,7 +23,10 @@ export function getDefaultTranslateAssistant(targetLanguage: string, text: strin
|
|||||||
const translateModel = getTranslateModel()
|
const translateModel = getTranslateModel()
|
||||||
const assistant: Assistant = getDefaultAssistant()
|
const assistant: Assistant = getDefaultAssistant()
|
||||||
assistant.model = translateModel
|
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
|
return assistant
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ const persistedReducer = persistReducer(
|
|||||||
{
|
{
|
||||||
key: 'cherry-studio',
|
key: 'cherry-studio',
|
||||||
storage,
|
storage,
|
||||||
version: 43,
|
version: 44,
|
||||||
blacklist: ['runtime'],
|
blacklist: ['runtime'],
|
||||||
migrate
|
migrate
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SYSTEM_MODELS } from '@renderer/config/models'
|
import { SYSTEM_MODELS } from '@renderer/config/models'
|
||||||
|
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
|
||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
import i18n from '@renderer/i18n'
|
import i18n from '@renderer/i18n'
|
||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
@ -699,6 +700,10 @@ const migrateConfig = {
|
|||||||
state.settings.proxyMode = 'system'
|
state.settings.proxyMode = 'system'
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
|
},
|
||||||
|
'44': (state: RootState) => {
|
||||||
|
state.settings.translateModelPrompt = TRANSLATE_PROMPT
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
import { TRANSLATE_PROMPT } from '@renderer/config/prompts'
|
||||||
import { CodeStyleVarious, LanguageVarious, ThemeMode } from '@renderer/types'
|
import { CodeStyleVarious, LanguageVarious, ThemeMode } from '@renderer/types'
|
||||||
|
|
||||||
export type SendMessageShortcut = 'Enter' | 'Shift+Enter'
|
export type SendMessageShortcut = 'Enter' | 'Shift+Enter'
|
||||||
@ -34,6 +35,7 @@ export interface SettingsState {
|
|||||||
webdavUser: string
|
webdavUser: string
|
||||||
webdavPass: string
|
webdavPass: string
|
||||||
webdavPath: string
|
webdavPath: string
|
||||||
|
translateModelPrompt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: SettingsState = {
|
const initialState: SettingsState = {
|
||||||
@ -65,7 +67,8 @@ const initialState: SettingsState = {
|
|||||||
webdavHost: '',
|
webdavHost: '',
|
||||||
webdavUser: '',
|
webdavUser: '',
|
||||||
webdavPass: '',
|
webdavPass: '',
|
||||||
webdavPath: '/cherry-studio'
|
webdavPath: '/cherry-studio',
|
||||||
|
translateModelPrompt: TRANSLATE_PROMPT
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
@ -165,6 +168,9 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setCodeStyle: (state, action: PayloadAction<CodeStyleVarious>) => {
|
setCodeStyle: (state, action: PayloadAction<CodeStyleVarious>) => {
|
||||||
state.codeStyle = action.payload
|
state.codeStyle = action.payload
|
||||||
|
},
|
||||||
|
setTranslateModelPrompt: (state, action: PayloadAction<string>) => {
|
||||||
|
state.translateModelPrompt = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -200,7 +206,8 @@ export const {
|
|||||||
setCodeCollapsible,
|
setCodeCollapsible,
|
||||||
setMathEngine,
|
setMathEngine,
|
||||||
setMessageStyle,
|
setMessageStyle,
|
||||||
setCodeStyle
|
setCodeStyle,
|
||||||
|
setTranslateModelPrompt
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user