feat: add preset and edit preset.

This commit is contained in:
Wentao Lyu 2023-04-02 04:15:07 +08:00
parent 80ef5008dd
commit 45e17da241
29 changed files with 592 additions and 493 deletions

View file

@ -1,78 +0,0 @@
import React from 'react';
import CheckMark from '../../svg/CheckMark';
import { Listbox } from '@headlessui/react';
import { useRecoilValue } from 'recoil';
import { cn } from '~/utils/';
import store from '~/store';
function ModelDropDown({ model, setModel, endpoint }) {
const endpointsConfig = useRecoilValue(store.endpointsConfig);
const models = endpointsConfig?.[endpoint]?.['availableModels'] || [];
return (
<div className="flex items-center justify-center gap-2">
<div className="relative w-full">
<Listbox
value={model}
onChange={setModel}
>
<Listbox.Button className="relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:border-green-600 focus:outline-none focus:ring-1 focus:ring-green-600 dark:border-white/20 dark:bg-gray-800 sm:text-sm">
<Listbox.Label
className="block text-xs text-gray-700 dark:text-gray-500"
id="headlessui-listbox-label-:r1:"
data-headlessui-state=""
>
Model
</Listbox.Label>
<span className="inline-flex w-full truncate">
<span className="flex h-6 items-center gap-1 truncate text-sm">{model}</span>
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg
stroke="currentColor"
fill="none"
strokeWidth="2"
viewBox="0 0 24 24"
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4 text-gray-400"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</span>
</Listbox.Button>
<Listbox.Options className="absolute z-10 mt-2 max-h-60 w-full overflow-auto rounded bg-white text-base text-xs ring-1 ring-black/10 focus:outline-none dark:bg-gray-800 dark:ring-white/20 dark:last:border-0 md:w-[100%]">
{models.map((modelOption, i) => (
<Listbox.Option
key={i}
value={modelOption}
className="group relative flex h-[42px] cursor-pointer select-none items-center overflow-hidden border-b border-black/10 pl-3 pr-9 text-gray-900 last:border-0 hover:bg-[#ECECF1] dark:border-white/20 dark:text-white dark:hover:bg-gray-700"
>
<span className="flex items-center gap-1.5 truncate">
<span
className={cn(
'flex h-6 items-center gap-1 text-gray-800 dark:text-gray-100',
modelOption === model ? 'font-semibold' : ''
)}
>
{modelOption}
</span>
{modelOption === model && (
<span className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-800 dark:text-gray-100">
<CheckMark />
</span>
)}
</span>
</Listbox.Option>
))}
</Listbox.Options>
</Listbox>
</div>
</div>
);
}
export default ModelDropDown;

View file

