mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00
🎚️ feat: Custom Parameters (#7342)
* # * - refactor: simplified getCustomConfig func * # * - feature: persist values for parameters with optionType of custom * # * - refactor: moved `Parameters/settings.ts` into `data-provider` so that both frontend and backend code can use it. * - feature: loadCustomConfig can now parse and validate customParams property for `endpoints.custom` in `librechat.yaml` * # fixed linter * # removed .strict() in config.ts * change: added packages/data-provider/src to SOURCE_DIRS for i18n check * # removed unnecessary lodash imports * # addressed PR comments # fixed lint for updated files * # better import for lodash (w/o relying on tree-shaking)
This commit is contained in:
parent
c79ee32006
commit
7ce782fec6
23 changed files with 340 additions and 132 deletions
2
.github/workflows/i18n-unused-keys.yml
vendored
2
.github/workflows/i18n-unused-keys.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
||||||
|
|
||||||
# Define paths
|
# Define paths
|
||||||
I18N_FILE="client/src/locales/en/translation.json"
|
I18N_FILE="client/src/locales/en/translation.json"
|
||||||
SOURCE_DIRS=("client/src" "api")
|
SOURCE_DIRS=("client/src" "api" "packages/data-provider/src")
|
||||||
|
|
||||||
# Check if translation file exists
|
# Check if translation file exists
|
||||||
if [[ ! -f "$I18N_FILE" ]]; then
|
if [[ ! -f "$I18N_FILE" ]]; then
|
||||||
|
|
|
@ -10,17 +10,7 @@ const getLogStores = require('~/cache/getLogStores');
|
||||||
* */
|
* */
|
||||||
async function getCustomConfig() {
|
async function getCustomConfig() {
|
||||||
const cache = getLogStores(CacheKeys.CONFIG_STORE);
|
const cache = getLogStores(CacheKeys.CONFIG_STORE);
|
||||||
let customConfig = await cache.get(CacheKeys.CUSTOM_CONFIG);
|
return (await cache.get(CacheKeys.CUSTOM_CONFIG)) || (await loadCustomConfig());
|
||||||
|
|
||||||
if (!customConfig) {
|
|
||||||
customConfig = await loadCustomConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!customConfig) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return customConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,7 +29,14 @@ async function loadConfigEndpoints(req) {
|
||||||
|
|
||||||
for (let i = 0; i < customEndpoints.length; i++) {
|
for (let i = 0; i < customEndpoints.length; i++) {
|
||||||
const endpoint = customEndpoints[i];
|
const endpoint = customEndpoints[i];
|
||||||
const { baseURL, apiKey, name: configName, iconURL, modelDisplayLabel } = endpoint;
|
const {
|
||||||
|
baseURL,
|
||||||
|
apiKey,
|
||||||
|
name: configName,
|
||||||
|
iconURL,
|
||||||
|
modelDisplayLabel,
|
||||||
|
customParams,
|
||||||
|
} = endpoint;
|
||||||
const name = normalizeEndpointName(configName);
|
const name = normalizeEndpointName(configName);
|
||||||
|
|
||||||
const resolvedApiKey = extractEnvVariable(apiKey);
|
const resolvedApiKey = extractEnvVariable(apiKey);
|
||||||
|
@ -41,6 +48,7 @@ async function loadConfigEndpoints(req) {
|
||||||
userProvideURL: isUserProvided(resolvedBaseURL),
|
userProvideURL: isUserProvided(resolvedBaseURL),
|
||||||
modelDisplayLabel,
|
modelDisplayLabel,
|
||||||
iconURL,
|
iconURL,
|
||||||
|
customParams,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { CacheKeys, configSchema, EImageOutputType } = require('librechat-data-provider');
|
const {
|
||||||
|
CacheKeys,
|
||||||
|
configSchema,
|
||||||
|
EImageOutputType,
|
||||||
|
validateSettingDefinitions,
|
||||||
|
agentParamSettings,
|
||||||
|
paramSettings,
|
||||||
|
} = require('librechat-data-provider');
|
||||||
const getLogStores = require('~/cache/getLogStores');
|
const getLogStores = require('~/cache/getLogStores');
|
||||||
const loadYaml = require('~/utils/loadYaml');
|
const loadYaml = require('~/utils/loadYaml');
|
||||||
const { logger } = require('~/config');
|
const { logger } = require('~/config');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
|
const keyBy = require('lodash/keyBy');
|
||||||
|
|
||||||
const projectRoot = path.resolve(__dirname, '..', '..', '..', '..');
|
const projectRoot = path.resolve(__dirname, '..', '..', '..', '..');
|
||||||
const defaultConfigPath = path.resolve(projectRoot, 'librechat.yaml');
|
const defaultConfigPath = path.resolve(projectRoot, 'librechat.yaml');
|
||||||
|
@ -105,6 +113,10 @@ https://www.librechat.ai/docs/configuration/stt_tts`);
|
||||||
logger.debug('Custom config:', customConfig);
|
logger.debug('Custom config:', customConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(customConfig.endpoints?.custom ?? [])
|
||||||
|
.filter((endpoint) => endpoint.customParams)
|
||||||
|
.forEach((endpoint) => parseCustomParams(endpoint.name, endpoint.customParams));
|
||||||
|
|
||||||
if (customConfig.cache) {
|
if (customConfig.cache) {
|
||||||
const cache = getLogStores(CacheKeys.CONFIG_STORE);
|
const cache = getLogStores(CacheKeys.CONFIG_STORE);
|
||||||
await cache.set(CacheKeys.CUSTOM_CONFIG, customConfig);
|
await cache.set(CacheKeys.CUSTOM_CONFIG, customConfig);
|
||||||
|
@ -117,4 +129,52 @@ https://www.librechat.ai/docs/configuration/stt_tts`);
|
||||||
return customConfig;
|
return customConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate and fill out missing values for custom parameters
|
||||||
|
function parseCustomParams(endpointName, customParams) {
|
||||||
|
const paramEndpoint = customParams.defaultParamsEndpoint;
|
||||||
|
customParams.paramDefinitions = customParams.paramDefinitions || [];
|
||||||
|
|
||||||
|
// Checks if `defaultParamsEndpoint` is a key in `paramSettings`.
|
||||||
|
const validEndpoints = new Set([
|
||||||
|
...Object.keys(paramSettings),
|
||||||
|
...Object.keys(agentParamSettings),
|
||||||
|
]);
|
||||||
|
if (!validEndpoints.has(paramEndpoint)) {
|
||||||
|
throw new Error(
|
||||||
|
`defaultParamsEndpoint of "${endpointName}" endpoint is invalid. ` +
|
||||||
|
`Valid options are ${Array.from(validEndpoints).join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates default param maps
|
||||||
|
const regularParams = paramSettings[paramEndpoint] ?? [];
|
||||||
|
const agentParams = agentParamSettings[paramEndpoint] ?? [];
|
||||||
|
const defaultParams = regularParams.concat(agentParams);
|
||||||
|
const defaultParamsMap = keyBy(defaultParams, 'key');
|
||||||
|
|
||||||
|
// TODO: Remove this check once we support new parameters not part of default parameters.
|
||||||
|
// Checks if every key in `paramDefinitions` is valid.
|
||||||
|
const validKeys = new Set(Object.keys(defaultParamsMap));
|
||||||
|
const paramKeys = customParams.paramDefinitions.map((param) => param.key);
|
||||||
|
if (paramKeys.some((key) => !validKeys.has(key))) {
|
||||||
|
throw new Error(
|
||||||
|
`paramDefinitions of "${endpointName}" endpoint contains invalid key(s). ` +
|
||||||
|
`Valid parameter keys are ${Array.from(validKeys).join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill out missing values for custom param definitions
|
||||||
|
customParams.paramDefinitions = customParams.paramDefinitions.map((param) => {
|
||||||
|
return { ...defaultParamsMap[param.key], ...param, optionType: 'custom' };
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
validateSettingDefinitions(customParams.paramDefinitions);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`Custom parameter definitions for "${endpointName}" endpoint is malformed: ${e.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = loadCustomConfig;
|
module.exports = loadCustomConfig;
|
||||||
|
|
|
@ -1,6 +1,34 @@
|
||||||
jest.mock('axios');
|
jest.mock('axios');
|
||||||
jest.mock('~/cache/getLogStores');
|
jest.mock('~/cache/getLogStores');
|
||||||
jest.mock('~/utils/loadYaml');
|
jest.mock('~/utils/loadYaml');
|
||||||
|
jest.mock('librechat-data-provider', () => {
|
||||||
|
const actual = jest.requireActual('librechat-data-provider');
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
paramSettings: { foo: {}, bar: {}, custom: {} },
|
||||||
|
agentParamSettings: {
|
||||||
|
custom: [],
|
||||||
|
google: [
|
||||||
|
{
|
||||||
|
key: 'pressure',
|
||||||
|
type: 'string',
|
||||||
|
component: 'input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'temperature',
|
||||||
|
type: 'number',
|
||||||
|
component: 'slider',
|
||||||
|
default: 0.5,
|
||||||
|
range: {
|
||||||
|
min: 0,
|
||||||
|
max: 2,
|
||||||
|
step: 0.01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const loadCustomConfig = require('./loadCustomConfig');
|
const loadCustomConfig = require('./loadCustomConfig');
|
||||||
|
@ -150,4 +178,126 @@ describe('loadCustomConfig', () => {
|
||||||
expect(logger.info).toHaveBeenCalledWith(JSON.stringify(mockConfig, null, 2));
|
expect(logger.info).toHaveBeenCalledWith(JSON.stringify(mockConfig, null, 2));
|
||||||
expect(logger.debug).toHaveBeenCalledWith('Custom config:', mockConfig);
|
expect(logger.debug).toHaveBeenCalledWith('Custom config:', mockConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parseCustomParams', () => {
|
||||||
|
const mockConfig = {
|
||||||
|
version: '1.0',
|
||||||
|
cache: false,
|
||||||
|
endpoints: {
|
||||||
|
custom: [
|
||||||
|
{
|
||||||
|
name: 'Google',
|
||||||
|
apiKey: 'user_provided',
|
||||||
|
customParams: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async function loadCustomParams(customParams) {
|
||||||
|
mockConfig.endpoints.custom[0].customParams = customParams;
|
||||||
|
loadYaml.mockReturnValue(mockConfig);
|
||||||
|
return await loadCustomConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
process.env.CONFIG_PATH = 'validConfig.yaml';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns no error when customParams is undefined', async () => {
|
||||||
|
const result = await loadCustomParams(undefined);
|
||||||
|
expect(result).toEqual(mockConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns no error when customParams is valid', async () => {
|
||||||
|
const result = await loadCustomParams({
|
||||||
|
defaultParamsEndpoint: 'google',
|
||||||
|
paramDefinitions: [
|
||||||
|
{
|
||||||
|
key: 'temperature',
|
||||||
|
default: 0.5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(result).toEqual(mockConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error when paramDefinitions contain unsupported keys', async () => {
|
||||||
|
const malformedCustomParams = {
|
||||||
|
defaultParamsEndpoint: 'google',
|
||||||
|
paramDefinitions: [
|
||||||
|
{ key: 'temperature', default: 0.5 },
|
||||||
|
{ key: 'unsupportedKey', range: 0.5 },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await expect(loadCustomParams(malformedCustomParams)).rejects.toThrow(
|
||||||
|
'paramDefinitions of "Google" endpoint contains invalid key(s). Valid parameter keys are pressure, temperature',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error when paramDefinitions is malformed', async () => {
|
||||||
|
const malformedCustomParams = {
|
||||||
|
defaultParamsEndpoint: 'google',
|
||||||
|
paramDefinitions: [
|
||||||
|
{
|
||||||
|
key: 'temperature',
|
||||||
|
type: 'noomba',
|
||||||
|
component: 'inpoot',
|
||||||
|
optionType: 'custom',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
await expect(loadCustomParams(malformedCustomParams)).rejects.toThrow(
|
||||||
|
/Custom parameter definitions for "Google" endpoint is malformed:/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error when defaultParamsEndpoint is not provided', async () => {
|
||||||
|
const malformedCustomParams = { defaultParamsEndpoint: undefined };
|
||||||
|
await expect(loadCustomParams(malformedCustomParams)).rejects.toThrow(
|
||||||
|
'defaultParamsEndpoint of "Google" endpoint is invalid. Valid options are foo, bar, custom, google',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fills the paramDefinitions with missing values', async () => {
|
||||||
|
const customParams = {
|
||||||
|
defaultParamsEndpoint: 'google',
|
||||||
|
paramDefinitions: [
|
||||||
|
{ key: 'temperature', default: 0.7, range: { min: 0.1, max: 0.9, step: 0.1 } },
|
||||||
|
{ key: 'pressure', component: 'textarea' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedConfig = await loadCustomParams(customParams);
|
||||||
|
const paramDefinitions = parsedConfig.endpoints.custom[0].customParams.paramDefinitions;
|
||||||
|
expect(paramDefinitions).toEqual([
|
||||||
|
{
|
||||||
|
columnSpan: 1,
|
||||||
|
component: 'slider',
|
||||||
|
default: 0.7, // overridden
|
||||||
|
includeInput: true,
|
||||||
|
key: 'temperature',
|
||||||
|
label: 'temperature',
|
||||||
|
optionType: 'custom',
|
||||||
|
range: {
|
||||||
|
// overridden
|
||||||
|
max: 0.9,
|
||||||
|
min: 0.1,
|
||||||
|
step: 0.1,
|
||||||
|
},
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
columnSpan: 1,
|
||||||
|
component: 'textarea', // overridden
|
||||||
|
key: 'pressure',
|
||||||
|
label: 'pressure',
|
||||||
|
optionType: 'custom',
|
||||||
|
placeholder: '',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -105,6 +105,7 @@ const initializeClient = async ({ req, res, endpointOption, optionsOnly, overrid
|
||||||
headers: resolvedHeaders,
|
headers: resolvedHeaders,
|
||||||
addParams: endpointConfig.addParams,
|
addParams: endpointConfig.addParams,
|
||||||
dropParams: endpointConfig.dropParams,
|
dropParams: endpointConfig.dropParams,
|
||||||
|
customParams: endpointConfig.customParams,
|
||||||
titleConvo: endpointConfig.titleConvo,
|
titleConvo: endpointConfig.titleConvo,
|
||||||
titleModel: endpointConfig.titleModel,
|
titleModel: endpointConfig.titleModel,
|
||||||
forcePrompt: endpointConfig.forcePrompt,
|
forcePrompt: endpointConfig.forcePrompt,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { getSettingsKeys } from 'librechat-data-provider';
|
||||||
import type { SettingDefinition } from 'librechat-data-provider';
|
import type { SettingDefinition } from 'librechat-data-provider';
|
||||||
import type { TModelSelectProps } from '~/common';
|
import type { TModelSelectProps } from '~/common';
|
||||||
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
||||||
import { presetSettings } from '~/components/SidePanel/Parameters/settings';
|
import { presetSettings } from 'librechat-data-provider';
|
||||||
|
|
||||||
export default function AnthropicSettings({
|
export default function AnthropicSettings({
|
||||||
conversation,
|
conversation,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { getSettingsKeys } from 'librechat-data-provider';
|
||||||
import type { SettingDefinition } from 'librechat-data-provider';
|
import type { SettingDefinition } from 'librechat-data-provider';
|
||||||
import type { TModelSelectProps } from '~/common';
|
import type { TModelSelectProps } from '~/common';
|
||||||
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
||||||
import { presetSettings } from '~/components/SidePanel/Parameters/settings';
|
import { presetSettings } from 'librechat-data-provider';
|
||||||
|
|
||||||
export default function BedrockSettings({
|
export default function BedrockSettings({
|
||||||
conversation,
|
conversation,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { getSettingsKeys } from 'librechat-data-provider';
|
import { getSettingsKeys } from 'librechat-data-provider';
|
||||||
import type { SettingDefinition, DynamicSettingProps } from 'librechat-data-provider';
|
import type { SettingDefinition } from 'librechat-data-provider';
|
||||||
import type { TModelSelectProps } from '~/common';
|
import type { TModelSelectProps } from '~/common';
|
||||||
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
||||||
import { presetSettings } from '~/components/SidePanel/Parameters/settings';
|
import { presetSettings } from 'librechat-data-provider';
|
||||||
|
|
||||||
export default function OpenAISettings({
|
export default function OpenAISettings({
|
||||||
conversation,
|
conversation,
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
import React, { useMemo, useEffect } from 'react';
|
import React, { useMemo, useEffect } from 'react';
|
||||||
import { ChevronLeft, RotateCcw } from 'lucide-react';
|
import { ChevronLeft, RotateCcw } from 'lucide-react';
|
||||||
import { useFormContext, useWatch, Controller } from 'react-hook-form';
|
import { useFormContext, useWatch, Controller } from 'react-hook-form';
|
||||||
import { getSettingsKeys, alternateName } from 'librechat-data-provider';
|
import {
|
||||||
|
getSettingsKeys,
|
||||||
|
alternateName,
|
||||||
|
agentParamSettings,
|
||||||
|
SettingDefinition,
|
||||||
|
} from 'librechat-data-provider';
|
||||||
import type * as t from 'librechat-data-provider';
|
import type * as t from 'librechat-data-provider';
|
||||||
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
|
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
|
||||||
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
||||||
import { agentSettings } from '~/components/SidePanel/Parameters/settings';
|
|
||||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||||
import { useGetEndpointsQuery } from '~/data-provider';
|
import { useGetEndpointsQuery } from '~/data-provider';
|
||||||
import { getEndpointField, cn } from '~/utils';
|
import { getEndpointField, cn } from '~/utils';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
import { Panel } from '~/common';
|
import { Panel } from '~/common';
|
||||||
|
import keyBy from 'lodash/keyBy';
|
||||||
|
|
||||||
export default function ModelPanel({
|
export default function ModelPanel({
|
||||||
setActivePanel,
|
setActivePanel,
|
||||||
|
@ -52,7 +57,7 @@ export default function ModelPanel({
|
||||||
}
|
}
|
||||||
}, [provider, models, modelsData, setValue, model]);
|
}, [provider, models, modelsData, setValue, model]);
|
||||||
|
|
||||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
const { data: endpointsConfig = {} } = useGetEndpointsQuery();
|
||||||
|
|
||||||
const bedrockRegions = useMemo(() => {
|
const bedrockRegions = useMemo(() => {
|
||||||
return endpointsConfig?.[provider]?.availableRegions ?? [];
|
return endpointsConfig?.[provider]?.availableRegions ?? [];
|
||||||
|
@ -63,10 +68,18 @@ export default function ModelPanel({
|
||||||
[provider, endpointsConfig],
|
[provider, endpointsConfig],
|
||||||
);
|
);
|
||||||
|
|
||||||
const parameters = useMemo(() => {
|
const parameters = useMemo((): SettingDefinition[] => {
|
||||||
|
const customParams = endpointsConfig[provider]?.customParams ?? {};
|
||||||
const [combinedKey, endpointKey] = getSettingsKeys(endpointType ?? provider, model ?? '');
|
const [combinedKey, endpointKey] = getSettingsKeys(endpointType ?? provider, model ?? '');
|
||||||
return agentSettings[combinedKey] ?? agentSettings[endpointKey];
|
const overriddenEndpointKey = customParams.defaultParamsEndpoint ?? endpointKey;
|
||||||
}, [endpointType, model, provider]);
|
const defaultParams =
|
||||||
|
agentParamSettings[combinedKey] ?? agentParamSettings[overriddenEndpointKey] ?? [];
|
||||||
|
const overriddenParams = endpointsConfig[provider]?.customParams?.paramDefinitions ?? [];
|
||||||
|
const overriddenParamsMap = keyBy(overriddenParams, 'key');
|
||||||
|
return defaultParams.map(
|
||||||
|
(param) => (overriddenParamsMap[param.key] as SettingDefinition) ?? param,
|
||||||
|
);
|
||||||
|
}, [endpointType, endpointsConfig, model, provider]);
|
||||||
|
|
||||||
const setOption = (optionKey: keyof t.AgentModelParameters) => (value: t.AgentParameterValue) => {
|
const setOption = (optionKey: keyof t.AgentModelParameters) => (value: t.AgentParameterValue) => {
|
||||||
setValue(`model_parameters.${optionKey}`, value);
|
setValue(`model_parameters.${optionKey}`, value);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { OptionTypes } from 'librechat-data-provider';
|
import { OptionTypes } from 'librechat-data-provider';
|
||||||
import type { DynamicSettingProps } from 'librechat-data-provider';
|
import type { DynamicSettingProps } from 'librechat-data-provider';
|
||||||
import { Label, Checkbox, HoverCard, HoverCardTrigger } from '~/components/ui';
|
import { Label, Checkbox, HoverCard, HoverCardTrigger } from '~/components/ui';
|
||||||
import { TranslationKeys, useLocalize, useParameterEffects } from '~/hooks';
|
import { TranslationKeys, useLocalize, useDebouncedInput, useParameterEffects } from '~/hooks';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
import OptionHover from './OptionHover';
|
import OptionHover from './OptionHover';
|
||||||
import { ESide } from '~/common';
|
import { ESide } from '~/common';
|
||||||
|
@ -23,23 +23,20 @@ function DynamicCheckbox({
|
||||||
}: DynamicSettingProps) {
|
}: DynamicSettingProps) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { preset } = useChatContext();
|
const { preset } = useChatContext();
|
||||||
const [inputValue, setInputValue] = useState<boolean>(!!(defaultValue as boolean | undefined));
|
|
||||||
|
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<boolean>({
|
||||||
|
optionKey: settingKey,
|
||||||
|
initialValue: optionType !== OptionTypes.Custom ? conversation?.[settingKey] : defaultValue,
|
||||||
|
setter: () => ({}),
|
||||||
|
setOption,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedValue = useMemo(() => {
|
const selectedValue = useMemo(() => {
|
||||||
if (optionType === OptionTypes.Custom) {
|
|
||||||
// TODO: custom logic, add to payload but not to conversation
|
|
||||||
return inputValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return conversation?.[settingKey] ?? defaultValue;
|
return conversation?.[settingKey] ?? defaultValue;
|
||||||
}, [conversation, defaultValue, optionType, settingKey, inputValue]);
|
}, [conversation, defaultValue, settingKey]);
|
||||||
|
|
||||||
const handleCheckedChange = (checked: boolean) => {
|
const handleCheckedChange = (checked: boolean) => {
|
||||||
if (optionType === OptionTypes.Custom) {
|
setInputValue(checked);
|
||||||
// TODO: custom logic, add to payload but not to conversation
|
|
||||||
setInputValue(checked);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOption(settingKey)(checked);
|
setOption(settingKey)(checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,8 +46,7 @@ function DynamicCheckbox({
|
||||||
defaultValue,
|
defaultValue,
|
||||||
conversation,
|
conversation,
|
||||||
inputValue,
|
inputValue,
|
||||||
setInputValue,
|
setInputValue: setLocalValue,
|
||||||
preventDelayedUpdate: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { useMemo, useState, useCallback } from 'react';
|
import { useMemo, useState, useCallback } from 'react';
|
||||||
import { OptionTypes } from 'librechat-data-provider';
|
|
||||||
import type { DynamicSettingProps } from 'librechat-data-provider';
|
import type { DynamicSettingProps } from 'librechat-data-provider';
|
||||||
import { Label, HoverCard, HoverCardTrigger } from '~/components/ui';
|
import { Label, HoverCard, HoverCardTrigger } from '~/components/ui';
|
||||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||||
|
@ -16,7 +15,6 @@ function DynamicCombobox({
|
||||||
description = '',
|
description = '',
|
||||||
columnSpan,
|
columnSpan,
|
||||||
setOption,
|
setOption,
|
||||||
optionType,
|
|
||||||
options: _options,
|
options: _options,
|
||||||
items: _items,
|
items: _items,
|
||||||
showLabel = true,
|
showLabel = true,
|
||||||
|
@ -36,11 +34,8 @@ function DynamicCombobox({
|
||||||
const [inputValue, setInputValue] = useState<string | null>(null);
|
const [inputValue, setInputValue] = useState<string | null>(null);
|
||||||
|
|
||||||
const selectedValue = useMemo(() => {
|
const selectedValue = useMemo(() => {
|
||||||
if (optionType === OptionTypes.Custom) {
|
|
||||||
return inputValue;
|
|
||||||
}
|
|
||||||
return conversation?.[settingKey] ?? defaultValue;
|
return conversation?.[settingKey] ?? defaultValue;
|
||||||
}, [conversation, defaultValue, optionType, settingKey, inputValue]);
|
}, [conversation, defaultValue, settingKey]);
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
if (_items != null) {
|
if (_items != null) {
|
||||||
|
@ -54,13 +49,10 @@ function DynamicCombobox({
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
if (optionType === OptionTypes.Custom) {
|
setInputValue(value);
|
||||||
setInputValue(value);
|
setOption(settingKey)(value);
|
||||||
} else {
|
|
||||||
setOption(settingKey)(value);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[optionType, setOption, settingKey],
|
[setOption, settingKey],
|
||||||
);
|
);
|
||||||
|
|
||||||
useParameterEffects({
|
useParameterEffects({
|
||||||
|
|
|
@ -12,7 +12,6 @@ function DynamicInput({
|
||||||
settingKey,
|
settingKey,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
description = '',
|
description = '',
|
||||||
type = 'string',
|
|
||||||
columnSpan,
|
columnSpan,
|
||||||
setOption,
|
setOption,
|
||||||
optionType,
|
optionType,
|
||||||
|
@ -28,7 +27,7 @@ function DynamicInput({
|
||||||
const { preset } = useChatContext();
|
const { preset } = useChatContext();
|
||||||
|
|
||||||
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<string | number>({
|
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<string | number>({
|
||||||
optionKey: optionType !== OptionTypes.Custom ? settingKey : undefined,
|
optionKey: settingKey,
|
||||||
initialValue: optionType !== OptionTypes.Custom ? conversation?.[settingKey] : defaultValue,
|
initialValue: optionType !== OptionTypes.Custom ? conversation?.[settingKey] : defaultValue,
|
||||||
setter: () => ({}),
|
setter: () => ({}),
|
||||||
setOption,
|
setOption,
|
||||||
|
@ -44,17 +43,7 @@ function DynamicInput({
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = e.target.value;
|
setInputValue(e, !isNaN(Number(e.target.value)));
|
||||||
if (type !== 'number') {
|
|
||||||
setInputValue(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === '') {
|
|
||||||
setInputValue(e);
|
|
||||||
} else if (!isNaN(Number(value))) {
|
|
||||||
setInputValue(e, true);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -33,7 +33,7 @@ function DynamicSlider({
|
||||||
);
|
);
|
||||||
|
|
||||||
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<string | number>({
|
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<string | number>({
|
||||||
optionKey: optionType !== OptionTypes.Custom ? settingKey : undefined,
|
optionKey: settingKey,
|
||||||
initialValue: optionType !== OptionTypes.Custom ? conversation?.[settingKey] : defaultValue,
|
initialValue: optionType !== OptionTypes.Custom ? conversation?.[settingKey] : defaultValue,
|
||||||
setter: () => ({}),
|
setter: () => ({}),
|
||||||
setOption,
|
setOption,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { useState, useMemo } from 'react';
|
import { useState } from 'react';
|
||||||
import { OptionTypes } from 'librechat-data-provider';
|
|
||||||
import type { DynamicSettingProps } from 'librechat-data-provider';
|
import type { DynamicSettingProps } from 'librechat-data-provider';
|
||||||
import { Label, Switch, HoverCard, HoverCardTrigger } from '~/components/ui';
|
import { Label, Switch, HoverCard, HoverCardTrigger } from '~/components/ui';
|
||||||
import { TranslationKeys, useLocalize, useParameterEffects } from '~/hooks';
|
import { TranslationKeys, useLocalize, useParameterEffects } from '~/hooks';
|
||||||
|
@ -14,7 +13,6 @@ function DynamicSwitch({
|
||||||
description = '',
|
description = '',
|
||||||
columnSpan,
|
columnSpan,
|
||||||
setOption,
|
setOption,
|
||||||
optionType,
|
|
||||||
readonly = false,
|
readonly = false,
|
||||||
showDefault = false,
|
showDefault = false,
|
||||||
labelCode = false,
|
labelCode = false,
|
||||||
|
@ -34,21 +32,10 @@ function DynamicSwitch({
|
||||||
preventDelayedUpdate: true,
|
preventDelayedUpdate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedValue = useMemo(() => {
|
const selectedValue = conversation?.[settingKey] ?? defaultValue;
|
||||||
if (optionType === OptionTypes.Custom) {
|
|
||||||
// TODO: custom logic, add to payload but not to conversation
|
|
||||||
return inputValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return conversation?.[settingKey] ?? defaultValue;
|
|
||||||
}, [conversation, defaultValue, optionType, settingKey, inputValue]);
|
|
||||||
|
|
||||||
const handleCheckedChange = (checked: boolean) => {
|
const handleCheckedChange = (checked: boolean) => {
|
||||||
if (optionType === OptionTypes.Custom) {
|
setInputValue(checked);
|
||||||
// TODO: custom logic, add to payload but not to conversation
|
|
||||||
setInputValue(checked);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOption(settingKey)(checked);
|
setOption(settingKey)(checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,7 +52,7 @@ function DynamicSwitch({
|
||||||
htmlFor={`${settingKey}-dynamic-switch`}
|
htmlFor={`${settingKey}-dynamic-switch`}
|
||||||
className="text-left text-sm font-medium"
|
className="text-left text-sm font-medium"
|
||||||
>
|
>
|
||||||
{labelCode ? localize(label as TranslationKeys) ?? label : label || settingKey}{' '}
|
{labelCode ? (localize(label as TranslationKeys) ?? label) : label || settingKey}{' '}
|
||||||
{showDefault && (
|
{showDefault && (
|
||||||
<small className="opacity-40">
|
<small className="opacity-40">
|
||||||
({localize('com_endpoint_default')}:{' '}
|
({localize('com_endpoint_default')}:{' '}
|
||||||
|
@ -84,7 +71,11 @@ function DynamicSwitch({
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
{description && (
|
{description && (
|
||||||
<OptionHover
|
<OptionHover
|
||||||
description={descriptionCode ? localize(description as TranslationKeys) ?? description : description}
|
description={
|
||||||
|
descriptionCode
|
||||||
|
? (localize(description as TranslationKeys) ?? description)
|
||||||
|
: description
|
||||||
|
}
|
||||||
side={ESide.Left}
|
side={ESide.Left}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { useState, useMemo, useCallback, useRef } from 'react';
|
import { useState, useMemo, useCallback, useRef } from 'react';
|
||||||
import { OptionTypes } from 'librechat-data-provider';
|
|
||||||
import type { DynamicSettingProps } from 'librechat-data-provider';
|
import type { DynamicSettingProps } from 'librechat-data-provider';
|
||||||
import { Label, Input, HoverCard, HoverCardTrigger, Tag } from '~/components/ui';
|
import { Label, Input, HoverCard, HoverCardTrigger, Tag } from '~/components/ui';
|
||||||
import { useChatContext, useToastContext } from '~/Providers';
|
import { useChatContext, useToastContext } from '~/Providers';
|
||||||
import { TranslationKeys, useLocalize, useParameterEffects } from '~/hooks';
|
import { TranslationKeys, useLocalize, useParameterEffects } from '~/hooks';
|
||||||
import { cn, defaultTextProps } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
import OptionHover from './OptionHover';
|
import OptionHover from './OptionHover';
|
||||||
import { ESide } from '~/common';
|
import { ESide } from '~/common';
|
||||||
|
|
||||||
|
@ -15,7 +14,6 @@ function DynamicTags({
|
||||||
description = '',
|
description = '',
|
||||||
columnSpan,
|
columnSpan,
|
||||||
setOption,
|
setOption,
|
||||||
optionType,
|
|
||||||
placeholder = '',
|
placeholder = '',
|
||||||
readonly = false,
|
readonly = false,
|
||||||
showDefault = false,
|
showDefault = false,
|
||||||
|
@ -38,14 +36,10 @@ function DynamicTags({
|
||||||
|
|
||||||
const updateState = useCallback(
|
const updateState = useCallback(
|
||||||
(update: string[]) => {
|
(update: string[]) => {
|
||||||
if (optionType === OptionTypes.Custom) {
|
setTags(update);
|
||||||
// TODO: custom logic, add to payload but not to conversation
|
|
||||||
setTags(update);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setOption(settingKey)(update);
|
setOption(settingKey)(update);
|
||||||
},
|
},
|
||||||
[optionType, setOption, settingKey],
|
[setOption, settingKey],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTagClick = useCallback(() => {
|
const onTagClick = useCallback(() => {
|
||||||
|
@ -54,18 +48,10 @@ function DynamicTags({
|
||||||
}
|
}
|
||||||
}, [inputRef]);
|
}, [inputRef]);
|
||||||
|
|
||||||
const currentTags: string[] | undefined = useMemo(() => {
|
const currentValue = conversation?.[settingKey];
|
||||||
if (optionType === OptionTypes.Custom) {
|
const currentTags = useMemo(() => {
|
||||||
// TODO: custom logic, add to payload but not to conversation
|
return currentValue ?? defaultValue ?? [];
|
||||||
return tags;
|
}, [currentValue, defaultValue]);
|
||||||
}
|
|
||||||
|
|
||||||
if (!conversation?.[settingKey]) {
|
|
||||||
return defaultValue ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return conversation[settingKey];
|
|
||||||
}, [conversation, defaultValue, optionType, settingKey, tags]);
|
|
||||||
|
|
||||||
const onTagRemove = useCallback(
|
const onTagRemove = useCallback(
|
||||||
(indexToRemove: number) => {
|
(indexToRemove: number) => {
|
||||||
|
@ -75,7 +61,7 @@ function DynamicTags({
|
||||||
|
|
||||||
if (minTags != null && currentTags.length <= minTags) {
|
if (minTags != null && currentTags.length <= minTags) {
|
||||||
showToast({
|
showToast({
|
||||||
message: localize('com_ui_min_tags',{ 0: minTags + '' }),
|
message: localize('com_ui_min_tags', { 0: minTags + '' }),
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -126,7 +112,7 @@ function DynamicTags({
|
||||||
htmlFor={`${settingKey}-dynamic-input`}
|
htmlFor={`${settingKey}-dynamic-input`}
|
||||||
className="text-left text-sm font-medium"
|
className="text-left text-sm font-medium"
|
||||||
>
|
>
|
||||||
{labelCode ? localize(label as TranslationKeys) ?? label : label || settingKey}{' '}
|
{labelCode ? (localize(label as TranslationKeys) ?? label) : label || settingKey}{' '}
|
||||||
{showDefault && (
|
{showDefault && (
|
||||||
<small className="opacity-40">
|
<small className="opacity-40">
|
||||||
(
|
(
|
||||||
|
@ -174,7 +160,11 @@ function DynamicTags({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onChange={(e) => setTagText(e.target.value)}
|
onChange={(e) => setTagText(e.target.value)}
|
||||||
placeholder={placeholderCode ? localize(placeholder as TranslationKeys) ?? placeholder : placeholder}
|
placeholder={
|
||||||
|
placeholderCode
|
||||||
|
? (localize(placeholder as TranslationKeys) ?? placeholder)
|
||||||
|
: placeholder
|
||||||
|
}
|
||||||
className={cn('flex h-10 max-h-10 border-none bg-surface-secondary px-3 py-2')}
|
className={cn('flex h-10 max-h-10 border-none bg-surface-secondary px-3 py-2')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -182,7 +172,11 @@ function DynamicTags({
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
{description && (
|
{description && (
|
||||||
<OptionHover
|
<OptionHover
|
||||||
description={descriptionCode ? localize(description as TranslationKeys) ?? description : description}
|
description={
|
||||||
|
descriptionCode
|
||||||
|
? (localize(description as TranslationKeys) ?? description)
|
||||||
|
: description
|
||||||
|
}
|
||||||
side={descriptionSide as ESide}
|
side={descriptionSide as ESide}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { OptionTypes } from 'librechat-data-provider';
|
||||||
import type { DynamicSettingProps } from 'librechat-data-provider';
|
import type { DynamicSettingProps } from 'librechat-data-provider';
|
||||||
import { Label, TextareaAutosize, HoverCard, HoverCardTrigger } from '~/components/ui';
|
import { Label, TextareaAutosize, HoverCard, HoverCardTrigger } from '~/components/ui';
|
||||||
import { useLocalize, useDebouncedInput, useParameterEffects, TranslationKeys } from '~/hooks';
|
import { useLocalize, useDebouncedInput, useParameterEffects, TranslationKeys } from '~/hooks';
|
||||||
import { cn, defaultTextProps } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
import OptionHover from './OptionHover';
|
import OptionHover from './OptionHover';
|
||||||
import { ESide } from '~/common';
|
import { ESide } from '~/common';
|
||||||
|
@ -27,7 +27,7 @@ function DynamicTextarea({
|
||||||
const { preset } = useChatContext();
|
const { preset } = useChatContext();
|
||||||
|
|
||||||
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<string | null>({
|
const [setInputValue, inputValue, setLocalValue] = useDebouncedInput<string | null>({
|
||||||
optionKey: optionType !== OptionTypes.Custom ? settingKey : undefined,
|
optionKey: settingKey,
|
||||||
initialValue:
|
initialValue:
|
||||||
optionType !== OptionTypes.Custom
|
optionType !== OptionTypes.Custom
|
||||||
? (conversation?.[settingKey] as string)
|
? (conversation?.[settingKey] as string)
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { RotateCcw } from 'lucide-react';
|
import { RotateCcw } from 'lucide-react';
|
||||||
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
import React, { useMemo, useState, useEffect, useCallback } from 'react';
|
||||||
import { excludedKeys, getSettingsKeys, tConvoUpdateSchema } from 'librechat-data-provider';
|
import {
|
||||||
|
excludedKeys,
|
||||||
|
getSettingsKeys,
|
||||||
|
tConvoUpdateSchema,
|
||||||
|
paramSettings,
|
||||||
|
SettingDefinition,
|
||||||
|
} from 'librechat-data-provider';
|
||||||
import type { TPreset } from 'librechat-data-provider';
|
import type { TPreset } from 'librechat-data-provider';
|
||||||
import { SaveAsPresetDialog } from '~/components/Endpoints';
|
import { SaveAsPresetDialog } from '~/components/Endpoints';
|
||||||
import { useSetIndexOptions, useLocalize } from '~/hooks';
|
import { useSetIndexOptions, useLocalize } from '~/hooks';
|
||||||
|
@ -8,7 +14,7 @@ import { useGetEndpointsQuery } from '~/data-provider';
|
||||||
import { getEndpointField, logger } from '~/utils';
|
import { getEndpointField, logger } from '~/utils';
|
||||||
import { componentMapping } from './components';
|
import { componentMapping } from './components';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
import { settings } from './settings';
|
import keyBy from 'lodash/keyBy';
|
||||||
|
|
||||||
export default function Parameters() {
|
export default function Parameters() {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
@ -18,7 +24,9 @@ export default function Parameters() {
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const [preset, setPreset] = useState<TPreset | null>(null);
|
const [preset, setPreset] = useState<TPreset | null>(null);
|
||||||
|
|
||||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
const { data: endpointsConfig = {} } = useGetEndpointsQuery();
|
||||||
|
const provider = conversation?.endpoint ?? '';
|
||||||
|
const model = conversation?.model ?? '';
|
||||||
|
|
||||||
const bedrockRegions = useMemo(() => {
|
const bedrockRegions = useMemo(() => {
|
||||||
return endpointsConfig?.[conversation?.endpoint ?? '']?.availableRegions ?? [];
|
return endpointsConfig?.[conversation?.endpoint ?? '']?.availableRegions ?? [];
|
||||||
|
@ -29,13 +37,17 @@ export default function Parameters() {
|
||||||
[conversation?.endpoint, endpointsConfig],
|
[conversation?.endpoint, endpointsConfig],
|
||||||
);
|
);
|
||||||
|
|
||||||
const parameters = useMemo(() => {
|
const parameters = useMemo((): SettingDefinition[] => {
|
||||||
const [combinedKey, endpointKey] = getSettingsKeys(
|
const customParams = endpointsConfig[provider]?.customParams ?? {};
|
||||||
endpointType ?? conversation?.endpoint ?? '',
|
const [combinedKey, endpointKey] = getSettingsKeys(endpointType ?? provider, model);
|
||||||
conversation?.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.map(
|
||||||
|
(param) => (overriddenParamsMap[param.key] as SettingDefinition) ?? param,
|
||||||
);
|
);
|
||||||
return settings[combinedKey] ?? settings[endpointKey];
|
}, [endpointType, endpointsConfig, model, provider]);
|
||||||
}, [conversation, endpointType]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
|
|
|
@ -279,6 +279,12 @@ export const endpointSchema = baseEndpointSchema.merge(
|
||||||
headers: z.record(z.any()).optional(),
|
headers: z.record(z.any()).optional(),
|
||||||
addParams: z.record(z.any()).optional(),
|
addParams: z.record(z.any()).optional(),
|
||||||
dropParams: z.array(z.string()).optional(),
|
dropParams: z.array(z.string()).optional(),
|
||||||
|
customParams: z
|
||||||
|
.object({
|
||||||
|
defaultParamsEndpoint: z.string().default('custom'),
|
||||||
|
paramDefinitions: z.array(z.record(z.any())).optional(),
|
||||||
|
})
|
||||||
|
.strict(),
|
||||||
customOrder: z.number().optional(),
|
customOrder: z.number().optional(),
|
||||||
directEndpoint: z.boolean().optional(),
|
directEndpoint: z.boolean().optional(),
|
||||||
titleMessageRole: z.string().optional(),
|
titleMessageRole: z.string().optional(),
|
||||||
|
|
|
@ -467,7 +467,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default value checks */
|
/* Default value checks */
|
||||||
if (setting.type === SettingTypes.Number && isNaN(setting.default as number)) {
|
if (setting.type === SettingTypes.Number && isNaN(setting.default as number) && setting.default != null) {
|
||||||
errors.push({
|
errors.push({
|
||||||
code: ZodIssueCode.custom,
|
code: ZodIssueCode.custom,
|
||||||
message: `Invalid default value for setting ${setting.key}. Must be a number.`,
|
message: `Invalid default value for setting ${setting.key}. Must be a number.`,
|
||||||
|
@ -475,7 +475,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting.type === SettingTypes.Boolean && typeof setting.default !== 'boolean') {
|
if (setting.type === SettingTypes.Boolean && typeof setting.default !== 'boolean' && setting.default != null) {
|
||||||
errors.push({
|
errors.push({
|
||||||
code: ZodIssueCode.custom,
|
code: ZodIssueCode.custom,
|
||||||
message: `Invalid default value for setting ${setting.key}. Must be a boolean.`,
|
message: `Invalid default value for setting ${setting.key}. Must be a boolean.`,
|
||||||
|
@ -485,7 +485,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(setting.type === SettingTypes.String || setting.type === SettingTypes.Enum) &&
|
(setting.type === SettingTypes.String || setting.type === SettingTypes.Enum) &&
|
||||||
typeof setting.default !== 'string'
|
typeof setting.default !== 'string' && setting.default != null
|
||||||
) {
|
) {
|
||||||
errors.push({
|
errors.push({
|
||||||
code: ZodIssueCode.custom,
|
code: ZodIssueCode.custom,
|
||||||
|
|
|
@ -36,3 +36,4 @@ import * as dataService from './data-service';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export { default as createPayload } from './createPayload';
|
export { default as createPayload } from './createPayload';
|
||||||
|
export * from './parameterSettings';
|
||||||
|
|
|
@ -6,8 +6,8 @@ import {
|
||||||
ReasoningEffort,
|
ReasoningEffort,
|
||||||
BedrockProviders,
|
BedrockProviders,
|
||||||
anthropicSettings,
|
anthropicSettings,
|
||||||
} from 'librechat-data-provider';
|
} from './types';
|
||||||
import type { SettingsConfiguration, SettingDefinition } from 'librechat-data-provider';
|
import { SettingDefinition, SettingsConfiguration } from './generate';
|
||||||
|
|
||||||
// Base definitions
|
// Base definitions
|
||||||
const baseDefinitions: Record<string, SettingDefinition> = {
|
const baseDefinitions: Record<string, SettingDefinition> = {
|
||||||
|
@ -654,7 +654,7 @@ const bedrockGeneralCol2: SettingsConfiguration = [
|
||||||
bedrock.region,
|
bedrock.region,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const settings: Record<string, SettingsConfiguration | undefined> = {
|
export const paramSettings: Record<string, SettingsConfiguration | undefined> = {
|
||||||
[EModelEndpoint.openAI]: openAI,
|
[EModelEndpoint.openAI]: openAI,
|
||||||
[EModelEndpoint.azureOpenAI]: openAI,
|
[EModelEndpoint.azureOpenAI]: openAI,
|
||||||
[EModelEndpoint.custom]: openAI,
|
[EModelEndpoint.custom]: openAI,
|
||||||
|
@ -682,9 +682,9 @@ const bedrockGeneralColumns = {
|
||||||
export const presetSettings: Record<
|
export const presetSettings: Record<
|
||||||
string,
|
string,
|
||||||
| {
|
| {
|
||||||
col1: SettingsConfiguration;
|
col1: SettingsConfiguration;
|
||||||
col2: SettingsConfiguration;
|
col2: SettingsConfiguration;
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
> = {
|
> = {
|
||||||
[EModelEndpoint.openAI]: openAIColumns,
|
[EModelEndpoint.openAI]: openAIColumns,
|
||||||
|
@ -716,11 +716,11 @@ export const presetSettings: Record<
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const agentSettings: Record<string, SettingsConfiguration | undefined> = Object.entries(
|
export const agentParamSettings: Record<string, SettingsConfiguration | undefined> = Object.entries(
|
||||||
presetSettings,
|
presetSettings,
|
||||||
).reduce((acc, [key, value]) => {
|
).reduce<Record<string, SettingsConfiguration | undefined>>((acc, [key, value]) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
acc[key] = value.col2;
|
acc[key] = value.col2;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
|
@ -10,6 +10,7 @@ import type {
|
||||||
TConversationTag,
|
TConversationTag,
|
||||||
TBanner,
|
TBanner,
|
||||||
} from './schemas';
|
} from './schemas';
|
||||||
|
import { SettingDefinition } from './generate';
|
||||||
export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
|
export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
|
||||||
|
|
||||||
export * from './schemas';
|
export * from './schemas';
|
||||||
|
@ -268,6 +269,10 @@ export type TConfig = {
|
||||||
disableBuilder?: boolean;
|
disableBuilder?: boolean;
|
||||||
retrievalModels?: string[];
|
retrievalModels?: string[];
|
||||||
capabilities?: string[];
|
capabilities?: string[];
|
||||||
|
customParams?: {
|
||||||
|
defaultParamsEndpoint?: string;
|
||||||
|
paramDefinitions?: SettingDefinition[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TEndpointsConfig =
|
export type TEndpointsConfig =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue