librechat-data-provider 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/package.json +1 -1
- package/specs/azure.spec.ts +567 -0
- package/specs/parsers.spec.ts +48 -0
- package/src/azure.ts +211 -0
- package/src/config.ts +73 -1
- package/src/index.ts +1 -0
- package/src/parsers.ts +97 -1
package/src/azure.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { ZodError } from 'zod';
|
|
2
|
+
import type {
|
|
3
|
+
TAzureGroups,
|
|
4
|
+
TAzureGroupMap,
|
|
5
|
+
TAzureModelGroupMap,
|
|
6
|
+
TValidatedAzureConfig,
|
|
7
|
+
} from '../src/config';
|
|
8
|
+
import { errorsToString, extractEnvVariable, envVarRegex } from '../src/parsers';
|
|
9
|
+
import { azureGroupConfigsSchema } from '../src/config';
|
|
10
|
+
|
|
11
|
+
export const deprecatedAzureVariables = [
|
|
12
|
+
/* "related to" precedes description text */
|
|
13
|
+
{ key: 'AZURE_OPENAI_DEFAULT_MODEL', description: 'setting a default model' },
|
|
14
|
+
{ key: 'AZURE_OPENAI_MODELS', description: 'setting models' },
|
|
15
|
+
{
|
|
16
|
+
key: 'AZURE_USE_MODEL_AS_DEPLOYMENT_NAME',
|
|
17
|
+
description: 'using model names as deployment names',
|
|
18
|
+
},
|
|
19
|
+
{ key: 'AZURE_API_KEY', description: 'setting a single Azure API key' },
|
|
20
|
+
{ key: 'AZURE_OPENAI_API_INSTANCE_NAME', description: 'setting a single Azure instance name' },
|
|
21
|
+
{
|
|
22
|
+
key: 'AZURE_OPENAI_API_DEPLOYMENT_NAME',
|
|
23
|
+
description: 'setting a single Azure deployment name',
|
|
24
|
+
},
|
|
25
|
+
{ key: 'AZURE_OPENAI_API_VERSION', description: 'setting a single Azure API version' },
|
|
26
|
+
{
|
|
27
|
+
key: 'AZURE_OPENAI_API_COMPLETIONS_DEPLOYMENT_NAME',
|
|
28
|
+
description: 'setting a single Azure completions deployment name',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME',
|
|
32
|
+
description: 'setting a single Azure embeddings deployment name',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'PLUGINS_USE_AZURE',
|
|
36
|
+
description: 'using Azure for Plugins',
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export const conflictingAzureVariables = [
|
|
41
|
+
{
|
|
42
|
+
key: 'INSTANCE_NAME',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: 'DEPLOYMENT_NAME',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
export function validateAzureGroups(configs: TAzureGroups): TValidatedAzureConfig & {
|
|
50
|
+
isValid: boolean;
|
|
51
|
+
errors: (ZodError | string)[];
|
|
52
|
+
} {
|
|
53
|
+
let isValid = true;
|
|
54
|
+
const modelNames: string[] = [];
|
|
55
|
+
const modelGroupMap: TAzureModelGroupMap = {};
|
|
56
|
+
const groupMap: TAzureGroupMap = {};
|
|
57
|
+
const errors: (ZodError | string)[] = [];
|
|
58
|
+
|
|
59
|
+
const result = azureGroupConfigsSchema.safeParse(configs);
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
isValid = false;
|
|
62
|
+
errors.push(errorsToString(result.error.errors));
|
|
63
|
+
} else {
|
|
64
|
+
for (const group of result.data) {
|
|
65
|
+
const {
|
|
66
|
+
group: groupName,
|
|
67
|
+
apiKey,
|
|
68
|
+
instanceName,
|
|
69
|
+
deploymentName,
|
|
70
|
+
version,
|
|
71
|
+
baseURL,
|
|
72
|
+
additionalHeaders,
|
|
73
|
+
models,
|
|
74
|
+
} = group;
|
|
75
|
+
|
|
76
|
+
if (groupMap[groupName]) {
|
|
77
|
+
errors.push(`Duplicate group name detected: "${groupName}". Group names must be unique.`);
|
|
78
|
+
return { isValid: false, modelNames, modelGroupMap, groupMap, errors };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
groupMap[groupName] = {
|
|
82
|
+
apiKey,
|
|
83
|
+
instanceName,
|
|
84
|
+
deploymentName,
|
|
85
|
+
version,
|
|
86
|
+
baseURL,
|
|
87
|
+
additionalHeaders,
|
|
88
|
+
models,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const modelName in group.models) {
|
|
92
|
+
modelNames.push(modelName);
|
|
93
|
+
const model = group.models[modelName];
|
|
94
|
+
|
|
95
|
+
if (modelGroupMap[modelName]) {
|
|
96
|
+
errors.push(
|
|
97
|
+
`Duplicate model name detected: "${modelName}". Model names must be unique across groups.`,
|
|
98
|
+
);
|
|
99
|
+
return { isValid: false, modelNames, modelGroupMap, groupMap, errors };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (typeof model === 'boolean') {
|
|
103
|
+
// For boolean models, check if group-level deploymentName and version are present.
|
|
104
|
+
if (!group.deploymentName || !group.version) {
|
|
105
|
+
errors.push(
|
|
106
|
+
`Model "${modelName}" in group "${groupName}" is missing a deploymentName or version.`,
|
|
107
|
+
);
|
|
108
|
+
return { isValid: false, modelNames, modelGroupMap, groupMap, errors };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
modelGroupMap[modelName] = {
|
|
112
|
+
group: groupName,
|
|
113
|
+
};
|
|
114
|
+
} else {
|
|
115
|
+
// For object models, check if deploymentName and version are required but missing.
|
|
116
|
+
if (
|
|
117
|
+
(!model.deploymentName && !group.deploymentName) ||
|
|
118
|
+
(!model.version && !group.version)
|
|
119
|
+
) {
|
|
120
|
+
errors.push(
|
|
121
|
+
`Model "${modelName}" in group "${groupName}" is missing a required deploymentName or version.`,
|
|
122
|
+
);
|
|
123
|
+
return { isValid: false, modelNames, modelGroupMap, groupMap, errors };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
modelGroupMap[modelName] = {
|
|
127
|
+
group: groupName,
|
|
128
|
+
// deploymentName: model.deploymentName || group.deploymentName,
|
|
129
|
+
// version: model.version || group.version,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { isValid, modelNames, modelGroupMap, groupMap, errors };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
type AzureOptions = {
|
|
140
|
+
azureOpenAIApiKey: string;
|
|
141
|
+
azureOpenAIApiInstanceName: string;
|
|
142
|
+
azureOpenAIApiDeploymentName: string;
|
|
143
|
+
azureOpenAIApiVersion: string;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
type MappedAzureConfig = {
|
|
147
|
+
azureOptions: AzureOptions;
|
|
148
|
+
baseURL?: string;
|
|
149
|
+
headers?: Record<string, string>;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export function mapModelToAzureConfig({
|
|
153
|
+
modelName,
|
|
154
|
+
modelGroupMap,
|
|
155
|
+
groupMap,
|
|
156
|
+
}: Omit<TValidatedAzureConfig, 'modelNames'> & {
|
|
157
|
+
modelName: string;
|
|
158
|
+
}): MappedAzureConfig {
|
|
159
|
+
const modelConfig = modelGroupMap[modelName];
|
|
160
|
+
if (!modelConfig) {
|
|
161
|
+
throw new Error(`Model named "${modelName}" not found in configuration.`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const groupConfig = groupMap[modelConfig.group];
|
|
165
|
+
if (!groupConfig) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Group "${modelConfig.group}" for model "${modelName}" not found in configuration.`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const modelDetails = groupConfig.models[modelName];
|
|
172
|
+
const deploymentName =
|
|
173
|
+
typeof modelDetails === 'object'
|
|
174
|
+
? modelDetails.deploymentName || groupConfig.deploymentName
|
|
175
|
+
: groupConfig.deploymentName;
|
|
176
|
+
const version =
|
|
177
|
+
typeof modelDetails === 'object'
|
|
178
|
+
? modelDetails.version || groupConfig.version
|
|
179
|
+
: groupConfig.version;
|
|
180
|
+
|
|
181
|
+
if (!deploymentName || !version) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Model "${modelName}" in group "${modelConfig.group}" is missing a deploymentName ("${deploymentName}") or version ("${version}").`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const azureOptions: AzureOptions = {
|
|
188
|
+
azureOpenAIApiKey: extractEnvVariable(groupConfig.apiKey),
|
|
189
|
+
azureOpenAIApiInstanceName: extractEnvVariable(groupConfig.instanceName),
|
|
190
|
+
azureOpenAIApiDeploymentName: extractEnvVariable(deploymentName),
|
|
191
|
+
azureOpenAIApiVersion: extractEnvVariable(version),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
for (const value of Object.values(azureOptions)) {
|
|
195
|
+
if (typeof value === 'string' && envVarRegex.test(value)) {
|
|
196
|
+
throw new Error(`Azure configuration environment variable "${value}" was not found.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const result: MappedAzureConfig = { azureOptions };
|
|
201
|
+
|
|
202
|
+
if (groupConfig.baseURL) {
|
|
203
|
+
result.baseURL = extractEnvVariable(groupConfig.baseURL);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (groupConfig.additionalHeaders) {
|
|
207
|
+
result.headers = groupConfig.additionalHeaders;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result;
|
|
211
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -8,6 +8,55 @@ export const defaultSocialLogins = ['google', 'facebook', 'openid', 'github', 'd
|
|
|
8
8
|
|
|
9
9
|
export const fileSourceSchema = z.nativeEnum(FileSources);
|
|
10
10
|
|
|
11
|
+
export const modelConfigSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
deploymentName: z.string().optional(),
|
|
14
|
+
version: z.string().optional(),
|
|
15
|
+
})
|
|
16
|
+
.or(z.boolean());
|
|
17
|
+
|
|
18
|
+
export type TAzureModelConfig = z.infer<typeof modelConfigSchema>;
|
|
19
|
+
|
|
20
|
+
export const azureBaseSchema = z.object({
|
|
21
|
+
apiKey: z.string(),
|
|
22
|
+
instanceName: z.string(),
|
|
23
|
+
deploymentName: z.string().optional(),
|
|
24
|
+
version: z.string().optional(),
|
|
25
|
+
baseURL: z.string().optional(),
|
|
26
|
+
additionalHeaders: z.record(z.any()).optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type TAzureBaseSchema = z.infer<typeof azureBaseSchema>;
|
|
30
|
+
|
|
31
|
+
export const azureGroupSchema = z
|
|
32
|
+
.object({
|
|
33
|
+
group: z.string(),
|
|
34
|
+
models: z.record(z.string(), modelConfigSchema),
|
|
35
|
+
})
|
|
36
|
+
.required()
|
|
37
|
+
.and(azureBaseSchema);
|
|
38
|
+
|
|
39
|
+
export const azureGroupConfigsSchema = z.array(azureGroupSchema).min(1);
|
|
40
|
+
export type TAzureGroups = z.infer<typeof azureGroupConfigsSchema>;
|
|
41
|
+
|
|
42
|
+
export type TAzureModelMapSchema = {
|
|
43
|
+
// deploymentName?: string;
|
|
44
|
+
// version?: string;
|
|
45
|
+
group: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type TAzureModelGroupMap = Record<string, TAzureModelMapSchema>;
|
|
49
|
+
export type TAzureGroupMap = Record<
|
|
50
|
+
string,
|
|
51
|
+
TAzureBaseSchema & { models: Record<string, TAzureModelConfig> }
|
|
52
|
+
>;
|
|
53
|
+
|
|
54
|
+
export type TValidatedAzureConfig = {
|
|
55
|
+
modelNames: string[];
|
|
56
|
+
modelGroupMap: TAzureModelGroupMap;
|
|
57
|
+
groupMap: TAzureGroupMap;
|
|
58
|
+
};
|
|
59
|
+
|
|
11
60
|
export const assistantEndpointSchema = z.object({
|
|
12
61
|
/* assistants specific */
|
|
13
62
|
disableBuilder: z.boolean().optional(),
|
|
@@ -56,8 +105,30 @@ export const endpointSchema = z.object({
|
|
|
56
105
|
headers: z.record(z.any()).optional(),
|
|
57
106
|
addParams: z.record(z.any()).optional(),
|
|
58
107
|
dropParams: z.array(z.string()).optional(),
|
|
108
|
+
customOrder: z.number().optional(),
|
|
59
109
|
});
|
|
60
110
|
|
|
111
|
+
export const azureEndpointSchema = z
|
|
112
|
+
.object({
|
|
113
|
+
groups: azureGroupConfigsSchema,
|
|
114
|
+
plugins: z.boolean().optional(),
|
|
115
|
+
})
|
|
116
|
+
.and(
|
|
117
|
+
endpointSchema
|
|
118
|
+
.pick({
|
|
119
|
+
titleConvo: true,
|
|
120
|
+
titleMethod: true,
|
|
121
|
+
titleModel: true,
|
|
122
|
+
summarize: true,
|
|
123
|
+
summaryModel: true,
|
|
124
|
+
customOrder: true,
|
|
125
|
+
})
|
|
126
|
+
.partial(),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
export type TAzureConfig = Omit<z.infer<typeof azureEndpointSchema>, 'groups'> &
|
|
130
|
+
TValidatedAzureConfig;
|
|
131
|
+
|
|
61
132
|
export const rateLimitSchema = z.object({
|
|
62
133
|
fileUploads: z
|
|
63
134
|
.object({
|
|
@@ -83,6 +154,7 @@ export const configSchema = z.object({
|
|
|
83
154
|
fileConfig: fileConfigSchema.optional(),
|
|
84
155
|
endpoints: z
|
|
85
156
|
.object({
|
|
157
|
+
[EModelEndpoint.azureOpenAI]: azureEndpointSchema.optional(),
|
|
86
158
|
[EModelEndpoint.assistants]: assistantEndpointSchema.optional(),
|
|
87
159
|
custom: z.array(endpointSchema.partial()).optional(),
|
|
88
160
|
})
|
|
@@ -371,7 +443,7 @@ export enum Constants {
|
|
|
371
443
|
/**
|
|
372
444
|
* Key for the Custom Config's version (librechat.yaml).
|
|
373
445
|
*/
|
|
374
|
-
CONFIG_VERSION = '1.0.
|
|
446
|
+
CONFIG_VERSION = '1.0.4',
|
|
375
447
|
/**
|
|
376
448
|
* Standard value for the first message's `parentMessageId` value, to indicate no parent exists.
|
|
377
449
|
*/
|
package/src/index.ts
CHANGED
package/src/parsers.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { ZodIssue } from 'zod';
|
|
1
2
|
import type { TConversation, TPreset } from './schemas';
|
|
2
|
-
import type { TEndpointOption } from './types';
|
|
3
|
+
import type { TConfig, TEndpointOption, TEndpointsConfig } from './types';
|
|
3
4
|
import {
|
|
4
5
|
EModelEndpoint,
|
|
5
6
|
openAISchema,
|
|
@@ -42,6 +43,101 @@ const endpointSchemas: Record<EModelEndpoint, EndpointSchema> = {
|
|
|
42
43
|
// [EModelEndpoint.google]: createGoogleSchema,
|
|
43
44
|
// };
|
|
44
45
|
|
|
46
|
+
/** Get the enabled endpoints from the `ENDPOINTS` environment variable */
|
|
47
|
+
export function getEnabledEndpoints() {
|
|
48
|
+
const defaultEndpoints: string[] = [
|
|
49
|
+
EModelEndpoint.openAI,
|
|
50
|
+
EModelEndpoint.assistants,
|
|
51
|
+
EModelEndpoint.azureOpenAI,
|
|
52
|
+
EModelEndpoint.google,
|
|
53
|
+
EModelEndpoint.bingAI,
|
|
54
|
+
EModelEndpoint.chatGPTBrowser,
|
|
55
|
+
EModelEndpoint.gptPlugins,
|
|
56
|
+
EModelEndpoint.anthropic,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const endpointsEnv = process.env.ENDPOINTS || '';
|
|
60
|
+
let enabledEndpoints = defaultEndpoints;
|
|
61
|
+
if (endpointsEnv) {
|
|
62
|
+
enabledEndpoints = endpointsEnv
|
|
63
|
+
.split(',')
|
|
64
|
+
.filter((endpoint) => endpoint?.trim())
|
|
65
|
+
.map((endpoint) => endpoint.trim());
|
|
66
|
+
}
|
|
67
|
+
return enabledEndpoints;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Orders an existing EndpointsConfig object based on enabled endpoint/custom ordering */
|
|
71
|
+
export function orderEndpointsConfig(endpointsConfig: TEndpointsConfig) {
|
|
72
|
+
if (!endpointsConfig) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
const enabledEndpoints = getEnabledEndpoints();
|
|
76
|
+
const endpointKeys = Object.keys(endpointsConfig);
|
|
77
|
+
const defaultCustomIndex = enabledEndpoints.indexOf(EModelEndpoint.custom);
|
|
78
|
+
return endpointKeys.reduce(
|
|
79
|
+
(accumulatedConfig: Record<string, TConfig | null | undefined>, currentEndpointKey) => {
|
|
80
|
+
const isCustom = !(currentEndpointKey in EModelEndpoint);
|
|
81
|
+
const isEnabled = enabledEndpoints.includes(currentEndpointKey);
|
|
82
|
+
if (!isEnabled && !isCustom) {
|
|
83
|
+
return accumulatedConfig;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const index = enabledEndpoints.indexOf(currentEndpointKey);
|
|
87
|
+
|
|
88
|
+
if (isCustom) {
|
|
89
|
+
accumulatedConfig[currentEndpointKey] = {
|
|
90
|
+
order: defaultCustomIndex >= 0 ? defaultCustomIndex : 9999,
|
|
91
|
+
...(endpointsConfig[currentEndpointKey] as Omit<TConfig, 'order'> & { order?: number }),
|
|
92
|
+
};
|
|
93
|
+
} else if (endpointsConfig[currentEndpointKey]) {
|
|
94
|
+
accumulatedConfig[currentEndpointKey] = {
|
|
95
|
+
...endpointsConfig[currentEndpointKey],
|
|
96
|
+
order: index,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return accumulatedConfig;
|
|
100
|
+
},
|
|
101
|
+
{},
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Converts an array of Zod issues into a string. */
|
|
106
|
+
export function errorsToString(errors: ZodIssue[]) {
|
|
107
|
+
return errors
|
|
108
|
+
.map((error) => {
|
|
109
|
+
const field = error.path.join('.');
|
|
110
|
+
const message = error.message;
|
|
111
|
+
|
|
112
|
+
return `${field}: ${message}`;
|
|
113
|
+
})
|
|
114
|
+
.join(' ');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const envVarRegex = /^\${(.+)}$/;
|
|
118
|
+
|
|
119
|
+
/** Extracts the value of an environment variable from a string. */
|
|
120
|
+
export function extractEnvVariable(value: string) {
|
|
121
|
+
const envVarMatch = value.match(envVarRegex);
|
|
122
|
+
if (envVarMatch) {
|
|
123
|
+
return process.env[envVarMatch[1]] || value;
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Resolves header values to env variables if detected */
|
|
129
|
+
export function resolveHeaders(headers: Record<string, string> | undefined) {
|
|
130
|
+
const resolvedHeaders = { ...(headers ?? {}) };
|
|
131
|
+
|
|
132
|
+
if (headers && typeof headers === 'object' && !Array.isArray(headers)) {
|
|
133
|
+
Object.keys(headers).forEach((key) => {
|
|
134
|
+
resolvedHeaders[key] = extractEnvVariable(headers[key]);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return resolvedHeaders;
|
|
139
|
+
}
|
|
140
|
+
|
|
45
141
|
export function getFirstDefinedValue(possibleValues: string[]) {
|
|
46
142
|
let returnValue;
|
|
47
143
|
for (const value of possibleValues) {
|