librechat-data-provider 0.7.4 → 0.7.7

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.
Files changed (49) hide show
  1. package/check_updates.sh +1 -0
  2. package/dist/index.es.js +1 -1
  3. package/dist/index.es.js.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/react-query/index.es.js +1 -1
  7. package/dist/react-query/index.es.js.map +1 -1
  8. package/dist/react-query/package.json +1 -1
  9. package/package.json +6 -6
  10. package/react-query/package.json +1 -1
  11. package/server-rollup.config.js +3 -3
  12. package/specs/actions.spec.ts +700 -36
  13. package/specs/azure.spec.ts +8 -5
  14. package/specs/filetypes.spec.ts +1 -7
  15. package/specs/mcp.spec.ts +52 -0
  16. package/specs/openapiSpecs.ts +127 -0
  17. package/specs/utils.spec.ts +129 -0
  18. package/src/actions.ts +311 -101
  19. package/src/api-endpoints.ts +70 -13
  20. package/src/artifacts.ts +3104 -0
  21. package/src/azure.ts +40 -33
  22. package/src/bedrock.ts +227 -0
  23. package/src/config.ts +344 -78
  24. package/src/createPayload.ts +3 -1
  25. package/src/data-service.ts +353 -90
  26. package/src/file-config.ts +13 -2
  27. package/src/generate.ts +31 -2
  28. package/src/index.ts +12 -4
  29. package/src/keys.ts +17 -0
  30. package/src/mcp.ts +87 -0
  31. package/src/models.ts +1 -1
  32. package/src/parsers.ts +118 -60
  33. package/src/react-query/react-query-service.ts +54 -115
  34. package/src/request.ts +31 -7
  35. package/src/roles.ts +91 -2
  36. package/src/schemas.ts +513 -340
  37. package/src/types/agents.ts +276 -0
  38. package/src/types/assistants.ts +181 -27
  39. package/src/types/files.ts +6 -0
  40. package/src/types/mutations.ts +170 -7
  41. package/src/types/queries.ts +43 -21
  42. package/src/types/runs.ts +23 -0
  43. package/src/types.ts +132 -67
  44. package/src/utils.ts +44 -0
  45. package/src/zod.spec.ts +526 -0
  46. package/src/zod.ts +86 -0
  47. package/tsconfig.json +1 -2
  48. package/specs/parsers.spec.ts +0 -48
  49. package/src/sse.js +0 -242
package/src/azure.ts CHANGED
@@ -6,8 +6,9 @@ import type {
6
6
  TValidatedAzureConfig,
7
7
  TAzureConfigValidationResult,
8
8
  } from '../src/config';
9
- import { errorsToString, extractEnvVariable, envVarRegex } from '../src/parsers';
9
+ import { extractEnvVariable, envVarRegex } from '../src/utils';
10
10
  import { azureGroupConfigsSchema } from '../src/config';
11
+ import { errorsToString } from '../src/parsers';
11
12
 
12
13
  export const deprecatedAzureVariables = [
13
14
  /* "related to" precedes description text */
@@ -63,13 +64,13 @@ export function validateAzureGroups(configs: TAzureGroups): TAzureConfigValidati
63
64
  const {
64
65
  group: groupName,
65
66
  apiKey,
66
- instanceName,
67
- deploymentName,
68
- version,
69
- baseURL,
67
+ instanceName = '',
68
+ deploymentName = '',
69
+ version = '',
70
+ baseURL = '',
70
71
  additionalHeaders,
71
72
  models,
72
- serverless,
73
+ serverless = false,
73
74
  ...rest
74
75
  } = group;
75
76
 
@@ -120,9 +121,11 @@ export function validateAzureGroups(configs: TAzureGroups): TAzureConfigValidati
120
121
  continue;
121
122
  }
122
123
 
