From 8dc5b320bc314f7a3fc4ce57403d0caf8de47aa6 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 17 Sep 2024 22:25:54 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=8A=20refactor:=20use=20Parameters=20f?= =?UTF-8?q?rom=20Side=20Panel=20for=20OpenAI,=20Anthropic,=20and=20Custom?= =?UTF-8?q?=20endpoints=20(#4092)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: openai parameters * refactor: anthropic/bedrock params, add preset params for openai, and add azure params * refactor: use 'compact' schemas for anthropic/openai * refactor: ensure custom endpoints are properly recognized as valid param endpoints * refactor: update paramEndpoints check in BaseClient.js * chore: optimize logging by omitting modelsConfig * refactor: update label casing in baseDefinitions combobox items * fix: remove 'stop' model options when using o1 series models * refactor(AnthropicClient): remove default `stop` value * refactor: reset params on parameters change * refactor: remove unused default parameter value map introduced in prior commit * fix: 'min' typo for 'max' value * refactor: preset settings * refactor: replace dropdown for image detail with slider; remove `preventDelayedUpdate` condition from DynamicSlider * fix: localizations for freq./pres. penalty * Refactor maxOutputTokens to use coerceNumber in tConversationSchema * refactor(AnthropicClient): use `getModelMaxOutputTokens` --- api/app/clients/AnthropicClient.js | 22 +- api/app/clients/BaseClient.js | 7 +- api/app/clients/OpenAIClient.js | 1 + api/server/controllers/AskController.js | 7 +- api/server/controllers/EditController.js | 1 + api/utils/tokens.js | 9 + .../components/Chat/Input/HeaderOptions.tsx | 10 +- .../Endpoints/Settings/Anthropic.tsx | 400 ++------------ .../components/Endpoints/Settings/Bedrock.tsx | 10 +- .../components/Endpoints/Settings/OpenAI.tsx | 513 ++---------------- .../SidePanel/Parameters/DynamicSlider.tsx | 1 - .../components/SidePanel/Parameters/Panel.tsx | 86 ++- .../SidePanel/Parameters/components.tsx | 5 +- .../SidePanel/Parameters/settings.ts | 358 +++++++++--- client/src/components/SidePanel/SidePanel.tsx | 7 +- client/src/hooks/Nav/useSideNavLinks.ts | 11 +- client/src/hooks/useNewConvo.ts | 11 +- packages/data-provider/src/config.ts | 5 - packages/data-provider/src/parsers.ts | 14 +- packages/data-provider/src/schemas.ts | 200 +------ 20 files changed, 575 insertions(+), 1103 deletions(-) diff --git a/api/app/clients/AnthropicClient.js b/api/app/clients/AnthropicClient.js index 9e1e503c2e..486af95c3f 100644 --- a/api/app/clients/AnthropicClient.js +++ b/api/app/clients/AnthropicClient.js @@ -17,8 +17,8 @@ const { parseParamFromPrompt, createContextHandlers, } = require('./prompts'); +const { getModelMaxTokens, getModelMaxOutputTokens, matchModelName } = require('~/utils'); const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens'); -const { getModelMaxTokens, matchModelName } = require('~/utils'); const { sleep } = require('~/server/utils'); const BaseClient = require('./BaseClient'); const { logger } = require('~/config'); @@ -120,7 +120,14 @@ class AnthropicClient extends BaseClient { this.options.maxContextTokens ?? getModelMaxTokens(this.modelOptions.model, EModelEndpoint.anthropic) ?? 100000; - this.maxResponseTokens = this.modelOptions.maxOutputTokens || 1500; + this.maxResponseTokens = + this.modelOptions.maxOutputTokens ?? + getModelMaxOutputTokens( + this.modelOptions.model, + this.options.endpointType ?? this.options.endpoint, + this.options.endpointTokenConfig, + ) ?? + 1500; this.maxPromptTokens = this.options.maxPromptTokens || this.maxContextTokens - this.maxResponseTokens; @@ -144,17 +151,6 @@ class AnthropicClient extends BaseClient { this.endToken = ''; this.gptEncoder = this.constructor.getTokenizer('cl100k_base'); - if (!this.modelOptions.stop) { - const stopTokens = [this.startToken]; - if (this.endToken && this.endToken !== this.startToken) { - stopTokens.push(this.endToken); - } - stopTokens.push(`${this.userLabel}`); - stopTokens.push('<|diff_marker|>'); - - this.modelOptions.stop = stopTokens; - } - return this; } diff --git a/api/app/clients/BaseClient.js b/api/app/clients/BaseClient.js index 403aa6b006..51d75d063b 100644 --- a/api/app/clients/BaseClient.js +++ b/api/app/clients/BaseClient.js @@ -3,7 +3,7 @@ const fetch = require('node-fetch'); const { supportsBalanceCheck, isAgentsEndpoint, - paramEndpoints, + isParamEndpoint, ErrorTypes, Constants, CacheKeys, @@ -588,7 +588,10 @@ class BaseClient { if (typeof completion === 'string') { responseMessage.text = addSpaceIfNeeded(generation) + completion; - } else if (Array.isArray(completion) && paramEndpoints.has(this.options.endpoint)) { + } else if ( + Array.isArray(completion) && + isParamEndpoint(this.options.endpoint, this.options.endpointType) + ) { responseMessage.text = ''; responseMessage.content = completion; } else if (Array.isArray(completion)) { diff --git a/api/app/clients/OpenAIClient.js b/api/app/clients/OpenAIClient.js index 639d7045e3..537d91191f 100644 --- a/api/app/clients/OpenAIClient.js +++ b/api/app/clients/OpenAIClient.js @@ -1295,6 +1295,7 @@ ${convo} if (modelOptions.stream && /\bo1\b/i.test(modelOptions.model)) { delete modelOptions.stream; + delete modelOptions.stop; } if (modelOptions.stream) { diff --git a/api/server/controllers/AskController.js b/api/server/controllers/AskController.js index dd5c8d6570..d2d774b009 100644 --- a/api/server/controllers/AskController.js +++ b/api/server/controllers/AskController.js @@ -16,7 +16,12 @@ const AskController = async (req, res, next, initializeClient, addTitle) => { overrideParentMessageId = null, } = req.body; - logger.debug('[AskController]', { text, conversationId, ...endpointOption }); + logger.debug('[AskController]', { + text, + conversationId, + ...endpointOption, + modelsConfig: endpointOption.modelsConfig ? 'exists' : '', + }); let userMessage; let userMessagePromise; diff --git a/api/server/controllers/EditController.js b/api/server/controllers/EditController.js index b3b94fcebb..28fe2c4fea 100644 --- a/api/server/controllers/EditController.js +++ b/api/server/controllers/EditController.js @@ -25,6 +25,7 @@ const EditController = async (req, res, next, initializeClient) => { isContinued, conversationId, ...endpointOption, + modelsConfig: endpointOption.modelsConfig ? 'exists' : '', }); let userMessage; diff --git a/api/utils/tokens.js b/api/utils/tokens.js index 2e74d4e2c0..8c2a8a6cc1 100644 --- a/api/utils/tokens.js +++ b/api/utils/tokens.js @@ -123,7 +123,16 @@ const modelMaxOutputs = { system_default: 1024, }; +const anthropicMaxOutputs = { + 'claude-3-haiku': 4096, + 'claude-3-sonnet': 4096, + 'claude-3-opus': 4096, + 'claude-3.5-sonnet': 8192, + 'claude-3-5-sonnet': 8192, +}; + const maxOutputTokensMap = { + [EModelEndpoint.anthropic]: anthropicMaxOutputs, [EModelEndpoint.azureOpenAI]: modelMaxOutputs, [EModelEndpoint.openAI]: modelMaxOutputs, [EModelEndpoint.custom]: modelMaxOutputs, diff --git a/client/src/components/Chat/Input/HeaderOptions.tsx b/client/src/components/Chat/Input/HeaderOptions.tsx index e7baa5d065..1696dd63e3 100644 --- a/client/src/components/Chat/Input/HeaderOptions.tsx +++ b/client/src/components/Chat/Input/HeaderOptions.tsx @@ -2,7 +2,7 @@ import { useRecoilState } from 'recoil'; import { Settings2 } from 'lucide-react'; import { Root, Anchor } from '@radix-ui/react-popover'; import { useState, useEffect, useMemo } from 'react'; -import { tPresetUpdateSchema, EModelEndpoint, paramEndpoints } from 'librechat-data-provider'; +import { tPresetUpdateSchema, EModelEndpoint, isParamEndpoint } from 'librechat-data-provider'; import type { TPreset, TInterfaceConfig } from 'librechat-data-provider'; import { EndpointSettings, SaveAsPresetDialog, AlternativeSettings } from '~/components/Endpoints'; import { PluginStoreDialog, TooltipAnchor } from '~/components'; @@ -28,7 +28,7 @@ export default function HeaderOptions({ useChatContext(); const { setOption } = useSetIndexOptions(); - const { endpoint, conversationId, jailbreak = false } = conversation ?? {}; + const { endpoint, endpointType, conversationId, jailbreak = false } = conversation ?? {}; const altConditions: { [key: string]: boolean } = { bingAI: !!(latestMessage && jailbreak && endpoint === 'bingAI'), @@ -64,6 +64,8 @@ export default function HeaderOptions({ const triggerAdvancedMode = altConditions[endpoint] ? altSettings[endpoint] : () => setShowPopover((prev) => !prev); + + const paramEndpoint = isParamEndpoint(endpoint, endpointType); return ( )} - {interfaceConfig?.parameters === true && !paramEndpoints.has(endpoint) && ( + {interfaceConfig?.parameters === true && paramEndpoint === false && ( ( - { - setOption, - optionKey: 'maxContextTokens', - initialValue: maxContextTokens, - }, - ); - if (!conversation) { +export default function AnthropicSettings({ + conversation, + setOption, + models, + readonly, +}: TModelSelectProps) { + const parameters = useMemo(() => { + const [combinedKey, endpointKey] = getSettingsKeys( + conversation?.endpointType ?? conversation?.endpoint ?? '', + conversation?.model ?? '', + ); + return presetSettings[combinedKey] ?? presetSettings[endpointKey]; + }, [conversation]); + + if (!parameters) { return null; } - const setModelLabel = setOption('modelLabel'); - const setPromptPrefix = setOption('promptPrefix'); - const setTemperature = setOption('temperature'); - const setTopP = setOption('topP'); - const setTopK = setOption('topK'); - const setResendFiles = setOption('resendFiles'); - const setPromptCache = setOption('promptCache'); - - const setModel = (newModel: string) => { - const modelSetter = setOption('model'); - const maxOutputSetter = setOption('maxOutputTokens'); - if (maxOutputTokens) { - maxOutputSetter(anthropicSettings.maxOutputTokens.set(maxOutputTokens, newModel)); + const renderComponent = (setting: SettingDefinition | undefined) => { + if (!setting) { + return null; } - modelSetter(newModel); - }; - - const setMaxOutputTokens = (value: number) => { - const setter = setOption('maxOutputTokens'); - if (model) { - setter(anthropicSettings.maxOutputTokens.set(value, model)); - } else { - setter(value); + const Component = componentMapping[setting.component]; + if (!Component) { + return null; } + const { key, default: defaultValue, ...rest } = setting; + + const props = { + key, + settingKey: key, + defaultValue, + ...rest, + readonly, + setOption, + conversation, + }; + + if (key === 'model') { + return ; + } + + return ; }; return ( -
-
-
- +
+
+
+ {parameters.col1.map(renderComponent)}
-
- - setModelLabel(e.target.value ?? null)} - placeholder={localize('com_endpoint_anthropic_custom_name_placeholder')} - className={cn( - defaultTextProps, - 'flex h-10 max-h-10 w-full resize-none px-3 py-2', - removeFocusOutlines, - )} - /> +
+ {parameters.col2.map(renderComponent)}
-
- - setPromptPrefix(e.target.value ?? null)} - placeholder={localize('com_endpoint_prompt_prefix_placeholder')} - className={cn( - defaultTextProps, - 'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2 ', - )} - /> -
-
-
- - -
- - -
-
- -
- - -
- - setTemperature(Number(value))} - max={anthropicSettings.temperature.max} - min={0} - step={0.01} - 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', - ), - )} - /> -
- setTemperature(value[0])} - doubleClickHandler={() => setTemperature(anthropicSettings.temperature.default)} - max={anthropicSettings.temperature.max} - min={0} - step={0.01} - className="flex h-4 w-full" - /> -
- -
- - -
- - setTopP(Number(value))} - max={anthropicSettings.topP.max} - min={0} - step={0.01} - 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', - ), - )} - /> -
- setTopP(value[0])} - doubleClickHandler={() => setTopP(anthropicSettings.topP.default)} - max={anthropicSettings.topP.max} - min={0} - step={0.01} - className="flex h-4 w-full" - /> -
- -
- - - -
- - setTopK(Number(value))} - max={anthropicSettings.topK.max} - min={1} - step={0.01} - 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', - ), - )} - /> -
- setTopK(value[0])} - doubleClickHandler={() => setTopK(anthropicSettings.topK.default)} - max={anthropicSettings.topK.max} - min={1} - step={0.01} - className="flex h-4 w-full" - /> -
- -
- - -
- - setMaxOutputTokens(Number(value))} - max={anthropicSettings.maxOutputTokens.max} - min={1} - step={1} - 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', - ), - )} - /> -
- setMaxOutputTokens(value[0])} - doubleClickHandler={() => - setMaxOutputTokens(anthropicSettings.maxOutputTokens.default) - } - max={anthropicSettings.maxOutputTokens.max} - min={1} - step={1} - className="flex h-4 w-full" - /> -
- -
- - -
- - setResendFiles(checked)} - disabled={readonly} - className="flex" - /> - -
-
-
- - -
- - setPromptCache(checked)} - disabled={readonly} - className="flex" - /> - -
-
-
); diff --git a/client/src/components/Endpoints/Settings/Bedrock.tsx b/client/src/components/Endpoints/Settings/Bedrock.tsx index 1e88bf326a..1f1634ea61 100644 --- a/client/src/components/Endpoints/Settings/Bedrock.tsx +++ b/client/src/components/Endpoints/Settings/Bedrock.tsx @@ -13,7 +13,7 @@ export default function BedrockSettings({ }: TModelSelectProps) { const parameters = useMemo(() => { const [combinedKey, endpointKey] = getSettingsKeys( - conversation?.endpoint ?? '', + conversation?.endpointType ?? conversation?.endpoint ?? '', conversation?.model ?? '', ); return presetSettings[combinedKey] ?? presetSettings[endpointKey]; @@ -23,8 +23,14 @@ export default function BedrockSettings({ return null; } - const renderComponent = (setting: SettingDefinition) => { + const renderComponent = (setting: SettingDefinition | undefined) => { + if (!setting) { + return null; + } const Component = componentMapping[setting.component]; + if (!Component) { + return null; + } const { key, default: defaultValue, ...rest } = setting; const props = { diff --git a/client/src/components/Endpoints/Settings/OpenAI.tsx b/client/src/components/Endpoints/Settings/OpenAI.tsx index 5062763583..eaa269e703 100644 --- a/client/src/components/Endpoints/Settings/OpenAI.tsx +++ b/client/src/components/Endpoints/Settings/OpenAI.tsx @@ -1,474 +1,63 @@ import { useMemo } from 'react'; -import TextareaAutosize from 'react-textarea-autosize'; -import { - openAISettings, - EModelEndpoint, - imageDetailValue, - imageDetailNumeric, -} from 'librechat-data-provider'; -import type { TModelSelectProps, OnInputNumberChange } from '~/common'; -import { - Input, - Label, - Switch, - Slider, - HoverCard, - InputNumber, - SelectDropDown, - HoverCardTrigger, -} from '~/components/ui'; -import { cn, defaultTextProps, optionText, removeFocusOutlines, removeFocusRings } from '~/utils'; -import { OptionHoverAlt, DynamicTags } from '~/components/SidePanel/Parameters'; -import { useLocalize, useDebouncedInput } from '~/hooks'; -import OptionHover from './OptionHover'; -import { ESide } from '~/common'; +import { getSettingsKeys } from 'librechat-data-provider'; +import type { SettingDefinition, DynamicSettingProps } from 'librechat-data-provider'; +import type { TModelSelectProps } from '~/common'; +import { componentMapping } from '~/components/SidePanel/Parameters/components'; +import { presetSettings } from '~/components/SidePanel/Parameters/settings'; -export default function Settings({ conversation, setOption, models, readonly }: TModelSelectProps) { - const localize = useLocalize(); - const { - endpoint, - endpointType, - model, - modelLabel, - chatGptLabel, - promptPrefix, - temperature, - top_p: topP, - frequency_penalty: freqP, - presence_penalty: presP, - resendFiles, - imageDetail, - maxContextTokens, - max_tokens, - } = conversation ?? {}; +export default function OpenAISettings({ + conversation, + setOption, + models, + readonly, +}: TModelSelectProps) { + const parameters = useMemo(() => { + const [combinedKey, endpointKey] = getSettingsKeys( + conversation?.endpointType ?? conversation?.endpoint ?? '', + conversation?.model ?? '', + ); + return presetSettings[combinedKey] ?? presetSettings[endpointKey]; + }, [conversation]); - const [setChatGptLabel, chatGptLabelValue] = useDebouncedInput({ - setOption, - optionKey: 'chatGptLabel', - initialValue: modelLabel ?? chatGptLabel, - }); - const [setPromptPrefix, promptPrefixValue] = useDebouncedInput({ - setOption, - optionKey: 'promptPrefix', - initialValue: promptPrefix, - }); - const [setTemperature, temperatureValue] = useDebouncedInput({ - setOption, - optionKey: 'temperature', - initialValue: temperature, - }); - const [setTopP, topPValue] = useDebouncedInput({ - setOption, - optionKey: 'top_p', - initialValue: topP, - }); - const [setFreqP, freqPValue] = useDebouncedInput({ - setOption, - optionKey: 'frequency_penalty', - initialValue: freqP, - }); - const [setPresP, presPValue] = useDebouncedInput({ - setOption, - 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( - () => optionEndpoint === EModelEndpoint.openAI || optionEndpoint === EModelEndpoint.azureOpenAI, - [optionEndpoint], - ); - - if (!conversation) { + if (!parameters) { return null; } - const setModel = setOption('model'); - const setResendFiles = setOption('resendFiles'); - const setImageDetail = setOption('imageDetail'); + const renderComponent = (setting: SettingDefinition | undefined) => { + if (!setting) { + return null; + } + const Component = componentMapping[setting.component]; + if (!Component) { + return null; + } + const { key, default: defaultValue, ...rest } = setting; + + const props = { + key, + settingKey: key, + defaultValue, + ...rest, + readonly, + setOption, + conversation, + }; + + if (key === 'model') { + return ; + } + + return ; + }; return ( -
-
-
- +
+
+
+ {parameters.col1.map(renderComponent)}
-
- - -
-
- - -
-
- -
-
-
- - -
- - -
-
- -
- - -
- - -
-
- -
- - -
- - -
- setTemperature(value[0])} - doubleClickHandler={() => setTemperature(openAISettings.temperature.default)} - max={openAISettings.temperature.max} - min={openAISettings.temperature.min} - step={openAISettings.temperature.step} - className="flex h-4 w-full" - /> -
- -
- - -
- - setTopP(Number(value))} - max={openAISettings.top_p.max} - min={openAISettings.top_p.min} - step={openAISettings.top_p.step} - 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', - ), - )} - /> -
- setTopP(value[0])} - doubleClickHandler={() => setTopP(openAISettings.top_p.default)} - max={openAISettings.top_p.max} - min={openAISettings.top_p.min} - step={openAISettings.top_p.step} - className="flex h-4 w-full" - /> -
- -
- - - -
- - setFreqP(Number(value))} - max={openAISettings.frequency_penalty.max} - min={openAISettings.frequency_penalty.min} - step={openAISettings.frequency_penalty.step} - 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', - ), - )} - /> -
- setFreqP(value[0])} - doubleClickHandler={() => setFreqP(openAISettings.frequency_penalty.default)} - max={openAISettings.frequency_penalty.max} - min={openAISettings.frequency_penalty.min} - step={openAISettings.frequency_penalty.step} - className="flex h-4 w-full" - /> -
- -
- - - -
- - setPresP(Number(value))} - max={openAISettings.presence_penalty.max} - min={openAISettings.presence_penalty.min} - step={openAISettings.presence_penalty.step} - 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', - ), - )} - /> -
- setPresP(value[0])} - doubleClickHandler={() => setPresP(openAISettings.presence_penalty.default)} - max={openAISettings.presence_penalty.max} - min={openAISettings.presence_penalty.min} - step={openAISettings.presence_penalty.step} - className="flex h-4 w-full" - /> -
- -
-
-
- - - -
-
- - - setResendFiles(checked)} - disabled={readonly} - className="flex" - /> - - - - - - setImageDetail(imageDetailValue[value[0]])} - doubleClickHandler={() => setImageDetail(openAISettings.imageDetail.default)} - max={openAISettings.imageDetail.max} - min={openAISettings.imageDetail.min} - step={openAISettings.imageDetail.step} - /> - - - -
+
+ {parameters.col2.map(renderComponent)}
diff --git a/client/src/components/SidePanel/Parameters/DynamicSlider.tsx b/client/src/components/SidePanel/Parameters/DynamicSlider.tsx index 6df3789cc5..2397ec7d6b 100644 --- a/client/src/components/SidePanel/Parameters/DynamicSlider.tsx +++ b/client/src/components/SidePanel/Parameters/DynamicSlider.tsx @@ -47,7 +47,6 @@ function DynamicSlider({ conversation, inputValue, setInputValue: setLocalValue, - preventDelayedUpdate: isEnum, }); const selectedValue = useMemo(() => { diff --git a/client/src/components/SidePanel/Parameters/Panel.tsx b/client/src/components/SidePanel/Parameters/Panel.tsx index c585dca458..4b941f5641 100644 --- a/client/src/components/SidePanel/Parameters/Panel.tsx +++ b/client/src/components/SidePanel/Parameters/Panel.tsx @@ -1,16 +1,34 @@ -import React, { useMemo, useState, useCallback } from 'react'; +import React, { useMemo, useState, useEffect, useCallback } from 'react'; import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { getSettingsKeys, tPresetUpdateSchema } from 'librechat-data-provider'; import type { TPreset } from 'librechat-data-provider'; import { SaveAsPresetDialog } from '~/components/Endpoints'; import { useSetIndexOptions, useLocalize } from '~/hooks'; +import { getEndpointField, logger } from '~/utils'; import { componentMapping } from './components'; import { useChatContext } from '~/Providers'; import { settings } from './settings'; +const excludedKeys = new Set([ + 'conversationId', + 'title', + 'endpoint', + 'endpointType', + 'createdAt', + 'updatedAt', + 'messages', + 'isArchived', + 'tags', + 'user', + '__v', + '_id', + 'tools', + 'model', +]); + export default function Parameters() { const localize = useLocalize(); - const { conversation } = useChatContext(); + const { conversation, setConversation } = useChatContext(); const { setOption } = useSetIndexOptions(); const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -22,13 +40,70 @@ export default function Parameters() { return endpointsConfig?.[conversation?.endpoint ?? '']?.availableRegions ?? []; }, [endpointsConfig, conversation?.endpoint]); + const endpointType = useMemo( + () => getEndpointField(endpointsConfig, conversation?.endpoint, 'type'), + [conversation?.endpoint, endpointsConfig], + ); + const parameters = useMemo(() => { const [combinedKey, endpointKey] = getSettingsKeys( - conversation?.endpoint ?? '', + endpointType ?? conversation?.endpoint ?? '', conversation?.model ?? '', ); return settings[combinedKey] ?? settings[endpointKey]; - }, [conversation]); + }, [conversation, endpointType]); + + useEffect(() => { + if (!parameters) { + return; + } + + // const defaultValueMap = new Map(); + // const paramKeys = new Set( + // parameters.map((setting) => { + // if (setting.default != null) { + // defaultValueMap.set(setting.key, setting.default); + // } + // return setting.key; + // }), + // ); + const paramKeys = new Set(parameters.map((setting) => setting.key)); + setConversation((prev) => { + if (!prev) { + return prev; + } + + const updatedConversation = { ...prev }; + + const conversationKeys = Object.keys(updatedConversation); + const updatedKeys: string[] = []; + conversationKeys.forEach((key) => { + // const defaultValue = defaultValueMap.get(key); + // if (paramKeys.has(key) && defaultValue != null && prev[key] != null) { + // updatedKeys.push(key); + // updatedConversation[key] = defaultValue; + // return; + // } + + if (paramKeys.has(key)) { + return; + } + + if (excludedKeys.has(key)) { + return; + } + + if (prev[key] != null) { + updatedKeys.push(key); + delete updatedConversation[key]; + } + }); + + logger.log('parameters', 'parameters effect, updated keys:', updatedKeys); + + return updatedConversation; + }); + }, [parameters, setConversation]); const openDialog = useCallback(() => { const newPreset = tPresetUpdateSchema.parse({ @@ -50,6 +125,9 @@ export default function Parameters() { {/* Below is an example of an applied dynamic setting, each be contained by a div with the column span specified */} {parameters.map((setting) => { const Component = componentMapping[setting.component]; + if (!Component) { + return null; + } const { key, default: defaultValue, ...rest } = setting; if (key === 'region' && bedrockRegions.length) { diff --git a/client/src/components/SidePanel/Parameters/components.tsx b/client/src/components/SidePanel/Parameters/components.tsx index 1d418c1a12..9c36d0eb20 100644 --- a/client/src/components/SidePanel/Parameters/components.tsx +++ b/client/src/components/SidePanel/Parameters/components.tsx @@ -11,7 +11,10 @@ import { DynamicTags, } from './'; -export const componentMapping: Record> = { +export const componentMapping: Record< + ComponentTypes, + React.ComponentType | undefined +> = { [ComponentTypes.Slider]: DynamicSlider, [ComponentTypes.Dropdown]: DynamicDropdown, [ComponentTypes.Switch]: DynamicSwitch, diff --git a/client/src/components/SidePanel/Parameters/settings.ts b/client/src/components/SidePanel/Parameters/settings.ts index 062b8e2c4c..f9b6398ae2 100644 --- a/client/src/components/SidePanel/Parameters/settings.ts +++ b/client/src/components/SidePanel/Parameters/settings.ts @@ -1,8 +1,14 @@ -import { EModelEndpoint, BedrockProviders } from 'librechat-data-provider'; +import { + ImageDetail, + EModelEndpoint, + openAISettings, + BedrockProviders, + anthropicSettings, +} from 'librechat-data-provider'; import type { SettingsConfiguration, SettingDefinition } from 'librechat-data-provider'; // Base definitions -const baseDefinitions: Record> = { +const baseDefinitions: Record = { model: { key: 'model', label: 'com_ui_model', @@ -38,20 +44,32 @@ const baseDefinitions: Record> = { optionType: 'model', columnSpan: 4, }, -}; - -const bedrock: Record = { - region: { - key: 'region', - type: 'string', - label: 'com_ui_region', + stop: { + key: 'stop', + label: 'com_endpoint_stop', labelCode: true, - component: 'combobox', + description: 'com_endpoint_openai_stop', + descriptionCode: true, + placeholder: 'com_endpoint_stop_placeholder', + placeholderCode: true, + type: 'array', + default: [], + component: 'tags', + optionType: 'conversation', + minTags: 0, + maxTags: 4, + }, + imageDetail: { + key: 'imageDetail', + label: 'com_endpoint_plug_image_detail', + labelCode: true, + description: 'com_endpoint_openai_detail', + descriptionCode: true, + type: 'enum', + default: ImageDetail.auto, + component: 'slider', + options: [ImageDetail.low, ImageDetail.auto, ImageDetail.high], optionType: 'conversation', - selectPlaceholder: 'com_ui_select_region', - searchPlaceholder: 'com_ui_select_search_region', - searchPlaceholderCode: true, - selectPlaceholderCode: true, columnSpan: 2, }, }; @@ -81,8 +99,10 @@ const librechat: Record = { labelCode: true, type: 'number', component: 'input', - placeholder: 'com_endpoint_context_info', + placeholder: 'com_nav_theme_system', placeholderCode: true, + description: 'com_endpoint_context_info', + descriptionCode: true, optionType: 'model', columnSpan: 2, }, @@ -112,7 +132,146 @@ const librechat: Record = { }, }; +const openAIParams: Record = { + chatGptLabel: { + ...librechat.modelLabel, + key: 'chatGptLabel', + }, + promptPrefix: librechat.promptPrefix, + temperature: createDefinition(baseDefinitions.temperature, { + default: openAISettings.temperature.default, + range: { + min: openAISettings.temperature.min, + max: openAISettings.temperature.max, + step: openAISettings.temperature.step, + }, + }), + top_p: createDefinition(baseDefinitions.topP, { + key: 'top_p', + default: openAISettings.top_p.default, + range: { + min: openAISettings.top_p.min, + max: openAISettings.top_p.max, + step: openAISettings.top_p.step, + }, + }), + frequency_penalty: { + key: 'frequency_penalty', + label: 'com_endpoint_frequency_penalty', + labelCode: true, + description: 'com_endpoint_openai_freq', + descriptionCode: true, + type: 'number', + default: openAISettings.frequency_penalty.default, + range: { + min: openAISettings.frequency_penalty.min, + max: openAISettings.frequency_penalty.max, + step: openAISettings.frequency_penalty.step, + }, + component: 'slider', + optionType: 'model', + columnSpan: 4, + }, + presence_penalty: { + key: 'presence_penalty', + label: 'com_endpoint_presence_penalty', + labelCode: true, + description: 'com_endpoint_openai_pres', + descriptionCode: true, + type: 'number', + default: openAISettings.presence_penalty.default, + range: { + min: openAISettings.presence_penalty.min, + max: openAISettings.presence_penalty.max, + step: openAISettings.presence_penalty.step, + }, + component: 'slider', + optionType: 'model', + columnSpan: 4, + }, + max_tokens: { + key: 'max_tokens', + label: 'com_endpoint_max_output_tokens', + labelCode: true, + type: 'number', + component: 'input', + description: 'com_endpoint_openai_max_tokens', + descriptionCode: true, + placeholder: 'com_nav_theme_system', + placeholderCode: true, + optionType: 'model', + columnSpan: 2, + }, +}; + const anthropic: Record = { + maxOutputTokens: { + key: 'maxOutputTokens', + label: 'com_endpoint_max_output_tokens', + labelCode: true, + type: 'number', + component: 'input', + description: 'com_endpoint_anthropic_maxoutputtokens', + descriptionCode: true, + placeholder: 'com_nav_theme_system', + placeholderCode: true, + range: { + min: anthropicSettings.maxOutputTokens.min, + max: anthropicSettings.maxOutputTokens.max, + step: anthropicSettings.maxOutputTokens.step, + }, + optionType: 'model', + columnSpan: 2, + }, + temperature: createDefinition(baseDefinitions.temperature, { + default: anthropicSettings.temperature.default, + range: { + min: anthropicSettings.temperature.min, + max: anthropicSettings.temperature.max, + step: anthropicSettings.temperature.step, + }, + }), + topP: createDefinition(baseDefinitions.topP, { + default: anthropicSettings.topP.default, + range: { + min: anthropicSettings.topP.min, + max: anthropicSettings.topP.max, + step: anthropicSettings.topP.step, + }, + }), + topK: { + key: 'topK', + label: 'com_endpoint_top_k', + labelCode: true, + description: 'com_endpoint_anthropic_topk', + descriptionCode: true, + type: 'number', + default: anthropicSettings.topK.default, + range: { + min: anthropicSettings.topK.min, + max: anthropicSettings.topK.max, + step: anthropicSettings.topK.step, + }, + component: 'slider', + optionType: 'model', + columnSpan: 4, + }, + promptCache: { + key: 'promptCache', + label: 'com_endpoint_prompt_cache', + labelCode: true, + description: 'com_endpoint_anthropic_prompt_cache', + descriptionCode: true, + type: 'boolean', + default: true, + component: 'switch', + optionType: 'conversation', + showDefault: false, + columnSpan: 2, + }, +}; + +const bedrock: Record = { system: { key: 'system', label: 'com_endpoint_prompt_prefix', @@ -124,6 +283,19 @@ const anthropic: Record = { placeholderCode: true, optionType: 'model', }, + region: { + key: 'region', + type: 'string', + label: 'com_ui_region', + labelCode: true, + component: 'combobox', + optionType: 'conversation', + selectPlaceholder: 'com_ui_select_region', + searchPlaceholder: 'com_ui_select_search_region', + searchPlaceholderCode: true, + selectPlaceholderCode: true, + columnSpan: 2, + }, maxTokens: { key: 'maxTokens', label: 'com_endpoint_max_output_tokens', @@ -139,37 +311,13 @@ const anthropic: Record = { default: 1, range: { min: 0, max: 1, step: 0.01 }, }), + topK: createDefinition(anthropic.topK, { + range: { min: 0, max: 500, step: 1 }, + }), topP: createDefinition(baseDefinitions.topP, { default: 0.999, range: { min: 0, max: 1, step: 0.01 }, }), - topK: { - key: 'topK', - label: 'com_endpoint_top_k', - labelCode: true, - description: 'com_endpoint_anthropic_topk', - descriptionCode: true, - type: 'number', - range: { min: 0, max: 500, step: 1 }, - component: 'slider', - optionType: 'model', - columnSpan: 4, - }, - stop: { - key: 'stop', - label: 'com_endpoint_stop', - labelCode: true, - description: 'com_endpoint_openai_stop', - descriptionCode: true, - placeholder: 'com_endpoint_stop_placeholder', - placeholderCode: true, - type: 'array', - default: [], - component: 'tags', - optionType: 'conversation', - minTags: 0, - maxTags: 4, - }, }; const mistral: Record = { @@ -204,15 +352,75 @@ const meta: Record = { }), }; -const bedrockAnthropic: SettingsConfiguration = [ - librechat.modelLabel, - anthropic.system, +const openAI: SettingsConfiguration = [ + openAIParams.chatGptLabel, + librechat.promptPrefix, librechat.maxContextTokens, - anthropic.maxTokens, + openAIParams.max_tokens, + openAIParams.temperature, + openAIParams.top_p, + openAIParams.frequency_penalty, + openAIParams.presence_penalty, + baseDefinitions.stop, + librechat.resendFiles, + baseDefinitions.imageDetail, +]; + +const openAICol1: SettingsConfiguration = [ + baseDefinitions.model as SettingDefinition, + openAIParams.chatGptLabel, + librechat.promptPrefix, + librechat.maxContextTokens, +]; + +const openAICol2: SettingsConfiguration = [ + openAIParams.max_tokens, + openAIParams.temperature, + openAIParams.top_p, + openAIParams.frequency_penalty, + openAIParams.presence_penalty, + baseDefinitions.stop, + librechat.resendFiles, + baseDefinitions.imageDetail, +]; + +const anthropicConfig: SettingsConfiguration = [ + librechat.modelLabel, + librechat.promptPrefix, + librechat.maxContextTokens, + anthropic.maxOutputTokens, anthropic.temperature, anthropic.topP, anthropic.topK, - anthropic.stop, + librechat.resendFiles, + anthropic.promptCache, +]; + +const anthropicCol1: SettingsConfiguration = [ + baseDefinitions.model as SettingDefinition, + librechat.modelLabel, + librechat.promptPrefix, +]; + +const anthropicCol2: SettingsConfiguration = [ + librechat.maxContextTokens, + anthropic.maxOutputTokens, + anthropic.temperature, + anthropic.topP, + anthropic.topK, + librechat.resendFiles, + anthropic.promptCache, +]; + +const bedrockAnthropic: SettingsConfiguration = [ + librechat.modelLabel, + bedrock.system, + librechat.maxContextTokens, + bedrock.maxTokens, + bedrock.temperature, + bedrock.topP, + bedrock.topK, + baseDefinitions.stop, bedrock.region, librechat.resendFiles, ]; @@ -221,7 +429,7 @@ const bedrockMistral: SettingsConfiguration = [ librechat.modelLabel, librechat.promptPrefix, librechat.maxContextTokens, - anthropic.maxTokens, + bedrock.maxTokens, mistral.temperature, mistral.topP, bedrock.region, @@ -232,7 +440,7 @@ const bedrockCohere: SettingsConfiguration = [ librechat.modelLabel, librechat.promptPrefix, librechat.maxContextTokens, - anthropic.maxTokens, + bedrock.maxTokens, cohere.temperature, cohere.topP, bedrock.region, @@ -252,16 +460,16 @@ const bedrockGeneral: SettingsConfiguration = [ const bedrockAnthropicCol1: SettingsConfiguration = [ baseDefinitions.model as SettingDefinition, librechat.modelLabel, - anthropic.system, - anthropic.stop, + bedrock.system, + baseDefinitions.stop, ]; const bedrockAnthropicCol2: SettingsConfiguration = [ librechat.maxContextTokens, - anthropic.maxTokens, - anthropic.temperature, - anthropic.topP, - anthropic.topK, + bedrock.maxTokens, + bedrock.temperature, + bedrock.topP, + bedrock.topK, bedrock.region, librechat.resendFiles, ]; @@ -274,7 +482,7 @@ const bedrockMistralCol1: SettingsConfiguration = [ const bedrockMistralCol2: SettingsConfiguration = [ librechat.maxContextTokens, - anthropic.maxTokens, + bedrock.maxTokens, mistral.temperature, mistral.topP, bedrock.region, @@ -289,7 +497,7 @@ const bedrockCohereCol1: SettingsConfiguration = [ const bedrockCohereCol2: SettingsConfiguration = [ librechat.maxContextTokens, - anthropic.maxTokens, + bedrock.maxTokens, cohere.temperature, cohere.topP, bedrock.region, @@ -311,6 +519,10 @@ const bedrockGeneralCol2: SettingsConfiguration = [ ]; export const settings: Record = { + [EModelEndpoint.openAI]: openAI, + [EModelEndpoint.azureOpenAI]: openAI, + [EModelEndpoint.custom]: openAI, + [EModelEndpoint.anthropic]: anthropicConfig, [`${EModelEndpoint.bedrock}-${BedrockProviders.Anthropic}`]: bedrockAnthropic, [`${EModelEndpoint.bedrock}-${BedrockProviders.MistralAI}`]: bedrockMistral, [`${EModelEndpoint.bedrock}-${BedrockProviders.Cohere}`]: bedrockCohere, @@ -319,6 +531,16 @@ export const settings: Record = { [`${EModelEndpoint.bedrock}-${BedrockProviders.Amazon}`]: bedrockGeneral, }; +const openAIColumns = { + col1: openAICol1, + col2: openAICol2, +}; + +const bedrockGeneralColumns = { + col1: bedrockGeneralCol1, + col2: bedrockGeneralCol2, +}; + export const presetSettings: Record< string, | { @@ -327,6 +549,13 @@ export const presetSettings: Record< } | undefined > = { + [EModelEndpoint.openAI]: openAIColumns, + [EModelEndpoint.azureOpenAI]: openAIColumns, + [EModelEndpoint.custom]: openAIColumns, + [EModelEndpoint.anthropic]: { + col1: anthropicCol1, + col2: anthropicCol2, + }, [`${EModelEndpoint.bedrock}-${BedrockProviders.Anthropic}`]: { col1: bedrockAnthropicCol1, col2: bedrockAnthropicCol2, @@ -339,16 +568,7 @@ export const presetSettings: Record< col1: bedrockCohereCol1, col2: bedrockCohereCol2, }, - [`${EModelEndpoint.bedrock}-${BedrockProviders.Meta}`]: { - col1: bedrockGeneralCol1, - col2: bedrockGeneralCol2, - }, - [`${EModelEndpoint.bedrock}-${BedrockProviders.AI21}`]: { - col1: bedrockGeneralCol1, - col2: bedrockGeneralCol2, - }, - [`${EModelEndpoint.bedrock}-${BedrockProviders.Amazon}`]: { - col1: bedrockGeneralCol1, - col2: bedrockGeneralCol2, - }, + [`${EModelEndpoint.bedrock}-${BedrockProviders.Meta}`]: bedrockGeneralColumns, + [`${EModelEndpoint.bedrock}-${BedrockProviders.AI21}`]: bedrockGeneralColumns, + [`${EModelEndpoint.bedrock}-${BedrockProviders.Amazon}`]: bedrockGeneralColumns, }; diff --git a/client/src/components/SidePanel/SidePanel.tsx b/client/src/components/SidePanel/SidePanel.tsx index 44bd0e0bce..de38ea4e52 100644 --- a/client/src/components/SidePanel/SidePanel.tsx +++ b/client/src/components/SidePanel/SidePanel.tsx @@ -12,9 +12,9 @@ import { ResizableHandleAlt, ResizablePanel, ResizablePanelGroup } from '~/compo import { useMediaQuery, useLocalStorage, useLocalize } from '~/hooks'; import useSideNavLinks from '~/hooks/Nav/useSideNavLinks'; import NavToggle from '~/components/Nav/NavToggle'; +import { cn, getEndpointField } from '~/utils'; import { useChatContext } from '~/Providers'; import Switcher from './Switcher'; -import { cn } from '~/utils'; import Nav from './Nav'; interface SidePanelProps { @@ -81,6 +81,10 @@ const SidePanel = ({ return typeof activePanel === 'string' ? activePanel : undefined; }, []); + const endpointType = useMemo( + () => getEndpointField(endpointsConfig, endpoint, 'type'), + [endpoint, endpointsConfig], + ); const assistants = useMemo(() => endpointsConfig?.[endpoint ?? ''], [endpoint, endpointsConfig]); const agents = useMemo(() => endpointsConfig?.[endpoint ?? ''], [endpoint, endpointsConfig]); @@ -108,6 +112,7 @@ const SidePanel = ({ hidePanel, assistants, keyProvided, + endpointType, interfaceConfig, }); diff --git a/client/src/hooks/Nav/useSideNavLinks.ts b/client/src/hooks/Nav/useSideNavLinks.ts index a50d73b7ff..a59d933a93 100644 --- a/client/src/hooks/Nav/useSideNavLinks.ts +++ b/client/src/hooks/Nav/useSideNavLinks.ts @@ -4,7 +4,7 @@ import { isAssistantsEndpoint, isAgentsEndpoint, PermissionTypes, - paramEndpoints, + isParamEndpoint, EModelEndpoint, Permissions, } from 'librechat-data-provider'; @@ -25,6 +25,7 @@ export default function useSideNavLinks({ agents, keyProvided, endpoint, + endpointType, interfaceConfig, }: { hidePanel: () => void; @@ -32,6 +33,7 @@ export default function useSideNavLinks({ agents?: TConfig | null; keyProvided: boolean; endpoint?: EModelEndpoint | null; + endpointType?: EModelEndpoint | null; interfaceConfig: Partial; }) { const hasAccessToPrompts = useHasAccess({ @@ -87,7 +89,11 @@ export default function useSideNavLinks({ }); } - if (interfaceConfig.parameters === true && paramEndpoints.has(endpoint ?? '') && keyProvided) { + if ( + interfaceConfig.parameters === true && + isParamEndpoint(endpoint ?? '', endpointType ?? '') === true && + keyProvided + ) { links.push({ title: 'com_sidepanel_parameters', label: '', @@ -128,6 +134,7 @@ export default function useSideNavLinks({ interfaceConfig.parameters, keyProvided, assistants, + endpointType, endpoint, agents, hasAccessToPrompts, diff --git a/client/src/hooks/useNewConvo.ts b/client/src/hooks/useNewConvo.ts index a97688df4c..120b0896d5 100644 --- a/client/src/hooks/useNewConvo.ts +++ b/client/src/hooks/useNewConvo.ts @@ -7,9 +7,9 @@ import { import { useNavigate } from 'react-router-dom'; import { FileSources, + isParamEndpoint, LocalStorageKeys, isAssistantsEndpoint, - paramEndpoints, } from 'librechat-data-provider'; import { useRecoilState, useRecoilValue, useSetRecoilState, useRecoilCallback } from 'recoil'; import type { @@ -185,12 +185,11 @@ const useNewConvo = (index = 0) => { pauseGlobalAudio(); const templateConvoId = _template.conversationId ?? ''; - const isParamEndpoint = - paramEndpoints.has(_template.endpoint ?? '') || - paramEndpoints.has(_preset?.endpoint ?? '') || - paramEndpoints.has(_template.endpointType ?? ''); + const paramEndpoint = + isParamEndpoint(_template.endpoint ?? '', _template.endpointType ?? '') === true || + isParamEndpoint(_preset?.endpoint ?? '', _preset?.endpointType ?? ''); const template = - isParamEndpoint && templateConvoId && templateConvoId === 'new' + paramEndpoint === true && templateConvoId && templateConvoId === 'new' ? { endpoint: _template.endpoint } : _template; diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 8d5c91f02e..152b59598b 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -720,11 +720,6 @@ export const modularEndpoints = new Set([ EModelEndpoint.bedrock, ]); -export const paramEndpoints = new Set([ - EModelEndpoint.agents, - EModelEndpoint.bedrock, -]); - export const supportsBalanceCheck = { [EModelEndpoint.custom]: true, [EModelEndpoint.openAI]: true, diff --git a/packages/data-provider/src/parsers.ts b/packages/data-provider/src/parsers.ts index 941cdf2ce7..3230374735 100644 --- a/packages/data-provider/src/parsers.ts +++ b/packages/data-provider/src/parsers.ts @@ -13,13 +13,11 @@ import { gptPluginsSchema, // agentsSchema, compactAgentsSchema, - compactOpenAISchema, compactGoogleSchema, compactChatGPTSchema, chatGPTBrowserSchema, compactPluginsSchema, compactAssistantSchema, - compactAnthropicSchema, } from './schemas'; import { bedrockInputSchema } from './bedrock'; import { alternateName } from './config'; @@ -302,20 +300,20 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string => }; type CompactEndpointSchema = - | typeof compactOpenAISchema + | typeof openAISchema | typeof compactAssistantSchema | typeof compactAgentsSchema | typeof compactGoogleSchema | typeof bingAISchema - | typeof compactAnthropicSchema + | typeof anthropicSchema | typeof compactChatGPTSchema | typeof bedrockInputSchema | typeof compactPluginsSchema; const compactEndpointSchemas: Record = { - [EModelEndpoint.openAI]: compactOpenAISchema, - [EModelEndpoint.azureOpenAI]: compactOpenAISchema, - [EModelEndpoint.custom]: compactOpenAISchema, + [EModelEndpoint.openAI]: openAISchema, + [EModelEndpoint.azureOpenAI]: openAISchema, + [EModelEndpoint.custom]: openAISchema, [EModelEndpoint.assistants]: compactAssistantSchema, [EModelEndpoint.azureAssistants]: compactAssistantSchema, [EModelEndpoint.agents]: compactAgentsSchema, @@ -323,7 +321,7 @@ const compactEndpointSchemas: Record = { [EModelEndpoint.bedrock]: bedrockInputSchema, /* BingAI needs all fields */ [EModelEndpoint.bingAI]: bingAISchema, - [EModelEndpoint.anthropic]: compactAnthropicSchema, + [EModelEndpoint.anthropic]: anthropicSchema, [EModelEndpoint.chatGPTBrowser]: compactChatGPTSchema, [EModelEndpoint.gptPlugins]: compactPluginsSchema, }; diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 3a4a9d68b8..baccf72cf1 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -28,6 +28,14 @@ export enum EModelEndpoint { bedrock = 'bedrock', } +export const paramEndpoints = new Set([ + EModelEndpoint.agents, + EModelEndpoint.bedrock, + EModelEndpoint.openAI, + EModelEndpoint.anthropic, + EModelEndpoint.custom, +]); + export enum BedrockProviders { AI21 = 'ai21', Amazon = 'amazon', @@ -72,6 +80,21 @@ export const isAgentsEndpoint = (_endpoint?: EModelEndpoint.agents | null | stri return endpoint === EModelEndpoint.agents; }; +export const isParamEndpoint = ( + endpoint: EModelEndpoint | string, + endpointType?: EModelEndpoint | string, +): boolean => { + if (paramEndpoints.has(endpoint)) { + return true; + } + + if (endpointType != null) { + return paramEndpoints.has(endpointType); + } + + return false; +}; + export enum ImageDetail { low = 'low', auto = 'auto', @@ -500,7 +523,7 @@ export const tConversationSchema = z.object({ frequency_penalty: z.number().optional(), presence_penalty: z.number().optional(), parentMessageId: z.string().optional(), - maxOutputTokens: z.number().optional(), + maxOutputTokens: coerceNumber.optional(), maxContextTokens: coerceNumber.optional(), max_tokens: coerceNumber.optional(), /* Anthropic */ @@ -630,71 +653,6 @@ export const tConversationTagSchema = z.object({ }); export type TConversationTag = z.infer; -export const openAISchema = tConversationSchema - .pick({ - model: true, - modelLabel: true, - chatGptLabel: true, - promptPrefix: true, - temperature: true, - top_p: true, - presence_penalty: true, - frequency_penalty: true, - resendFiles: true, - artifacts: true, - imageDetail: true, - stop: true, - iconURL: true, - greeting: true, - spec: true, - maxContextTokens: true, - max_tokens: true, - }) - .transform((obj) => { - const result = { - ...obj, - model: obj.model ?? openAISettings.model.default, - chatGptLabel: obj.chatGptLabel ?? obj.modelLabel ?? null, - promptPrefix: obj.promptPrefix ?? null, - temperature: obj.temperature ?? openAISettings.temperature.default, - top_p: obj.top_p ?? openAISettings.top_p.default, - presence_penalty: obj.presence_penalty ?? openAISettings.presence_penalty.default, - frequency_penalty: obj.frequency_penalty ?? openAISettings.frequency_penalty.default, - resendFiles: - typeof obj.resendFiles === 'boolean' ? obj.resendFiles : openAISettings.resendFiles.default, - imageDetail: obj.imageDetail ?? openAISettings.imageDetail.default, - stop: obj.stop ?? undefined, - iconURL: obj.iconURL ?? undefined, - greeting: obj.greeting ?? undefined, - spec: obj.spec ?? undefined, - maxContextTokens: obj.maxContextTokens ?? undefined, - max_tokens: obj.max_tokens ?? undefined, - }; - - if (obj.modelLabel != null && obj.modelLabel !== '') { - result.modelLabel = null; - } - - return result; - }) - .catch(() => ({ - model: openAISettings.model.default, - chatGptLabel: null, - promptPrefix: null, - temperature: openAISettings.temperature.default, - top_p: openAISettings.top_p.default, - presence_penalty: openAISettings.presence_penalty.default, - frequency_penalty: openAISettings.frequency_penalty.default, - resendFiles: openAISettings.resendFiles.default, - imageDetail: openAISettings.imageDetail.default, - stop: undefined, - iconURL: undefined, - greeting: undefined, - spec: undefined, - maxContextTokens: undefined, - max_tokens: undefined, - })); - export const googleSchema = tConversationSchema .pick({ model: true, @@ -778,64 +736,6 @@ export const bingAISchema = tConversationSchema invocationId: 1, })); -export const anthropicSchema = tConversationSchema - .pick({ - model: true, - modelLabel: true, - promptPrefix: true, - temperature: true, - maxOutputTokens: true, - topP: true, - topK: true, - resendFiles: true, - promptCache: true, - artifacts: true, - iconURL: true, - greeting: true, - spec: true, - maxContextTokens: true, - }) - .transform((obj) => { - const model = obj.model ?? anthropicSettings.model.default; - return { - ...obj, - model, - modelLabel: obj.modelLabel ?? null, - promptPrefix: obj.promptPrefix ?? null, - temperature: obj.temperature ?? anthropicSettings.temperature.default, - maxOutputTokens: obj.maxOutputTokens ?? anthropicSettings.maxOutputTokens.reset(model), - topP: obj.topP ?? anthropicSettings.topP.default, - topK: obj.topK ?? anthropicSettings.topK.default, - promptCache: - typeof obj.promptCache === 'boolean' - ? obj.promptCache - : anthropicSettings.promptCache.default, - resendFiles: - typeof obj.resendFiles === 'boolean' - ? obj.resendFiles - : anthropicSettings.resendFiles.default, - iconURL: obj.iconURL ?? undefined, - greeting: obj.greeting ?? undefined, - spec: obj.spec ?? undefined, - maxContextTokens: obj.maxContextTokens ?? anthropicSettings.maxContextTokens.default, - }; - }) - .catch(() => ({ - model: anthropicSettings.model.default, - modelLabel: null, - promptPrefix: null, - temperature: anthropicSettings.temperature.default, - maxOutputTokens: anthropicSettings.maxOutputTokens.default, - topP: anthropicSettings.topP.default, - topK: anthropicSettings.topK.default, - resendFiles: anthropicSettings.resendFiles.default, - promptCache: anthropicSettings.promptCache.default, - iconURL: undefined, - greeting: undefined, - spec: undefined, - maxContextTokens: anthropicSettings.maxContextTokens.default, - })); - export const chatGPTBrowserSchema = tConversationSchema .pick({ model: true, @@ -1027,7 +927,7 @@ export const agentsSchema = tConversationSchema maxContextTokens: undefined, })); -export const compactOpenAISchema = tConversationSchema +export const openAISchema = tConversationSchema .pick({ model: true, chatGptLabel: true, @@ -1046,29 +946,7 @@ export const compactOpenAISchema = tConversationSchema maxContextTokens: true, max_tokens: true, }) - .transform((obj: Partial) => { - const newObj: Partial = { ...obj }; - if (newObj.temperature === openAISettings.temperature.default) { - delete newObj.temperature; - } - if (newObj.top_p === openAISettings.top_p.default) { - delete newObj.top_p; - } - if (newObj.presence_penalty === openAISettings.presence_penalty.default) { - delete newObj.presence_penalty; - } - if (newObj.frequency_penalty === openAISettings.frequency_penalty.default) { - delete newObj.frequency_penalty; - } - if (newObj.resendFiles === openAISettings.resendFiles.default) { - delete newObj.resendFiles; - } - if (newObj.imageDetail === openAISettings.imageDetail.default) { - delete newObj.imageDetail; - } - - return removeNullishValues(newObj); - }) + .transform((obj: Partial) => removeNullishValues(obj)) .catch(() => ({})); export const compactGoogleSchema = tConversationSchema @@ -1106,7 +984,7 @@ export const compactGoogleSchema = tConversationSchema }) .catch(() => ({})); -export const compactAnthropicSchema = tConversationSchema +export const anthropicSchema = tConversationSchema .pick({ model: true, modelLabel: true, @@ -1123,29 +1001,7 @@ export const compactAnthropicSchema = tConversationSchema spec: true, maxContextTokens: true, }) - .transform((obj) => { - const newObj: Partial = { ...obj }; - if (newObj.temperature === anthropicSettings.temperature.default) { - delete newObj.temperature; - } - if (newObj.maxOutputTokens === anthropicSettings.legacy.maxOutputTokens.default) { - delete newObj.maxOutputTokens; - } - if (newObj.topP === anthropicSettings.topP.default) { - delete newObj.topP; - } - if (newObj.topK === anthropicSettings.topK.default) { - delete newObj.topK; - } - if (newObj.resendFiles === anthropicSettings.resendFiles.default) { - delete newObj.resendFiles; - } - if (newObj.promptCache === anthropicSettings.promptCache.default) { - delete newObj.promptCache; - } - - return removeNullishValues(newObj); - }) + .transform((obj) => removeNullishValues(obj)) .catch(() => ({})); export const compactChatGPTSchema = tConversationSchema