import React, { useMemo, useState, useEffect, useCallback } from 'react'; import keyBy from 'lodash/keyBy'; import { RotateCcw } from 'lucide-react'; import { excludedKeys, paramSettings, getSettingsKeys, getEndpointField, SettingDefinition, tConvoUpdateSchema, } from 'librechat-data-provider'; import type { TPreset } from 'librechat-data-provider'; import { SaveAsPresetDialog } from '~/components/Endpoints'; import { useSetIndexOptions, useLocalize } from '~/hooks'; import { useGetEndpointsQuery } from '~/data-provider'; import { componentMapping } from './components'; import { useChatContext } from '~/Providers'; import { logger } from '~/utils'; export default function Parameters() { const localize = useLocalize(); const { conversation, setConversation } = useChatContext(); const { setOption } = useSetIndexOptions(); const [isDialogOpen, setIsDialogOpen] = useState(false); const [preset, setPreset] = useState(null); const { data: endpointsConfig = {} } = useGetEndpointsQuery(); const provider = conversation?.endpoint ?? ''; const model = conversation?.model ?? ''; const bedrockRegions = useMemo(() => { return endpointsConfig?.[conversation?.endpoint ?? '']?.availableRegions ?? []; }, [endpointsConfig, conversation?.endpoint]); const endpointType = useMemo( () => getEndpointField(endpointsConfig, conversation?.endpoint, 'type'), [conversation?.endpoint, endpointsConfig], ); const parameters = useMemo((): SettingDefinition[] => { const customParams = endpointsConfig[provider]?.customParams ?? {}; const [combinedKey, endpointKey] = getSettingsKeys(endpointType ?? provider, model); const overriddenEndpointKey = customParams.defaultParamsEndpoint ?? endpointKey; const defaultParams = paramSettings[combinedKey] ?? paramSettings[overriddenEndpointKey] ?? []; const overriddenParams = endpointsConfig[provider]?.customParams?.paramDefinitions ?? []; const overriddenParamsMap = keyBy(overriddenParams, 'key'); return defaultParams .filter((param) => param != null) .map((param) => (overriddenParamsMap[param.key] as SettingDefinition) ?? param); }, [endpointType, endpointsConfig, model, provider]); const filteredParameters = useMemo((): SettingDefinition[] => { const allowXHigh = /^gpt-5\\.2/.test(model ?? ''); return parameters?.map((param) => { if (param?.key !== 'reasoning_effort' || !param.options) { return param; } const filteredOptions = allowXHigh ? param.options : param.options.filter((option) => option !== 'xhigh'); const filteredEnumMappings = param.enumMappings ? Object.fromEntries( Object.entries(param.enumMappings).filter(([key]) => allowXHigh || key !== 'xhigh'), ) : undefined; return { ...param, options: filteredOptions, enumMappings: filteredEnumMappings, }; }); }, [parameters, model]); useEffect(() => { if (!filteredParameters) { 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( filteredParameters.filter((setting) => setting != null).map((setting) => setting.key), ); setConversation((prev) => { if (!prev) { return prev; } const updatedConversation = { ...prev }; const conversationKeys = Object.keys(updatedConversation); const updatedKeys: string[] = []; const allowXHigh = /^gpt-5\\.2/.test(model ?? ''); if (!allowXHigh && updatedConversation.reasoning_effort === 'xhigh') { updatedKeys.push('reasoning_effort'); delete updatedConversation.reasoning_effort; } 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; }); }, [filteredParameters, setConversation, model]); const resetParameters = useCallback(() => { setConversation((prev) => { if (!prev) { return prev; } const updatedConversation = { ...prev }; const resetKeys: string[] = []; Object.keys(updatedConversation).forEach((key) => { if (excludedKeys.has(key)) { return; } if (updatedConversation[key] !== undefined) { resetKeys.push(key); delete updatedConversation[key]; } }); logger.log('parameters', 'parameters reset, affected keys:', resetKeys); return updatedConversation; }); }, [setConversation]); const openDialog = useCallback(() => { const newPreset = tConvoUpdateSchema.parse({ ...conversation, }) as TPreset; setPreset(newPreset); setIsDialogOpen(true); }, [conversation]); if (!filteredParameters) { return null; } return (
{' '} {/* This is the parent element containing all settings */} {/* Below is an example of an applied dynamic setting, each be contained by a div with the column span specified */} {filteredParameters.map((setting) => { const Component = componentMapping[setting.component]; if (!Component) { return null; } const { key, default: defaultValue, ...rest } = setting; if (key === 'region' && bedrockRegions.length) { rest.options = bedrockRegions; } return ( ); })}
{preset && ( )}
); }