librechat-data-provider 0.4.7 → 0.4.9

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.
@@ -1,5 +1,5 @@
1
1
  import type { TAzureGroups } from '../src/config';
2
- import { validateAzureGroups, mapModelToAzureConfig } from '../src/azure';
2
+ import { validateAzureGroups, mapModelToAzureConfig, mapGroupToAzureConfig } from '../src/azure';
3
3
 
4
4
  describe('validateAzureGroups', () => {
5
5
  it('should validate a correct configuration', () => {
@@ -785,3 +785,57 @@ describe('validateAzureGroups with modelGroupMap and groupMap', () => {
785
785
  });
786
786
  });
787
787
  });
788
+
789
+ describe('mapGroupToAzureConfig', () => {
790
+ // Test setup for a basic config with 2 groups
791
+ const groupMap = {
792
+ group1: {
793
+ apiKey: 'key-for-group1',
794
+ instanceName: 'instance-group1',
795
+ models: {
796
+ model1: { deploymentName: 'deployment1', version: '1.0' },
797
+ },
798
+ },
799
+ group2: {
800
+ apiKey: 'key-for-group2',
801
+ instanceName: 'instance-group2',
802
+ serverless: true,
803
+ baseURL: 'https://group2.example.com',
804
+ models: {
805
+ model2: true, // demonstrating a boolean style model configuration
806
+ },
807
+ },
808
+ };
809
+
810
+ it('should successfully map non-serverless group configuration', () => {
811
+ const groupName = 'group1';
812
+ const result = mapGroupToAzureConfig({ groupName, groupMap });
813
+ expect(result).toEqual({
814
+ azureOptions: expect.objectContaining({
815
+ azureOpenAIApiKey: 'key-for-group1',
816
+ azureOpenAIApiInstanceName: 'instance-group1',
817
+ azureOpenAIApiDeploymentName: expect.any(String),
818
+ azureOpenAIApiVersion: expect.any(String),
819
+ }),
820
+ });
821
+ });
822
+
823
+ it('should successfully map serverless group configuration', () => {
824
+ const groupName = 'group2';
825
+ const result = mapGroupToAzureConfig({ groupName, groupMap });
826
+ expect(result).toEqual({
827
+ azureOptions: expect.objectContaining({
828
+ azureOpenAIApiKey: 'key-for-group2',
829
+ }),
830
+ baseURL: 'https://group2.example.com',
831
+ serverless: true,
832
+ });
833
+ });
834
+
835
+ it('should throw error for nonexistent group name', () => {
836
+ const groupName = 'nonexistent-group';
837
+ expect(() => {
838
+ mapGroupToAzureConfig({ groupName, groupMap });
839
+ }).toThrow(`Group named "${groupName}" not found in configuration.`);
840
+ });
841
+ });
@@ -66,7 +66,20 @@ export const plugins = () => '/api/plugins';
66
66
 
67
67
  export const config = () => '/api/config';
68
68
 
69
- export const assistants = (id?: string) => `/api/assistants${id ? `/${id}` : ''}`;
69
+ export const assistants = (id?: string, options?: Record<string, string>) => {
70
+ let url = '/api/assistants';
71
+
72
+ if (id) {
73
+ url += `/${id}`;
74
+ }
75
+
76
+ if (options && Object.keys(options).length > 0) {
77
+ const queryParams = new URLSearchParams(options).toString();
78
+ url += `?${queryParams}`;
79
+ }
80
+
81
+ return url;
82
+ };
70
83
 
71
84
  export const files = () => '/api/files';
72
85
 
package/src/azure.ts CHANGED
@@ -234,14 +234,16 @@ export function mapModelToAzureConfig({
234
234
  }
235
235
 
236
236
  const modelDetails = groupConfig.models[modelName];
237
- const deploymentName =
237
+ const { deploymentName, version } =
238
238
  typeof modelDetails === 'object'
239
- ? modelDetails.deploymentName || groupConfig.deploymentName
240
- : groupConfig.deploymentName;
241
- const version =
242
- typeof modelDetails === 'object'
243
- ? modelDetails.version || groupConfig.version
244
- : groupConfig.version;
239
+ ? {
240
+ deploymentName: modelDetails.deploymentName || groupConfig.deploymentName,
241
+ version: modelDetails.version || groupConfig.version,
242
+ }
243
+ : {
244
+ deploymentName: groupConfig.deploymentName,
245
+ version: groupConfig.version,
246
+ };
245
247
 
246
248
  if (!deploymentName || !version) {
247
249
  throw new Error(
@@ -274,3 +276,86 @@ export function mapModelToAzureConfig({
274
276
 
275
277
  return result;
276
278
  }
279
+
280
+ export function mapGroupToAzureConfig({
281
+ groupName,
282
+ groupMap,
283
+ }: {
284
+ groupName: string;
285
+ groupMap: TAzureGroupMap;
286
+ }): MappedAzureConfig {
287
+ const groupConfig = groupMap[groupName];
288
+ if (!groupConfig) {
289
+ throw new Error(`Group named "${groupName}" not found in configuration.`);
290
+ }
291
+
292
+ const instanceName = groupConfig.instanceName as string;
293
+
294
+ if (!instanceName && !groupConfig.serverless) {
295
+ throw new Error(
296
+ `Group "${groupName}" is missing an instanceName for non-serverless configuration.`,
297
+ );
298
+ }
299
+
300
+ if (groupConfig.serverless && !groupConfig.baseURL) {
301
+ throw new Error(
302
+ `Group "${groupName}" is missing the required base URL for serverless configuration.`,
303
+ );
304
+ }
305
+
306
+ const models = Object.keys(groupConfig.models);
307
+ if (models.length === 0) {
308
+ throw new Error(`Group "${groupName}" does not have any models configured.`);
309
+ }
310
+
311
+ // Use the first available model in the group
312
+ const firstModelName = models[0];
313
+ const modelDetails = groupConfig.models[firstModelName];
314
+
315
+ const azureOptions: AzureOptions = {
316
+ azureOpenAIApiKey: extractEnvVariable(groupConfig.apiKey),
317
+ azureOpenAIApiInstanceName: extractEnvVariable(instanceName),
318
+ // DeploymentName and Version set below
319
+ };
320
+
321
+ if (groupConfig.serverless) {
322
+ return {
323
+ azureOptions,
324
+ baseURL: extractEnvVariable(groupConfig.baseURL ?? ''),
325
+ serverless: true,
326
+ ...(groupConfig.additionalHeaders && { headers: groupConfig.additionalHeaders }),
327
+ };
328
+ }
329
+
330
+ const { deploymentName, version } =
331
+ typeof modelDetails === 'object'
332
+ ? {
333
+ deploymentName: modelDetails.deploymentName || groupConfig.deploymentName,
334
+ version: modelDetails.version || groupConfig.version,
335
+ }
336
+ : {
337
+ deploymentName: groupConfig.deploymentName,
338
+ version: groupConfig.version,
339
+ };
340
+
341
+ if (!deploymentName || !version) {
342
+ throw new Error(
343
+ `Model "${firstModelName}" in group "${groupName}" or the group itself is missing a deploymentName ("${deploymentName}") or version ("${version}").`,
344
+ );
345
+ }
346
+
347
+ azureOptions.azureOpenAIApiDeploymentName = extractEnvVariable(deploymentName);
348
+ azureOptions.azureOpenAIApiVersion = extractEnvVariable(version);
349
+
350
+ const result: MappedAzureConfig = { azureOptions };
351
+
352
+ if (groupConfig.baseURL) {
353
+ result.baseURL = extractEnvVariable(groupConfig.baseURL);
354
+ }
355
+
356
+ if (groupConfig.additionalHeaders) {
357
+ result.headers = groupConfig.additionalHeaders;
358
+ }
359
+
360
+ return result;
361
+ }
package/src/config.ts CHANGED
@@ -6,12 +6,25 @@ import { FileSources } from './types/files';
6
6
 
7
7
  export const defaultSocialLogins = ['google', 'facebook', 'openid', 'github', 'discord'];
8
8
 
9
+ export const defaultRetrievalModels = [
10
+ 'gpt-4-turbo-preview',
11
+ 'gpt-3.5-turbo-0125',
12
+ 'gpt-4-0125-preview',
13
+ 'gpt-4-1106-preview',
14
+ 'gpt-3.5-turbo-1106',
15
+ 'gpt-3.5-turbo-0125',
16
+ 'gpt-4-turbo',
17
+ 'gpt-4-0125',
18
+ 'gpt-4-1106',
19
+ ];
20
+
9
21
  export const fileSourceSchema = z.nativeEnum(FileSources);
10
22
 
11
23
  export const modelConfigSchema = z
12
24
  .object({
13
25
  deploymentName: z.string().optional(),
14
26
  version: z.string().optional(),
27
+ assistants: z.boolean().optional(),
15
28
  })
16
29
  .or(z.boolean());
17
30
 
@@ -22,6 +35,7 @@ export const azureBaseSchema = z.object({
22
35
  serverless: z.boolean().optional(),
23
36
  instanceName: z.string().optional(),
24
37
  deploymentName: z.string().optional(),
38
+ assistants: z.boolean().optional(),
25
39
  addParams: z.record(z.any()).optional(),
26
40
  dropParams: z.array(z.string()).optional(),
27
41
  forcePrompt: z.boolean().optional(),
@@ -61,6 +75,13 @@ export type TValidatedAzureConfig = {
61
75
  groupMap: TAzureGroupMap;
62
76
  };
63
77
 
78
+ export enum Capabilities {
79
+ code_interpreter = 'code_interpreter',
80
+ retrieval = 'retrieval',
81
+ actions = 'actions',
82
+ tools = 'tools',
83
+ }
84
+
64
85
  export const assistantEndpointSchema = z.object({
65
86
  /* assistants specific */
66
87
  disableBuilder: z.boolean().optional(),
@@ -68,6 +89,16 @@ export const assistantEndpointSchema = z.object({
68
89
  timeoutMs: z.number().optional(),
69
90
  supportedIds: z.array(z.string()).min(1).optional(),
70
91
  excludedIds: z.array(z.string()).min(1).optional(),
92
+ retrievalModels: z.array(z.string()).min(1).optional().default(defaultRetrievalModels),
93
+ capabilities: z
94
+ .array(z.nativeEnum(Capabilities))
95
+ .optional()
96
+ .default([
97
+ Capabilities.code_interpreter,
98
+ Capabilities.retrieval,
99
+ Capabilities.actions,
100
+ Capabilities.tools,
101
+ ]),
71
102
  /* general */
72
103
  apiKey: z.string().optional(),
73
104
  baseURL: z.string().optional(),
@@ -116,6 +147,7 @@ export const azureEndpointSchema = z
116
147
  .object({
117
148
  groups: azureGroupConfigsSchema,
118
149
  plugins: z.boolean().optional(),
150
+ assistants: z.boolean().optional(),
119
151
  })
120
152
  .and(
121
153
  endpointSchema
@@ -147,6 +179,22 @@ export const rateLimitSchema = z.object({
147
179
  export const configSchema = z.object({
148
180
  version: z.string(),
149
181
  cache: z.boolean(),
182
+ interface: z
183
+ .object({
184
+ privacyPolicy: z
185
+ .object({
186
+ externalUrl: z.string().optional(),
187
+ openNewTab: z.boolean().optional(),
188
+ })
189
+ .optional(),
190
+ termsOfService: z
191
+ .object({
192
+ externalUrl: z.string().optional(),
193
+ openNewTab: z.boolean().optional(),
194
+ })
195
+ .optional(),
196
+ })
197
+ .optional(),
150
198
  fileStrategy: fileSourceSchema.optional(),
151
199
  registration: z
152
200
  .object({
@@ -272,14 +320,6 @@ export const defaultModels = {
272
320
  ],
273
321
  };
274
322
 
275
- export const supportsRetrieval = new Set([
276
- 'gpt-3.5-turbo-0125',
277
- 'gpt-4-0125-preview',
278
- 'gpt-4-turbo-preview',
279
- 'gpt-4-1106-preview',
280
- 'gpt-3.5-turbo-1106',
281
- ]);
282
-
283
323
  export const EndpointURLs: { [key in EModelEndpoint]: string } = {
284
324
  [EModelEndpoint.openAI]: `/api/ask/${EModelEndpoint.openAI}`,
285
325
  [EModelEndpoint.bingAI]: `/api/ask/${EModelEndpoint.bingAI}`,
@@ -302,11 +342,12 @@ export const modularEndpoints = new Set<EModelEndpoint | string>([
302
342
  ]);
303
343
 
304
344
  export const supportsBalanceCheck = {
345
+ [EModelEndpoint.custom]: true,
305
346
  [EModelEndpoint.openAI]: true,
306
347
  [EModelEndpoint.anthropic]: true,
307
- [EModelEndpoint.azureOpenAI]: true,
308
348
  [EModelEndpoint.gptPlugins]: true,
309
- [EModelEndpoint.custom]: true,
349
+ [EModelEndpoint.assistants]: true,
350
+ [EModelEndpoint.azureOpenAI]: true,
310
351
  };
311
352
 
312
353
  export const visionModels = ['gpt-4-vision', 'llava-13b', 'gemini-pro-vision', 'claude-3'];
@@ -396,6 +437,10 @@ export enum ViolationTypes {
396
437
  * Illegal Model Request (not available).
397
438
  */
398
439
  ILLEGAL_MODEL_REQUEST = 'illegal_model_request',
440
+ /**
441
+ * Token Limit Violation.
442
+ */
443
+ TOKEN_BALANCE = 'token_balance',
399
444
  }
400
445
 
401
446
  /**
@@ -469,7 +514,7 @@ export enum Constants {
469
514
  /**
470
515
  * Key for the Custom Config's version (librechat.yaml).
471
516
  */
472
- CONFIG_VERSION = '1.0.4',
517
+ CONFIG_VERSION = '1.0.5',
473
518
  /**
474
519
  * Standard value for the first message's `parentMessageId` value, to indicate no parent exists.
475
520
  */
@@ -186,8 +186,8 @@ export const updateAssistant = (
186
186
  return request.patch(endpoints.assistants(assistant_id), data);
187
187
  };
188
188
 
189
- export const deleteAssistant = (assistant_id: string): Promise<void> => {
190
- return request.delete(endpoints.assistants(assistant_id));
189
+ export const deleteAssistant = (assistant_id: string, model: string): Promise<void> => {
190
+ return request.delete(endpoints.assistants(assistant_id, { model }));
191
191
  };
192
192
 
193
193
  export const listAssistants = (
@@ -225,7 +225,10 @@ export const uploadAvatar = (data: FormData): Promise<f.AvatarUploadResponse> =>
225
225
  };
226
226
 
227
227
  export const uploadAssistantAvatar = (data: m.AssistantAvatarVariables): Promise<a.Assistant> => {
228
- return request.postMultiPart(endpoints.assistants(`avatar/${data.assistant_id}`), data.formData);
228
+ return request.postMultiPart(
229
+ endpoints.assistants(`avatar/${data.assistant_id}`, { model: data.model }),
230
+ data.formData,
231
+ );
229
232
  };
230
233
 
231
234
  export const updateAction = (data: m.UpdateActionVariables): Promise<m.UpdateActionResponse> => {
package/src/schemas.ts CHANGED
@@ -22,7 +22,7 @@ export const defaultAssistantFormValues = {
22
22
  name: '',
23
23
  description: '',
24
24
  instructions: '',
25
- model: 'gpt-3.5-turbo-1106',
25
+ model: '',
26
26
  functions: [],
27
27
  code_interpreter: false,
28
28
  retrieval: false,
@@ -46,6 +46,7 @@ export type LogoutOptions = {
46
46
 
47
47
  export type AssistantAvatarVariables = {
48
48
  assistant_id: string;
49
+ model: string;
49
50
  formData: FormData;
50
51
  postCreation?: boolean;
51
52
  };
@@ -86,6 +87,8 @@ export type UpdateAssistantMutationOptions = {
86
87
  ) => void;
87
88
  };
88
89
 
90
+ export type DeleteAssistantBody = { assistant_id: string; model: string };
91
+
89
92
  export type DeleteAssistantMutationOptions = {
90
93
  onSuccess?: (data: void, variables: { assistant_id: string }, context?: unknown) => void;
91
94
  onMutate?: (variables: { assistant_id: string }) => void | Promise<unknown>;
package/src/types.ts CHANGED
@@ -146,6 +146,8 @@ export type TConfig = {
146
146
  userProvide?: boolean | null;
147
147
  userProvideURL?: boolean | null;
148
148
  disableBuilder?: boolean;
149
+ retrievalModels?: string[];
150
+ capabilities?: string[];
149
151
  };
150
152
 
151
153
  export type TEndpointsConfig =
@@ -193,9 +195,21 @@ export type TResetPassword = {
193
195
  confirm_password?: string;
194
196
  };
195
197
 
198
+ export type TInterfaceConfig = {
199
+ privacyPolicy?: {
200
+ externalUrl?: string;
201
+ openNewTab?: boolean;
202
+ };
203
+ termsOfService?: {
204
+ externalUrl?: string;
205
+ openNewTab?: boolean;
206
+ };
207
+ };
208
+
196
209
  export type TStartupConfig = {
197
210
  appTitle: string;
198
211
  socialLogins?: string[];
212
+ interface?: TInterfaceConfig;
199
213
  discordLoginEnabled: boolean;
200
214
  facebookLoginEnabled: boolean;
201
215
  githubLoginEnabled: boolean;