feat: advanced style for openAI endpoint

This commit is contained in:
Wentao Lyu 2023-04-01 14:33:26 +08:00
parent 60be4f12b7
commit edaf7c3ad1
3 changed files with 276 additions and 80 deletions

View file

@ -4,14 +4,12 @@ import { HoverCardPortal, HoverCardContent } from '~/components/ui/HoverCard.tsx
const types = { const types = {
temp: 'Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.', 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.", max: "The max tokens to generate. The total length of input tokens and generated tokens is limited by the model's context length.",
'top-p': 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.',
'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.", 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." 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 }) { function OptionHover({ type, side }) {
// const options = {}; // const options = {};
// if (type === 'pres') { // if (type === 'pres') {
// options.sideOffset = 45; // options.sideOffset = 45;
@ -21,7 +19,7 @@ function OptionHover({ type, side }) {
<HoverCardPortal> <HoverCardPortal>
<HoverCardContent <HoverCardContent
side={side} side={side}
className="w-52 " className="w-80 "
// {...options} // {...options}
> >
<div className="space-y-2"> <div className="space-y-2">

View file

@ -7,12 +7,17 @@ import OptionHover from './OptionHover';
import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx'; import { HoverCard, HoverCardTrigger } from '~/components/ui/HoverCard.tsx';
import { cn } from '~/utils/'; import { cn } from '~/utils/';
const defaultTextProps = 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-none dark:bg-gray-700 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-none dark:focus:border-transparent dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0'; '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 = 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'; '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({ isOpen }) { function Settings({ isOpen }) {
// const endpointsConfig = useRecoilValue(store.endpointsConfig);
// const availableModels = endpointsConfig?.['openAI']?.['availableModels'] || [];
const [model, setModel] = useState('text-davinci-003');
const [chatGptLabel, setChatGptLabel] = useState(''); const [chatGptLabel, setChatGptLabel] = useState('');
const [promptPrefix, setPromptPrefix] = useState(''); const [promptPrefix, setPromptPrefix] = useState('');
const [temperature, setTemperature] = useState(1); const [temperature, setTemperature] = useState(1);
@ -20,32 +25,55 @@ function Settings({ isOpen }) {
const [topP, setTopP] = useState(1); const [topP, setTopP] = useState(1);
const [freqP, setFreqP] = useState(0); const [freqP, setFreqP] = useState(0);
const [presP, setPresP] = useState(0); const [presP, setPresP] = useState(0);
const textareaRef = useRef(null); // const textareaRef = useRef(null);
const inputRef = useRef(null); // const inputRef = useRef(null);
return ( return (
<> <>
<div className="grid gap-4 py-4 md:px-14 lg:px-20"> <div className="grid grid-cols-2 gap-6">
<div className="grid grid-cols-4 items-center gap-4"> <div className="col-span-1 flex flex-col items-center justify-start gap-6">
<div className="grid w-full items-center gap-2">
<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 <Label
htmlFor="chatGptLabel" htmlFor="chatGptLabel"
className="text-right text-sm" className="text-left text-sm font-medium"
> >
Custom Name Custom Name
</Label> </Label>
<Input <Input
id="chatGptLabel" id="chatGptLabel"
value={chatGptLabel} value={chatGptLabel}
ref={inputRef} // ref={inputRef}
onChange={e => setChatGptLabel(e.target.value)} onChange={e => setChatGptLabel(e.target.value)}
placeholder="Set a custom name for ChatGPT" placeholder="Set a custom name for ChatGPT"
className={cn(defaultTextProps, 'col-span-3 flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:ring-0 focus:ring-offset-0 focus:ring-opacity-0 focus:outline-none')} 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>
<div className="grid grid-cols-4 items-center gap-4"> <div className="grid w-full items-center gap-2">
<Label <Label
htmlFor="promptPrefix" htmlFor="promptPrefix"
className="text-right text-sm" className="text-left text-sm font-medium"
> >
Prompt Prefix Prompt Prefix
</Label> </Label>
@ -54,19 +82,193 @@ function Settings({ isOpen }) {
value={promptPrefix} value={promptPrefix}
onChange={e => setPromptPrefix(e.target.value)} onChange={e => setPromptPrefix(e.target.value)}
placeholder="Set custom instructions. Defaults to: 'You are ChatGPT, a large language model trained by OpenAI.'" placeholder="Set custom instructions. Defaults to: 'You are ChatGPT, a large language model trained by OpenAI.'"
className={cn(defaultTextProps, 'col-span-3 flex h-10 max-h-10 w-full resize-none px-3 py-2 ')} className={cn(
onFocus={() => { defaultTextProps,
textareaRef.current.classList.remove('max-h-10'); 'flex max-h-[300px] min-h-[100px] w-full resize-none px-3 py-2 '
textareaRef.current.classList.add('max-h-52'); )}
}} // onFocus={() => {
onBlur={() => { // textareaRef.current.classList.remove('max-h-10');
textareaRef.current.classList.remove('max-h-52'); // textareaRef.current.classList.add('max-h-52');
textareaRef.current.classList.add('max-h-10'); // }}
}} // onBlur={() => {
ref={textareaRef} // textareaRef.current.classList.remove('max-h-52');
// textareaRef.current.classList.add('max-h-10');
// }}
// ref={textareaRef}
/> />
</div> </div>
<div className="flex justify-around"> </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
</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)}
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)}
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
</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)}
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
</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)}
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
</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)}
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> <HoverCard>
<HoverCardTrigger className="group/temp mr-4 flex w-full items-center justify-end gap-4"> <HoverCardTrigger className="group/temp mr-4 flex w-full items-center justify-end gap-4">
<Label <Label
@ -109,14 +311,6 @@ function Settings({ isOpen }) {
</HoverCard> </HoverCard>
</div> </div>
<div className="grid grid-cols-2 items-center gap-5"> <div className="grid grid-cols-2 items-center gap-5">
<Slider
value={[temperature]}
onValueChange={value => setTemperature(value)}
max={2}
min={0}
step={0.01}
className="w-full"
/>
<Slider <Slider
value={[maxTokens]} value={[maxTokens]}
onValueChange={value => setMaxTokens(value)} onValueChange={value => setMaxTokens(value)}
@ -225,6 +419,7 @@ function Settings({ isOpen }) {
step={0.01} step={0.01}
className="w-full" className="w-full"
/> />
</div> */}
</div> </div>
</div> </div>
</> </>

View file

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Settings2 } from 'lucide-react'; import { Settings2 } from 'lucide-react';
import { useRecoilState } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import ModelSelect from './ModelSelect'; import ModelSelect from './ModelSelect';
import { Button } from '../../ui/Button.tsx'; import { Button } from '../../ui/Button.tsx';
import Settings from './Settings.jsx'; import Settings from './Settings.jsx';
@ -11,8 +11,30 @@ import store from '~/store';
function OpenAIOptions() { function OpenAIOptions() {
const [advancedMode, setAdvancedMode] = useState(false); const [advancedMode, setAdvancedMode] = useState(false);
const [conversation, setConversation] = useRecoilState(store.conversation) || {}; const [conversation, setConversation] = useRecoilState(store.conversation) || {};
const endpointsConfig = useRecoilValue(store.endpointsConfig);
const { endpoint, conversationId } = conversation; const { endpoint, conversationId } = conversation;
useEffect(() => {
const { endpoint, chatGptLabel, promptPrefix, temperature, top_p, presence_penalty } = conversation;
if (endpoint !== 'openAI') return;
const mustInAdvancedMode =
chatGptLabel !== null ||
promptPrefix !== null ||
temperature !== 0.8 ||
top_p !== 1 ||
presence_penalty !== 1;
if (mustInAdvancedMode && !advancedMode) setAdvancedMode(true);
}, [conversation, advancedMode]);
if (endpoint !== 'openAI') return null;
if (conversationId !== 'new') return null;
const { model } = conversation;
const availableModels = endpointsConfig?.['openAI']?.['availableModels'] || [];
const triggerAdvancedMode = () => setAdvancedMode(prev => !prev); const triggerAdvancedMode = () => setAdvancedMode(prev => !prev);
const switchToSimpleMode = () => { const switchToSimpleMode = () => {
@ -34,26 +56,6 @@ function OpenAIOptions() {
})); }));
}; };
useEffect(() => {
const { endpoint, chatGptLabel, promptPrefix, temperature, top_p, presence_penalty } = conversation;
if (endpoint !== 'openAI') return;
const mustInAdvancedMode =
chatGptLabel !== null ||
promptPrefix !== null ||
temperature !== 0.8 ||
top_p !== 1 ||
presence_penalty !== 1;
if (mustInAdvancedMode && !advancedMode) setAdvancedMode(true);
}, [conversation, advancedMode]);
if (endpoint !== 'openAI') return null;
if (conversationId !== 'new') return null;
const { model } = conversation;
const cardStyle = const cardStyle =
'shadow-md rounded-md min-w-[75px] font-normal bg-white border-black/10 border dark:bg-gray-700 text-black dark:text-white'; 'shadow-md rounded-md min-w-[75px] font-normal bg-white border-black/10 border dark:bg-gray-700 text-black dark:text-white';
@ -67,6 +69,7 @@ function OpenAIOptions() {
> >
<ModelSelect <ModelSelect
model={model} model={model}
availableModels={availableModels}
onChange={setModel} onChange={setModel}
type="button" type="button"
className={cn( className={cn(
@ -107,8 +110,8 @@ function OpenAIOptions() {
Switch to simple mode Switch to simple mode
</Button> </Button>
</div> </div>
<div className="h-[375px] p-5"> <div className="px-4 py-4">
<Settings isOpen={advancedMode}/> <Settings isOpen={advancedMode} />
</div> </div>
</div> </div>
</div> </div>