124
+ const groupDeploymentName = group.deploymentName ?? '';
125
+ const groupVersion = group.version ?? '';
123
126
  if (typeof model === 'boolean') {
124
127
  // For boolean models, check if group-level deploymentName and version are present.
125
- if (!group.deploymentName || !group.version) {
128
+ if (!groupDeploymentName || !groupVersion) {
126
129
  errors.push(
127
130
  `Model "${modelName}" in group "${groupName}" is missing a deploymentName or version.`,
128
131
  );
@@ -133,11 +136,10 @@ export function validateAzureGroups(configs: TAzureGroups): TAzureConfigValidati
133
136
  group: groupName,
134
137
  };
135
138
  } else {
139
+ const modelDeploymentName = model.deploymentName ?? '';
140
+ const modelVersion = model.version ?? '';
136
141
  // For object models, check if deploymentName and version are required but missing.
137
- if (
138
- (!model.deploymentName && !group.deploymentName) ||
139
- (!model.version && !group.version)
140
- ) {
142
+ if ((!modelDeploymentName && !groupDeploymentName) || (!modelVersion && !groupVersion)) {
141
143
  errors.push(
142
144
  `Model "${modelName}" in group "${groupName}" is missing a required deploymentName or version.`,
143
145
  );
@@ -146,8 +148,8 @@ export function validateAzureGroups(configs: TAzureGroups): TAzureConfigValidati
146
148
 
147
149
  modelGroupMap[modelName] = {
148
150
  group: groupName,
149
- // deploymentName: model.deploymentName || group.deploymentName,
150
- // version: model.version || group.version,
151
+ // deploymentName: modelDeploymentName || groupDeploymentName,
152
+ // version: modelVersion || groupVersion,
151
153
  };
152
154
  }
153
155
  }
@@ -190,26 +192,28 @@ export function mapModelToAzureConfig({
190
192
  );
191
193
  }
192
194
 
193
- const instanceName = groupConfig.instanceName;
195
+ const instanceName = groupConfig.instanceName ?? '';
194
196
 
195
- if (!instanceName && !groupConfig.serverless) {
197
+ if (!instanceName && groupConfig.serverless !== true) {
196
198
  throw new Error(
197
199
  `Group "${modelConfig.group}" is missing an instanceName for non-serverless configuration.`,
198
200
  );
199
201
  }
200
202
 
201
- if (groupConfig.serverless && !groupConfig.baseURL) {
203
+ const baseURL = groupConfig.baseURL ?? '';
204
+ if (groupConfig.serverless === true && !baseURL) {
202
205
  throw new Error(
203
206
  `Group "${modelConfig.group}" is missing the required base URL for serverless configuration.`,
204
207
  );
205
208
  }
206
209
 
207
- if (groupConfig.serverless) {
210
+ if (groupConfig.serverless === true) {
208
211
  const result: MappedAzureConfig = {
209
212
  azureOptions: {
213
+ azureOpenAIApiVersion: extractEnvVariable(groupConfig.version ?? ''),
210
214
  azureOpenAIApiKey: extractEnvVariable(groupConfig.apiKey),
211
215
  },
212
- baseURL: extractEnvVariable(groupConfig.baseURL as string),
216
+ baseURL: extractEnvVariable(baseURL),
213
217
  serverless: true,
214
218
  };
215
219
 
@@ -232,11 +236,11 @@ export function mapModelToAzureConfig({
232
236
  }
233
237
 
234
238
  const modelDetails = groupConfig.models[modelName];
235
- const { deploymentName, version } =
239
+ const { deploymentName = '', version = '' } =
236
240
  typeof modelDetails === 'object'
237
241
  ? {
238
- deploymentName: modelDetails.deploymentName || groupConfig.deploymentName,
239
- version: modelDetails.version || groupConfig.version,
242
+ deploymentName: modelDetails.deploymentName ?? groupConfig.deploymentName,
243
+ version: modelDetails.version ?? groupConfig.version,
240
244
  }
241
245
  : {
242
246
  deploymentName: groupConfig.deploymentName,
@@ -264,8 +268,8 @@ export function mapModelToAzureConfig({
264
268
 
265
269
  const result: MappedAzureConfig = { azureOptions };
266
270
 
267
- if (groupConfig.baseURL) {
268
- result.baseURL = extractEnvVariable(groupConfig.baseURL);
271
+ if (baseURL) {
272
+ result.baseURL = extractEnvVariable(baseURL);
269
273
  }
270
274
 
271
275
  if (groupConfig.additionalHeaders) {
@@ -287,15 +291,17 @@ export function mapGroupToAzureConfig({
287
291
  throw new Error(`Group named "${groupName}" not found in configuration.`);
288
292
  }
289
293
 
290
- const instanceName = groupConfig.instanceName as string;
294
+ const instanceName = groupConfig.instanceName ?? '';
295
+ const serverless = groupConfig.serverless ?? false;
296
+ const baseURL = groupConfig.baseURL ?? '';
291
297
 
292
- if (!instanceName && !groupConfig.serverless) {
298
+ if (!instanceName && !serverless) {
293
299
  throw new Error(
294
300
  `Group "${groupName}" is missing an instanceName for non-serverless configuration.`,
295
301
  );
296
302
  }
297
303
 
298
- if (groupConfig.serverless && !groupConfig.baseURL) {
304
+ if (serverless && !baseURL) {
299
305
  throw new Error(
300
306
  `Group "${groupName}" is missing the required base URL for serverless configuration.`,
301
307
  );
@@ -311,25 +317,26 @@ export function mapGroupToAzureConfig({
311
317
  const modelDetails = groupConfig.models[firstModelName];
312
318
 
313
319
  const azureOptions: AzureOptions = {
320
+ azureOpenAIApiVersion: extractEnvVariable(groupConfig.version ?? ''),
314
321
  azureOpenAIApiKey: extractEnvVariable(groupConfig.apiKey),
315
322
  azureOpenAIApiInstanceName: extractEnvVariable(instanceName),
316
323
  // DeploymentName and Version set below
317
324
  };
318
325
 
319
- if (groupConfig.serverless) {
326
+ if (serverless) {
320
327
  return {
321
328
  azureOptions,
322
- baseURL: extractEnvVariable(groupConfig.baseURL ?? ''),
329
+ baseURL: extractEnvVariable(baseURL),
323
330
  serverless: true,
324
331
  ...(groupConfig.additionalHeaders && { headers: groupConfig.additionalHeaders }),
325
332
  };
326
333
  }
327
334
 
328
- const { deploymentName, version } =
335
+ const { deploymentName = '', version = '' } =
329
336
  typeof modelDetails === 'object'
330
337
  ? {
331
- deploymentName: modelDetails.deploymentName || groupConfig.deploymentName,
332
- version: modelDetails.version || groupConfig.version,
338
+ deploymentName: modelDetails.deploymentName ?? groupConfig.deploymentName,
339
+ version: modelDetails.version ?? groupConfig.version,
333
340
  }
334
341
  : {
335
342
  deploymentName: groupConfig.deploymentName,
@@ -347,8 +354,8 @@ export function mapGroupToAzureConfig({
347
354
 
348
355
  const result: MappedAzureConfig = { azureOptions };
349
356
 
350
- if (groupConfig.baseURL) {
351
- result.baseURL = extractEnvVariable(groupConfig.baseURL);
357
+ if (baseURL) {
358
+ result.baseURL = extractEnvVariable(baseURL);
352
359
  }
353
360
 
354
361
  if (groupConfig.additionalHeaders) {
package/src/bedrock.ts ADDED
@@ -0,0 +1,227 @@
1
+ import { z } from 'zod';
2
+ import * as s from './schemas';
3
+
4
+ type ThinkingConfig = {
5
+ type: 'enabled';
6
+ budget_tokens: number;
7
+ };
8
+ type AnthropicReasoning = {
9
+ thinking?: ThinkingConfig | boolean;
10
+ thinkingBudget?: number;
11
+ };
12
+
13
+ type AnthropicInput = BedrockConverseInput & {
14
+ additionalModelRequestFields: BedrockConverseInput['additionalModelRequestFields'] &
15
+ AnthropicReasoning;
16
+ };
17
+
18
+ export const bedrockInputSchema = s.tConversationSchema
19
+ .pick({
20
+ /* LibreChat params; optionType: 'conversation' */
21
+ modelLabel: true,
22
+ promptPrefix: true,
23
+ resendFiles: true,
24
+ iconURL: true,
25
+ greeting: true,
26
+ spec: true,
27
+ maxOutputTokens: true,
28
+ maxContextTokens: true,
29
+ artifacts: true,
30
+ /* Bedrock params; optionType: 'model' */
31
+ region: true,
32
+ system: true,
33
+ model: true,
34
+ maxTokens: true,
35
+ temperature: true,
36
+ topP: true,
37
+ stop: true,
38
+ thinking: true,
39
+ thinkingBudget: true,
40
+ /* Catch-all fields */
41
+ topK: true,
42
+ additionalModelRequestFields: true,
43
+ })
44
+ .transform((obj) => {
45
+ if ((obj as AnthropicInput).additionalModelRequestFields?.thinking != null) {
46
+ const _obj = obj as AnthropicInput;
47
+ obj.thinking = !!_obj.additionalModelRequestFields.thinking;
48
+ obj.thinkingBudget =
49
+ typeof _obj.additionalModelRequestFields.thinking === 'object'
50
+ ? (_obj.additionalModelRequestFields.thinking as ThinkingConfig)?.budget_tokens
51
+ : undefined;
52
+ delete obj.additionalModelRequestFields;
53
+ }
54
+ return s.removeNullishValues(obj);
55
+ })
56
+ .catch(() => ({}));
57
+
58
+ export type BedrockConverseInput = z.infer<typeof bedrockInputSchema>;
59
+
60
+ export const bedrockInputParser = s.tConversationSchema
61
+ .pick({
62
+ /* LibreChat params; optionType: 'conversation' */
63
+ modelLabel: true,
64
+ promptPrefix: true,
65
+ resendFiles: true,
66
+ iconURL: true,
67
+ greeting: true,
68
+ spec: true,
69
+ artifacts: true,
70
+ maxOutputTokens: true,
71
+ maxContextTokens: true,
72
+ /* Bedrock params; optionType: 'model' */
73
+ region: true,
74
+ model: true,
75
+ maxTokens: true,
76
+ temperature: true,
77
+ topP: true,
78
+ stop: true,
79
+ thinking: true,
80
+ thinkingBudget: true,
81
+ /* Catch-all fields */
82
+ topK: true,
83
+ additionalModelRequestFields: true,
84
+ })
85
+ .catchall(z.any())
86
+ .transform((data) => {
87
+ const knownKeys = [
88
+ 'modelLabel',
89
+ 'promptPrefix',
90
+ 'resendFiles',
91
+ 'iconURL',
92
+ 'greeting',
93
+ 'spec',
94
+ 'maxOutputTokens',
95
+ 'artifacts',
96
+ 'additionalModelRequestFields',
97
+ 'region',
98
+ 'model',
99
+ 'maxTokens',
100
+ 'temperature',
101
+ 'topP',
102
+ 'stop',
103
+ ];
104
+
105
+ const additionalFields: Record<string, unknown> = {};
106
+ const typedData = data as Record<string, unknown>;
107
+
108
+ Object.entries(typedData).forEach(([key, value]) => {
109
+ if (!knownKeys.includes(key)) {
110
+ if (key === 'topK') {
111
+ additionalFields['top_k'] = value;
112
+ } else {
113
+ additionalFields[key] = value;
114
+ }
115
+ delete typedData[key];
116
+ }
117
+ });
118
+
119
+ /** Default thinking and thinkingBudget for 'anthropic.claude-3-7-sonnet' models, if not defined */
120
+ if (
121
+ typeof typedData.model === 'string' &&
122
+ typedData.model.includes('anthropic.claude-3-7-sonnet')
123
+ ) {
124
+ if (additionalFields.thinking === undefined) {
125
+ additionalFields.thinking = true;
126
+ } else if (additionalFields.thinking === false) {
127
+ delete additionalFields.thinking;
128
+ delete additionalFields.thinkingBudget;
129
+ }
130
+
131
+ if (additionalFields.thinking === true && additionalFields.thinkingBudget === undefined) {
132
+ additionalFields.thinkingBudget = 2000;
133
+ }
134
+ additionalFields.anthropic_beta = ['output-128k-2025-02-19'];
135
+ } else if (additionalFields.thinking != null || additionalFields.thinkingBudget != null) {
136
+ delete additionalFields.thinking;
137
+ delete additionalFields.thinkingBudget;
138
+ }
139
+
140
+ if (Object.keys(additionalFields).length > 0) {
141
+ typedData.additionalModelRequestFields = {
142
+ ...((typedData.additionalModelRequestFields as Record<string, unknown> | undefined) || {}),
143
+ ...additionalFields,
144
+ };
145
+ }
146
+
147
+ if (typedData.maxOutputTokens !== undefined) {
148
+ typedData.maxTokens = typedData.maxOutputTokens;
149
+ } else if (typedData.maxTokens !== undefined) {
150
+ typedData.maxOutputTokens = typedData.maxTokens;
151
+ }
152
+
153
+ return s.removeNullishValues(typedData) as BedrockConverseInput;
154
+ })
155
+ .catch(() => ({}));
156
+
157
+ /**
158
+ * Configures the "thinking" parameter based on given input and thinking options.
159
+ *
160
+ * @param data - The parsed Bedrock request options object
161
+ * @returns The object with thinking configured appropriately
162
+ */
163
+ function configureThinking(data: AnthropicInput): AnthropicInput {
164
+ const updatedData = { ...data };
165
+ if (updatedData.additionalModelRequestFields?.thinking === true) {
166
+ updatedData.maxTokens = updatedData.maxTokens ?? updatedData.maxOutputTokens ?? 8192;
167
+ delete updatedData.maxOutputTokens;
168
+ const thinkingConfig: AnthropicReasoning['thinking'] = {
169
+ type: 'enabled',
170
+ budget_tokens: updatedData.additionalModelRequestFields.thinkingBudget ?? 2000,
171
+ };
172
+
173
+ if (thinkingConfig.budget_tokens > updatedData.maxTokens) {
174
+ thinkingConfig.budget_tokens = Math.floor(updatedData.maxTokens * 0.9);
175
+ }
176
+ updatedData.additionalModelRequestFields.thinking = thinkingConfig;
177
+ delete updatedData.additionalModelRequestFields.thinkingBudget;
178
+ }
179
+ return updatedData;
180
+ }
181
+
182
+ export const bedrockOutputParser = (data: Record<string, unknown>) => {
183
+ const knownKeys = [...Object.keys(s.tConversationSchema.shape), 'topK', 'top_k'];
184
+ let result: Record<string, unknown> = {};
185
+
186
+ // Extract known fields from the root level
187
+ Object.entries(data).forEach(([key, value]) => {
188
+ if (knownKeys.includes(key)) {
189
+ result[key] = value;
190
+ }
191
+ });
192
+
193
+ // Extract known fields from additionalModelRequestFields
194
+ if (
195
+ typeof data.additionalModelRequestFields === 'object' &&
196
+ data.additionalModelRequestFields !== null
197
+ ) {
198
+ Object.entries(data.additionalModelRequestFields as Record<string, unknown>).forEach(
199
+ ([key, value]) => {
200
+ if (knownKeys.includes(key)) {
201
+ if (key === 'top_k') {
202
+ result['topK'] = value;
203
+ } else if (key === 'thinking' || key === 'thinkingBudget') {
204
+ return;
205
+ } else {
206
+ result[key] = value;
207
+ }
208
+ }
209
+ },
210
+ );
211
+ }
212
+
213
+ // Handle maxTokens and maxOutputTokens
214
+ if (result.maxTokens !== undefined && result.maxOutputTokens === undefined) {
215
+ result.maxOutputTokens = result.maxTokens;
216
+ } else if (result.maxOutputTokens !== undefined && result.maxTokens === undefined) {
217
+ result.maxTokens = result.maxOutputTokens;
218
+ }
219
+
220
+ result = configureThinking(result as AnthropicInput);
221
+ // Remove additionalModelRequestFields from the result if it doesn't thinking config
222
+ if ((result as AnthropicInput).additionalModelRequestFields?.thinking == null) {
223
+ delete result.additionalModelRequestFields;
224
+ }
225
+
226
+ return result;
227
+ };