Merge branch 'main' into feature/entra-id-azure-integration

This commit is contained in:
victorbjor 2025-10-07 08:34:44 +02:00 committed by GitHub
commit be58d8e4f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
244 changed files with 6722 additions and 3399 deletions

View file

@ -1,6 +1,6 @@
{
"name": "librechat-data-provider",
"version": "0.8.010",
"version": "0.8.020",
"description": "data services for librechat apps",
"main": "dist/index.js",
"module": "dist/index.es.js",

View file

@ -10,44 +10,6 @@ import { extractEnvVariable, envVarRegex } from '../src/utils';
import { azureGroupConfigsSchema } from '../src/config';
import { errorsToString } from '../src/parsers';
export const deprecatedAzureVariables = [
/* "related to" precedes description text */
{ key: 'AZURE_OPENAI_DEFAULT_MODEL', description: 'setting a default model' },
{ key: 'AZURE_OPENAI_MODELS', description: 'setting models' },
{
key: 'AZURE_USE_MODEL_AS_DEPLOYMENT_NAME',
description: 'using model names as deployment names',
},
{ key: 'AZURE_API_KEY', description: 'setting a single Azure API key' },
{ key: 'AZURE_OPENAI_API_INSTANCE_NAME', description: 'setting a single Azure instance name' },
{
key: 'AZURE_OPENAI_API_DEPLOYMENT_NAME',
description: 'setting a single Azure deployment name',
},
{ key: 'AZURE_OPENAI_API_VERSION', description: 'setting a single Azure API version' },
{
key: 'AZURE_OPENAI_API_COMPLETIONS_DEPLOYMENT_NAME',
description: 'setting a single Azure completions deployment name',
},
{
key: 'AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME',
description: 'setting a single Azure embeddings deployment name',
},
{
key: 'PLUGINS_USE_AZURE',
description: 'using Azure for Plugins',
},
];
export const conflictingAzureVariables = [
{
key: 'INSTANCE_NAME',
},
{
key: 'DEPLOYMENT_NAME',
},
];
export function validateAzureGroups(configs: TAzureGroups): TAzureConfigValidationResult {
let isValid = true;
const modelNames: string[] = [];
@ -239,13 +201,13 @@ export function mapModelToAzureConfig({
const { deploymentName = '', version = '' } =
typeof modelDetails === 'object'
? {
deploymentName: modelDetails.deploymentName ?? groupConfig.deploymentName,
version: modelDetails.version ?? groupConfig.version,
}
deploymentName: modelDetails.deploymentName ?? groupConfig.deploymentName,
version: modelDetails.version ?? groupConfig.version,
}
: {
deploymentName: groupConfig.deploymentName,
version: groupConfig.version,
};
deploymentName: groupConfig.deploymentName,
version: groupConfig.version,
};
if (!deploymentName || !version) {
throw new Error(
@ -335,13 +297,13 @@ export function mapGroupToAzureConfig({
const { deploymentName = '', version = '' } =
typeof modelDetails === 'object'
? {
deploymentName: modelDetails.deploymentName ?? groupConfig.deploymentName,
version: modelDetails.version ?? groupConfig.version,
}
deploymentName: modelDetails.deploymentName ?? groupConfig.deploymentName,
version: modelDetails.version ?? groupConfig.version,
}
: {
deploymentName: groupConfig.deploymentName,
version: groupConfig.version,
};
deploymentName: groupConfig.deploymentName,
version: groupConfig.version,
};
if (!deploymentName || !version) {
throw new Error(

View file

@ -155,8 +155,10 @@ export type TAzureGroupMap = Record<
export type TValidatedAzureConfig = {
modelNames: string[];
modelGroupMap: TAzureModelGroupMap;
groupMap: TAzureGroupMap;
assistantModels?: string[];
assistantGroups?: string[];
modelGroupMap: TAzureModelGroupMap;
};
export type TAzureConfigValidationResult = TValidatedAzureConfig & {
@ -648,7 +650,7 @@ export type TStartupConfig = {
minPasswordLength?: number;
webSearch?: {
searchProvider?: SearchProviders;
scraperType?: ScraperTypes;
scraperProvider?: ScraperProviders;
rerankerType?: RerankerTypes;
};
mcpServers?: Record<
@ -687,7 +689,7 @@ export enum SearchProviders {
SEARXNG = 'searxng',
}
export enum ScraperTypes {
export enum ScraperProviders {
FIRECRAWL = 'firecrawl',
SERPER = 'serper',
}
@ -709,11 +711,12 @@ export const webSearchSchema = z.object({
searxngApiKey: z.string().optional().default('${SEARXNG_API_KEY}'),
firecrawlApiKey: z.string().optional().default('${FIRECRAWL_API_KEY}'),
firecrawlApiUrl: z.string().optional().default('${FIRECRAWL_API_URL}'),
firecrawlVersion: z.string().optional().default('${FIRECRAWL_VERSION}'),
jinaApiKey: z.string().optional().default('${JINA_API_KEY}'),
jinaApiUrl: z.string().optional().default('${JINA_API_URL}'),
cohereApiKey: z.string().optional().default('${COHERE_API_KEY}'),
searchProvider: z.nativeEnum(SearchProviders).optional(),
scraperType: z.nativeEnum(ScraperTypes).optional(),
scraperProvider: z.nativeEnum(ScraperProviders).optional(),
rerankerType: z.nativeEnum(RerankerTypes).optional(),
scraperTimeout: z.number().optional(),
safeSearch: z.nativeEnum(SafeSearchTypes).default(SafeSearchTypes.MODERATE),
@ -752,7 +755,7 @@ export const webSearchSchema = z.object({
.optional(),
});
export type TWebSearchConfig = z.infer<typeof webSearchSchema>;
export type TWebSearchConfig = DeepPartial<z.infer<typeof webSearchSchema>>;
export const ocrSchema = z.object({
mistralModel: z.string().optional(),
@ -799,7 +802,7 @@ export const memorySchema = z.object({
.optional(),
});
export type TMemoryConfig = z.infer<typeof memorySchema>;
export type TMemoryConfig = DeepPartial<z.infer<typeof memorySchema>>;
const customEndpointsSchema = z.array(endpointSchema.partial()).optional();
@ -862,9 +865,27 @@ export const configSchema = z.object({
.optional(),
});
export const getConfigDefaults = () => getSchemaDefaults(configSchema);
/**
* Recursively makes all properties of T optional, including nested objects.
* Handles arrays, primitives, functions, and Date objects correctly.
*/
export type DeepPartial<T> = T extends (infer U)[]
? DeepPartial<U>[]
: T extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
T extends Function
? T
: T extends Date
? T
: T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;
export type TCustomConfig = z.infer<typeof configSchema>;
export const getConfigDefaults = () => getSchemaDefaults(configSchema);
export type TCustomConfig = DeepPartial<z.infer<typeof configSchema>>;
export type TCustomEndpoints = z.infer<typeof customEndpointsSchema>;
export type TProviderSchema =
@ -930,6 +951,13 @@ export const alternateName = {
};
const sharedOpenAIModels = [
'gpt-5',
'gpt-5-mini',
'gpt-5-nano',
'gpt-5-chat-latest',
'gpt-4.1',
'gpt-4.1-mini',
'gpt-4.1-nano',
'gpt-4o-mini',
'gpt-4o',
'gpt-4.5-preview',
@ -952,10 +980,14 @@ const sharedOpenAIModels = [
];
const sharedAnthropicModels = [
'claude-sonnet-4-5',
'claude-sonnet-4-5-20250929',
'claude-opus-4-1',
'claude-opus-4-1-20250805',
'claude-sonnet-4-20250514',
'claude-sonnet-4-latest',
'claude-sonnet-4-0',
'claude-opus-4-20250514',
'claude-opus-4-latest',
'claude-opus-4-0',
'claude-3-7-sonnet-latest',
'claude-3-7-sonnet-20250219',
'claude-3-5-haiku-20241022',
@ -1013,18 +1045,13 @@ export const defaultModels = {
[EModelEndpoint.assistants]: [...sharedOpenAIModels, 'chatgpt-4o-latest'],
[EModelEndpoint.agents]: sharedOpenAIModels, // TODO: Add agent models (agentsModels)
[EModelEndpoint.google]: [
// Gemini 2.5 Models
'gemini-2.5-pro',
'gemini-2.5-flash',
'gemini-2.5-flash-lite',
// Gemini 2.0 Models
'gemini-2.0-flash-001',
'gemini-2.0-flash-exp',
'gemini-2.0-flash-lite',
'gemini-2.0-pro-exp-02-05',
// Gemini 1.5 Models
'gemini-1.5-flash-001',
'gemini-1.5-flash-002',
'gemini-1.5-pro-001',
'gemini-1.5-pro-002',
// Gemini 1.0 Models
'gemini-1.0-pro-001',
],
[EModelEndpoint.anthropic]: sharedAnthropicModels,
[EModelEndpoint.openAI]: [
@ -1107,6 +1134,7 @@ export const visionModels = [
'gemini-exp',
'gemini-1.5',
'gemini-2',
'gemini-2.5',
'gemini-3',
'moondream',
'llama3.2-vision',
@ -1530,9 +1558,9 @@ export enum TTSProviders {
/** Enum for app-wide constants */
export enum Constants {
/** Key for the app's version. */
VERSION = 'v0.8.0-rc4',
VERSION = 'v0.8.0',
/** Key for the Custom Config's version (librechat.yaml). */
CONFIG_VERSION = '1.2.9',
CONFIG_VERSION = '1.3.0',
/** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */
NO_PARENT = '00000000-0000-0000-0000-000000000000',
/** Standard value to use whatever the submission prelim. `responseMessageId` is */

View file

@ -57,6 +57,27 @@ export const fullMimeTypesList = [
'application/zip',
'image/svg',
'image/svg+xml',
// Video formats
'video/mp4',
'video/avi',
'video/mov',
'video/wmv',
'video/flv',
'video/webm',
'video/mkv',
'video/m4v',
'video/3gp',
'video/ogv',
// Audio formats
'audio/mp3',
'audio/wav',
'audio/ogg',
'audio/m4a',
'audio/aac',
'audio/flac',
'audio/wma',
'audio/opus',
'audio/mpeg',
...excelFileTypes,
];
@ -115,7 +136,7 @@ export const excelMimeTypes =
/^application\/(vnd\.ms-excel|msexcel|x-msexcel|x-ms-excel|x-excel|x-dos_ms_excel|xls|x-xls|vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet)$/;
export const textMimeTypes =
/^(text\/(x-c|x-csharp|tab-separated-values|x-c\+\+|x-h|x-java|html|markdown|x-php|x-python|x-script\.python|x-ruby|x-tex|plain|css|vtt|javascript|csv))$/;
/^(text\/(x-c|x-csharp|tab-separated-values|x-c\+\+|x-h|x-java|html|markdown|x-php|x-python|x-script\.python|x-ruby|x-tex|plain|css|vtt|javascript|csv|xml))$/;
export const applicationMimeTypes =
/^(application\/(epub\+zip|csv|json|pdf|x-tar|typescript|vnd\.openxmlformats-officedocument\.(wordprocessingml\.document|presentationml\.presentation|spreadsheetml\.sheet)|xml|zip))$/;
@ -123,7 +144,9 @@ export const applicationMimeTypes =
export const imageMimeTypes = /^image\/(jpeg|gif|png|webp|heic|heif)$/;
export const audioMimeTypes =
/^audio\/(mp3|mpeg|mpeg3|wav|wave|x-wav|ogg|vorbis|mp4|x-m4a|flac|x-flac|webm)$/;
/^audio\/(mp3|mpeg|mpeg3|wav|wave|x-wav|ogg|vorbis|mp4|m4a|x-m4a|flac|x-flac|webm|aac|wma|opus)$/;
export const videoMimeTypes = /^video\/(mp4|avi|mov|wmv|flv|webm|mkv|m4v|3gp|ogv)$/;
export const defaultOCRMimeTypes = [
imageMimeTypes,
@ -142,8 +165,9 @@ export const supportedMimeTypes = [
excelMimeTypes,
applicationMimeTypes,
imageMimeTypes,
videoMimeTypes,
audioMimeTypes,
/** Supported by LC Code Interpreter PAI */
/** Supported by LC Code Interpreter API */
/^image\/(svg|svg\+xml)$/,
];
@ -199,6 +223,13 @@ export const fileConfig = {
[EModelEndpoint.assistants]: assistantsFileConfig,
[EModelEndpoint.azureAssistants]: assistantsFileConfig,
[EModelEndpoint.agents]: assistantsFileConfig,
[EModelEndpoint.anthropic]: {
fileLimit: 10,
fileSizeLimit: defaultSizeLimit,
totalSizeLimit: defaultSizeLimit,
supportedMimeTypes,
disabled: false,
},
default: {
fileLimit: 10,
fileSizeLimit: defaultSizeLimit,

View file

@ -369,7 +369,7 @@ export function parseTextParts(
continue;
}
if (part.type === ContentTypes.TEXT) {
const textValue = typeof part.text === 'string' ? part.text : part.text.value;
const textValue = (typeof part.text === 'string' ? part.text : part.text?.value) || '';
if (
result.length > 0 &&

View file

@ -31,6 +31,61 @@ export enum EModelEndpoint {
gptPlugins = 'gptPlugins',
}
/** Mirrors `@librechat/agents` providers */
export enum Providers {
OPENAI = 'openAI',
ANTHROPIC = 'anthropic',
AZURE = 'azureOpenAI',
GOOGLE = 'google',
VERTEXAI = 'vertexai',
BEDROCK = 'bedrock',
BEDROCK_LEGACY = 'bedrock_legacy',
MISTRALAI = 'mistralai',
MISTRAL = 'mistral',
OLLAMA = 'ollama',
DEEPSEEK = 'deepseek',
OPENROUTER = 'openrouter',
XAI = 'xai',
}
/**
* Endpoints that support direct PDF processing in the agent system
*/
export const documentSupportedProviders = new Set<string>([
EModelEndpoint.anthropic,
EModelEndpoint.openAI,
EModelEndpoint.custom,
EModelEndpoint.azureOpenAI,
EModelEndpoint.google,
Providers.VERTEXAI,
Providers.MISTRALAI,
Providers.MISTRAL,
Providers.OLLAMA,
Providers.DEEPSEEK,
Providers.OPENROUTER,
Providers.XAI,
]);
const openAILikeProviders = new Set<string>([
Providers.OPENAI,
Providers.AZURE,
EModelEndpoint.custom,
Providers.MISTRALAI,
Providers.MISTRAL,
Providers.OLLAMA,
Providers.DEEPSEEK,
Providers.OPENROUTER,
Providers.XAI,
]);
export const isOpenAILikeProvider = (provider?: string | null): boolean => {
return openAILikeProviders.has(provider ?? '');
};
export const isDocumentSupportedProvider = (provider?: string | null): boolean => {
return documentSupportedProviders.has(provider ?? '');
};
export const paramEndpoints = new Set<EModelEndpoint | string>([
EModelEndpoint.agents,
EModelEndpoint.openAI,

View file

@ -475,10 +475,20 @@ export type ContentPart = (
) &
PartMetadata;
export type TextData = (Text & PartMetadata) | undefined;
export type TMessageContentParts =
| { type: ContentTypes.ERROR; text?: string | (Text & PartMetadata); error?: string }
| { type: ContentTypes.THINK; think: string | (Text & PartMetadata) }
| { type: ContentTypes.TEXT; text: string | (Text & PartMetadata); tool_call_ids?: string[] }
| {
type: ContentTypes.ERROR;
text?: string | TextData;
error?: string;
}
| { type: ContentTypes.THINK; think?: string | TextData }
| {
type: ContentTypes.TEXT;
text?: string | TextData;
tool_call_ids?: string[];
}
| {
type: ContentTypes.TOOL_CALL;
tool_call: (