mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
fix: model menu key issue
This commit is contained in:
parent
9bc85ea83d
commit
8e513d83a5
6 changed files with 73 additions and 57 deletions
|
|
@ -1,9 +1,14 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { getCustomGpts, updateCustomGpt, updateByLabel, deleteCustomGpts } = require('../../models');
|
const {
|
||||||
|
getCustomGpts,
|
||||||
|
updateCustomGpt,
|
||||||
|
updateByLabel,
|
||||||
|
deleteCustomGpts
|
||||||
|
} = require('../../models');
|
||||||
|
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', async (req, res) => {
|
||||||
const models = (await getCustomGpts()).map(model => {
|
const models = (await getCustomGpts()).map((model) => {
|
||||||
model = model.toObject();
|
model = model.toObject();
|
||||||
model._id = model._id.toString();
|
model._id = model._id.toString();
|
||||||
return model;
|
return model;
|
||||||
|
|
@ -15,8 +20,14 @@ router.post('/delete', async (req, res) => {
|
||||||
const { arg } = req.body;
|
const { arg } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbResponse = await deleteCustomGpts(arg);
|
await deleteCustomGpts(arg);
|
||||||
res.status(201).send(dbResponse);
|
const models = (await getCustomGpts()).map((model) => {
|
||||||
|
model = model.toObject();
|
||||||
|
model._id = model._id.toString();
|
||||||
|
return model;
|
||||||
|
});
|
||||||
|
res.status(201).send(models);
|
||||||
|
// res.status(201).send(dbResponse);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
res.status(500).send(error);
|
res.status(500).send(error);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
module.exports = (req, res, next) => {
|
|
||||||
let { stopStream } = req.body;
|
|
||||||
if (stopStream) {
|
|
||||||
console.log('stopStream');
|
|
||||||
res.write('event: stop\ndata:\n\n');
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -4,9 +4,10 @@ import ModelItem from './ModelItem';
|
||||||
export default function MenuItems({ models, onSelect }) {
|
export default function MenuItems({ models, onSelect }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{models.map((modelItem, i) => (
|
{models.map((modelItem) => (
|
||||||
<ModelItem
|
<ModelItem
|
||||||
key={i}
|
key={modelItem._id}
|
||||||
|
id={modelItem._id}
|
||||||
modelName={modelItem.name}
|
modelName={modelItem.name}
|
||||||
value={modelItem.value}
|
value={modelItem.value}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function ModelDialog({ mutate, setModelSave, handleSaveState }) {
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const updateCustomGpt = manualSWR(`/api/customGpts/`, 'post');
|
const updateCustomGpt = manualSWR(`/api/customGpts/`, 'post');
|
||||||
|
|
||||||
const submitHandler = (e) => {
|
const selectHandler = (e) => {
|
||||||
if (chatGptLabel.length === 0) {
|
if (chatGptLabel.length === 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setRequired(true);
|
setRequired(true);
|
||||||
|
|
@ -140,10 +140,10 @@ export default function ModelDialog({ mutate, setModelSave, handleSaveState }) {
|
||||||
{saveText}
|
{saveText}
|
||||||
</Button>
|
</Button>
|
||||||
<DialogClose
|
<DialogClose
|
||||||
onClick={submitHandler}
|
onClick={selectHandler}
|
||||||
className="inline-flex h-10 items-center justify-center rounded-md border-none bg-gray-900 py-2 px-4 text-sm font-semibold text-white transition-colors hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200 dark:focus:ring-gray-400 dark:focus:ring-offset-gray-900"
|
className="inline-flex h-10 items-center justify-center rounded-md border-none bg-gray-900 py-2 px-4 text-sm font-semibold text-white transition-colors hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200 dark:focus:ring-gray-400 dark:focus:ring-offset-gray-900"
|
||||||
>
|
>
|
||||||
Submit
|
Select
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { DropdownMenuRadioItem } from '../ui/DropdownMenu.tsx';
|
import { DropdownMenuRadioItem } from '../ui/DropdownMenu.tsx';
|
||||||
|
import { setModels } from '~/store/modelSlice';
|
||||||
import { Circle } from 'lucide-react';
|
import { Circle } from 'lucide-react';
|
||||||
import { DialogTrigger } from '../ui/Dialog.tsx';
|
import { DialogTrigger } from '../ui/Dialog.tsx';
|
||||||
import RenameButton from '../Conversations/RenameButton';
|
import RenameButton from '../Conversations/RenameButton';
|
||||||
import TrashIcon from '../svg/TrashIcon';
|
import TrashIcon from '../svg/TrashIcon';
|
||||||
import manualSWR from '~/utils/fetchers';
|
import manualSWR from '~/utils/fetchers';
|
||||||
|
|
||||||
export default function ModelItem({ modelName, value, onSelect }) {
|
export default function ModelItem({ modelName, value, onSelect, id }) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
const { customModel } = useSelector((state) => state.submit);
|
const { customModel } = useSelector((state) => state.submit);
|
||||||
const { initial } = useSelector((state) => state.models);
|
const { initial } = useSelector((state) => state.models);
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
|
|
@ -16,7 +18,14 @@ export default function ModelItem({ modelName, value, onSelect }) {
|
||||||
const [modelInput, setModelInput] = useState(modelName);
|
const [modelInput, setModelInput] = useState(modelName);
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const rename = manualSWR(`/api/customGpts`, 'post');
|
const rename = manualSWR(`/api/customGpts`, 'post');
|
||||||
const deleteCustom = manualSWR(`/api/customGpts/delete`, 'post');
|
const deleteCustom = manualSWR(`/api/customGpts/delete`, 'post', (res) => {
|
||||||
|
const fetchedModels = res.data.map((modelItem) => ({
|
||||||
|
...modelItem,
|
||||||
|
name: modelItem.chatGptLabel
|
||||||
|
}));
|
||||||
|
|
||||||
|
dispatch(setModels(fetchedModels));
|
||||||
|
});
|
||||||
|
|
||||||
if (value === 'chatgptCustom') {
|
if (value === 'chatgptCustom') {
|
||||||
return (
|
return (
|
||||||
|
|
@ -77,8 +86,7 @@ export default function ModelItem({ modelName, value, onSelect }) {
|
||||||
|
|
||||||
const onDelete = async (e) => {
|
const onDelete = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await deleteCustom.trigger({ value: currentName.toLowerCase() });
|
await deleteCustom.trigger({ _id: id });
|
||||||
// await mutate();
|
|
||||||
onSelect('chatgpt', true);
|
onSelect('chatgpt', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -90,25 +98,24 @@ export default function ModelItem({ modelName, value, onSelect }) {
|
||||||
|
|
||||||
const buttonClass = {
|
const buttonClass = {
|
||||||
className:
|
className:
|
||||||
'z-50 rounded-md m-0 text-gray-400 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'
|
'invisible group-hover:visible z-50 rounded-md m-0 text-gray-400 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'
|
||||||
};
|
};
|
||||||
|
|
||||||
const itemClass = {
|
const itemClass = {
|
||||||
className:
|
className:
|
||||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm font-medium outline-none hover:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:hover:bg-slate-700 dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800'
|
'relative flex group cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm font-medium outline-none hover:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:hover:bg-slate-700 dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800'
|
||||||
};
|
};
|
||||||
|
|
||||||
const showButtons = isHovering && !initial[value];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
value={value}
|
value={value}
|
||||||
className={itemClass.className}
|
className={itemClass.className}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
if (isHovering) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
onSelect(value, true);
|
onSelect(value, true);
|
||||||
}}
|
}}
|
||||||
onMouseOver={handleMouseOver}
|
|
||||||
onMouseOut={handleMouseOut}
|
|
||||||
>
|
>
|
||||||
{customModel === value && (
|
{customModel === value && (
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
|
@ -118,35 +125,36 @@ export default function ModelItem({ modelName, value, onSelect }) {
|
||||||
{renaming === true ? (
|
{renaming === true ? (
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
key={id}
|
||||||
type="text"
|
type="text"
|
||||||
className="pointer-events-auto z-50 m-0 mr-2 w-3/4 border border-blue-500 bg-transparent p-0 text-sm leading-tight outline-none"
|
className="pointer-events-auto z-50 m-0 mr-2 w-3/4 border border-blue-500 bg-transparent p-0 text-sm leading-tight outline-none"
|
||||||
value={modelInput}
|
value={modelInput}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onChange={(e) => setModelInput(e.target.value)}
|
onChange={(e) => setModelInput(e.target.value)}
|
||||||
onBlur={onRename}
|
// onBlur={onRename}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
modelInput
|
<div className="w-3/4 overflow-hidden">{modelInput}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{value === 'chatgpt' && <sup>$</sup>}
|
{value === 'chatgpt' && <sup>$</sup>}
|
||||||
{showButtons && (
|
<RenameButton
|
||||||
<>
|
twcss={`ml-auto mr-2 ${buttonClass.className}`}
|
||||||
<RenameButton
|
onRename={onRename}
|
||||||
twcss={`ml-auto mr-2 ${buttonClass.className}`}
|
renaming={renaming}
|
||||||
onRename={onRename}
|
onMouseOver={handleMouseOver}
|
||||||
renaming={renaming}
|
onMouseOut={handleMouseOut}
|
||||||
renameHandler={renameHandler}
|
renameHandler={renameHandler}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
{...buttonClass}
|
{...buttonClass}
|
||||||
onClick={onDelete}
|
onMouseOver={handleMouseOver}
|
||||||
>
|
onMouseOut={handleMouseOut}
|
||||||
<TrashIcon />
|
onClick={onDelete}
|
||||||
</button>
|
>
|
||||||
</>
|
<TrashIcon />
|
||||||
)}
|
</button>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { setSubmission, setModel, setDisabled, setCustomGpt, setCustomModel } from '~/store/submitSlice';
|
import {
|
||||||
|
setSubmission,
|
||||||
|
setModel,
|
||||||
|
setDisabled,
|
||||||
|
setCustomGpt,
|
||||||
|
setCustomModel
|
||||||
|
} from '~/store/submitSlice';
|
||||||
import { setNewConvo } from '~/store/convoSlice';
|
import { setNewConvo } from '~/store/convoSlice';
|
||||||
import ModelDialog from './ModelDialog';
|
import ModelDialog from './ModelDialog';
|
||||||
import MenuItems from './MenuItems';
|
import MenuItems from './MenuItems';
|
||||||
import manualSWR from '~/utils/fetchers';
|
import { swr } from '~/utils/fetchers';
|
||||||
import { setModels } from '~/store/modelSlice';
|
import { setModels } from '~/store/modelSlice';
|
||||||
import GPTIcon from '../svg/GPTIcon';
|
import GPTIcon from '../svg/GPTIcon';
|
||||||
import BingIcon from '../svg/BingIcon';
|
import BingIcon from '../svg/BingIcon';
|
||||||
|
|
@ -27,7 +33,7 @@ export default function ModelMenu() {
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = 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(`/api/customGpts`, 'get', (res) => {
|
const { data, isLoading, mutate } = swr(`/api/customGpts`, (res) => {
|
||||||
const fetchedModels = res.map((modelItem) => ({
|
const fetchedModels = res.map((modelItem) => ({
|
||||||
...modelItem,
|
...modelItem,
|
||||||
name: modelItem.chatGptLabel
|
name: modelItem.chatGptLabel
|
||||||
|
|
@ -37,7 +43,7 @@ export default function ModelMenu() {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
trigger();
|
mutate();
|
||||||
const lastSelected = JSON.parse(localStorage.getItem('model'));
|
const lastSelected = JSON.parse(localStorage.getItem('model'));
|
||||||
if (lastSelected && lastSelected !== 'chatgptCustom' && initial[lastSelected]) {
|
if (lastSelected && lastSelected !== 'chatgptCustom' && initial[lastSelected]) {
|
||||||
dispatch(setModel(lastSelected));
|
dispatch(setModel(lastSelected));
|
||||||
|
|
@ -51,6 +57,9 @@ export default function ModelMenu() {
|
||||||
}, [model]);
|
}, [model]);
|
||||||
|
|
||||||
const onChange = (value, custom = false) => {
|
const onChange = (value, custom = false) => {
|
||||||
|
// if (custom) {
|
||||||
|
// mutate();
|
||||||
|
// }
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return;
|
return;
|
||||||
} else if (value === 'chatgptCustom') {
|
} else if (value === 'chatgptCustom') {
|
||||||
|
|
@ -59,9 +68,6 @@ export default function ModelMenu() {
|
||||||
dispatch(setModel(value));
|
dispatch(setModel(value));
|
||||||
dispatch(setDisabled(false));
|
dispatch(setDisabled(false));
|
||||||
dispatch(setCustomModel(null));
|
dispatch(setCustomModel(null));
|
||||||
if (custom) {
|
|
||||||
trigger();
|
|
||||||
}
|
|
||||||
} 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;
|
||||||
|
|
@ -81,6 +87,7 @@ export default function ModelMenu() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenChange = (open) => {
|
const onOpenChange = (open) => {
|
||||||
|
mutate();
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setModelSave(false);
|
setModelSave(false);
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +164,7 @@ export default function ModelMenu() {
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<ModelDialog
|
<ModelDialog
|
||||||
mutate={trigger}
|
mutate={mutate}
|
||||||
modelMap={modelMap}
|
modelMap={modelMap}
|
||||||
setModelSave={setModelSave}
|
setModelSave={setModelSave}
|
||||||
handleSaveState={handleSaveState}
|
handleSaveState={handleSaveState}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue