mirror of
https://github.com/NovaOSS/nova-betterchat.git
synced 2024-11-25 19:13:59 +01:00
feat: customise default model parameters and system message
Fixes #97, Fixes #89, Fixes #35
This commit is contained in:
parent
92f09c275b
commit
b0bfe56fd3
|
@ -21,5 +21,8 @@
|
|||
"frequencyPenalty": {
|
||||
"label": "Frequency Penalty",
|
||||
"description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. (Default: 0)"
|
||||
}
|
||||
},
|
||||
"defaultChatConfig": "Default Chat Config",
|
||||
"defaultSystemMessage": "Default System Message",
|
||||
"resetToDefault": "Reset To Default"
|
||||
}
|
||||
|
|
|
@ -21,5 +21,8 @@
|
|||
"frequencyPenalty": {
|
||||
"label": "频率惩罚",
|
||||
"description": "数值在 -2.0 到 2.0 之间。正值会根据新 token 在文本中的现有频率来惩罚它们,降低模型直接重复相同语句的可能性。(默认: 0)"
|
||||
}
|
||||
},
|
||||
"defaultChatConfig": "默认聊天配置",
|
||||
"defaultSystemMessage": "默认系统消息",
|
||||
"resetToDefault": "重置为默认"
|
||||
}
|
||||
|
|
|
@ -21,5 +21,8 @@
|
|||
"frequencyPenalty": {
|
||||
"label": "頻率懲罰",
|
||||
"description": "係一個 -2.0 到 2.0 之間嘅數值。正嘅數值表示,如果 token 喺之前嘅文字中出現頻率越高,輸出嗰陣就會越大力噉懲罰佢,令到佢被揀中嘅機率降低,即係可以降低模型重複同一句説話嘅機會。(預設: 0)"
|
||||
}
|
||||
},
|
||||
"defaultChatConfig": "預設聊天配置",
|
||||
"defaultSystemMessage": "預設系統消息",
|
||||
"resetToDefault": "重置為預設"
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { shallow } from 'zustand/shallow';
|
|||
import useStore from '@store/store';
|
||||
import ConfigMenu from '@components/ConfigMenu';
|
||||
import { ChatInterface, ConfigInterface } from '@type/chat';
|
||||
import { defaultChatConfig } from '@constants/chat';
|
||||
import { _defaultChatConfig } from '@constants/chat';
|
||||
|
||||
const ChatTitle = React.memo(() => {
|
||||
const { t } = useTranslation('model');
|
||||
|
@ -35,7 +35,7 @@ const ChatTitle = React.memo(() => {
|
|||
const chats = useStore.getState().chats;
|
||||
if (chats && chats.length > 0 && currentChatIndex !== -1 && !config) {
|
||||
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
||||
updatedChats[currentChatIndex].config = { ...defaultChatConfig };
|
||||
updatedChats[currentChatIndex].config = { ..._defaultChatConfig };
|
||||
setChats(updatedChats);
|
||||
}
|
||||
}, [currentChatIndex]);
|
||||
|
|
168
src/components/ChatConfigMenu/ChatConfigMenu.tsx
Normal file
168
src/components/ChatConfigMenu/ChatConfigMenu.tsx
Normal file
|
@ -0,0 +1,168 @@
|
|||
import React, { useState } from 'react';
|
||||
import useStore from '@store/store';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import PopupModal from '@components/PopupModal';
|
||||
import {
|
||||
FrequencyPenaltySlider,
|
||||
MaxTokenSlider,
|
||||
ModelSelector,
|
||||
PresencePenaltySlider,
|
||||
TemperatureSlider,
|
||||
TopPSlider,
|
||||
} from '@components/ConfigMenu/ConfigMenu';
|
||||
|
||||
import { ModelOptions } from '@type/chat';
|
||||
import { _defaultChatConfig, _defaultSystemMessage } from '@constants/chat';
|
||||
|
||||
const ChatConfigMenu = () => {
|
||||
const { t } = useTranslation('model');
|
||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||
return (
|
||||
<div>
|
||||
<button className='btn btn-neutral' onClick={() => setIsModalOpen(true)}>
|
||||
{t('defaultChatConfig')}
|
||||
</button>
|
||||
{isModalOpen && <ChatConfigPopup setIsModalOpen={setIsModalOpen} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ChatConfigPopup = ({
|
||||
setIsModalOpen,
|
||||
}: {
|
||||
setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}) => {
|
||||
const config = useStore.getState().defaultChatConfig;
|
||||
const setDefaultChatConfig = useStore((state) => state.setDefaultChatConfig);
|
||||
const setDefaultSystemMessage = useStore(
|
||||
(state) => state.setDefaultSystemMessage
|
||||
);
|
||||
|
||||
const [_systemMessage, _setSystemMessage] = useState<string>(
|
||||
useStore.getState().defaultSystemMessage
|
||||
);
|
||||
const [_model, _setModel] = useState<ModelOptions>(config.model);
|
||||
const [_maxToken, _setMaxToken] = useState<number>(config.max_tokens);
|
||||
const [_temperature, _setTemperature] = useState<number>(config.temperature);
|
||||
const [_topP, _setTopP] = useState<number>(config.top_p);
|
||||
const [_presencePenalty, _setPresencePenalty] = useState<number>(
|
||||
config.presence_penalty
|
||||
);
|
||||
const [_frequencyPenalty, _setFrequencyPenalty] = useState<number>(
|
||||
config.frequency_penalty
|
||||
);
|
||||
|
||||
const { t } = useTranslation('model');
|
||||
|
||||
const handleSave = () => {
|
||||
setDefaultChatConfig({
|
||||
model: _model,
|
||||
max_tokens: _maxToken,
|
||||
temperature: _temperature,
|
||||
top_p: _topP,
|
||||
presence_penalty: _presencePenalty,
|
||||
frequency_penalty: _frequencyPenalty,
|
||||
});
|
||||
setDefaultSystemMessage(_systemMessage);
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
_setModel(_defaultChatConfig.model);
|
||||
_setMaxToken(_defaultChatConfig.max_tokens);
|
||||
_setTemperature(_defaultChatConfig.temperature);
|
||||
_setTopP(_defaultChatConfig.top_p);
|
||||
_setPresencePenalty(_defaultChatConfig.presence_penalty);
|
||||
_setFrequencyPenalty(_defaultChatConfig.frequency_penalty);
|
||||
_setSystemMessage(_defaultSystemMessage);
|
||||
};
|
||||
|
||||
return (
|
||||
<PopupModal
|
||||
title={t('defaultChatConfig') as string}
|
||||
setIsModalOpen={setIsModalOpen}
|
||||
handleConfirm={handleSave}
|
||||
>
|
||||
<div className='p-6 border-b border-gray-200 dark:border-gray-600 w-[90vw] max-w-full text-sm text-gray-900 dark:text-gray-300'>
|
||||
<DefaultSystemChat
|
||||
_systemMessage={_systemMessage}
|
||||
_setSystemMessage={_setSystemMessage}
|
||||
/>
|
||||
<ModelSelector _model={_model} _setModel={_setModel} />
|
||||
<MaxTokenSlider
|
||||
_maxToken={_maxToken}
|
||||
_setMaxToken={_setMaxToken}
|
||||
_model={_model}
|
||||
/>
|
||||
<TemperatureSlider
|
||||
_temperature={_temperature}
|
||||
_setTemperature={_setTemperature}
|
||||
/>
|
||||
<TopPSlider _topP={_topP} _setTopP={_setTopP} />
|
||||
<PresencePenaltySlider
|
||||
_presencePenalty={_presencePenalty}
|
||||
_setPresencePenalty={_setPresencePenalty}
|
||||
/>
|
||||
<FrequencyPenaltySlider
|
||||
_frequencyPenalty={_frequencyPenalty}
|
||||
_setFrequencyPenalty={_setFrequencyPenalty}
|
||||
/>
|
||||
<div
|
||||
className='btn btn-neutral cursor-pointer mt-5'
|
||||
onClick={handleReset}
|
||||
>
|
||||
{t('resetToDefault')}
|
||||
</div>
|
||||
</div>
|
||||
</PopupModal>
|
||||
);
|
||||
};
|
||||
|
||||
const DefaultSystemChat = ({
|
||||
_systemMessage,
|
||||
_setSystemMessage,
|
||||
}: {
|
||||
_systemMessage: string;
|
||||
_setSystemMessage: React.Dispatch<React.SetStateAction<string>>;
|
||||
}) => {
|
||||
const { t } = useTranslation('model');
|
||||
|
||||
const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
e.target.style.height = 'auto';
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
e.target.style.maxHeight = `${e.target.scrollHeight}px`;
|
||||
};
|
||||
|
||||
const handleOnFocus = (e: React.FocusEvent<HTMLTextAreaElement, Element>) => {
|
||||
e.target.style.height = 'auto';
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
e.target.style.maxHeight = `${e.target.scrollHeight}px`;
|
||||
};
|
||||
|
||||
const handleOnBlur = (e: React.FocusEvent<HTMLTextAreaElement, Element>) => {
|
||||
e.target.style.height = 'auto';
|
||||
e.target.style.maxHeight = '2.5rem';
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('defaultSystemMessage')}
|
||||
</div>
|
||||
<textarea
|
||||
className='my-2 mx-0 px-2 resize-none rounded-lg bg-transparent overflow-y-hidden leading-7 p-1 border border-gray-400/50 focus:ring-1 focus:ring-blue w-full max-h-10 transition-all'
|
||||
onFocus={handleOnFocus}
|
||||
onBlur={handleOnBlur}
|
||||
onChange={(e) => {
|
||||
_setSystemMessage(e.target.value);
|
||||
}}
|
||||
onInput={handleInput}
|
||||
value={_systemMessage}
|
||||
rows={1}
|
||||
></textarea>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatConfigMenu;
|
1
src/components/ChatConfigMenu/index.ts
Normal file
1
src/components/ChatConfigMenu/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './ChatConfigMenu';
|
|
@ -52,92 +52,25 @@ const ConfigMenu = ({
|
|||
_setMaxToken={_setMaxToken}
|
||||
_model={_model}
|
||||
/>
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('temperature.label')}: {_temperature}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_temperature}
|
||||
onChange={(e) => {
|
||||
_setTemperature(Number(e.target.value));
|
||||
}}
|
||||
min={0}
|
||||
max={2}
|
||||
step={0.1}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
<TemperatureSlider
|
||||
_temperature={_temperature}
|
||||
_setTemperature={_setTemperature}
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('temperature.description')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('topP.label')}: {_topP}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_topP}
|
||||
onChange={(e) => {
|
||||
_setTopP(Number(e.target.value));
|
||||
}}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
<TopPSlider _topP={_topP} _setTopP={_setTopP} />
|
||||
<PresencePenaltySlider
|
||||
_presencePenalty={_presencePenalty}
|
||||
_setPresencePenalty={_setPresencePenalty}
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('topP.description')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('presencePenalty.label')}: {_presencePenalty}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_presencePenalty}
|
||||
onChange={(e) => {
|
||||
_setPresencePenalty(Number(e.target.value));
|
||||
}}
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.1}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
<FrequencyPenaltySlider
|
||||
_frequencyPenalty={_frequencyPenalty}
|
||||
_setFrequencyPenalty={_setFrequencyPenalty}
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('presencePenalty.description')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('frequencyPenalty.label')}: {_frequencyPenalty}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_frequencyPenalty}
|
||||
onChange={(e) => {
|
||||
_setFrequencyPenalty(Number(e.target.value));
|
||||
}}
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.1}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('frequencyPenalty.description')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopupModal>
|
||||
);
|
||||
};
|
||||
|
||||
const ModelSelector = ({
|
||||
export const ModelSelector = ({
|
||||
_model,
|
||||
_setModel,
|
||||
}: {
|
||||
|
@ -184,7 +117,7 @@ const ModelSelector = ({
|
|||
);
|
||||
};
|
||||
|
||||
const MaxTokenSlider = ({
|
||||
export const MaxTokenSlider = ({
|
||||
_maxToken,
|
||||
_setMaxToken,
|
||||
_model,
|
||||
|
@ -226,4 +159,136 @@ const MaxTokenSlider = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const TemperatureSlider = ({
|
||||
_temperature,
|
||||
_setTemperature,
|
||||
}: {
|
||||
_temperature: number;
|
||||
_setTemperature: React.Dispatch<React.SetStateAction<number>>;
|
||||
}) => {
|
||||
const { t } = useTranslation('model');
|
||||
|
||||
return (
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('temperature.label')}: {_temperature}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_temperature}
|
||||
onChange={(e) => {
|
||||
_setTemperature(Number(e.target.value));
|
||||
}}
|
||||
min={0}
|
||||
max={2}
|
||||
step={0.1}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('temperature.description')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const TopPSlider = ({
|
||||
_topP,
|
||||
_setTopP,
|
||||
}: {
|
||||
_topP: number;
|
||||
_setTopP: React.Dispatch<React.SetStateAction<number>>;
|
||||
}) => {
|
||||
const { t } = useTranslation('model');
|
||||
|
||||
return (
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('topP.label')}: {_topP}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_topP}
|
||||
onChange={(e) => {
|
||||
_setTopP(Number(e.target.value));
|
||||
}}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('topP.description')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const PresencePenaltySlider = ({
|
||||
_presencePenalty,
|
||||
_setPresencePenalty,
|
||||
}: {
|
||||
_presencePenalty: number;
|
||||
_setPresencePenalty: React.Dispatch<React.SetStateAction<number>>;
|
||||
}) => {
|
||||
const { t } = useTranslation('model');
|
||||
|
||||
return (
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('presencePenalty.label')}: {_presencePenalty}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_presencePenalty}
|
||||
onChange={(e) => {
|
||||
_setPresencePenalty(Number(e.target.value));
|
||||
}}
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.1}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('presencePenalty.description')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const FrequencyPenaltySlider = ({
|
||||
_frequencyPenalty,
|
||||
_setFrequencyPenalty,
|
||||
}: {
|
||||
_frequencyPenalty: number;
|
||||
_setFrequencyPenalty: React.Dispatch<React.SetStateAction<number>>;
|
||||
}) => {
|
||||
const { t } = useTranslation('model');
|
||||
|
||||
return (
|
||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
||||
{t('frequencyPenalty.label')}: {_frequencyPenalty}
|
||||
</label>
|
||||
<input
|
||||
id='default-range'
|
||||
type='range'
|
||||
value={_frequencyPenalty}
|
||||
onChange={(e) => {
|
||||
_setFrequencyPenalty(Number(e.target.value));
|
||||
}}
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.1}
|
||||
className='w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'
|
||||
/>
|
||||
<div className='min-w-fit text-gray-500 dark:text-gray-300 text-sm mt-2'>
|
||||
{t('frequencyPenalty.description')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigMenu;
|
||||
|
|
|
@ -8,6 +8,7 @@ import ThemeSwitcher from '@components/Menu/MenuOptions/ThemeSwitcher';
|
|||
import LanguageSelector from '@components/LanguageSelector';
|
||||
import AutoTitleToggle from './AutoTitleToggle';
|
||||
import PromptLibraryMenu from '@components/PromptLibraryMenu';
|
||||
import ChatConfigMenu from '@components/ChatConfigMenu';
|
||||
|
||||
const SettingsMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
|
@ -39,6 +40,7 @@ const SettingsMenu = () => {
|
|||
<ThemeSwitcher />
|
||||
<AutoTitleToggle />
|
||||
<PromptLibraryMenu />
|
||||
<ChatConfigMenu />
|
||||
</div>
|
||||
</PopupModal>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ChatInterface, ConfigInterface, ModelOptions } from '@type/chat';
|
||||
import useStore from '@store/store';
|
||||
|
||||
const date = new Date();
|
||||
const dateString =
|
||||
|
@ -9,7 +10,7 @@ const dateString =
|
|||
('0' + date.getDate()).slice(-2);
|
||||
|
||||
// default system message obtained using the following method: https://twitter.com/DeminDimin/status/1619935545144279040
|
||||
export const defaultSystemMessage = `You are ChatGPT, a large language model trained by OpenAI.
|
||||
export const _defaultSystemMessage = `You are ChatGPT, a large language model trained by OpenAI.
|
||||
Knowledge cutoff: 2021-09
|
||||
Current date: ${dateString}`;
|
||||
|
||||
|
@ -35,7 +36,7 @@ export const modelMaxToken = {
|
|||
|
||||
export const defaultUserMaxToken = 4000;
|
||||
|
||||
export const defaultChatConfig: ConfigInterface = {
|
||||
export const _defaultChatConfig: ConfigInterface = {
|
||||
model: defaultModel,
|
||||
max_tokens: defaultUserMaxToken,
|
||||
temperature: 1,
|
||||
|
@ -46,8 +47,10 @@ export const defaultChatConfig: ConfigInterface = {
|
|||
|
||||
export const generateDefaultChat = (title?: string): ChatInterface => ({
|
||||
title: title ? title : 'New Chat',
|
||||
messages: [{ role: 'system', content: defaultSystemMessage }],
|
||||
config: { ...defaultChatConfig },
|
||||
messages: [
|
||||
{ role: 'system', content: useStore.getState().defaultSystemMessage },
|
||||
],
|
||||
config: { ...useStore.getState().defaultChatConfig },
|
||||
titleSet: false,
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ChatInterface, MessageInterface } from '@type/chat';
|
|||
import { getChatCompletion, getChatCompletionStream } from '@api/api';
|
||||
import { parseEventSource } from '@api/helper';
|
||||
import { limitMessageTokens } from '@utils/messageUtils';
|
||||
import { defaultChatConfig } from '@constants/chat';
|
||||
import { _defaultChatConfig } from '@constants/chat';
|
||||
|
||||
const useSubmit = () => {
|
||||
const error = useStore((state) => state.error);
|
||||
|
@ -24,13 +24,13 @@ const useSubmit = () => {
|
|||
data = await getChatCompletion(
|
||||
useStore.getState().apiEndpoint,
|
||||
message,
|
||||
defaultChatConfig
|
||||
_defaultChatConfig
|
||||
);
|
||||
} else if (apiKey) {
|
||||
data = await getChatCompletion(
|
||||
useStore.getState().apiEndpoint,
|
||||
message,
|
||||
defaultChatConfig,
|
||||
_defaultChatConfig,
|
||||
apiKey
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
import { StoreSlice } from './store';
|
||||
import { Theme } from '@type/theme';
|
||||
import { ConfigInterface } from '@type/chat';
|
||||
import { _defaultChatConfig, _defaultSystemMessage } from '@constants/chat';
|
||||
|
||||
export interface ConfigSlice {
|
||||
openConfig: boolean;
|
||||
theme: Theme;
|
||||
autoTitle: boolean;
|
||||
defaultChatConfig: ConfigInterface;
|
||||
defaultSystemMessage: string;
|
||||
setOpenConfig: (openConfig: boolean) => void;
|
||||
setTheme: (theme: Theme) => void;
|
||||
setAutoTitle: (autoTitle: boolean) => void;
|
||||
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => void;
|
||||
setDefaultSystemMessage: (defaultSystemMessage: string) => void;
|
||||
}
|
||||
|
||||
export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||
openConfig: false,
|
||||
theme: 'dark',
|
||||
autoTitle: false,
|
||||
defaultChatConfig: _defaultChatConfig,
|
||||
defaultSystemMessage: _defaultSystemMessage,
|
||||
setOpenConfig: (openConfig: boolean) => {
|
||||
set((prev: ConfigSlice) => ({
|
||||
...prev,
|
||||
|
@ -32,4 +40,16 @@ export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
|||
autoTitle: autoTitle,
|
||||
}));
|
||||
},
|
||||
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => {
|
||||
set((prev: ConfigSlice) => ({
|
||||
...prev,
|
||||
defaultChatConfig: defaultChatConfig,
|
||||
}));
|
||||
},
|
||||
setDefaultSystemMessage: (defaultSystemMessage: string) => {
|
||||
set((prev: ConfigSlice) => ({
|
||||
...prev,
|
||||
defaultSystemMessage: defaultSystemMessage,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
LocalStorageInterfaceV5ToV6,
|
||||
} from '@type/chat';
|
||||
import {
|
||||
defaultChatConfig,
|
||||
_defaultChatConfig,
|
||||
defaultModel,
|
||||
defaultUserMaxToken,
|
||||
} from '@constants/chat';
|
||||
|
@ -17,7 +17,7 @@ import defaultPrompts from '@constants/prompt';
|
|||
export const migrateV0 = (persistedState: LocalStorageInterfaceV0ToV1) => {
|
||||
persistedState.chats.forEach((chat) => {
|
||||
chat.titleSet = false;
|
||||
if (!chat.config) chat.config = { ...defaultChatConfig };
|
||||
if (!chat.config) chat.config = { ..._defaultChatConfig };
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -33,8 +33,8 @@ export const migrateV2 = (persistedState: LocalStorageInterfaceV2ToV3) => {
|
|||
persistedState.chats.forEach((chat) => {
|
||||
chat.config = {
|
||||
...chat.config,
|
||||
top_p: defaultChatConfig.top_p,
|
||||
frequency_penalty: defaultChatConfig.frequency_penalty,
|
||||
top_p: _defaultChatConfig.top_p,
|
||||
frequency_penalty: _defaultChatConfig.frequency_penalty,
|
||||
};
|
||||
});
|
||||
persistedState.autoTitle = false;
|
||||
|
|
|
@ -53,6 +53,8 @@ const useStore = create<StoreState>()(
|
|||
theme: state.theme,
|
||||
autoTitle: state.autoTitle,
|
||||
prompts: state.prompts,
|
||||
defaultChatConfig: state.defaultChatConfig,
|
||||
defaultSystemMessage: state.defaultSystemMessage,
|
||||
}),
|
||||
version: 6,
|
||||
migrate: (persistedState, version) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import jsPDF from 'jspdf';
|
|||
import { ChatInterface, ConfigInterface, MessageInterface } from '@type/chat';
|
||||
import { roles } from '@type/chat';
|
||||
import { Theme } from '@type/theme';
|
||||
import { defaultChatConfig } from '@constants/chat';
|
||||
import { _defaultChatConfig } from '@constants/chat';
|
||||
|
||||
export const validateAndFixChats = (chats: any): chats is ChatInterface[] => {
|
||||
if (!Array.isArray(chats)) return false;
|
||||
|
@ -32,21 +32,21 @@ const validateMessage = (messages: MessageInterface[]) => {
|
|||
};
|
||||
|
||||
const validateAndFixChatConfig = (config: ConfigInterface) => {
|
||||
if (config === undefined) config = defaultChatConfig;
|
||||
if (config === undefined) config = _defaultChatConfig;
|
||||
if (!(typeof config === 'object')) return false;
|
||||
|
||||
if (!config.temperature) config.temperature = defaultChatConfig.temperature;
|
||||
if (!config.temperature) config.temperature = _defaultChatConfig.temperature;
|
||||
if (!(typeof config.temperature === 'number')) return false;
|
||||
|
||||
if (!config.presence_penalty)
|
||||
config.presence_penalty = defaultChatConfig.presence_penalty;
|
||||
config.presence_penalty = _defaultChatConfig.presence_penalty;
|
||||
if (!(typeof config.presence_penalty === 'number')) return false;
|
||||
|
||||
if (!config.top_p) config.top_p = defaultChatConfig.top_p;
|
||||
if (!config.top_p) config.top_p = _defaultChatConfig.top_p;
|
||||
if (!(typeof config.top_p === 'number')) return false;
|
||||
|
||||
if (!config.frequency_penalty)
|
||||
config.frequency_penalty = defaultChatConfig.frequency_penalty;
|
||||
config.frequency_penalty = _defaultChatConfig.frequency_penalty;
|
||||
if (!(typeof config.frequency_penalty === 'number')) return false;
|
||||
|
||||
return true;
|
||||
|
|
Loading…
Reference in a new issue