diff --git a/client/src/common/types.ts b/client/src/common/types.ts index d8f408ab87..e574e90d89 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -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 { diff --git a/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx b/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx index 9022b7e202..6fd0e0a310 100644 --- a/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx +++ b/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx @@ -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={ -
+
@@ -126,6 +126,7 @@ const EditPresetDialog = ({
} + footerClassName="bg-white dark:bg-gray-700" /> ); diff --git a/client/src/components/Endpoints/Settings/Anthropic.tsx b/client/src/components/Endpoints/Settings/Anthropic.tsx index 92f511d2ce..5f7a8ef3ef 100644 --- a/client/src/components/Endpoints/Settings/Anthropic.tsx +++ b/client/src/components/Endpoints/Settings/Anthropic.tsx @@ -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( + { + 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 }:
- + + +
+ + +
+
+ +
diff --git a/client/src/components/Endpoints/Settings/Google.tsx b/client/src/components/Endpoints/Settings/Google.tsx index 98d7d007d1..f16fb567dd 100644 --- a/client/src/components/Endpoints/Settings/Google.tsx +++ b/client/src/components/Endpoints/Settings/Google.tsx @@ -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( + { + setOption, + optionKey: 'maxContextTokens', + initialValue: maxContextTokens, + }, + ); + if (!conversation) { return null; } @@ -104,28 +120,40 @@ export default function Settings({ conversation, setOption, models, readonly }:
- + + +
+ + +
+
+ +
diff --git a/client/src/components/Endpoints/Settings/OpenAI.tsx b/client/src/components/Endpoints/Settings/OpenAI.tsx index 8563cb359f..3ca19fc11f 100644 --- a/client/src/components/Endpoints/Settings/OpenAI.tsx +++ b/client/src/components/Endpoints/Settings/OpenAI.tsx @@ -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({ @@ -73,6 +73,18 @@ export default function Settings({ conversation, setOption, models, readonly }: optionKey: 'presence_penalty', initialValue: presP, }); + const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput( + { + setOption, + optionKey: 'maxContextTokens', + initialValue: maxContextTokens, + }, + ); + const [setMaxOutputTokens, maxOutputTokensValue] = useDebouncedInput({ + 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 }:
- - + + +
+ + +
+
+ +
+ + +
+ + +
+
+ +
diff --git a/client/src/components/Endpoints/Settings/Plugins.tsx b/client/src/components/Endpoints/Settings/Plugins.tsx index 9085810e1d..5ad11bfd14 100644 --- a/client/src/components/Endpoints/Settings/Plugins.tsx +++ b/client/src/components/Endpoints/Settings/Plugins.tsx @@ -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({ @@ -101,6 +102,13 @@ export default function Settings({ optionKey: 'presence_penalty', initialValue: presP, }); + const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput( + { + 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" /> - + + +
+ + +
+
+ +
diff --git a/client/src/components/ui/DialogTemplate.tsx b/client/src/components/ui/DialogTemplate.tsx index 58c42e14ed..cf3d50d3a5 100644 --- a/client/src/components/ui/DialogTemplate.tsx +++ b/client/src/components/ui/DialogTemplate.tsx @@ -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
{main ? main : null}
- +
{leftButtons ? leftButtons : null}
{showCancelButton && ( diff --git a/packages/data-provider/package.json b/packages/data-provider/package.json index 661f2a7888..610fc61d5a 100644 --- a/packages/data-provider/package.json +++ b/packages/data-provider/package.json @@ -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", diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 6f13098984..e0f2070dd8 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -280,6 +280,13 @@ export type TMessage = z.input & { files?: Partial[]; }; +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 */