mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
feat: support edit preset,
feat: support view current conversation options. feat: save current conversation as a preset. feat: preset delete all.
This commit is contained in:
parent
dae0c2d5e3
commit
d2579b44d1
15 changed files with 340 additions and 269 deletions
|
|
@ -16,7 +16,7 @@ const optionText =
|
|||
|
||||
function Settings(props) {
|
||||
// const [showSystemMessage, setShowSystemMessage] = React.useState(false);
|
||||
const { context, setContext, systemMessage, setSystemMessage, jailbreak, setJailbreak } = props;
|
||||
const { readonly, context, setContext, systemMessage, setSystemMessage, jailbreak, setJailbreak } = props;
|
||||
const showSystemMessage = jailbreak;
|
||||
|
||||
return (
|
||||
|
|
@ -32,6 +32,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<TextareaAutosize
|
||||
id="context"
|
||||
disabled={readonly}
|
||||
value={context || ''}
|
||||
onChange={e => setContext(e.target.value || null)}
|
||||
placeholder="Set custom instructions. Defaults to: 'You are ChatGPT, a large language model trained by OpenAI.'"
|
||||
|
|
@ -47,6 +48,7 @@ function Settings(props) {
|
|||
<div className="flex items-center space-x-3">
|
||||
<Checkbox
|
||||
id="jailbreak"
|
||||
disabled={readonly}
|
||||
checked={jailbreak}
|
||||
className="dark:border-gray-500 dark:bg-gray-700 dark:text-gray-50 focus:ring-opacity-20 dark:focus:ring-opacity-50 dark:focus:ring-offset-0 dark:focus:ring-gray-600"
|
||||
// onCheckedChange={setJailbreak}
|
||||
|
|
@ -79,6 +81,7 @@ function Settings(props) {
|
|||
</Label> */}
|
||||
<TextareaAutosize
|
||||
id="systemMessage"
|
||||
disabled={readonly}
|
||||
value={systemMessage || ''}
|
||||
onChange={e => setSystemMessage(e.target.value || null)}
|
||||
placeholder="Set custom instructions. Defaults to: 'You are ChatGPT, a large language model trained by OpenAI.'"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import { Input } from '../ui/Input.tsx';
|
|||
import { Label } from '../ui/Label.tsx';
|
||||
import Dropdown from '../ui/Dropdown';
|
||||
import { cn } from '~/utils/';
|
||||
import cleanupPreset from '~/utils/cleanupPreset';
|
||||
|
||||
import OpenAISettings from './OpenAI/Settings';
|
||||
import BingAISettings from './BingAI/Settings.jsx';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
|
|
@ -50,39 +53,29 @@ const EditPresetDialog = ({ open, onOpenChange, preset: _preset }) => {
|
|||
setPresP={setOption('frequency_penalty')}
|
||||
/>
|
||||
);
|
||||
else if (endpoint === 'bingAI')
|
||||
return (
|
||||
<BingAISettings
|
||||
readonly={true}
|
||||
context={preset?.context}
|
||||
setContext={setOption('context')}
|
||||
systemMessage={preset?.systemMessage}
|
||||
setSystemMessage={setOption('systemMessage')}
|
||||
jailbreak={preset?.jailbreak}
|
||||
setJailbreak={setOption('jailbreak')}
|
||||
/>
|
||||
);
|
||||
else return null;
|
||||
};
|
||||
|
||||
const defaultTextProps =
|
||||
'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
||||
// in order not to use wrong data from other endpoint
|
||||
const clearPreset = () => {
|
||||
if (preset?.endpoint === 'openAI')
|
||||
return {
|
||||
title: preset?.title,
|
||||
endpoint: preset?.endpoint,
|
||||
model: preset?.model,
|
||||
chatGptLabel: preset?.chatGptLabel,
|
||||
promptPrefix: preset?.promptPrefix,
|
||||
temperature: preset?.temperature,
|
||||
top_p: preset?.top_p,
|
||||
presence_penalty: preset?.presence_penalty,
|
||||
frequency_penalty: preset?.frequency_penalty
|
||||
};
|
||||
else
|
||||
return {
|
||||
title: preset?.title,
|
||||
endpoint: preset?.endpoint
|
||||
};
|
||||
// TODO: else
|
||||
};
|
||||
|
||||
const submitPreset = () => {
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/api/presets',
|
||||
data: clearPreset(),
|
||||
data: cleanupPreset(preset),
|
||||
withCredentials: true
|
||||
}).then(res => {
|
||||
setPresets(res?.data);
|
||||
|
|
@ -91,7 +84,7 @@ const EditPresetDialog = ({ open, onOpenChange, preset: _preset }) => {
|
|||
|
||||
const exportPreset = () => {
|
||||
exportFromJSON({
|
||||
data: clearPreset(),
|
||||
data: cleanupPreset(preset),
|
||||
fileName: `${preset?.title}.json`,
|
||||
exportType: exportFromJSON.types.json
|
||||
});
|
||||
|
|
@ -168,7 +161,7 @@ const EditPresetDialog = ({ open, onOpenChange, preset: _preset }) => {
|
|||
<>
|
||||
<DialogButton
|
||||
onClick={exportPreset}
|
||||
className="dark:hover:gray-400 bg-red border-gray-700"
|
||||
className="dark:hover:gray-400 border-gray-700"
|
||||
>
|
||||
Export
|
||||
</DialogButton>
|
||||
|
|
|
|||
134
client/src/components/Endpoints/EndpointOptionsDialog.jsx
Normal file
134
client/src/components/Endpoints/EndpointOptionsDialog.jsx
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useSetRecoilState, useRecoilValue } from 'recoil';
|
||||
import axios from 'axios';
|
||||
import exportFromJSON from 'export-from-json';
|
||||
import DialogTemplate from '../ui/DialogTemplate.jsx';
|
||||
import { Dialog, DialogClose, DialogButton } from '../ui/Dialog.tsx';
|
||||
import { Input } from '../ui/Input.tsx';
|
||||
import { Label } from '../ui/Label.tsx';
|
||||
import Dropdown from '../ui/Dropdown.jsx';
|
||||
import SaveAsPresetDialog from './SaveAsPresetDialog';
|
||||
import { cn } from '~/utils/';
|
||||
import cleanupPreset from '~/utils/cleanupPreset';
|
||||
|
||||
import OpenAISettings from './OpenAI/Settings.jsx';
|
||||
import BingAISettings from './BingAI/Settings.jsx';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
// A preset dialog to show readonly preset values.
|
||||
const EndpointOptionsDialog = ({ open, onOpenChange, preset: _preset, title }) => {
|
||||
// const [title, setTitle] = useState('My Preset');
|
||||
const [preset, setPreset] = useState({});
|
||||
|
||||
const [saveAsDialogShow, setSaveAsDialogShow] = useState(false);
|
||||
|
||||
const setOption = param => newValue => {
|
||||
let update = {};
|
||||
update[param] = newValue;
|
||||
setPreset(prevState => ({
|
||||
...prevState,
|
||||
...update
|
||||
}));
|
||||
};
|
||||
|
||||
const renderSettings = () => {
|
||||
const { endpoint } = preset || {};
|
||||
|
||||
if (endpoint === 'openAI')
|
||||
return (
|
||||
<OpenAISettings
|
||||
readonly={true}
|
||||
model={preset?.model}
|
||||
setModel={setOption('model')}
|
||||
chatGptLabel={preset?.chatGptLabel}
|
||||
setChatGptLabel={setOption('chatGptLabel')}
|
||||
promptPrefix={preset?.promptPrefix}
|
||||
setPromptPrefix={setOption('promptPrefix')}
|
||||
temperature={preset?.temperature}
|
||||
setTemperature={setOption('temperature')}
|
||||
topP={preset?.top_p}
|
||||
setTopP={setOption('top_p')}
|
||||
freqP={preset?.presence_penalty}
|
||||
setFreqP={setOption('presence_penalty')}
|
||||
presP={preset?.frequency_penalty}
|
||||
setPresP={setOption('frequency_penalty')}
|
||||
/>
|
||||
);
|
||||
else if (endpoint === 'bingAI')
|
||||
return (
|
||||
<BingAISettings
|
||||
readonly={true}
|
||||
context={preset?.context}
|
||||
setContext={setOption('context')}
|
||||
systemMessage={preset?.systemMessage}
|
||||
setSystemMessage={setOption('systemMessage')}
|
||||
jailbreak={preset?.jailbreak}
|
||||
setJailbreak={setOption('jailbreak')}
|
||||
/>
|
||||
);
|
||||
else return null;
|
||||
};
|
||||
|
||||
const saveAsPreset = () => {
|
||||
setSaveAsDialogShow(true);
|
||||
};
|
||||
|
||||
const exportPreset = () => {
|
||||
exportFromJSON({
|
||||
data: cleanupPreset(preset),
|
||||
fileName: `${preset?.title}.json`,
|
||||
exportType: exportFromJSON.types.json
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPreset(_preset);
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
>
|
||||
<DialogTemplate
|
||||
title={title || 'View Options'}
|
||||
className="max-w-full sm:max-w-4xl"
|
||||
main={
|
||||
<div className="flex w-full flex-col items-center gap-2">
|
||||
<div className="w-full p-0">{renderSettings()}</div>
|
||||
</div>
|
||||
}
|
||||
buttons={
|
||||
<>
|
||||
<DialogButton
|
||||
onClick={saveAsPreset}
|
||||
className="dark:hover:gray-400 border-gray-700 bg-green-600 text-white hover:bg-green-700 dark:hover:bg-green-800"
|
||||
>
|
||||
Save As Preset
|
||||
</DialogButton>
|
||||
</>
|
||||
}
|
||||
leftButtons={
|
||||
<>
|
||||
<DialogButton
|
||||
onClick={exportPreset}
|
||||
className="dark:hover:gray-400 border-gray-700"
|
||||
>
|
||||
Export
|
||||
</DialogButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Dialog>
|
||||
<SaveAsPresetDialog
|
||||
open={saveAsDialogShow}
|
||||
onOpenChange={setSaveAsDialogShow}
|
||||
preset={preset}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EndpointOptionsDialog;
|
||||
|
|
@ -4,6 +4,7 @@ import ModelDropDown from '../../ui/ModelDropDown';
|
|||
import { Input } from '~/components/ui/Input.tsx';
|
||||
import { Label } from '~/components/ui/Label.tsx';
|
||||
import { Slider } from '~/components/ui/Slider.tsx';
|
||||
import { InputNumber } from '../../ui/InputNumber';
|
||||
import OptionHover from './OptionHover';
|
||||
import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx';
|
||||
import { cn } from '~/utils/';
|
||||
|
|
@ -15,6 +16,7 @@ const optionText =
|
|||
|
||||
function Settings(props) {
|
||||
const {
|
||||
readonly,
|
||||
model,
|
||||
setModel,
|
||||
chatGptLabel,
|
||||
|
|
@ -38,6 +40,7 @@ function Settings(props) {
|
|||
<div className="grid w-full items-center gap-2">
|
||||
<ModelDropDown
|
||||
model={model}
|
||||
disabled={readonly}
|
||||
setModel={setModel}
|
||||
endpoint="openAI"
|
||||
className={cn(
|
||||
|
|
@ -73,6 +76,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<Input
|
||||
id="chatGptLabel"
|
||||
disabled={readonly}
|
||||
value={chatGptLabel || ''}
|
||||
// ref={inputRef}
|
||||
onChange={e => setChatGptLabel(e.target.value || null)}
|
||||
|
|
@ -92,6 +96,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<TextareaAutosize
|
||||
id="promptPrefix"
|
||||
disabled={readonly}
|
||||
value={promptPrefix || ''}
|
||||
onChange={e => setPromptPrefix(e.target.value || null)}
|
||||
placeholder="Set custom instructions. Defaults to: 'You are ChatGPT, a large language model trained by OpenAI.'"
|
||||
|
|
@ -123,6 +128,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<Input
|
||||
id="temp-int"
|
||||
disabled
|
||||
value={temperature}
|
||||
onChange={e => setTemperature(e.target.value)}
|
||||
className={cn(
|
||||
|
|
@ -132,6 +138,7 @@ function Settings(props) {
|
|||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[temperature]}
|
||||
onValueChange={value => setTemperature(value[0])}
|
||||
max={2}
|
||||
|
|
@ -157,6 +164,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<Input
|
||||
id="max-tokens-int"
|
||||
disabled
|
||||
value={maxTokens}
|
||||
onChange={e => setMaxTokens(e.target.value)}
|
||||
className={cn(
|
||||
|
|
@ -166,6 +174,7 @@ function Settings(props) {
|
|||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[maxTokens]}
|
||||
onValueChange={value => setMaxTokens(value[0])}
|
||||
max={2048} // should be dynamic to the currently selected model
|
||||
|
|
@ -191,6 +200,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<Input
|
||||
id="top-p-int"
|
||||
disabled
|
||||
value={topP}
|
||||
onChange={e => setTopP(e.target.value)}
|
||||
className={cn(
|
||||
|
|
@ -200,6 +210,7 @@ function Settings(props) {
|
|||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[topP]}
|
||||
onValueChange={value => setTopP(value[0])}
|
||||
max={1}
|
||||
|
|
@ -225,6 +236,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<Input
|
||||
id="freq-penalty-int"
|
||||
disabled
|
||||
value={freqP}
|
||||
onChange={e => setFreqP(e.target.value)}
|
||||
className={cn(
|
||||
|
|
@ -234,6 +246,7 @@ function Settings(props) {
|
|||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[freqP]}
|
||||
onValueChange={value => setFreqP(value[0])}
|
||||
max={2}
|
||||
|
|
@ -259,6 +272,7 @@ function Settings(props) {
|
|||
</Label>
|
||||
<Input
|
||||
id="pres-penalty-int"
|
||||
disabled
|
||||
value={presP}
|
||||
onChange={e => setPresP(e.target.value)}
|
||||
className={cn(
|
||||
|
|
@ -268,6 +282,7 @@ function Settings(props) {
|
|||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[presP]}
|
||||
onValueChange={value => setPresP(value[0])}
|
||||
max={2}
|
||||
|
|
@ -281,158 +296,6 @@ function Settings(props) {
|
|||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
{/* <div className="flex justify-around">
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="group/temp mr-4 flex w-full items-center justify-end gap-4">
|
||||
<Label
|
||||
htmlFor="temperature"
|
||||
className="mr-2 text-right"
|
||||
>
|
||||
Temperature
|
||||
</Label>
|
||||
<Input
|
||||
id="temp-int"
|
||||
value={temperature}
|
||||
onChange={e => setTemperature(e.target.value)}
|
||||
className={cn(defaultTextProps, cn(optionText, 'w-10 group-hover/temp:border-gray-200'))}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="temp"
|
||||
side="right"
|
||||
/>
|
||||
</HoverCard>
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="group/max mr-4 flex w-full items-center justify-end gap-4">
|
||||
<Label
|
||||
htmlFor="max-tokens"
|
||||
className="mr-2 w-full text-right"
|
||||
>
|
||||
Max tokens
|
||||
</Label>
|
||||
<Input
|
||||
id="max-tokens-int"
|
||||
value={maxTokens}
|
||||
onChange={e => setMaxTokens(e.target.value)}
|
||||
className={cn(defaultTextProps, cn(optionText, 'w-11 group-hover/max:border-gray-200'))}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="max"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-5">
|
||||
<Slider
|
||||
value={[maxTokens]}
|
||||
onValueChange={value => setMaxTokens(value)}
|
||||
max={2048} // should be dynamic to the currently selected model
|
||||
min={1}
|
||||
step={1}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-around">
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="group/top mr-4 flex w-full items-center justify-end gap-4">
|
||||
<Label
|
||||
htmlFor="top-p"
|
||||
className="mr-2 text-right"
|
||||
>
|
||||
Top P
|
||||
</Label>
|
||||
<Input
|
||||
id="top-p-int"
|
||||
value={topP}
|
||||
onChange={e => setTopP(e.target.value)}
|
||||
className={cn(defaultTextProps, cn(optionText, 'w-10 group-hover/top:border-gray-200'))}
|
||||
/>
|
||||
<OptionHover
|
||||
type="top-p"
|
||||
side="right"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
</HoverCard>
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="group/freq mr-4 flex w-full items-center justify-end gap-4">
|
||||
<Label
|
||||
htmlFor="freq-penalty"
|
||||
className="mr-2 w-full text-right"
|
||||
>
|
||||
Frequency Penalty
|
||||
</Label>
|
||||
<Input
|
||||
id="freq-penalty-int"
|
||||
value={freqP}
|
||||
onChange={e => setFreqP(e.target.value)}
|
||||
className={cn(defaultTextProps, cn(optionText, 'w-10 group-hover/freq:border-gray-200'))}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="freq"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-5">
|
||||
<Slider
|
||||
value={[topP]}
|
||||
onValueChange={value => setTopP(value)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="w-full"
|
||||
/>
|
||||
<Slider
|
||||
value={[freqP]}
|
||||
onValueChange={value => setFreqP(value)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<HoverCard>
|
||||
<HoverCardTrigger className="group/pres mr-4 flex items-center justify-end gap-4">
|
||||
<Label
|
||||
htmlFor="pres-penalty"
|
||||
className="mr-2 text-right"
|
||||
>
|
||||
Presence Penalty
|
||||
</Label>
|
||||
<Input
|
||||
id="pres-penalty-int"
|
||||
value={presP}
|
||||
onChange={e => setPresP(e.target.value)}
|
||||
className={cn(defaultTextProps, cn(optionText, 'w-10 group-hover/pres:border-gray-200'))}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover
|
||||
type="pres"
|
||||
side="left"
|
||||
/>
|
||||
</HoverCard>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-5">
|
||||
<Slider
|
||||
value={[presP]}
|
||||
onValueChange={value => setPresP(value)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="w-full opacity-0"
|
||||
/>
|
||||
<Slider
|
||||
value={[presP]}
|
||||
onValueChange={value => setPresP(value)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="w-full"
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import { Dialog } from '../ui/Dialog.tsx';
|
|||
import { Input } from '../ui/Input.tsx';
|
||||
import { Label } from '../ui/Label.tsx';
|
||||
import { cn } from '~/utils/';
|
||||
import buildPresetByConversation from '~/utils/buildPresetByConversation';
|
||||
import cleanupPreset from '~/utils/cleanupPreset';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
const SaveAsPresetDialog = ({ open, onOpenChange, conversation }) => {
|
||||
const SaveAsPresetDialog = ({ open, onOpenChange, preset }) => {
|
||||
const [title, setTitle] = useState('My Preset');
|
||||
const setPresets = useSetRecoilState(store.presets);
|
||||
|
||||
|
|
@ -18,15 +18,15 @@ const SaveAsPresetDialog = ({ open, onOpenChange, conversation }) => {
|
|||
'rounded-md border border-gray-300 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.10)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-400 dark:bg-gray-700 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
||||
const submitPreset = () => {
|
||||
const preset = buildPresetByConversation({
|
||||
title,
|
||||
conversation
|
||||
const _preset = cleanupPreset({
|
||||
...preset,
|
||||
title
|
||||
});
|
||||
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/api/presets',
|
||||
data: preset,
|
||||
data: _preset,
|
||||
withCredentials: true
|
||||
}).then(res => {
|
||||
setPresets(res?.data);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ function BingAIOptions() {
|
|||
<SaveAsPresetDialog
|
||||
open={saveAsDialogShow}
|
||||
onOpenChange={setSaveAsDialogShow}
|
||||
conversation={conversation}
|
||||
preset={conversation}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useRecoilState } from 'recoil';
|
||||
import EditPresetDialog from '../../Endpoints/EditPresetDialog';
|
||||
import EndpointItems from './EndpointItems';
|
||||
import PresetItems from './PresetItems';
|
||||
import getIcon from '~/utils/getIcon';
|
||||
import manualSWR from '~/utils/fetchers';
|
||||
|
||||
import { Button } from '../../ui/Button.tsx';
|
||||
import {
|
||||
|
|
@ -27,13 +28,18 @@ export default function NewConversationMenu() {
|
|||
// const models = useRecoilValue(store.models);
|
||||
const availableEndpoints = useRecoilValue(store.availableEndpoints);
|
||||
// const setCustomGPTModels = useSetRecoilState(store.customGPTModels);
|
||||
const presets = useRecoilValue(store.presets);
|
||||
const [presets, setPresets] = useRecoilState(store.presets);
|
||||
|
||||
const conversation = useRecoilValue(store.conversation) || {};
|
||||
const { endpoint, conversationId } = conversation;
|
||||
// const { model, promptPrefix, chatGptLabel, conversationId } = conversation;
|
||||
const { newConversation } = store.useConversation();
|
||||
|
||||
const { trigger: clearPresetsTrigger } = manualSWR(`/api/presets/delete`, 'post', data => {
|
||||
console.log(data);
|
||||
setPresets(data);
|
||||
});
|
||||
|
||||
// update the default model when availableModels changes
|
||||
// typically, availableModels changes => modelsFilter or customGPTModels changes
|
||||
useEffect(() => {
|
||||
|
|
@ -73,6 +79,10 @@ export default function NewConversationMenu() {
|
|||
setPreset(preset);
|
||||
};
|
||||
|
||||
const clearPreset = () => {
|
||||
clearPresetsTrigger({});
|
||||
};
|
||||
|
||||
const icon = getIcon({
|
||||
size: 32,
|
||||
...conversation,
|
||||
|
|
@ -99,7 +109,7 @@ export default function NewConversationMenu() {
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="min-w-56 dark:bg-gray-700"
|
||||
className="min-w-[300px] dark:bg-gray-700"
|
||||
onCloseAutoFocus={event => event.preventDefault()}
|
||||
>
|
||||
<DropdownMenuLabel className="dark:text-gray-300">Select an Endpoint</DropdownMenuLabel>
|
||||
|
|
@ -121,7 +131,17 @@ export default function NewConversationMenu() {
|
|||
|
||||
<div className="mt-6 w-full" />
|
||||
|
||||
<DropdownMenuLabel className="dark:text-gray-300">Select a Preset</DropdownMenuLabel>
|
||||
<DropdownMenuLabel className="flex items-center dark:text-gray-300">
|
||||
<span>Select a Preset</span>
|
||||
<div className="flex-1" />
|
||||
<Button
|
||||
type="button"
|
||||
className="h-auto bg-transparent px-2 py-1 text-xs font-medium font-normal text-red-700 hover:bg-slate-200 hover:text-red-700 dark:bg-transparent dark:text-red-400 dark:hover:bg-gray-800 dark:hover:text-red-400"
|
||||
onClick={clearPreset}
|
||||
>
|
||||
Clear All
|
||||
</Button>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuRadioGroup
|
||||
onValueChange={onSelectPreset}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ function OpenAIOptions() {
|
|||
<SaveAsPresetDialog
|
||||
open={saveAsDialogShow}
|
||||
onOpenChange={setSaveAsDialogShow}
|
||||
conversation={conversation}
|
||||
preset={conversation}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import EndpointOptionsDialog from '../Endpoints/EndpointOptionsDialog';
|
||||
import { Button } from '../ui/Button.tsx';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
|
|
@ -14,6 +16,7 @@ const clipPromptPrefix = str => {
|
|||
};
|
||||
|
||||
const MessageHeader = ({ isSearchView = false }) => {
|
||||
const [saveAsDialogShow, setSaveAsDialogShow] = useState(false);
|
||||
const [extended, setExtended] = useState(false);
|
||||
const conversation = useRecoilValue(store.conversation);
|
||||
const searchQuery = useRecoilValue(store.searchQuery);
|
||||
|
|
@ -64,6 +67,7 @@ const MessageHeader = ({ isSearchView = false }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
'dark:text-gray-450 w-full gap-1 border-b border-black/10 bg-gray-50 text-sm text-gray-500 transition-all hover:bg-gray-100 dark:border-gray-900/50 dark:bg-gray-700 dark:hover:bg-gray-600' +
|
||||
|
|
@ -85,9 +89,17 @@ const MessageHeader = ({ isSearchView = false }) => {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button onClick={() => setSaveAsDialogShow(true)}>View</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<EndpointOptionsDialog
|
||||
open={saveAsDialogShow}
|
||||
onOpenChange={setSaveAsDialogShow}
|
||||
preset={conversation}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export default function DialogTemplate({
|
|||
{main ? main : null}
|
||||
<DialogFooter>
|
||||
<div>{leftButtons ? leftButtons : null}</div>
|
||||
<div>
|
||||
<div className="flex gap-2">
|
||||
<DialogClose className="dark:hover:gray-400 border-gray-700">Cancel</DialogClose>
|
||||
{buttons ? buttons : null}
|
||||
{selection ? (
|
||||
|
|
|
|||
43
client/src/components/ui/InputNumber.jsx
Normal file
43
client/src/components/ui/InputNumber.jsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
|
||||
// import * as InputNumberPrimitive from 'rc-input-number';
|
||||
|
||||
import { cn } from '../../utils/index.jsx';
|
||||
|
||||
// TODO help needed
|
||||
// const _InputNumber = React.forwardRef< React.ElementRef<typeof InputNumber>, InputNumberPrimitive.InputNumberProps>(
|
||||
// ({ className, ...props }, ref) => {
|
||||
// return (
|
||||
// <InputNumber
|
||||
// className={cn(
|
||||
// "flex h-10 w-full rounded-md border border-slate-300 bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900",
|
||||
// className
|
||||
// )}
|
||||
// ref={ref}
|
||||
// {...props}
|
||||
// />
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
// _InputNumber.displayName = "Input"
|
||||
|
||||
// console.log(_InputNumber);
|
||||
|
||||
const InputNumber = React.forwardRef(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<NumericFormat
|
||||
className={cn(
|
||||
'flex h-10 w-full rounded-md border border-slate-300 bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export { InputNumber };
|
||||
|
|
@ -7,6 +7,7 @@ import store from '~/store';
|
|||
|
||||
function ModelDropDown({
|
||||
model,
|
||||
disabled,
|
||||
setModel,
|
||||
endpoint,
|
||||
showAbove = false,
|
||||
|
|
@ -23,6 +24,7 @@ function ModelDropDown({
|
|||
<Listbox
|
||||
value={model}
|
||||
onChange={setModel}
|
||||
disabled={disabled}
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import getDefaultConversation from '~/utils/getDefaultConversation';
|
|||
// frequency_penalty: 0,
|
||||
// // for bingAI only
|
||||
// jailbreak: false,
|
||||
// context: null,
|
||||
// systemMessage: null,
|
||||
// jailbreakConversationId: null,
|
||||
// conversationSignature: null,
|
||||
// clientId: null,
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
const buildPresetByConversation = ({ title, conversation, ...others }) => {
|
||||
const { endpoint } = conversation;
|
||||
|
||||
let preset = {};
|
||||
if (endpoint === 'azureOpenAI' || endpoint === 'openAI') {
|
||||
preset = {
|
||||
endpoint,
|
||||
model: conversation?.model || 'gpt-3.5-turbo',
|
||||
chatGptLabel: conversation?.chatGptLabel || null,
|
||||
promptPrefix: conversation?.promptPrefix || null,
|
||||
temperature: conversation?.temperature || 1,
|
||||
top_p: conversation?.top_p || 1,
|
||||
presence_penalty: conversation?.presence_penalty || 0,
|
||||
frequency_penalty: conversation?.frequency_penalty || 0,
|
||||
title,
|
||||
...others
|
||||
};
|
||||
} else if (endpoint === 'bingAI') {
|
||||
preset = {
|
||||
endpoint,
|
||||
jailbreak: conversation?.jailbreak || false,
|
||||
jailbreakConversationId: conversation?.jailbreakConversationId || null,
|
||||
conversationSignature: null,
|
||||
clientId: null,
|
||||
invocationId: 1,
|
||||
toneStyle: conversation?.toneStyle || 'fast',
|
||||
title,
|
||||
...others
|
||||
};
|
||||
} else if (endpoint === 'chatGPTBrowser') {
|
||||
preset = {
|
||||
endpoint,
|
||||
model: conversation?.model || 'Default (GPT-3.5)',
|
||||
title,
|
||||
...others
|
||||
};
|
||||
} else if (endpoint === null) {
|
||||
preset = {
|
||||
...conversation,
|
||||
endpoint,
|
||||
title,
|
||||
...others
|
||||
};
|
||||
} else {
|
||||
console.error(`Unknown endpoint ${endpoint}`);
|
||||
preset = {
|
||||
endpoint: null,
|
||||
title,
|
||||
...others
|
||||
};
|
||||
}
|
||||
|
||||
return preset;
|
||||
};
|
||||
|
||||
export default buildPresetByConversation;
|
||||
55
client/src/utils/cleanupPreset.js
Normal file
55
client/src/utils/cleanupPreset.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
const cleanupPreset = _preset => {
|
||||
const { endpoint } = _preset;
|
||||
|
||||
let preset = {};
|
||||
if (endpoint === 'azureOpenAI' || endpoint === 'openAI') {
|
||||
preset = {
|
||||
endpoint,
|
||||
presetId: _preset?.presetId || null,
|
||||
model: _preset?.model || 'gpt-3.5-turbo',
|
||||
chatGptLabel: _preset?.chatGptLabel || null,
|
||||
promptPrefix: _preset?.promptPrefix || null,
|
||||
temperature: _preset?.temperature || 1,
|
||||
top_p: _preset?.top_p || 1,
|
||||
presence_penalty: _preset?.presence_penalty || 0,
|
||||
frequency_penalty: _preset?.frequency_penalty || 0,
|
||||
title: _preset?.title || 'New Preset'
|
||||
};
|
||||
} else if (endpoint === 'bingAI') {
|
||||
preset = {
|
||||
endpoint,
|
||||
presetId: _preset?.presetId || null,
|
||||
jailbreak: _preset?.jailbreak || false,
|
||||
jailbreakpresetId: _preset?._jailbreakpresetId || null,
|
||||
presetSignature: null,
|
||||
clientId: null,
|
||||
invocationId: 1,
|
||||
toneStyle: _preset?.toneStyle || 'fast',
|
||||
title: _preset?.title || 'New Preset'
|
||||
};
|
||||
} else if (endpoint === 'chatGPTBrowser') {
|
||||
preset = {
|
||||
endpoint,
|
||||
presetId: _preset?.presetId || null,
|
||||
model: _preset?.model || 'Default (GPT-3.5)',
|
||||
title: _preset?.title || 'New Preset'
|
||||
};
|
||||
} else if (endpoint === null) {
|
||||
preset = {
|
||||
endpoint,
|
||||
presetId: _preset?.presetId || null,
|
||||
title: _preset?.title || 'New Preset'
|
||||
};
|
||||
} else {
|
||||
console.error(`Unknown endpoint ${endpoint}`);
|
||||
preset = {
|
||||
endpoint: null,
|
||||
presetId: _preset?.presetId || null,
|
||||
title: _preset?.title || 'New Preset'
|
||||
};
|
||||
}
|
||||
|
||||
return preset;
|
||||
};
|
||||
|
||||
export default cleanupPreset;
|
||||
Loading…
Add table
Add a link
Reference in a new issue