mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
improve custom model selection ux
This commit is contained in:
parent
d93282640c
commit
6d757ff91f
6 changed files with 49 additions and 20 deletions
12
README.md
12
README.md
|
|
@ -61,7 +61,7 @@ Currently, this project is only functional with the `text-davinci-003` model.
|
||||||
|
|
||||||
> **Warning**
|
> **Warning**
|
||||||
|
|
||||||
> This is a work in progress. I'm building this in public. You can follow the progress here or on my [Linkedin](https://www.linkedin.com/in/danny-avila).
|
> This is a work in progress. I'm building this in public. FYI there is still a lot of tech debt to cleanup. You can follow the progress here or on my [Linkedin](https://www.linkedin.com/in/danny-avila).
|
||||||
|
|
||||||
Here are my recently completed and planned features:
|
Here are my recently completed and planned features:
|
||||||
|
|
||||||
|
|
@ -153,9 +153,9 @@ Warning: There may be a high chance of your account being banned with this metho
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>BingAI Instructions</strong></summary>
|
<summary><strong>BingAI Instructions</strong></summary>
|
||||||
|
The Bing Access Token is the "_U" cookie from bing.com. Use dev tools or an extension while logged into the site to view it.
|
||||||
|
|
||||||
|
Note: Specific error handling and styling for this model is still in progress.
|
||||||
Note: Specific Styling for this model is still in progress.
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Updating
|
### Updating
|
||||||
|
|
@ -167,15 +167,15 @@ Note: Specific Styling for this model is still in progress.
|
||||||
- Using the official API, you'd have to generate 7.5 million words to expense the same cost as ChatGPT Plus ($20).
|
- Using the official API, you'd have to generate 7.5 million words to expense the same cost as ChatGPT Plus ($20).
|
||||||
- ChatGPT/Google Bard/Bing AI conversations are lost in space or
|
- ChatGPT/Google Bard/Bing AI conversations are lost in space or
|
||||||
cannot be searched past a certain timeframe.
|
cannot be searched past a certain timeframe.
|
||||||
- Customize ChatGPT
|
- **Customize ChatGPT**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- API is not as limited as ChatGPT Free (at [chat.openai.com](https://chat.openai.com/chat))
|
- **API is not as limited as ChatGPT Free (at [chat.openai.com](https://chat.openai.com/chat))**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- ChatGPT Free is down.
|
- **ChatGPT Free is down.**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
OPENAI_KEY=
|
OPENAI_KEY=
|
||||||
PORT=3080
|
PORT=3080
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
MONGO_URI="mongodb://127.0.0.1:27017/chatgpt-client"
|
# Change this to your MongoDB URI if different and I recommend appending chatgpt-clone
|
||||||
|
MONGO_URI="mongodb://127.0.0.1:27017/chatgpt-clone"
|
||||||
CHATGPT_TOKEN=""
|
CHATGPT_TOKEN=""
|
||||||
BING_TOKEN=""
|
BING_TOKEN=""
|
||||||
|
|
@ -45,8 +45,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
updateCustomGpt: async ({ value, ...update }) => {
|
updateCustomGpt: async ({ value, ...update }) => {
|
||||||
try {
|
try {
|
||||||
console.log('updateCustomGpt', value, update);
|
|
||||||
|
|
||||||
const customGpt = await CustomGpt.findOne({ value }).exec();
|
const customGpt = await CustomGpt.findOne({ value }).exec();
|
||||||
|
|
||||||
if (!customGpt) {
|
if (!customGpt) {
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@ import {
|
||||||
DialogTitle
|
DialogTitle
|
||||||
} from '../ui/Dialog.tsx';
|
} from '../ui/Dialog.tsx';
|
||||||
|
|
||||||
export default function ModelDialog({ mutate, modelMap }) {
|
export default function ModelDialog({ mutate, modelMap, setModelSave, handleSaveState }) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [chatGptLabel, setChatGptLabel] = useState('');
|
const [chatGptLabel, setChatGptLabel] = useState('');
|
||||||
const [promptPrefix, setPromptPrefix] = useState('');
|
const [promptPrefix, setPromptPrefix] = useState('');
|
||||||
|
|
||||||
const [saveText, setSaveText] = useState('Save');
|
const [saveText, setSaveText] = useState('Save');
|
||||||
const [required, setRequired] = useState(false);
|
const [required, setRequired] = useState(false);
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
|
|
@ -34,11 +35,13 @@ export default function ModelDialog({ mutate, modelMap }) {
|
||||||
}
|
}
|
||||||
dispatch(setCustomGpt({ chatGptLabel, promptPrefix }));
|
dispatch(setCustomGpt({ chatGptLabel, promptPrefix }));
|
||||||
dispatch(setModel('chatgptCustom'));
|
dispatch(setModel('chatgptCustom'));
|
||||||
|
handleSaveState(chatGptLabel.toLowerCase());
|
||||||
// dispatch(setDisabled(false));
|
// dispatch(setDisabled(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveHandler = (e) => {
|
const saveHandler = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setModelSave(true);
|
||||||
const value = chatGptLabel.toLowerCase();
|
const value = chatGptLabel.toLowerCase();
|
||||||
|
|
||||||
if (chatGptLabel.length === 0) {
|
if (chatGptLabel.length === 0) {
|
||||||
|
|
@ -60,7 +63,11 @@ export default function ModelDialog({ mutate, modelMap }) {
|
||||||
// dispatch(setDisabled(false));
|
// dispatch(setDisabled(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (modelMap[chatGptLabel.toLowerCase()] && saveText === 'Save') {
|
if (
|
||||||
|
chatGptLabel !== 'chatgptCustom' &&
|
||||||
|
modelMap[chatGptLabel.toLowerCase()] &&
|
||||||
|
saveText === 'Save'
|
||||||
|
) {
|
||||||
setSaveText('Update');
|
setSaveText('Update');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { setModel, setDisabled, setCustomGpt, setCustomModel } from '~/store/submitSlice';
|
import { setModel, setDisabled, setCustomGpt, setCustomModel } from '~/store/submitSlice';
|
||||||
import { setConversation } from '~/store/convoSlice';
|
import { setConversation } from '~/store/convoSlice';
|
||||||
|
|
@ -23,6 +23,8 @@ import { Dialog } from '../ui/Dialog.tsx';
|
||||||
|
|
||||||
export default function ModelMenu() {
|
export default function ModelMenu() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const [modelSave, setModelSave] = useState(false);
|
||||||
|
// const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const { model, customModel } = useSelector((state) => state.submit);
|
const { model, customModel } = useSelector((state) => state.submit);
|
||||||
const { models, modelMap, initial } = useSelector((state) => state.models);
|
const { models, modelMap, initial } = useSelector((state) => state.models);
|
||||||
const { trigger } = manualSWR(`http://localhost:3080/api/customGpts`, 'get', (res) => {
|
const { trigger } = manualSWR(`http://localhost:3080/api/customGpts`, 'get', (res) => {
|
||||||
|
|
@ -60,15 +62,15 @@ export default function ModelMenu() {
|
||||||
} else if (initial[value]) {
|
} else if (initial[value]) {
|
||||||
dispatch(setModel(value));
|
dispatch(setModel(value));
|
||||||
dispatch(setDisabled(false));
|
dispatch(setDisabled(false));
|
||||||
setCustomModel(null);
|
dispatch(setCustomModel(null));
|
||||||
} else if (!initial[value]) {
|
} else if (!initial[value]) {
|
||||||
const chatGptLabel = modelMap[value]?.chatGptLabel;
|
const chatGptLabel = modelMap[value]?.chatGptLabel;
|
||||||
const promptPrefix = modelMap[value]?.promptPrefix;
|
const promptPrefix = modelMap[value]?.promptPrefix;
|
||||||
dispatch(setCustomGpt({ chatGptLabel, promptPrefix }));
|
dispatch(setCustomGpt({ chatGptLabel, promptPrefix }));
|
||||||
dispatch(setModel('chatgptCustom'));
|
dispatch(setModel('chatgptCustom'));
|
||||||
setCustomModel(value);
|
dispatch(setCustomModel(value));
|
||||||
} else if (!modelMap[value]) {
|
} else if (!modelMap[value]) {
|
||||||
setCustomModel(null);
|
dispatch(setCustomModel(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set new conversation
|
// Set new conversation
|
||||||
|
|
@ -82,6 +84,21 @@ export default function ModelMenu() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onOpenChange = (open) => {
|
||||||
|
if (!open) {
|
||||||
|
setModelSave(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveState = (value) => {
|
||||||
|
if (!modelSave) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(setCustomModel(value));
|
||||||
|
setModelSave(false);
|
||||||
|
};
|
||||||
|
|
||||||
const defaultColorProps = [
|
const defaultColorProps = [
|
||||||
'text-gray-500',
|
'text-gray-500',
|
||||||
'hover:bg-gray-100',
|
'hover:bg-gray-100',
|
||||||
|
|
@ -107,8 +124,8 @@ export default function ModelMenu() {
|
||||||
const icon = model === 'bingai' ? <BingIcon button={true} /> : <GPTIcon button={true} />;
|
const icon = model === 'bingai' ? <BingIcon button={true} /> : <GPTIcon button={true} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog onOpenChange={onOpenChange}>
|
||||||
<DropdownMenu >
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -124,7 +141,7 @@ export default function ModelMenu() {
|
||||||
<DropdownMenuLabel className="dark:text-gray-300">Select a Model</DropdownMenuLabel>
|
<DropdownMenuLabel className="dark:text-gray-300">Select a Model</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuRadioGroup
|
<DropdownMenuRadioGroup
|
||||||
value={customModel ? customModel : model}
|
value={customModel?.length > 0 ? customModel : model}
|
||||||
onValueChange={onChange}
|
onValueChange={onChange}
|
||||||
className="overflow-y-auto"
|
className="overflow-y-auto"
|
||||||
>
|
>
|
||||||
|
|
@ -132,7 +149,12 @@ export default function ModelMenu() {
|
||||||
</DropdownMenuRadioGroup>
|
</DropdownMenuRadioGroup>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<ModelDialog mutate={trigger} modelMap={modelMap}/>
|
<ModelDialog
|
||||||
|
mutate={trigger}
|
||||||
|
modelMap={modelMap}
|
||||||
|
setModelSave={setModelSave}
|
||||||
|
handleSaveState={handleSaveState}
|
||||||
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const initialState = {
|
||||||
model: 'chatgpt',
|
model: 'chatgpt',
|
||||||
promptPrefix: '',
|
promptPrefix: '',
|
||||||
chatGptLabel: '',
|
chatGptLabel: '',
|
||||||
customModel: null
|
customModel: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentSlice = createSlice({
|
const currentSlice = createSlice({
|
||||||
|
|
@ -27,6 +27,7 @@ const currentSlice = createSlice({
|
||||||
state.chatGptLabel = action.payload.chatGptLabel;
|
state.chatGptLabel = action.payload.chatGptLabel;
|
||||||
},
|
},
|
||||||
setCustomModel: (state, action) => {
|
setCustomModel: (state, action) => {
|
||||||
|
console.log('setCustomModel', action.payload);
|
||||||
state.customModel = action.payload;
|
state.customModel = action.payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue