🔧 fix: Preset Dialog Styling and Values (#2657)

* style: preset dialog styling

* refactor: coerce number input for convo schema

* refactor: replace dynamic input number with static component
This commit is contained in:
Danny Avila 2024-05-10 03:05:45 -04:00 committed by GitHub
parent 98c96cd020
commit 2b37a44b8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 258 additions and 133 deletions

View file

@ -1,4 +1,5 @@
import { FileSources } from 'librechat-data-provider';
import type * as InputNumberPrimitive from 'rc-input-number';
import type { ColumnDef } from '@tanstack/react-table';
import type { SetterOrUpdater } from 'recoil';
import type {
@ -115,6 +116,8 @@ export type TSetExample = (
newValue: number | string | boolean | null,
) => void;
export type OnInputNumberChange = InputNumberPrimitive.InputNumberProps['onChange'];
export const defaultDebouncedDelay = 450;
export enum ESide {

View file

@ -49,9 +49,9 @@ const EditPresetDialog = ({
title={`${localize('com_ui_edit') + ' ' + localize('com_endpoint_preset')} - ${
preset?.title
}`}
className="h-full max-w-full overflow-y-auto pb-4 sm:w-[680px] sm:pb-0 md:h-[720px] md:w-[750px] md:overflow-y-hidden lg:w-[950px] xl:h-[720px]"
className="h-full max-w-full overflow-y-auto pb-4 sm:w-[680px] sm:pb-0 md:h-[720px] md:w-[750px] md:overflow-y-hidden md:overflow-y-hidden lg:w-[950px] xl:h-[720px]"
main={
<div className="flex w-full flex-col items-center gap-2 md:h-[530px]">
<div className="flex w-full flex-col items-center gap-2 md:h-[550px] md:overflow-y-auto">
<div className="grid w-full">
<div className="col-span-4 flex flex-col items-start justify-start gap-6 pb-4 md:flex-row">
<div className="flex w-full flex-col">
@ -126,6 +126,7 @@ const EditPresetDialog = ({
</DialogClose>
</div>
}
footerClassName="bg-white dark:bg-gray-700"
/>
</Dialog>
);

View file

@ -1,6 +1,6 @@
import React from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import type { TModelSelectProps } from '~/common';
import type { TModelSelectProps, OnInputNumberChange } from '~/common';
import {
Input,
Label,
@ -12,18 +12,34 @@ import {
HoverCardTrigger,
} from '~/components/ui';
import { cn, defaultTextProps, optionText, removeFocusOutlines } from '~/utils';
import { DynamicInputNumber } from '~/components/SidePanel/Parameters';
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
import { useLocalize, useDebouncedInput } from '~/hooks';
import OptionHover from './OptionHover';
import { useLocalize } from '~/hooks';
import { ESide } from '~/common';
export default function Settings({ conversation, setOption, models, readonly }: TModelSelectProps) {
const localize = useLocalize();
const {
model,
modelLabel,
promptPrefix,
temperature,
topP,
topK,
maxOutputTokens,
maxContextTokens,
resendFiles,
} = conversation ?? {};
const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput<number | null | undefined>(
{
setOption,
optionKey: 'maxContextTokens',
initialValue: maxContextTokens,
},
);
if (!conversation) {
return null;
}
const { model, modelLabel, promptPrefix, temperature, topP, topK, maxOutputTokens, resendFiles } =
conversation;
const setModel = setOption('model');
const setModelLabel = setOption('modelLabel');
@ -84,28 +100,40 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
</div>
<div className="col-span-5 flex flex-col items-center justify-start gap-6 px-3 sm:col-span-2">
<DynamicInputNumber
columnSpan={2}
settingKey="maxContextTokens"
setOption={setOption}
label="com_endpoint_context_tokens"
labelCode={true}
description="com_endpoint_context_info"
descriptionCode={true}
placeholder="com_nav_theme_system"
placeholderCode={true}
descriptionSide="right"
conversation={conversation}
readonly={readonly}
range={{
min: 10,
max: 2000000,
step: 1000,
}}
className="mt-1 w-full justify-between"
inputClassName="w-1/3"
showDefault={false}
/>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="mt-1 flex w-full justify-between">
<Label htmlFor="max-context-tokens" className="text-left text-sm font-medium">
{localize('com_endpoint_context_tokens')}{' '}
</Label>
<InputNumber
id="max-context-tokens"
stringMode={false}
disabled={readonly}
value={maxContextTokensValue as number}
onChange={setMaxContextTokens as OnInputNumberChange}
placeholder={localize('com_nav_theme_system')}
min={10}
max={2000000}
step={1000}
controls={false}
className={cn(
defaultTextProps,
cn(
optionText,
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
'w-1/3',
),
)}
/>
</div>
</HoverCardTrigger>
<OptionHoverAlt
description="com_endpoint_context_info"
langCode={true}
side={ESide.Left}
/>
</HoverCard>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">

View file

@ -1,7 +1,7 @@
import { useEffect } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import { EModelEndpoint, endpointSettings } from 'librechat-data-provider';
import type { TModelSelectProps } from '~/common';
import type { TModelSelectProps, OnInputNumberChange } from '~/common';
import {
Input,
Label,
@ -12,16 +12,24 @@ import {
HoverCardTrigger,
} from '~/components/ui';
import { cn, defaultTextProps, optionText, removeFocusOutlines } from '~/utils';
import { DynamicInputNumber } from '~/components/SidePanel/Parameters';
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
import { useLocalize, useDebouncedInput } from '~/hooks';
import OptionHover from './OptionHover';
import { useLocalize } from '~/hooks';
import { ESide } from '~/common';
export default function Settings({ conversation, setOption, models, readonly }: TModelSelectProps) {
const localize = useLocalize();
const google = endpointSettings[EModelEndpoint.google];
const { model, modelLabel, promptPrefix, temperature, topP, topK, maxOutputTokens } =
conversation ?? {};
const {
model,
modelLabel,
promptPrefix,
temperature,
topP,
topK,
maxContextTokens,
maxOutputTokens,
} = conversation ?? {};
const isGemini = model?.toLowerCase()?.includes('gemini');
@ -42,6 +50,14 @@ export default function Settings({ conversation, setOption, models, readonly }:
[model],
);
const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput<number | null | undefined>(
{
setOption,
optionKey: 'maxContextTokens',
initialValue: maxContextTokens,
},
);
if (!conversation) {
return null;
}
@ -104,28 +120,40 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
</div>
<div className="col-span-5 flex flex-col items-center justify-start gap-6 px-3 sm:col-span-2">
<DynamicInputNumber
columnSpan={2}
settingKey="maxContextTokens"
setOption={setOption}
label="com_endpoint_context_tokens"
labelCode={true}
description="com_endpoint_context_info"
descriptionCode={true}
placeholder="com_nav_theme_system"
placeholderCode={true}
descriptionSide="right"
conversation={conversation}
readonly={readonly}
range={{
min: 10,
max: 2000000,
step: 1000,
}}
className="mt-1 w-full justify-between"
inputClassName="w-1/3"
showDefault={false}
/>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="mt-1 flex w-full justify-between">
<Label htmlFor="max-context-tokens" className="text-left text-sm font-medium">
{localize('com_endpoint_context_tokens')}{' '}
</Label>
<InputNumber
id="max-context-tokens"
stringMode={false}
disabled={readonly}
value={maxContextTokensValue as number}
onChange={setMaxContextTokens as OnInputNumberChange}
placeholder={localize('com_nav_theme_system')}
min={10}
max={2000000}
step={1000}
controls={false}
className={cn(
defaultTextProps,
cn(
optionText,
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
'w-1/3',
),
)}
/>
</div>
</HoverCardTrigger>
<OptionHoverAlt
description="com_endpoint_context_info"
langCode={true}
side={ESide.Left}
/>
</HoverCard>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">

View file

@ -1,12 +1,12 @@
import { useMemo } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import * as InputNumberPrimitive from 'rc-input-number';
import {
EModelEndpoint,
ImageDetail,
imageDetailNumeric,
imageDetailValue,
} from 'librechat-data-provider';
import type { TModelSelectProps, OnInputNumberChange } from '~/common';
import {
Input,
Label,
@ -18,14 +18,12 @@ import {
HoverCardTrigger,
} from '~/components/ui';
import { cn, defaultTextProps, optionText, removeFocusOutlines } from '~/utils';
import { DynamicTags, DynamicInputNumber } from '~/components/SidePanel/Parameters';
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
import { DynamicTags } from '~/components/SidePanel/Parameters';
import { useLocalize, useDebouncedInput } from '~/hooks';
import type { TModelSelectProps } from '~/common';
import OptionHover from './OptionHover';
import { ESide } from '~/common';
type OnInputNumberChange = InputNumberPrimitive.InputNumberProps['onChange'];
export default function Settings({ conversation, setOption, models, readonly }: TModelSelectProps) {
const localize = useLocalize();
const {
@ -41,6 +39,8 @@ export default function Settings({ conversation, setOption, models, readonly }:
presence_penalty: presP,
resendFiles,
imageDetail,
maxContextTokens,
max_tokens,
} = conversation ?? {};
const [setChatGptLabel, chatGptLabelValue] = useDebouncedInput<string | null | undefined>({
@ -73,6 +73,18 @@ export default function Settings({ conversation, setOption, models, readonly }:
optionKey: 'presence_penalty',
initialValue: presP,
});
const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput<number | null | undefined>(
{
setOption,
optionKey: 'maxContextTokens',
initialValue: maxContextTokens,
},
);
const [setMaxOutputTokens, maxOutputTokensValue] = useDebouncedInput<number | null | undefined>({
setOption,
optionKey: 'max_tokens',
initialValue: max_tokens,
});
const optionEndpoint = useMemo(() => endpointType ?? endpoint, [endpoint, endpointType]);
const isOpenAI = useMemo(
@ -154,50 +166,74 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
</div>
<div className="col-span-5 flex flex-col items-center justify-start gap-6 px-3 sm:col-span-2">
<DynamicInputNumber
columnSpan={2}
settingKey="maxContextTokens"
setOption={setOption}
label="com_endpoint_context_tokens"
labelCode={true}
description="com_endpoint_context_info"
descriptionCode={true}
placeholder="com_nav_theme_system"
placeholderCode={true}
descriptionSide="right"
conversation={conversation}
readonly={readonly}
range={{
min: 10,
max: 2000000,
step: 1000,
}}
className="mt-1 w-full justify-between"
inputClassName="w-1/3"
showDefault={false}
/>
<DynamicInputNumber
columnSpan={2}
settingKey="max_tokens"
setOption={setOption}
label="com_endpoint_max_output_tokens"
labelCode={true}
description="com_endpoint_openai_max_tokens"
descriptionCode={true}
placeholder="com_nav_theme_system"
placeholderCode={true}
descriptionSide="top"
conversation={conversation}
readonly={readonly}
range={{
min: 10,
max: 2000000,
step: 1000,
}}
className="mt-1 w-full justify-between"
inputClassName="w-1/3"
showDefault={false}
/>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="mt-1 flex w-full justify-between">
<Label htmlFor="max-context-tokens" className="text-left text-sm font-medium">
{localize('com_endpoint_context_tokens')}{' '}
</Label>
<InputNumber
id="max-context-tokens"
stringMode={false}
disabled={readonly}
value={maxContextTokensValue as number}
onChange={setMaxContextTokens as OnInputNumberChange}
placeholder={localize('com_nav_theme_system')}
min={10}
max={2000000}
step={1000}
controls={false}
className={cn(
defaultTextProps,
cn(
optionText,
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
'w-1/3',
),
)}
/>
</div>
</HoverCardTrigger>
<OptionHoverAlt
description="com_endpoint_context_info"
langCode={true}
side={ESide.Left}
/>
</HoverCard>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="mt-1 flex w-full justify-between">
<Label htmlFor="max-output-tokens" className="text-left text-sm font-medium">
{localize('com_endpoint_max_output_tokens')}{' '}
</Label>
<InputNumber
id="max-output-tokens"
stringMode={false}
disabled={readonly}
value={maxOutputTokensValue as number}
onChange={setMaxOutputTokens as OnInputNumberChange}
placeholder={localize('com_nav_theme_system')}
min={10}
max={2000000}
step={1000}
controls={false}
className={cn(
defaultTextProps,
cn(
optionText,
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
'w-1/3',
),
)}
/>
</div>
</HoverCardTrigger>
<OptionHoverAlt
description="com_endpoint_openai_max_tokens"
langCode={true}
side={ESide.Left}
/>
</HoverCard>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">

View file

@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil';
import TextareaAutosize from 'react-textarea-autosize';
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
import type { TPlugin } from 'librechat-data-provider';
import type { TModelSelectProps } from '~/common';
import type { TModelSelectProps, OnInputNumberChange } from '~/common';
import {
Input,
Label,
@ -22,7 +22,7 @@ import {
processPlugins,
selectPlugins,
} from '~/utils';
import { DynamicInputNumber } from '~/components/SidePanel/Parameters';
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
import { useLocalize, useDebouncedInput } from '~/hooks';
import OptionHover from './OptionHover';
import { ESide } from '~/common';
@ -69,6 +69,7 @@ export default function Settings({
top_p: topP,
frequency_penalty: freqP,
presence_penalty: presP,
maxContextTokens,
} = conversation ?? {};
const [setChatGptLabel, chatGptLabelValue] = useDebouncedInput<string | null | undefined>({
@ -101,6 +102,13 @@ export default function Settings({
optionKey: 'presence_penalty',
initialValue: presP,
});
const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput<number | null | undefined>(
{
setOption,
optionKey: 'maxContextTokens',
initialValue: maxContextTokens,
},
);
const setModel = setOption('model');
@ -177,28 +185,40 @@ export default function Settings({
containerClassName="flex w-full resize-none border border-transparent"
labelClassName="dark:text-white"
/>
<DynamicInputNumber
columnSpan={2}
settingKey="maxContextTokens"
setOption={setOption}
label="com_endpoint_context_tokens"
labelCode={true}
description="com_endpoint_context_info"
descriptionCode={true}
placeholder="com_nav_theme_system"
placeholderCode={true}
descriptionSide="right"
conversation={conversation}
readonly={readonly}
range={{
min: 10,
max: 2000000,
step: 1000,
}}
className="mt-1 w-full justify-between"
inputClassName="w-1/3"
showDefault={false}
/>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="mt-1 flex w-full justify-between">
<Label htmlFor="max-context-tokens" className="text-left text-sm font-medium">
{localize('com_endpoint_context_tokens')}{' '}
</Label>
<InputNumber
id="max-context-tokens"
stringMode={false}
disabled={readonly}
value={maxContextTokensValue as number}
onChange={setMaxContextTokens as OnInputNumberChange}
placeholder={localize('com_nav_theme_system')}
min={10}
max={2000000}
step={1000}
controls={false}
className={cn(
defaultTextProps,
cn(
optionText,
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
'w-1/3',
),
)}
/>
</div>
</HoverCardTrigger>
<OptionHoverAlt
description="com_endpoint_context_info"
langCode={true}
side={ESide.Left}
/>
</HoverCard>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
<div className="flex justify-between">

View file

@ -25,6 +25,7 @@ type DialogTemplateProps = {
selection?: SelectionProps;
className?: string;
headerClassName?: string;
footerClassName?: string;
showCloseButton?: boolean;
showCancelButton?: boolean;
};
@ -40,6 +41,7 @@ const DialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivE
selection,
className,
headerClassName,
footerClassName,
showCloseButton,
showCancelButton = true,
} = props;
@ -66,7 +68,7 @@ const DialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivE
)}
</DialogHeader>
<div className="px-6">{main ? main : null}</div>
<DialogFooter>
<DialogFooter className={footerClassName}>
<div>{leftButtons ? leftButtons : null}</div>
<div className="flex h-auto gap-3">
{showCancelButton && (

View file

@ -1,6 +1,6 @@
{
"name": "librechat-data-provider",
"version": "0.6.1",
"version": "0.6.2",
"description": "data services for librechat apps",
"main": "dist/index.js",
"module": "dist/index.es.js",

View file

@ -280,6 +280,13 @@ export type TMessage = z.input<typeof tMessageSchema> & {
files?: Partial<TFile>[];
};
export const coerceNumber = z.union([z.number(), z.string()]).transform((val) => {
if (typeof val === 'string') {
return val.trim() === '' ? undefined : parseFloat(val);
}
return val;
});
export const tConversationSchema = z.object({
conversationId: z.string().nullable(),
title: z.string().nullable().or(z.literal('New Chat')).default('New Chat'),
@ -315,8 +322,8 @@ export const tConversationSchema = z.object({
maxOutputTokens: z.number().optional(),
agentOptions: tAgentOptionsSchema.nullable().optional(),
file_ids: z.array(z.string()).optional(),
maxContextTokens: z.number().optional(),
max_tokens: z.number().optional(),
maxContextTokens: coerceNumber.optional(),
max_tokens: coerceNumber.optional(),
/** @deprecated */
resendImages: z.boolean().optional(),
/* vision */