@ -1,52 +0,0 @@
import React, { useState } from 'react';
import { Button } from '../../ui/Button.tsx';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuSeparator,
DropdownMenuTrigger,
DropdownMenuRadioItem
} from '../../ui/DropdownMenu.tsx';
const ModelSelect = ({ model, onChange, availableModels, ...props }) => {
const [menuOpen, setMenuOpen] = useState(false);
return (
<DropdownMenu
open={menuOpen}
onOpenChange={setMenuOpen}
>
<DropdownMenuTrigger asChild>
<Button {...props}>
<span className="w-full text-center text-xs font-medium font-normal">Model: {model}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-56 dark:bg-gray-700"
onCloseAutoFocus={event => event.preventDefault()}
>
<DropdownMenuLabel className="dark:text-gray-300">Select a model</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup
value={model}
onValueChange={onChange}
className="overflow-y-auto"
>
{availableModels.map(model => (
<DropdownMenuRadioItem
key={model}
value={model}
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
>
{model}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
);
};
export default ModelSelect;

View file

@ -1,33 +0,0 @@
import React from 'react';
import { HoverCardPortal, HoverCardContent } from '~/components/ui/HoverCard.tsx';
const types = {
temp: 'Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.',
max: "The max tokens to generate. The total length of input tokens and generated tokens is limited by the model's context length.",
topp: 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We recommend altering this or temperature but not both.',
freq: "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.",
pres: "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics."
};
function OptionHover({ type, side }) {
// const options = {};
// if (type === 'pres') {
// options.sideOffset = 45;
// }
return (
<HoverCardPortal>
<HoverCardContent
side={side}
className="w-80 "
// {...options}
>
<div className="space-y-2">
<p className="text-sm text-gray-600 dark:text-gray-300">{types[type]}</p>
</div>
</HoverCardContent>
</HoverCardPortal>
);
}
export default OptionHover;

View file

@ -1,437 +0,0 @@
import React from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import ModelDropDown from './ModelDropDown';
import { Input } from '~/components/ui/Input.tsx';
import { Label } from '~/components/ui/Label.tsx';
import { Slider } from '~/components/ui/Slider.tsx';
import OptionHover from './OptionHover';
import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx';
import { cn } from '~/utils/';
const defaultTextProps =
'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 optionText =
'p-0 shadow-none text-right pr-1 h-8 border-transparent focus:ring-[#10a37f] focus:ring-offset-0 focus:ring-opacity-100 hover:bg-gray-800/10 dark:hover:bg-white/10 focus:bg-gray-800/10 dark:focus:bg-white/10 transition-colors';
function Settings(props) {
const {
model,
setModel,
chatGptLabel,
setChatGptLabel,
promptPrefix,
setPromptPrefix,
temperature,
setTemperature,
topP,
setTopP,
freqP,
setFreqP,
presP,
setPresP
} = props;
return (
<>
<div className="grid gap-6 sm:grid-cols-2">
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
<div className="grid w-full items-center gap-2">
<ModelDropDown
model={model}
setModel={setModel}
endpoint="openAI"
/>
{/* <Label
htmlFor="model"
className="text-left text-sm font-medium"
>
Model
</Label>
<Input
id="model"
value={model}
// ref={inputRef}
onChange={e => setModel(e.target.value)}
placeholder="Set a custom name for ChatGPT"
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
)}
/> */}
</div>
<div className="grid w-full items-center gap-2">
<Label
htmlFor="chatGptLabel"
className="text-left text-sm font-medium"
>
Custom Name <small className="opacity-40">(default: blank)</small>
</Label>
<Input
id="chatGptLabel"
value={chatGptLabel || ''}
// ref={inputRef}
onChange={e => setChatGptLabel(e.target.value || null)}
placeholder="Set a custom name for ChatGPT"
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
)}
/>
</div>
<div className="grid w-full items-center gap-2">
<Label
htmlFor="promptPrefix"
className="text-left text-sm font-medium"
>
Prompt Prefix <small className="opacity-40">(default: blank)</small>
</Label>
<TextareaAutosize
id="promptPrefix"
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.'"
className={cn(
defaultTextProps,
'flex max-h-[300px] min-h-[100px] w-full resize-none px-3 py-2 '
)}
// onFocus={() => {
// textareaRef.current.classList.remove('max-h-10');
// textareaRef.current.classList.add('max-h-52');
// }}
// onBlur={() => {
// textareaRef.current.classList.remove('max-h-52');
// textareaRef.current.classList.add('max-h-10');
// }}
// ref={textareaRef}
/>
</div>
</div>
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
<HoverCard>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">
<Label
htmlFor="chatGptLabel"
className="text-left text-sm font-medium"
>
Temperature <small className="opacity-40">(default: 1)</small>
</Label>
<Input
id="temp-int"
value={temperature}
onChange={e => setTemperature(e.target.value)}
className={cn(
defaultTextProps,
cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200')
)}
/>
</div>
<Slider
value={[temperature]}
onValueChange={value => setTemperature(value[0])}
max={2}
min={0}
step={0.01}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
<OptionHover
type="temp"
side="left"
/>
</HoverCard>
{/* <HoverCard>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">
<Label
htmlFor="chatGptLabel"
className="text-left text-sm font-medium"
>
Max tokens
</Label>
<Input
id="max-tokens-int"
value={maxTokens}
onChange={e => setMaxTokens(e.target.value)}
className={cn(
defaultTextProps,
cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200')
)}
/>
</div>
<Slider
value={[maxTokens]}
onValueChange={value => setMaxTokens(value[0])}
max={2048} // should be dynamic to the currently selected model
min={1}
step={1}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
<OptionHover
type="max"
side="left"
/>
</HoverCard> */}
<HoverCard>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">
<Label
htmlFor="chatGptLabel"
className="text-left text-sm font-medium"
>
Top P <small className="opacity-40">(default: 1)</small>
</Label>
<Input
id="top-p-int"
value={topP}
onChange={e => setTopP(e.target.value)}
className={cn(
defaultTextProps,
cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200')
)}
/>
</div>
<Slider
value={[topP]}
onValueChange={value => setTopP(value[0])}
max={1}
min={0}
step={0.01}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
<OptionHover
type="topp"
side="left"
/>
</HoverCard>
<HoverCard>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">
<Label
htmlFor="chatGptLabel"
className="text-left text-sm font-medium"
>
Frequency Penalty <small className="opacity-40">(default: 0)</small>
</Label>
<Input
id="freq-penalty-int"
value={freqP}
onChange={e => setFreqP(e.target.value)}
className={cn(
defaultTextProps,
cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200')
)}
/>
</div>
<Slider
value={[freqP]}
onValueChange={value => setFreqP(value[0])}
max={2}
min={-2}
step={0.01}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
<OptionHover
type="freq"
side="left"
/>
</HoverCard>
<HoverCard>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">
<Label
htmlFor="chatGptLabel"
className="text-left text-sm font-medium"
>
Presence Penalty <small className="opacity-40">(default: 0)</small>
</Label>
<Input
id="pres-penalty-int"
value={presP}
onChange={e => setPresP(e.target.value)}
className={cn(
defaultTextProps,
cn(optionText, 'h-auto w-12 border-0 group-hover/temp:border-gray-200')
)}
/>
</div>
<Slider
value={[presP]}
onValueChange={value => setPresP(value[0])}
max={2}
min={-2}
step={0.01}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
<OptionHover
type="pres"
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>
</>
);
}
export default Settings;

View file

@ -1,12 +1,11 @@
import React, { useEffect, useState } from 'react';
import { Settings2 } from 'lucide-react';
import { useRecoilState, useRecoilValue } from 'recoil';
import ModelSelect from './ModelSelect';
import EndpointOptionsPopover from '../../ui/EndpointOptionsPopover';
import DialogTemplate from '../../ui/DialogTemplate';
import ModelSelect from '../../ui/ModelSelect';
import EndpointOptionsPopover from '../../Endpoints/EndpointOptionsPopover';
import SaveAsPresetDialog from '../../Endpoints/SaveAsPresetDialog';
import { Button } from '../../ui/Button.tsx';
import { Dialog, DialogTrigger } from '../../ui/Dialog.tsx';
import Settings from './Settings.jsx';
import Settings from '../../Endpoints/OpenAI/Settings.jsx';
import { cn } from '~/utils/';
import store from '~/store';
@ -125,18 +124,11 @@ function OpenAIOptions() {
saveAsPreset={saveAsPreset}
switchToSimpleMode={switchToSimpleMode}
/>
<Dialog
<SaveAsPresetDialog
open={saveAsDialogShow}
onOpenChange={setSaveAsDialogShow}
>
<DialogTemplate
title="title"
description="desc"
main="tttt"
buttons={null}
selection={{}}
/>
</Dialog>
conversation={conversation}
/>
</>
);
}