mirror of
https://github.com/NovaOSS/nova-betterchat.git
synced 2024-11-25 21:23:58 +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": {
|
"frequencyPenalty": {
|
||||||
"label": "Frequency Penalty",
|
"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)"
|
"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": {
|
"frequencyPenalty": {
|
||||||
"label": "频率惩罚",
|
"label": "频率惩罚",
|
||||||
"description": "数值在 -2.0 到 2.0 之间。正值会根据新 token 在文本中的现有频率来惩罚它们,降低模型直接重复相同语句的可能性。(默认: 0)"
|
"description": "数值在 -2.0 到 2.0 之间。正值会根据新 token 在文本中的现有频率来惩罚它们,降低模型直接重复相同语句的可能性。(默认: 0)"
|
||||||
}
|
},
|
||||||
|
"defaultChatConfig": "默认聊天配置",
|
||||||
|
"defaultSystemMessage": "默认系统消息",
|
||||||
|
"resetToDefault": "重置为默认"
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,5 +21,8 @@
|
||||||
"frequencyPenalty": {
|
"frequencyPenalty": {
|
||||||
"label": "頻率懲罰",
|
"label": "頻率懲罰",
|
||||||
"description": "係一個 -2.0 到 2.0 之間嘅數值。正嘅數值表示,如果 token 喺之前嘅文字中出現頻率越高,輸出嗰陣就會越大力噉懲罰佢,令到佢被揀中嘅機率降低,即係可以降低模型重複同一句説話嘅機會。(預設: 0)"
|
"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 useStore from '@store/store';
|
||||||
import ConfigMenu from '@components/ConfigMenu';
|
import ConfigMenu from '@components/ConfigMenu';
|
||||||
import { ChatInterface, ConfigInterface } from '@type/chat';
|
import { ChatInterface, ConfigInterface } from '@type/chat';
|
||||||
import { defaultChatConfig } from '@constants/chat';
|
import { _defaultChatConfig } from '@constants/chat';
|
||||||
|
|
||||||
const ChatTitle = React.memo(() => {
|
const ChatTitle = React.memo(() => {
|
||||||
const { t } = useTranslation('model');
|
const { t } = useTranslation('model');
|
||||||
|
@ -35,7 +35,7 @@ const ChatTitle = React.memo(() => {
|
||||||
const chats = useStore.getState().chats;
|
const chats = useStore.getState().chats;
|
||||||
if (chats && chats.length > 0 && currentChatIndex !== -1 && !config) {
|
if (chats && chats.length > 0 && currentChatIndex !== -1 && !config) {
|
||||||
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
||||||
updatedChats[currentChatIndex].config = { ...defaultChatConfig };
|
updatedChats[currentChatIndex].config = { ..._defaultChatConfig };
|
||||||
setChats(updatedChats);
|
setChats(updatedChats);
|
||||||
}
|
}
|
||||||
}, [currentChatIndex]);
|
}, [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}
|
_setMaxToken={_setMaxToken}
|
||||||
_model={_model}
|
_model={_model}
|
||||||
/>
|
/>
|
||||||
<div className='mt-5 pt-5 border-t border-gray-500'>
|
<TemperatureSlider
|
||||||
<label className='block text-sm font-medium text-gray-900 dark:text-white'>
|
_temperature={_temperature}
|
||||||
{t('temperature.label')}: {_temperature}
|
_setTemperature={_setTemperature}
|
||||||
</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'>
|
<TopPSlider _topP={_topP} _setTopP={_setTopP} />
|
||||||
{t('temperature.description')}
|
<PresencePenaltySlider
|
||||||
</div>
|
_presencePenalty={_presencePenalty}
|
||||||
</div>
|
_setPresencePenalty={_setPresencePenalty}
|
||||||
<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'>
|
<FrequencyPenaltySlider
|
||||||
{t('topP.description')}
|
_frequencyPenalty={_frequencyPenalty}
|
||||||
</div>
|
_setFrequencyPenalty={_setFrequencyPenalty}
|
||||||
</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'
|
|
||||||
/>
|
/>
|
||||||
<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>
|
</div>
|
||||||
</PopupModal>
|
</PopupModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModelSelector = ({
|
export const ModelSelector = ({
|
||||||
_model,
|
_model,
|
||||||
_setModel,
|
_setModel,
|
||||||
}: {
|
}: {
|
||||||
|
@ -184,7 +117,7 @@ const ModelSelector = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const MaxTokenSlider = ({
|
export const MaxTokenSlider = ({
|
||||||
_maxToken,
|
_maxToken,
|
||||||
_setMaxToken,
|
_setMaxToken,
|
||||||
_model,
|
_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;
|
export default ConfigMenu;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import ThemeSwitcher from '@components/Menu/MenuOptions/ThemeSwitcher';
|
||||||
import LanguageSelector from '@components/LanguageSelector';
|
import LanguageSelector from '@components/LanguageSelector';
|
||||||
import AutoTitleToggle from './AutoTitleToggle';
|
import AutoTitleToggle from './AutoTitleToggle';
|
||||||
import PromptLibraryMenu from '@components/PromptLibraryMenu';
|
import PromptLibraryMenu from '@components/PromptLibraryMenu';
|
||||||
|
import ChatConfigMenu from '@components/ChatConfigMenu';
|
||||||
|
|
||||||
const SettingsMenu = () => {
|
const SettingsMenu = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -39,6 +40,7 @@ const SettingsMenu = () => {
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
<AutoTitleToggle />
|
<AutoTitleToggle />
|
||||||
<PromptLibraryMenu />
|
<PromptLibraryMenu />
|
||||||
|
<ChatConfigMenu />
|
||||||
</div>
|
</div>
|
||||||
</PopupModal>
|
</PopupModal>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { ChatInterface, ConfigInterface, ModelOptions } from '@type/chat';
|
import { ChatInterface, ConfigInterface, ModelOptions } from '@type/chat';
|
||||||
|
import useStore from '@store/store';
|
||||||
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const dateString =
|
const dateString =
|
||||||
|
@ -9,7 +10,7 @@ const dateString =
|
||||||
('0' + date.getDate()).slice(-2);
|
('0' + date.getDate()).slice(-2);
|
||||||
|
|
||||||
// default system message obtained using the following method: https://twitter.com/DeminDimin/status/1619935545144279040
|
// 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
|
Knowledge cutoff: 2021-09
|
||||||
Current date: ${dateString}`;
|
Current date: ${dateString}`;
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ export const modelMaxToken = {
|
||||||
|
|
||||||
export const defaultUserMaxToken = 4000;
|
export const defaultUserMaxToken = 4000;
|
||||||
|
|
||||||
export const defaultChatConfig: ConfigInterface = {
|
export const _defaultChatConfig: ConfigInterface = {
|
||||||
model: defaultModel,
|
model: defaultModel,
|
||||||
max_tokens: defaultUserMaxToken,
|
max_tokens: defaultUserMaxToken,
|
||||||
temperature: 1,
|
temperature: 1,
|
||||||
|
@ -46,8 +47,10 @@ export const defaultChatConfig: ConfigInterface = {
|
||||||
|
|
||||||
export const generateDefaultChat = (title?: string): ChatInterface => ({
|
export const generateDefaultChat = (title?: string): ChatInterface => ({
|
||||||
title: title ? title : 'New Chat',
|
title: title ? title : 'New Chat',
|
||||||
messages: [{ role: 'system', content: defaultSystemMessage }],
|
messages: [
|
||||||
config: { ...defaultChatConfig },
|
{ role: 'system', content: useStore.getState().defaultSystemMessage },
|
||||||
|
],
|
||||||
|
config: { ...useStore.getState().defaultChatConfig },
|
||||||
titleSet: false,
|
titleSet: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ChatInterface, MessageInterface } from '@type/chat';
|
||||||
import { getChatCompletion, getChatCompletionStream } from '@api/api';
|
import { getChatCompletion, getChatCompletionStream } from '@api/api';
|
||||||
import { parseEventSource } from '@api/helper';
|
import { parseEventSource } from '@api/helper';
|
||||||
import { limitMessageTokens } from '@utils/messageUtils';
|
import { limitMessageTokens } from '@utils/messageUtils';
|
||||||
import { defaultChatConfig } from '@constants/chat';
|
import { _defaultChatConfig } from '@constants/chat';
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const error = useStore((state) => state.error);
|
const error = useStore((state) => state.error);
|
||||||
|
@ -24,13 +24,13 @@ const useSubmit = () => {
|
||||||
data = await getChatCompletion(
|
data = await getChatCompletion(
|
||||||
useStore.getState().apiEndpoint,
|
useStore.getState().apiEndpoint,
|
||||||
message,
|
message,
|
||||||
defaultChatConfig
|
_defaultChatConfig
|
||||||
);
|
);
|
||||||
} else if (apiKey) {
|
} else if (apiKey) {
|
||||||
data = await getChatCompletion(
|
data = await getChatCompletion(
|
||||||
useStore.getState().apiEndpoint,
|
useStore.getState().apiEndpoint,
|
||||||
message,
|
message,
|
||||||
defaultChatConfig,
|
_defaultChatConfig,
|
||||||
apiKey
|
apiKey
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
import { StoreSlice } from './store';
|
import { StoreSlice } from './store';
|
||||||
import { Theme } from '@type/theme';
|
import { Theme } from '@type/theme';
|
||||||
|
import { ConfigInterface } from '@type/chat';
|
||||||
|
import { _defaultChatConfig, _defaultSystemMessage } from '@constants/chat';
|
||||||
|
|
||||||
export interface ConfigSlice {
|
export interface ConfigSlice {
|
||||||
openConfig: boolean;
|
openConfig: boolean;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
autoTitle: boolean;
|
autoTitle: boolean;
|
||||||
|
defaultChatConfig: ConfigInterface;
|
||||||
|
defaultSystemMessage: string;
|
||||||
setOpenConfig: (openConfig: boolean) => void;
|
setOpenConfig: (openConfig: boolean) => void;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
setAutoTitle: (autoTitle: boolean) => void;
|
setAutoTitle: (autoTitle: boolean) => void;
|
||||||
|
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => void;
|
||||||
|
setDefaultSystemMessage: (defaultSystemMessage: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||||
openConfig: false,
|
openConfig: false,
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
autoTitle: false,
|
autoTitle: false,
|
||||||
|
defaultChatConfig: _defaultChatConfig,
|
||||||
|
defaultSystemMessage: _defaultSystemMessage,
|
||||||
setOpenConfig: (openConfig: boolean) => {
|
setOpenConfig: (openConfig: boolean) => {
|
||||||
set((prev: ConfigSlice) => ({
|
set((prev: ConfigSlice) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
@ -32,4 +40,16 @@ export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||||
autoTitle: autoTitle,
|
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,
|
LocalStorageInterfaceV5ToV6,
|
||||||
} from '@type/chat';
|
} from '@type/chat';
|
||||||
import {
|
import {
|
||||||
defaultChatConfig,
|
_defaultChatConfig,
|
||||||
defaultModel,
|
defaultModel,
|
||||||
defaultUserMaxToken,
|
defaultUserMaxToken,
|
||||||
} from '@constants/chat';
|
} from '@constants/chat';
|
||||||
|
@ -17,7 +17,7 @@ import defaultPrompts from '@constants/prompt';
|
||||||
export const migrateV0 = (persistedState: LocalStorageInterfaceV0ToV1) => {
|
export const migrateV0 = (persistedState: LocalStorageInterfaceV0ToV1) => {
|
||||||
persistedState.chats.forEach((chat) => {
|
persistedState.chats.forEach((chat) => {
|
||||||
chat.titleSet = false;
|
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) => {
|
persistedState.chats.forEach((chat) => {
|
||||||
chat.config = {
|
chat.config = {
|
||||||
...chat.config,
|
...chat.config,
|
||||||
top_p: defaultChatConfig.top_p,
|
top_p: _defaultChatConfig.top_p,
|
||||||
frequency_penalty: defaultChatConfig.frequency_penalty,
|
frequency_penalty: _defaultChatConfig.frequency_penalty,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
persistedState.autoTitle = false;
|
persistedState.autoTitle = false;
|
||||||
|
|
|
@ -53,6 +53,8 @@ const useStore = create<StoreState>()(
|
||||||
theme: state.theme,
|
theme: state.theme,
|
||||||
autoTitle: state.autoTitle,
|
autoTitle: state.autoTitle,
|
||||||
prompts: state.prompts,
|
prompts: state.prompts,
|
||||||
|
defaultChatConfig: state.defaultChatConfig,
|
||||||
|
defaultSystemMessage: state.defaultSystemMessage,
|
||||||
}),
|
}),
|
||||||
version: 6,
|
version: 6,
|
||||||
migrate: (persistedState, version) => {
|
migrate: (persistedState, version) => {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import jsPDF from 'jspdf';
|
||||||
import { ChatInterface, ConfigInterface, MessageInterface } from '@type/chat';
|
import { ChatInterface, ConfigInterface, MessageInterface } from '@type/chat';
|
||||||
import { roles } from '@type/chat';
|
import { roles } from '@type/chat';
|
||||||
import { Theme } from '@type/theme';
|
import { Theme } from '@type/theme';
|
||||||
import { defaultChatConfig } from '@constants/chat';
|
import { _defaultChatConfig } from '@constants/chat';
|
||||||
|
|
||||||
export const validateAndFixChats = (chats: any): chats is ChatInterface[] => {
|
export const validateAndFixChats = (chats: any): chats is ChatInterface[] => {
|
||||||
if (!Array.isArray(chats)) return false;
|
if (!Array.isArray(chats)) return false;
|
||||||
|
@ -32,21 +32,21 @@ const validateMessage = (messages: MessageInterface[]) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateAndFixChatConfig = (config: ConfigInterface) => {
|
const validateAndFixChatConfig = (config: ConfigInterface) => {
|
||||||
if (config === undefined) config = defaultChatConfig;
|
if (config === undefined) config = _defaultChatConfig;
|
||||||
if (!(typeof config === 'object')) return false;
|
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 (!(typeof config.temperature === 'number')) return false;
|
||||||
|
|
||||||
if (!config.presence_penalty)
|
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 (!(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 (!(typeof config.top_p === 'number')) return false;
|
||||||
|
|
||||||
if (!config.frequency_penalty)
|
if (!config.frequency_penalty)
|
||||||
config.frequency_penalty = defaultChatConfig.frequency_penalty;
|
config.frequency_penalty = _defaultChatConfig.frequency_penalty;
|
||||||
if (!(typeof config.frequency_penalty === 'number')) return false;
|
if (!(typeof config.frequency_penalty === 'number')) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue