🔃 refactor: Decouple Effects from AppService, move to data-schemas (#9974)

* chore: linting for `loadCustomConfig`

* refactor: decouple CDN init and variable/health checks from AppService

* refactor: move AppService to packages/data-schemas

* chore: update AppConfig import path to use data-schemas

* chore: update JsonSchemaType import path to use data-schemas

* refactor: update UserController to import webSearchKeys and redefine FunctionTool typedef

* chore: remove AppService.js

* refactor: update AppConfig interface to use Partial<TCustomConfig> and make paths and fileStrategies optional

* refactor: update checkConfig function to accept Partial<TCustomConfig>

* chore: fix types

* refactor: move handleRateLimits to startup checks as is an effect

* test: remove outdated rate limit tests from AppService.spec and add new handleRateLimits tests in checks.spec
This commit is contained in:
Danny Avila 2025-10-05 06:37:57 -04:00 committed by GitHub
parent 9ff608e6af
commit 838fb53208
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
73 changed files with 1383 additions and 1326 deletions

View file

@ -0,0 +1,24 @@
import { EModelEndpoint, agentsEndpointSchema } from 'librechat-data-provider';
import type { TCustomConfig, TAgentsEndpoint } from 'librechat-data-provider';
/**
* Sets up the Agents configuration from the config (`librechat.yaml`) file.
* If no agents config is defined, uses the provided defaults or parses empty object.
*
* @param config - The loaded custom configuration.
* @param [defaultConfig] - Default configuration from getConfigDefaults.
* @returns The Agents endpoint configuration.
*/
export function agentsConfigSetup(
config: Partial<TCustomConfig>,
defaultConfig?: Partial<TAgentsEndpoint>,
): Partial<TAgentsEndpoint> {
const agentsConfig = config?.endpoints?.[EModelEndpoint.agents];
if (!agentsConfig) {
return defaultConfig || agentsEndpointSchema.parse({});
}
const parsedConfig = agentsEndpointSchema.parse(agentsConfig);
return parsedConfig;
}

View file

@ -0,0 +1,70 @@
import logger from '~/config/winston';
import {
Capabilities,
EModelEndpoint,
assistantEndpointSchema,
defaultAssistantsVersion,
} from 'librechat-data-provider';
import type { TCustomConfig, TAssistantEndpoint } from 'librechat-data-provider';
/**
* Sets up the minimum, default Assistants configuration if Azure OpenAI Assistants option is enabled.
* @returns The Assistants endpoint configuration.
*/
export function azureAssistantsDefaults(): {
capabilities: TAssistantEndpoint['capabilities'];
version: TAssistantEndpoint['version'];
} {
return {
capabilities: [Capabilities.tools, Capabilities.actions, Capabilities.code_interpreter],
version: defaultAssistantsVersion.azureAssistants,
};
}
/**
* Sets up the Assistants configuration from the config (`librechat.yaml`) file.
* @param config - The loaded custom configuration.
* @param assistantsEndpoint - The Assistants endpoint name.
* - The previously loaded assistants configuration from Azure OpenAI Assistants option.
* @param [prevConfig]
* @returns The Assistants endpoint configuration.
*/
export function assistantsConfigSetup(
config: Partial<TCustomConfig>,
assistantsEndpoint: EModelEndpoint.assistants | EModelEndpoint.azureAssistants,
prevConfig: Partial<TAssistantEndpoint> = {},
): Partial<TAssistantEndpoint> {
const assistantsConfig = config.endpoints?.[assistantsEndpoint];
const parsedConfig = assistantEndpointSchema.parse(assistantsConfig);
if (assistantsConfig?.supportedIds?.length && assistantsConfig.excludedIds?.length) {
logger.warn(
`Configuration conflict: The '${assistantsEndpoint}' endpoint has both 'supportedIds' and 'excludedIds' defined. The 'excludedIds' will be ignored.`,
);
}
if (
assistantsConfig?.privateAssistants &&
(assistantsConfig.supportedIds?.length || assistantsConfig.excludedIds?.length)
) {
logger.warn(
`Configuration conflict: The '${assistantsEndpoint}' endpoint has both 'privateAssistants' and 'supportedIds' or 'excludedIds' defined. The 'supportedIds' and 'excludedIds' will be ignored.`,
);
}
return {
...prevConfig,
retrievalModels: parsedConfig.retrievalModels,
disableBuilder: parsedConfig.disableBuilder,
pollIntervalMs: parsedConfig.pollIntervalMs,
supportedIds: parsedConfig.supportedIds,
capabilities: parsedConfig.capabilities,
excludedIds: parsedConfig.excludedIds,
privateAssistants: parsedConfig.privateAssistants,
timeoutMs: parsedConfig.timeoutMs,
streamRate: parsedConfig.streamRate,
titlePrompt: parsedConfig.titlePrompt,
titleMethod: parsedConfig.titleMethod,
titleModel: parsedConfig.titleModel,
titleEndpoint: parsedConfig.titleEndpoint,
titlePromptTemplate: parsedConfig.titlePromptTemplate,
};
}

View file

@ -0,0 +1,71 @@
import logger from '~/config/winston';
import {
EModelEndpoint,
validateAzureGroups,
mapModelToAzureConfig,
} from 'librechat-data-provider';
import type { TCustomConfig, TAzureConfig } from 'librechat-data-provider';
/**
* Sets up the Azure OpenAI configuration from the config (`librechat.yaml`) file.
* @param config - The loaded custom configuration.
* @returns The Azure OpenAI configuration.
*/
export function azureConfigSetup(config: Partial<TCustomConfig>): TAzureConfig {
const azureConfig = config.endpoints?.[EModelEndpoint.azureOpenAI];
if (!azureConfig) {
throw new Error('Azure OpenAI configuration is missing.');
}
const { groups, ...azureConfiguration } = azureConfig;
const { isValid, modelNames, modelGroupMap, groupMap, errors } = validateAzureGroups(groups);
if (!isValid) {
const errorString = errors.join('\n');
const errorMessage = 'Invalid Azure OpenAI configuration:\n' + errorString;
logger.error(errorMessage);
throw new Error(errorMessage);
}
const assistantModels: string[] = [];
const assistantGroups = new Set<string>();
for (const modelName of modelNames) {
mapModelToAzureConfig({ modelName, modelGroupMap, groupMap });
const groupName = modelGroupMap?.[modelName]?.group;
const modelGroup = groupMap?.[groupName];
const supportsAssistants = modelGroup?.assistants || modelGroup?.[modelName]?.assistants;
if (supportsAssistants) {
assistantModels.push(modelName);
if (!assistantGroups.has(groupName)) {
assistantGroups.add(groupName);
}
}
}
if (azureConfiguration.assistants && assistantModels.length === 0) {
throw new Error(
'No Azure models are configured to support assistants. Please remove the `assistants` field or configure at least one model to support assistants.',
);
}
if (
azureConfiguration.assistants &&
process.env.ENDPOINTS &&
!process.env.ENDPOINTS.includes(EModelEndpoint.azureAssistants)
) {
logger.warn(
`Azure Assistants are configured, but the endpoint will not be accessible as it's not included in the ENDPOINTS environment variable.
Please add the value "${EModelEndpoint.azureAssistants}" to the ENDPOINTS list if expected.`,
);
}
return {
errors,
isValid,
groupMap,
modelNames,
modelGroupMap,
assistantModels,
assistantGroups: Array.from(assistantGroups),
...azureConfiguration,
};
}

View file

@ -0,0 +1,66 @@
import { EModelEndpoint } from 'librechat-data-provider';
import type { TCustomConfig, TAgentsEndpoint } from 'librechat-data-provider';
import type { AppConfig } from '~/types';
import { azureAssistantsDefaults, assistantsConfigSetup } from './assistants';
import { agentsConfigSetup } from './agents';
import { azureConfigSetup } from './azure';
/**
* Loads custom config endpoints
* @param [config]
* @param [agentsDefaults]
*/
export const loadEndpoints = (
config: Partial<TCustomConfig>,
agentsDefaults?: Partial<TAgentsEndpoint>,
) => {
const loadedEndpoints: AppConfig['endpoints'] = {};
const endpoints = config?.endpoints;
if (endpoints?.[EModelEndpoint.azureOpenAI]) {
loadedEndpoints[EModelEndpoint.azureOpenAI] = azureConfigSetup(config);
}
if (endpoints?.[EModelEndpoint.azureOpenAI]?.assistants) {
loadedEndpoints[EModelEndpoint.azureAssistants] = azureAssistantsDefaults();
}
if (endpoints?.[EModelEndpoint.azureAssistants]) {
loadedEndpoints[EModelEndpoint.azureAssistants] = assistantsConfigSetup(
config,
EModelEndpoint.azureAssistants,
loadedEndpoints[EModelEndpoint.azureAssistants],
);
}
if (endpoints?.[EModelEndpoint.assistants]) {
loadedEndpoints[EModelEndpoint.assistants] = assistantsConfigSetup(
config,
EModelEndpoint.assistants,
loadedEndpoints[EModelEndpoint.assistants],
);
}
loadedEndpoints[EModelEndpoint.agents] = agentsConfigSetup(config, agentsDefaults);
const endpointKeys = [
EModelEndpoint.openAI,
EModelEndpoint.google,
EModelEndpoint.custom,
EModelEndpoint.bedrock,
EModelEndpoint.anthropic,
];
endpointKeys.forEach((key) => {
const currentKey = key as keyof typeof endpoints;
if (endpoints?.[currentKey]) {
loadedEndpoints[currentKey] = endpoints[currentKey];
}
});
if (endpoints?.all) {
loadedEndpoints.all = endpoints.all;
}
return loadedEndpoints;
};

View file

@ -0,0 +1,6 @@
export * from './agents';
export * from './interface';
export * from './service';
export * from './specs';
export * from './turnstile';
export * from './web';

View file

@ -0,0 +1,61 @@
import { removeNullishValues } from 'librechat-data-provider';
import type { TCustomConfig, TConfigDefaults } from 'librechat-data-provider';
import type { AppConfig } from '~/types/app';
import { isMemoryEnabled } from './memory';
/**
* Loads the default interface object.
* @param params - The loaded custom configuration.
* @param params.config - The loaded custom configuration.
* @param params.configDefaults - The custom configuration default values.
* @returns default interface object.
*/
export async function loadDefaultInterface({
config,
configDefaults,
}: {
config?: Partial<TCustomConfig>;
configDefaults: TConfigDefaults;
}): Promise<AppConfig['interfaceConfig']> {
const { interface: interfaceConfig } = config ?? {};
const { interface: defaults } = configDefaults;
const hasModelSpecs = (config?.modelSpecs?.list?.length ?? 0) > 0;
const includesAddedEndpoints = (config?.modelSpecs?.addedEndpoints?.length ?? 0) > 0;
const memoryConfig = config?.memory;
const memoryEnabled = isMemoryEnabled(memoryConfig);
/** Only disable memories if memory config is present but disabled/invalid */
const shouldDisableMemories = memoryConfig && !memoryEnabled;
const loadedInterface: AppConfig['interfaceConfig'] = removeNullishValues({
// UI elements - use schema defaults
endpointsMenu:
interfaceConfig?.endpointsMenu ?? (hasModelSpecs ? false : defaults.endpointsMenu),
modelSelect:
interfaceConfig?.modelSelect ??
(hasModelSpecs ? includesAddedEndpoints : defaults.modelSelect),
parameters: interfaceConfig?.parameters ?? (hasModelSpecs ? false : defaults.parameters),
presets: interfaceConfig?.presets ?? (hasModelSpecs ? false : defaults.presets),
sidePanel: interfaceConfig?.sidePanel ?? defaults.sidePanel,
privacyPolicy: interfaceConfig?.privacyPolicy ?? defaults.privacyPolicy,
termsOfService: interfaceConfig?.termsOfService ?? defaults.termsOfService,
mcpServers: interfaceConfig?.mcpServers ?? defaults.mcpServers,
customWelcome: interfaceConfig?.customWelcome ?? defaults.customWelcome,
// Permissions - only include if explicitly configured
bookmarks: interfaceConfig?.bookmarks,
memories: shouldDisableMemories ? false : interfaceConfig?.memories,
prompts: interfaceConfig?.prompts,
multiConvo: interfaceConfig?.multiConvo,
agents: interfaceConfig?.agents,
temporaryChat: interfaceConfig?.temporaryChat,
runCode: interfaceConfig?.runCode,
webSearch: interfaceConfig?.webSearch,
fileSearch: interfaceConfig?.fileSearch,
fileCitations: interfaceConfig?.fileCitations,
peoplePicker: interfaceConfig?.peoplePicker,
marketplace: interfaceConfig?.marketplace,
});
return loadedInterface;
}

View file

@ -0,0 +1,28 @@
import { memorySchema } from 'librechat-data-provider';
import type { TCustomConfig, TMemoryConfig } from 'librechat-data-provider';
const hasValidAgent = (agent: TMemoryConfig['agent']) =>
!!agent &&
(('id' in agent && !!agent.id) ||
('provider' in agent && 'model' in agent && !!agent.provider && !!agent.model));
const isDisabled = (config?: TMemoryConfig | TCustomConfig['memory']) =>
!config || config.disabled === true;
export function loadMemoryConfig(config: TCustomConfig['memory']): TMemoryConfig | undefined {
if (!config) return undefined;
if (isDisabled(config)) return config as TMemoryConfig;
if (!hasValidAgent(config.agent)) {
return { ...config, disabled: true } as TMemoryConfig;
}
const charLimit = memorySchema.shape.charLimit.safeParse(config.charLimit).data ?? 10000;
return { ...config, charLimit };
}
export function isMemoryEnabled(config: TMemoryConfig | undefined): boolean {
if (isDisabled(config)) return false;
return hasValidAgent(config!.agent);
}

View file

@ -0,0 +1,15 @@
import { OCRStrategy } from 'librechat-data-provider';
import type { TCustomConfig } from 'librechat-data-provider';
export function loadOCRConfig(config?: TCustomConfig['ocr']): TCustomConfig['ocr'] | undefined {
if (!config) return;
const baseURL = config?.baseURL ?? '';
const apiKey = config?.apiKey ?? '';
const mistralModel = config?.mistralModel ?? '';
return {
apiKey,
baseURL,
mistralModel,
strategy: config?.strategy ?? OCRStrategy.MISTRAL_OCR,
};
}

View file

@ -0,0 +1,113 @@
import { EModelEndpoint, getConfigDefaults } from 'librechat-data-provider';
import type { TCustomConfig, FileSources, DeepPartial } from 'librechat-data-provider';
import type { AppConfig, FunctionTool } from '~/types/app';
import { loadDefaultInterface } from './interface';
import { loadTurnstileConfig } from './turnstile';
import { agentsConfigSetup } from './agents';
import { loadWebSearchConfig } from './web';
import { processModelSpecs } from './specs';
import { loadMemoryConfig } from './memory';
import { loadEndpoints } from './endpoints';
import { loadOCRConfig } from './ocr';
export type Paths = {
root: string;
uploads: string;
clientPath: string;
dist: string;
publicPath: string;
fonts: string;
assets: string;
imageOutput: string;
structuredTools: string;
pluginManifest: string;
};
/**
* Loads custom config and initializes app-wide variables.
* @function AppService
*/
export const AppService = async (params?: {
config: DeepPartial<TCustomConfig>;
paths?: Paths;
systemTools?: Record<string, FunctionTool>;
}): Promise<AppConfig> => {
const { config, paths, systemTools } = params || {};
if (!config) {
throw new Error('Config is required');
}
const configDefaults = getConfigDefaults();
const ocr = loadOCRConfig(config.ocr);
const webSearch = loadWebSearchConfig(config.webSearch);
const memory = loadMemoryConfig(config.memory);
const filteredTools = config.filteredTools;
const includedTools = config.includedTools;
const fileStrategy = (config.fileStrategy ?? configDefaults.fileStrategy) as
| FileSources.local
| FileSources.s3
| FileSources.firebase
| FileSources.azure_blob;
const startBalance = process.env.START_BALANCE;
const balance = config.balance ?? {
enabled: process.env.CHECK_BALANCE?.toLowerCase().trim() === 'true',
startBalance: startBalance ? parseInt(startBalance, 10) : undefined,
};
const transactions = config.transactions ?? configDefaults.transactions;
const imageOutputType = config?.imageOutputType ?? configDefaults.imageOutputType;
process.env.CDN_PROVIDER = fileStrategy;
const availableTools = systemTools;
const mcpConfig = config.mcpServers || null;
const registration = config.registration ?? configDefaults.registration;
const interfaceConfig = await loadDefaultInterface({ config, configDefaults });
const turnstileConfig = loadTurnstileConfig(config, configDefaults);
const speech = config.speech;
const defaultConfig = {
ocr,
paths,
config,
memory,
speech,
balance,
transactions,
mcpConfig,
webSearch,
fileStrategy,
registration,
filteredTools,
includedTools,
availableTools,
imageOutputType,
interfaceConfig,
turnstileConfig,
fileStrategies: config.fileStrategies,
};
const agentsDefaults = agentsConfigSetup(config);
if (!Object.keys(config).length) {
const appConfig = {
...defaultConfig,
endpoints: {
[EModelEndpoint.agents]: agentsDefaults,
},
};
return appConfig;
}
const loadedEndpoints = loadEndpoints(config, agentsDefaults);
const appConfig = {
...defaultConfig,
fileConfig: config?.fileConfig,
secureImageLinks: config?.secureImageLinks,
modelSpecs: processModelSpecs(config?.endpoints, config.modelSpecs, interfaceConfig),
endpoints: loadedEndpoints,
};
return appConfig;
};

View file

@ -0,0 +1,94 @@
import logger from '~/config/winston';
import { EModelEndpoint } from 'librechat-data-provider';
import type { TCustomConfig } from 'librechat-data-provider';
/**
* Normalize the endpoint name to system-expected value.
* @param name
*/
function normalizeEndpointName(name = ''): string {
return name.toLowerCase() === 'ollama' ? 'ollama' : name;
}
/**
* Sets up Model Specs from the config (`librechat.yaml`) file.
* @param [endpoints] - The loaded custom configuration for endpoints.
* @param [modelSpecs] - The loaded custom configuration for model specs.
* @param [interfaceConfig] - The loaded interface configuration.
* @returns The processed model specs, if any.
*/
export function processModelSpecs(
endpoints?: TCustomConfig['endpoints'],
_modelSpecs?: TCustomConfig['modelSpecs'],
interfaceConfig?: TCustomConfig['interface'],
): TCustomConfig['modelSpecs'] | undefined {
if (!_modelSpecs) {
return undefined;
}
const list = _modelSpecs.list;
const modelSpecs: typeof list = [];
const customEndpoints = endpoints?.[EModelEndpoint.custom] ?? [];
if (interfaceConfig?.modelSelect !== true && (_modelSpecs.addedEndpoints?.length ?? 0) > 0) {
logger.warn(
`To utilize \`addedEndpoints\`, which allows provider/model selections alongside model specs, set \`modelSelect: true\` in the interface configuration.
Example:
\`\`\`yaml
interface:
modelSelect: true
\`\`\`
`,
);
}
if (!list || list.length === 0) {
return undefined;
}
for (const spec of list) {
const currentEndpoint = spec.preset?.endpoint as EModelEndpoint | undefined;
if (!currentEndpoint) {
logger.warn(
'A model spec is missing the `endpoint` field within its `preset`. Skipping model spec...',
);
continue;
}
if (EModelEndpoint[currentEndpoint] && currentEndpoint !== EModelEndpoint.custom) {
modelSpecs.push(spec);
continue;
} else if (currentEndpoint === EModelEndpoint.custom) {
logger.warn(
`Model Spec with endpoint "${currentEndpoint}" is not supported. You must specify the name of the custom endpoint (case-sensitive, as defined in your config). Skipping model spec...`,
);
continue;
}
const normalizedName = normalizeEndpointName(currentEndpoint);
const endpoint = customEndpoints.find(
(customEndpoint) => normalizedName === normalizeEndpointName(customEndpoint.name),
);
if (!endpoint) {
logger.warn(`Model spec with endpoint "${currentEndpoint}" was skipped: Endpoint not found in configuration. The \`endpoint\` value must exactly match either a system-defined endpoint or a custom endpoint defined by the user.
For more information, see the documentation at https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/model_specs#endpoint`);
continue;
}
modelSpecs.push({
...spec,
preset: {
...spec.preset,
endpoint: normalizedName,
},
});
}
return {
..._modelSpecs,
list: modelSpecs,
};
}

View file

@ -0,0 +1,45 @@
import logger from '~/config/winston';
import { removeNullishValues } from 'librechat-data-provider';
import type { TCustomConfig, TConfigDefaults } from 'librechat-data-provider';
/**
* Loads and maps the Cloudflare Turnstile configuration.
*
* Expected config structure:
*
* turnstile:
* siteKey: "your-site-key-here"
* options:
* language: "auto" // "auto" or an ISO 639-1 language code (e.g. en)
* size: "normal" // Options: "normal", "compact", "flexible", or "invisible"
*
* @param config - The loaded custom configuration.
* @param configDefaults - The custom configuration default values.
* @returns The mapped Turnstile configuration.
*/
export function loadTurnstileConfig(
config: Partial<TCustomConfig> | undefined,
configDefaults: TConfigDefaults,
): Partial<TCustomConfig['turnstile']> {
const { turnstile: customTurnstile } = config ?? {};
const { turnstile: defaults } = configDefaults;
const loadedTurnstile = removeNullishValues({
siteKey:
customTurnstile?.siteKey ?? (defaults as TCustomConfig['turnstile'] | undefined)?.siteKey,
options:
customTurnstile?.options ?? (defaults as TCustomConfig['turnstile'] | undefined)?.options,
});
const enabled = Boolean(loadedTurnstile.siteKey);
if (enabled) {
logger.debug(
'Turnstile is ENABLED with configuration:\n' + JSON.stringify(loadedTurnstile, null, 2),
);
} else {
logger.debug('Turnstile is DISABLED (no siteKey provided).');
}
return loadedTurnstile;
}

View file

@ -0,0 +1,84 @@
import { SafeSearchTypes } from 'librechat-data-provider';
import type { TCustomConfig } from 'librechat-data-provider';
import type { TWebSearchKeys, TWebSearchCategories } from '~/types/web';
export const webSearchAuth = {
providers: {
serper: {
serperApiKey: 1 as const,
},
searxng: {
searxngInstanceUrl: 1 as const,
/** Optional (0) */
searxngApiKey: 0 as const,
},
},
scrapers: {
firecrawl: {
firecrawlApiKey: 1 as const,
/** Optional (0) */
firecrawlApiUrl: 0 as const,
},
},
rerankers: {
jina: {
jinaApiKey: 1 as const,
/** Optional (0) */
jinaApiUrl: 0 as const,
},
cohere: { cohereApiKey: 1 as const },
},
};
/**
* Extracts all API keys from the webSearchAuth configuration object
*/
export function getWebSearchKeys(): TWebSearchKeys[] {
const keys: TWebSearchKeys[] = [];
// Iterate through each category (providers, scrapers, rerankers)
for (const category of Object.keys(webSearchAuth)) {
const categoryObj = webSearchAuth[category as TWebSearchCategories];
// Iterate through each service within the category
for (const service of Object.keys(categoryObj)) {
const serviceObj = categoryObj[service as keyof typeof categoryObj];
// Extract the API keys from the service
for (const key of Object.keys(serviceObj)) {
keys.push(key as TWebSearchKeys);
}
}
}
return keys;
}
export const webSearchKeys: TWebSearchKeys[] = getWebSearchKeys();
export function loadWebSearchConfig(
config: TCustomConfig['webSearch'],
): TCustomConfig['webSearch'] {
const serperApiKey = config?.serperApiKey ?? '${SERPER_API_KEY}';
const searxngInstanceUrl = config?.searxngInstanceUrl ?? '${SEARXNG_INSTANCE_URL}';
const searxngApiKey = config?.searxngApiKey ?? '${SEARXNG_API_KEY}';
const firecrawlApiKey = config?.firecrawlApiKey ?? '${FIRECRAWL_API_KEY}';
const firecrawlApiUrl = config?.firecrawlApiUrl ?? '${FIRECRAWL_API_URL}';
const jinaApiKey = config?.jinaApiKey ?? '${JINA_API_KEY}';
const jinaApiUrl = config?.jinaApiUrl ?? '${JINA_API_URL}';
const cohereApiKey = config?.cohereApiKey ?? '${COHERE_API_KEY}';
const safeSearch = config?.safeSearch ?? SafeSearchTypes.MODERATE;
return {
...config,
safeSearch,
jinaApiKey,
jinaApiUrl,
cohereApiKey,
serperApiKey,
searxngInstanceUrl,
searxngApiKey,
firecrawlApiKey,
firecrawlApiUrl,
};
}

View file

@ -1,3 +1,4 @@
export * from './app';
export * from './common';
export * from './crypto';
export * from './schema';

View file

@ -1,4 +1,5 @@
import pluginAuthSchema, { IPluginAuth } from '~/schema/pluginAuth';
import pluginAuthSchema from '~/schema/pluginAuth';
import type { IPluginAuth } from '~/types/pluginAuth';
/**
* Creates or returns the PluginAuth model using the provided mongoose instance and schema

View file

@ -1,4 +1,5 @@
import promptSchema, { IPrompt } from '~/schema/prompt';
import promptSchema from '~/schema/prompt';
import type { IPrompt } from '~/types/prompts';
/**
* Creates or returns the Prompt model using the provided mongoose instance and schema

View file

@ -1,4 +1,5 @@
import promptGroupSchema, { IPromptGroupDocument } from '~/schema/promptGroup';
import promptGroupSchema from '~/schema/promptGroup';
import type { IPromptGroupDocument } from '~/types/prompts';
/**
* Creates or returns the PromptGroup model using the provided mongoose instance and schema

View file

@ -0,0 +1,116 @@
import type {
TEndpoint,
FileSources,
TAzureConfig,
TCustomConfig,
TMemoryConfig,
EModelEndpoint,
TAgentsEndpoint,
TCustomEndpoints,
TAssistantEndpoint,
} from 'librechat-data-provider';
export type JsonSchemaType = {
type: 'string' | 'number' | 'integer' | 'float' | 'boolean' | 'array' | 'object';
enum?: string[];
items?: JsonSchemaType;
properties?: Record<string, JsonSchemaType>;
required?: string[];
description?: string;
additionalProperties?: boolean | JsonSchemaType;
};
export type ConvertJsonSchemaToZodOptions = {
allowEmptyObject?: boolean;
dropFields?: string[];
transformOneOfAnyOf?: boolean;
};
export interface FunctionTool {
type: 'function';
function: {
description: string;
name: string;
parameters: JsonSchemaType;
};
}
/**
* Application configuration object
* Based on the configuration defined in api/server/services/Config/getAppConfig.js
*/
export interface AppConfig {
/** The main custom configuration */
config: Partial<TCustomConfig>;
/** OCR configuration */
ocr?: TCustomConfig['ocr'];
/** File paths configuration */
paths?: {
uploads: string;
imageOutput: string;
publicPath: string;
[key: string]: string;
};
/** Memory configuration */
memory?: TMemoryConfig;
/** Web search configuration */
webSearch?: TCustomConfig['webSearch'];
/** File storage strategy ('local', 's3', 'firebase', 'azure_blob') */
fileStrategy: FileSources.local | FileSources.s3 | FileSources.firebase | FileSources.azure_blob;
/** File strategies configuration */
fileStrategies?: TCustomConfig['fileStrategies'];
/** Registration configurations */
registration?: TCustomConfig['registration'];
/** Actions configurations */
actions?: TCustomConfig['actions'];
/** Admin-filtered tools */
filteredTools?: string[];
/** Admin-included tools */
includedTools?: string[];
/** Image output type configuration */
imageOutputType: string;
/** Interface configuration */
interfaceConfig?: TCustomConfig['interface'];
/** Turnstile configuration */
turnstileConfig?: Partial<TCustomConfig['turnstile']>;
/** Balance configuration */
balance?: Partial<TCustomConfig['balance']>;
/** Transactions configuration */
transactions?: TCustomConfig['transactions'];
/** Speech configuration */
speech?: TCustomConfig['speech'];
/** MCP server configuration */
mcpConfig?: TCustomConfig['mcpServers'] | null;
/** File configuration */
fileConfig?: TCustomConfig['fileConfig'];
/** Secure image links configuration */
secureImageLinks?: TCustomConfig['secureImageLinks'];
/** Processed model specifications */
modelSpecs?: TCustomConfig['modelSpecs'];
/** Available tools */
availableTools?: Record<string, FunctionTool>;
endpoints?: {
/** OpenAI endpoint configuration */
openAI?: Partial<TEndpoint>;
/** Google endpoint configuration */
google?: Partial<TEndpoint>;
/** Bedrock endpoint configuration */
bedrock?: Partial<TEndpoint>;
/** Anthropic endpoint configuration */
anthropic?: Partial<TEndpoint>;
/** GPT plugins endpoint configuration */
gptPlugins?: Partial<TEndpoint>;
/** Azure OpenAI endpoint configuration */
azureOpenAI?: TAzureConfig;
/** Assistants endpoint configuration */
assistants?: Partial<TAssistantEndpoint>;
/** Azure assistants endpoint configuration */
azureAssistants?: Partial<TAssistantEndpoint>;
/** Agents endpoint configuration */
[EModelEndpoint.agents]?: Partial<TAgentsEndpoint>;
/** Custom endpoints configuration */
[EModelEndpoint.custom]?: TCustomEndpoints;
/** Global endpoint configuration */
all?: Partial<TEndpoint>;
};
}

View file

@ -1,6 +1,7 @@
import type { Types } from 'mongoose';
export type ObjectId = Types.ObjectId;
export * from './app';
export * from './user';
export * from './token';
export * from './convo';
@ -24,3 +25,5 @@ export * from './prompts';
export * from './accessRole';
export * from './aclEntry';
export * from './group';
/* Web */
export * from './web';

View file

@ -1,4 +1,5 @@
import { PermissionTypes, Permissions } from 'librechat-data-provider';
import type { DeepPartial } from 'librechat-data-provider';
import type { Document } from 'mongoose';
import { CursorPaginationParams } from '~/common';
@ -54,9 +55,6 @@ export interface IRole extends Document {
}
export type RolePermissions = IRole['permissions'];
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
export type RolePermissionsInput = DeepPartial<RolePermissions>;
export interface CreateRoleRequest {

View file

@ -0,0 +1,16 @@
import type { SearchCategories } from 'librechat-data-provider';
export type TWebSearchKeys =
| 'serperApiKey'
| 'searxngInstanceUrl'
| 'searxngApiKey'
| 'firecrawlApiKey'
| 'firecrawlApiUrl'
| 'jinaApiKey'
| 'jinaApiUrl'
| 'cohereApiKey';
export type TWebSearchCategories =
| SearchCategories.PROVIDERS
| SearchCategories.SCRAPERS
| SearchCategories.RERANKERS;