@librechat/data-schemas 0.0.23 → 0.0.30

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.cjs CHANGED
@@ -1,1875 +1,2378 @@
1
1
  'use strict';
2
2
 
3
3
  var librechatDataProvider = require('librechat-data-provider');
4
- var jwt = require('jsonwebtoken');
5
- var node_crypto = require('node:crypto');
6
- var mongoose = require('mongoose');
7
4
  var winston = require('winston');
8
5
  require('winston-daily-rotate-file');
9
6
  var klona = require('klona');
10
7
  var path = require('path');
8
+ var jwt = require('jsonwebtoken');
9
+ var node_crypto = require('node:crypto');
10
+ var mongoose = require('mongoose');
11
11
  var _ = require('lodash');
12
12
  var meilisearch = require('meilisearch');
13
13
  var nanoid = require('nanoid');
14
14
 
15
15
  /**
16
- * Common role combinations
16
+ * Sets up the Agents configuration from the config (`librechat.yaml`) file.
17
+ * If no agents config is defined, uses the provided defaults or parses empty object.
18
+ *
19
+ * @param config - The loaded custom configuration.
20
+ * @param [defaultConfig] - Default configuration from getConfigDefaults.
21
+ * @returns The Agents endpoint configuration.
17
22
  */
18
- exports.RoleBits = void 0;
19
- (function (RoleBits) {
20
- /** 0001 = 1 */
21
- RoleBits[RoleBits["VIEWER"] = librechatDataProvider.PermissionBits.VIEW] = "VIEWER";
22
- /** 0011 = 3 */
23
- RoleBits[RoleBits["EDITOR"] = librechatDataProvider.PermissionBits.VIEW | librechatDataProvider.PermissionBits.EDIT] = "EDITOR";
24
- /** 0111 = 7 */
25
- RoleBits[RoleBits["MANAGER"] = librechatDataProvider.PermissionBits.VIEW | librechatDataProvider.PermissionBits.EDIT | librechatDataProvider.PermissionBits.DELETE] = "MANAGER";
26
- /** 1111 = 15 */
27
- RoleBits[RoleBits["OWNER"] = librechatDataProvider.PermissionBits.VIEW | librechatDataProvider.PermissionBits.EDIT | librechatDataProvider.PermissionBits.DELETE | librechatDataProvider.PermissionBits.SHARE] = "OWNER";
28
- })(exports.RoleBits || (exports.RoleBits = {}));
23
+ function agentsConfigSetup(config, defaultConfig) {
24
+ var _a;
25
+ const agentsConfig = (_a = config === null || config === void 0 ? void 0 : config.endpoints) === null || _a === void 0 ? void 0 : _a[librechatDataProvider.EModelEndpoint.agents];
26
+ if (!agentsConfig) {
27
+ return defaultConfig || librechatDataProvider.agentsEndpointSchema.parse({});
28
+ }
29
+ const parsedConfig = librechatDataProvider.agentsEndpointSchema.parse(agentsConfig);
30
+ return parsedConfig;
31
+ }
29
32
 
30
- async function signPayload({ payload, secret, expirationTime, }) {
31
- return jwt.sign(payload, secret, { expiresIn: expirationTime });
33
+ const hasValidAgent = (agent) => !!agent &&
34
+ (('id' in agent && !!agent.id) ||
35
+ ('provider' in agent && 'model' in agent && !!agent.provider && !!agent.model));
36
+ const isDisabled = (config) => !config || config.disabled === true;
37
+ function loadMemoryConfig(config) {
38
+ var _a;
39
+ if (!config)
40
+ return undefined;
41
+ if (isDisabled(config))
42
+ return config;
43
+ if (!hasValidAgent(config.agent)) {
44
+ return { ...config, disabled: true };
45
+ }
46
+ const charLimit = (_a = librechatDataProvider.memorySchema.shape.charLimit.safeParse(config.charLimit).data) !== null && _a !== void 0 ? _a : 10000;
47
+ return { ...config, charLimit };
32
48
  }
33
- async function hashToken(str) {
34
- const data = new TextEncoder().encode(str);
35
- const hashBuffer = await node_crypto.webcrypto.subtle.digest('SHA-256', data);
36
- return Buffer.from(hashBuffer).toString('hex');
49
+ function isMemoryEnabled(config) {
50
+ if (isDisabled(config))
51
+ return false;
52
+ return hasValidAgent(config.agent);
37
53
  }
38
54
 
39
- // Define the Auth sub-schema with type-safety.
40
- const AuthSchema = new mongoose.Schema({
41
- authorization_type: { type: String },
42
- custom_auth_header: { type: String },
43
- type: { type: String, enum: ['service_http', 'oauth', 'none'] },
44
- authorization_content_type: { type: String },
45
- authorization_url: { type: String },
46
- client_url: { type: String },
47
- scope: { type: String },
48
- token_exchange_method: { type: String, enum: ['default_post', 'basic_auth_header', null] },
49
- }, { _id: false });
50
- const Action = new mongoose.Schema({
51
- user: {
52
- type: mongoose.Schema.Types.ObjectId,
53
- ref: 'User',
54
- index: true,
55
- required: true,
56
- },
57
- action_id: {
58
- type: String,
59
- index: true,
60
- required: true,
61
- },
62
- type: {
63
- type: String,
64
- default: 'action_prototype',
65
- },
66
- settings: mongoose.Schema.Types.Mixed,
67
- agent_id: String,
68
- assistant_id: String,
69
- metadata: {
70
- api_key: String,
71
- auth: AuthSchema,
72
- domain: {
73
- type: String,
74
- required: true,
55
+ /**
56
+ * Loads the default interface object.
57
+ * @param params - The loaded custom configuration.
58
+ * @param params.config - The loaded custom configuration.
59
+ * @param params.configDefaults - The custom configuration default values.
60
+ * @returns default interface object.
61
+ */
62
+ async function loadDefaultInterface({ config, configDefaults, }) {
63
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
64
+ const { interface: interfaceConfig } = config !== null && config !== void 0 ? config : {};
65
+ const { interface: defaults } = configDefaults;
66
+ const hasModelSpecs = ((_c = (_b = (_a = config === null || config === void 0 ? void 0 : config.modelSpecs) === null || _a === void 0 ? void 0 : _a.list) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 0;
67
+ const includesAddedEndpoints = ((_f = (_e = (_d = config === null || config === void 0 ? void 0 : config.modelSpecs) === null || _d === void 0 ? void 0 : _d.addedEndpoints) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0) > 0;
68
+ const memoryConfig = config === null || config === void 0 ? void 0 : config.memory;
69
+ const memoryEnabled = isMemoryEnabled(memoryConfig);
70
+ /** Only disable memories if memory config is present but disabled/invalid */
71
+ const shouldDisableMemories = memoryConfig && !memoryEnabled;
72
+ const loadedInterface = librechatDataProvider.removeNullishValues({
73
+ // UI elements - use schema defaults
74
+ endpointsMenu: (_g = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.endpointsMenu) !== null && _g !== void 0 ? _g : (hasModelSpecs ? false : defaults.endpointsMenu),
75
+ modelSelect: (_h = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.modelSelect) !== null && _h !== void 0 ? _h : (hasModelSpecs ? includesAddedEndpoints : defaults.modelSelect),
76
+ parameters: (_j = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.parameters) !== null && _j !== void 0 ? _j : (hasModelSpecs ? false : defaults.parameters),
77
+ presets: (_k = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.presets) !== null && _k !== void 0 ? _k : (hasModelSpecs ? false : defaults.presets),
78
+ sidePanel: (_l = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.sidePanel) !== null && _l !== void 0 ? _l : defaults.sidePanel,
79
+ privacyPolicy: (_m = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.privacyPolicy) !== null && _m !== void 0 ? _m : defaults.privacyPolicy,
80
+ termsOfService: (_o = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.termsOfService) !== null && _o !== void 0 ? _o : defaults.termsOfService,
81
+ mcpServers: (_p = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.mcpServers) !== null && _p !== void 0 ? _p : defaults.mcpServers,
82
+ customWelcome: (_q = interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.customWelcome) !== null && _q !== void 0 ? _q : defaults.customWelcome,
83
+ // Permissions - only include if explicitly configured
84
+ bookmarks: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.bookmarks,
85
+ memories: shouldDisableMemories ? false : interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.memories,
86
+ prompts: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.prompts,
87
+ multiConvo: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.multiConvo,
88
+ agents: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.agents,
89
+ temporaryChat: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.temporaryChat,
90
+ runCode: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.runCode,
91
+ webSearch: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.webSearch,
92
+ fileSearch: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.fileSearch,
93
+ fileCitations: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.fileCitations,
94
+ peoplePicker: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.peoplePicker,
95
+ marketplace: interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.marketplace,
96
+ });
97
+ return loadedInterface;
98
+ }
99
+
100
+ /**
101
+ * ESM-native object traversal utility
102
+ * Simplified implementation focused on the forEach use case
103
+ */
104
+ function isObject(value) {
105
+ if (value === null || typeof value !== 'object') {
106
+ return false;
107
+ }
108
+ // Treat these built-in types as leaf nodes, not objects to traverse
109
+ if (value instanceof Date)
110
+ return false;
111
+ if (value instanceof RegExp)
112
+ return false;
113
+ if (value instanceof Error)
114
+ return false;
115
+ if (value instanceof URL)
116
+ return false;
117
+ // Check for Buffer (Node.js)
118
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))
119
+ return false;
120
+ // Check for TypedArrays and ArrayBuffer
121
+ if (ArrayBuffer.isView(value))
122
+ return false;
123
+ if (value instanceof ArrayBuffer)
124
+ return false;
125
+ if (value instanceof SharedArrayBuffer)
126
+ return false;
127
+ // Check for other built-in types that shouldn't be traversed
128
+ if (value instanceof Promise)
129
+ return false;
130
+ if (value instanceof WeakMap)
131
+ return false;
132
+ if (value instanceof WeakSet)
133
+ return false;
134
+ if (value instanceof Map)
135
+ return false;
136
+ if (value instanceof Set)
137
+ return false;
138
+ // Check if it's a primitive wrapper object
139
+ const stringTag = Object.prototype.toString.call(value);
140
+ if (stringTag === '[object Boolean]' ||
141
+ stringTag === '[object Number]' ||
142
+ stringTag === '[object String]') {
143
+ return false;
144
+ }
145
+ return true;
146
+ }
147
+ // Helper to safely set a property on an object or array
148
+ function setProperty(obj, key, value) {
149
+ if (Array.isArray(obj) && typeof key === 'number') {
150
+ obj[key] = value;
151
+ }
152
+ else if (!Array.isArray(obj) && typeof key === 'string') {
153
+ obj[key] = value;
154
+ }
155
+ else if (!Array.isArray(obj) && typeof key === 'number') {
156
+ // Handle numeric keys on objects
157
+ obj[key] = value;
158
+ }
159
+ }
160
+ // Helper to safely delete a property from an object
161
+ function deleteProperty(obj, key) {
162
+ if (Array.isArray(obj) && typeof key === 'number') {
163
+ // For arrays, we should use splice, but this is handled in remove()
164
+ // This function is only called for non-array deletion
165
+ return;
166
+ }
167
+ if (!Array.isArray(obj)) {
168
+ delete obj[key];
169
+ }
170
+ }
171
+ function forEach(obj, callback) {
172
+ const visited = new WeakSet();
173
+ function walk(node, path = [], parent) {
174
+ // Check for circular references
175
+ let circular = null;
176
+ if (isObject(node)) {
177
+ if (visited.has(node)) {
178
+ // Find the circular reference in the parent chain
179
+ let p = parent;
180
+ while (p) {
181
+ if (p.node === node) {
182
+ circular = p;
183
+ break;
184
+ }
185
+ p = p.parent;
186
+ }
187
+ return; // Skip circular references
188
+ }
189
+ visited.add(node);
190
+ }
191
+ const key = path.length > 0 ? path[path.length - 1] : undefined;
192
+ const isRoot = path.length === 0;
193
+ const level = path.length;
194
+ // Determine if this is a leaf node
195
+ const isLeaf = !isObject(node) ||
196
+ (Array.isArray(node) && node.length === 0) ||
197
+ Object.keys(node).length === 0;
198
+ // Create context
199
+ const context = {
200
+ node,
201
+ path: [...path],
202
+ parent,
203
+ key,
204
+ isLeaf,
205
+ notLeaf: !isLeaf,
206
+ isRoot,
207
+ notRoot: !isRoot,
208
+ level,
209
+ circular,
210
+ update(value) {
211
+ if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
212
+ setProperty(parent.node, key, value);
213
+ }
214
+ this.node = value;
215
+ },
216
+ remove() {
217
+ if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
218
+ if (Array.isArray(parent.node) && typeof key === 'number') {
219
+ parent.node.splice(key, 1);
220
+ }
221
+ else {
222
+ deleteProperty(parent.node, key);
223
+ }
224
+ }
225
+ },
226
+ };
227
+ // Call the callback with the context
228
+ callback.call(context, node);
229
+ // Traverse children if not circular and is an object
230
+ if (!circular && isObject(node) && !isLeaf) {
231
+ if (Array.isArray(node)) {
232
+ for (let i = 0; i < node.length; i++) {
233
+ walk(node[i], [...path, i], context);
234
+ }
235
+ }
236
+ else {
237
+ for (const [childKey, childValue] of Object.entries(node)) {
238
+ walk(childValue, [...path, childKey], context);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ walk(obj);
244
+ }
245
+ // Main traverse function that returns an object with forEach method
246
+ function traverse(obj) {
247
+ return {
248
+ forEach(callback) {
249
+ forEach(obj, callback);
75
250
  },
76
- privacy_policy_url: String,
77
- raw_spec: String,
78
- oauth_client_id: String,
79
- oauth_client_secret: String,
80
- },
81
- });
251
+ };
252
+ }
82
253
 
83
- const agentSchema = new mongoose.Schema({
84
- id: {
85
- type: String,
86
- index: true,
87
- unique: true,
88
- required: true,
89
- },
90
- name: {
91
- type: String,
92
- },
93
- description: {
94
- type: String,
95
- },
96
- instructions: {
97
- type: String,
98
- },
99
- avatar: {
100
- type: mongoose.Schema.Types.Mixed,
101
- default: undefined,
102
- },
103
- provider: {
104
- type: String,
105
- required: true,
106
- },
107
- model: {
108
- type: String,
109
- required: true,
110
- },
111
- model_parameters: {
112
- type: Object,
113
- },
114
- artifacts: {
115
- type: String,
116
- },
117
- access_level: {
118
- type: Number,
119
- },
120
- recursion_limit: {
121
- type: Number,
122
- },
123
- tools: {
124
- type: [String],
125
- default: undefined,
126
- },
127
- tool_kwargs: {
128
- type: [{ type: mongoose.Schema.Types.Mixed }],
129
- },
130
- actions: {
131
- type: [String],
132
- default: undefined,
133
- },
134
- author: {
135
- type: mongoose.Schema.Types.ObjectId,
136
- ref: 'User',
137
- required: true,
138
- },
139
- authorName: {
140
- type: String,
141
- default: undefined,
142
- },
143
- hide_sequential_outputs: {
144
- type: Boolean,
145
- },
146
- end_after_tools: {
147
- type: Boolean,
148
- },
149
- agent_ids: {
150
- type: [String],
151
- },
152
- isCollaborative: {
153
- type: Boolean,
154
- default: undefined,
155
- },
156
- conversation_starters: {
157
- type: [String],
158
- default: [],
159
- },
160
- tool_resources: {
161
- type: mongoose.Schema.Types.Mixed,
162
- default: {},
163
- },
164
- projectIds: {
165
- type: [mongoose.Schema.Types.ObjectId],
166
- ref: 'Project',
167
- index: true,
168
- },
169
- versions: {
170
- type: [mongoose.Schema.Types.Mixed],
171
- default: [],
172
- },
173
- category: {
174
- type: String,
175
- trim: true,
176
- index: true,
177
- default: 'general',
178
- },
179
- support_contact: {
180
- type: mongoose.Schema.Types.Mixed,
181
- default: undefined,
182
- },
183
- is_promoted: {
184
- type: Boolean,
185
- default: false,
186
- index: true,
187
- },
188
- }, {
189
- timestamps: true,
190
- });
191
- agentSchema.index({ updatedAt: -1, _id: 1 });
192
-
193
- const agentCategorySchema = new mongoose.Schema({
194
- value: {
195
- type: String,
196
- required: true,
197
- unique: true,
198
- trim: true,
199
- lowercase: true,
200
- index: true,
201
- },
202
- label: {
203
- type: String,
204
- required: true,
205
- trim: true,
206
- },
207
- description: {
208
- type: String,
209
- trim: true,
210
- default: '',
211
- },
212
- order: {
213
- type: Number,
214
- default: 0,
215
- index: true,
216
- },
217
- isActive: {
218
- type: Boolean,
219
- default: true,
220
- index: true,
221
- },
222
- custom: {
223
- type: Boolean,
224
- default: false,
225
- },
226
- }, {
227
- timestamps: true,
254
+ const SPLAT_SYMBOL = Symbol.for('splat');
255
+ const MESSAGE_SYMBOL = Symbol.for('message');
256
+ const CONSOLE_JSON_STRING_LENGTH = parseInt(process.env.CONSOLE_JSON_STRING_LENGTH || '', 10) || 255;
257
+ const DEBUG_MESSAGE_LENGTH = parseInt(process.env.DEBUG_MESSAGE_LENGTH || '', 10) || 150;
258
+ const sensitiveKeys = [
259
+ /^(sk-)[^\s]+/, // OpenAI API key pattern
260
+ /(Bearer )[^\s]+/, // Header: Bearer token pattern
261
+ /(api-key:? )[^\s]+/, // Header: API key pattern
262
+ /(key=)[^\s]+/, // URL query param: sensitive key pattern (Google)
263
+ ];
264
+ /**
265
+ * Determines if a given value string is sensitive and returns matching regex patterns.
266
+ *
267
+ * @param valueStr - The value string to check.
268
+ * @returns An array of regex patterns that match the value string.
269
+ */
270
+ function getMatchingSensitivePatterns(valueStr) {
271
+ if (valueStr) {
272
+ // Filter and return all regex patterns that match the value string
273
+ return sensitiveKeys.filter((regex) => regex.test(valueStr));
274
+ }
275
+ return [];
276
+ }
277
+ /**
278
+ * Redacts sensitive information from a console message and trims it to a specified length if provided.
279
+ * @param str - The console message to be redacted.
280
+ * @param trimLength - The optional length at which to trim the redacted message.
281
+ * @returns The redacted and optionally trimmed console message.
282
+ */
283
+ function redactMessage(str, trimLength) {
284
+ if (!str) {
285
+ return '';
286
+ }
287
+ const patterns = getMatchingSensitivePatterns(str);
288
+ patterns.forEach((pattern) => {
289
+ str = str.replace(pattern, '$1[REDACTED]');
290
+ });
291
+ return str;
292
+ }
293
+ /**
294
+ * Redacts sensitive information from log messages if the log level is 'error'.
295
+ * Note: Intentionally mutates the object.
296
+ * @param info - The log information object.
297
+ * @returns The modified log information object.
298
+ */
299
+ const redactFormat = winston.format((info) => {
300
+ if (info.level === 'error') {
301
+ // Type guard to ensure message is a string
302
+ if (typeof info.message === 'string') {
303
+ info.message = redactMessage(info.message);
304
+ }
305
+ // Handle MESSAGE_SYMBOL with type safety
306
+ const symbolValue = info[MESSAGE_SYMBOL];
307
+ if (typeof symbolValue === 'string') {
308
+ info[MESSAGE_SYMBOL] = redactMessage(symbolValue);
309
+ }
310
+ }
311
+ return info;
228
312
  });
229
- agentCategorySchema.index({ isActive: 1, order: 1 });
230
- agentCategorySchema.index({ order: 1, label: 1 });
231
-
232
- const assistantSchema = new mongoose.Schema({
233
- user: {
234
- type: mongoose.Schema.Types.ObjectId,
235
- ref: 'User',
236
- required: true,
237
- },
238
- assistant_id: {
239
- type: String,
240
- index: true,
241
- required: true,
242
- },
243
- avatar: {
244
- type: mongoose.Schema.Types.Mixed,
245
- default: undefined,
246
- },
247
- conversation_starters: {
248
- type: [String],
249
- default: [],
250
- },
251
- access_level: {
252
- type: Number,
253
- },
254
- file_ids: { type: [String], default: undefined },
255
- actions: { type: [String], default: undefined },
256
- append_current_datetime: {
257
- type: Boolean,
258
- default: false,
259
- },
260
- }, {
261
- timestamps: true,
313
+ /**
314
+ * Truncates long strings, especially base64 image data, within log messages.
315
+ *
316
+ * @param value - The value to be inspected and potentially truncated.
317
+ * @param length - The length at which to truncate the value. Default: 100.
318
+ * @returns The truncated or original value.
319
+ */
320
+ const truncateLongStrings = (value, length = 100) => {
321
+ if (typeof value === 'string') {
322
+ return value.length > length ? value.substring(0, length) + '... [truncated]' : value;
323
+ }
324
+ return value;
325
+ };
326
+ /**
327
+ * An array mapping function that truncates long strings (objects converted to JSON strings).
328
+ * @param item - The item to be condensed.
329
+ * @returns The condensed item.
330
+ */
331
+ const condenseArray = (item) => {
332
+ if (typeof item === 'string') {
333
+ return truncateLongStrings(JSON.stringify(item));
334
+ }
335
+ else if (typeof item === 'object') {
336
+ return truncateLongStrings(JSON.stringify(item));
337
+ }
338
+ return item;
339
+ };
340
+ /**
341
+ * Formats log messages for debugging purposes.
342
+ * - Truncates long strings within log messages.
343
+ * - Condenses arrays by truncating long strings and objects as strings within array items.
344
+ * - Redacts sensitive information from log messages if the log level is 'error'.
345
+ * - Converts log information object to a formatted string.
346
+ *
347
+ * @param options - The options for formatting log messages.
348
+ * @returns The formatted log message.
349
+ */
350
+ const debugTraverse = winston.format.printf(({ level, message, timestamp, ...metadata }) => {
351
+ if (!message) {
352
+ return `${timestamp} ${level}`;
353
+ }
354
+ // Type-safe version of the CJS logic: !message?.trim || typeof message !== 'string'
355
+ if (typeof message !== 'string' || !message.trim) {
356
+ return `${timestamp} ${level}: ${JSON.stringify(message)}`;
357
+ }
358
+ const msgParts = [
359
+ `${timestamp} ${level}: ${truncateLongStrings(message.trim(), DEBUG_MESSAGE_LENGTH)}`,
360
+ ];
361
+ try {
362
+ if (level !== 'debug') {
363
+ return msgParts[0];
364
+ }
365
+ if (!metadata) {
366
+ return msgParts[0];
367
+ }
368
+ // Type-safe access to SPLAT_SYMBOL using bracket notation
369
+ const metadataRecord = metadata;
370
+ const splatArray = metadataRecord[SPLAT_SYMBOL];
371
+ const debugValue = Array.isArray(splatArray) ? splatArray[0] : undefined;
372
+ if (!debugValue) {
373
+ return msgParts[0];
374
+ }
375
+ if (debugValue && Array.isArray(debugValue)) {
376
+ msgParts.push(`\n${JSON.stringify(debugValue.map(condenseArray))}`);
377
+ return msgParts.join('');
378
+ }
379
+ if (typeof debugValue !== 'object') {
380
+ msgParts.push(` ${debugValue}`);
381
+ return msgParts.join('');
382
+ }
383
+ msgParts.push('\n{');
384
+ const copy = klona.klona(metadata);
385
+ try {
386
+ const traversal = traverse(copy);
387
+ traversal.forEach(function (value) {
388
+ var _a;
389
+ if (typeof (this === null || this === void 0 ? void 0 : this.key) === 'symbol') {
390
+ return;
391
+ }
392
+ let _parentKey = '';
393
+ const parent = this.parent;
394
+ if (typeof (parent === null || parent === void 0 ? void 0 : parent.key) !== 'symbol' && (parent === null || parent === void 0 ? void 0 : parent.key) !== undefined) {
395
+ _parentKey = String(parent.key);
396
+ }
397
+ const parentKey = `${parent && parent.notRoot ? _parentKey + '.' : ''}`;
398
+ const tabs = `${parent && parent.notRoot ? ' ' : ' '}`;
399
+ const currentKey = (_a = this === null || this === void 0 ? void 0 : this.key) !== null && _a !== void 0 ? _a : 'unknown';
400
+ if (this.isLeaf && typeof value === 'string') {
401
+ const truncatedText = truncateLongStrings(value);
402
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${JSON.stringify(truncatedText)},`);
403
+ }
404
+ else if (this.notLeaf && Array.isArray(value) && value.length > 0) {
405
+ const currentMessage = `\n${tabs}// ${value.length} ${String(currentKey).replace(/s$/, '')}(s)`;
406
+ this.update(currentMessage);
407
+ msgParts.push(currentMessage);
408
+ const stringifiedArray = value.map(condenseArray);
409
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: [${stringifiedArray}],`);
410
+ }
411
+ else if (this.isLeaf && typeof value === 'function') {
412
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: function,`);
413
+ }
414
+ else if (this.isLeaf) {
415
+ msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${value},`);
416
+ }
417
+ });
418
+ }
419
+ catch (e) {
420
+ const errorMessage = e instanceof Error ? e.message : 'Unknown error';
421
+ msgParts.push(`\n[LOGGER TRAVERSAL ERROR] ${errorMessage}`);
422
+ }
423
+ msgParts.push('\n}');
424
+ return msgParts.join('');
425
+ }
426
+ catch (e) {
427
+ const errorMessage = e instanceof Error ? e.message : 'Unknown error';
428
+ msgParts.push(`\n[LOGGER PARSING ERROR] ${errorMessage}`);
429
+ return msgParts.join('');
430
+ }
262
431
  });
263
-
264
- const balanceSchema = new mongoose.Schema({
265
- user: {
266
- type: mongoose.Schema.Types.ObjectId,
267
- ref: 'User',
268
- index: true,
269
- required: true,
270
- },
271
- // 1000 tokenCredits = 1 mill ($0.001 USD)
272
- tokenCredits: {
273
- type: Number,
274
- default: 0,
275
- },
276
- // Automatic refill settings
277
- autoRefillEnabled: {
278
- type: Boolean,
279
- default: false,
280
- },
281
- refillIntervalValue: {
282
- type: Number,
283
- default: 30,
284
- },
285
- refillIntervalUnit: {
286
- type: String,
287
- enum: ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months'],
288
- default: 'days',
289
- },
290
- lastRefill: {
291
- type: Date,
292
- default: Date.now,
293
- },
294
- // amount to add on each refill
295
- refillAmount: {
296
- type: Number,
297
- default: 0,
298
- },
432
+ /**
433
+ * Truncates long string values in JSON log objects.
434
+ * Prevents outputting extremely long values (e.g., base64, blobs).
435
+ */
436
+ const jsonTruncateFormat = winston.format((info) => {
437
+ const truncateLongStrings = (str, maxLength) => str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
438
+ const seen = new WeakSet();
439
+ const truncateObject = (obj) => {
440
+ if (typeof obj !== 'object' || obj === null) {
441
+ return obj;
442
+ }
443
+ // Handle circular references - now with proper object type
444
+ if (seen.has(obj)) {
445
+ return '[Circular]';
446
+ }
447
+ seen.add(obj);
448
+ if (Array.isArray(obj)) {
449
+ return obj.map((item) => truncateObject(item));
450
+ }
451
+ // We know this is an object at this point
452
+ const objectRecord = obj;
453
+ const newObj = {};
454
+ Object.entries(objectRecord).forEach(([key, value]) => {
455
+ if (typeof value === 'string') {
456
+ newObj[key] = truncateLongStrings(value, CONSOLE_JSON_STRING_LENGTH);
457
+ }
458
+ else {
459
+ newObj[key] = truncateObject(value);
460
+ }
461
+ });
462
+ return newObj;
463
+ };
464
+ return truncateObject(info);
299
465
  });
300
466
 
301
- const bannerSchema = new mongoose.Schema({
302
- bannerId: {
303
- type: String,
304
- required: true,
305
- },
306
- message: {
307
- type: String,
308
- required: true,
309
- },
310
- displayFrom: {
311
- type: Date,
312
- required: true,
313
- default: Date.now,
314
- },
315
- displayTo: {
316
- type: Date,
317
- },
318
- type: {
319
- type: String,
320
- enum: ['banner', 'popup'],
321
- default: 'banner',
322
- },
323
- isPublic: {
324
- type: Boolean,
325
- default: false,
326
- },
327
- }, { timestamps: true });
467
+ /**
468
+ * Determine the log directory in a cross-compatible way.
469
+ * Priority:
470
+ * 1. LIBRECHAT_LOG_DIR environment variable
471
+ * 2. If running within LibreChat monorepo (when cwd ends with /api), use api/logs
472
+ * 3. If api/logs exists relative to cwd, use that (for running from project root)
473
+ * 4. Otherwise, use logs directory relative to process.cwd()
474
+ *
475
+ * This avoids using __dirname which is not available in ESM modules
476
+ */
477
+ const getLogDirectory = () => {
478
+ if (process.env.LIBRECHAT_LOG_DIR) {
479
+ return process.env.LIBRECHAT_LOG_DIR;
480
+ }
481
+ const cwd = process.cwd();
482
+ // Check if we're running from within the api directory
483
+ if (cwd.endsWith('/api') || cwd.endsWith('\\api')) {
484
+ return path.join(cwd, 'logs');
485
+ }
486
+ // Check if api/logs exists relative to current directory (running from project root)
487
+ // We'll just use the path and let the file system create it if needed
488
+ const apiLogsPath = path.join(cwd, 'api', 'logs');
489
+ // For LibreChat project structure, use api/logs
490
+ // For external consumers, they should set LIBRECHAT_LOG_DIR
491
+ if (cwd.includes('LibreChat')) {
492
+ return apiLogsPath;
493
+ }
494
+ // Default to logs directory relative to current working directory
495
+ return path.join(cwd, 'logs');
496
+ };
328
497
 
329
- const categoriesSchema = new mongoose.Schema({
330
- label: {
331
- type: String,
332
- required: true,
333
- unique: true,
334
- },
335
- value: {
336
- type: String,
337
- required: true,
338
- unique: true,
339
- },
498
+ const logDir$1 = getLogDirectory();
499
+ const { NODE_ENV: NODE_ENV$1, DEBUG_LOGGING: DEBUG_LOGGING$1, CONSOLE_JSON, DEBUG_CONSOLE } = process.env;
500
+ const useConsoleJson = typeof CONSOLE_JSON === 'string' && CONSOLE_JSON.toLowerCase() === 'true';
501
+ const useDebugConsole = typeof DEBUG_CONSOLE === 'string' && DEBUG_CONSOLE.toLowerCase() === 'true';
502
+ const useDebugLogging$1 = typeof DEBUG_LOGGING$1 === 'string' && DEBUG_LOGGING$1.toLowerCase() === 'true';
503
+ const levels$1 = {
504
+ error: 0,
505
+ warn: 1,
506
+ info: 2,
507
+ http: 3,
508
+ verbose: 4,
509
+ debug: 5,
510
+ activity: 6,
511
+ silly: 7,
512
+ };
513
+ winston.addColors({
514
+ info: 'green',
515
+ warn: 'italic yellow',
516
+ error: 'red',
517
+ debug: 'blue',
518
+ });
519
+ const level$1 = () => {
520
+ const env = NODE_ENV$1 || 'development';
521
+ return env === 'development' ? 'debug' : 'warn';
522
+ };
523
+ const fileFormat$1 = winston.format.combine(redactFormat(), winston.format.timestamp({ format: () => new Date().toISOString() }), winston.format.errors({ stack: true }), winston.format.splat());
524
+ const transports$1 = [
525
+ new winston.transports.DailyRotateFile({
526
+ level: 'error',
527
+ filename: `${logDir$1}/error-%DATE%.log`,
528
+ datePattern: 'YYYY-MM-DD',
529
+ zippedArchive: true,
530
+ maxSize: '20m',
531
+ maxFiles: '14d',
532
+ format: winston.format.combine(fileFormat$1, winston.format.json()),
533
+ }),
534
+ ];
535
+ if (useDebugLogging$1) {
536
+ transports$1.push(new winston.transports.DailyRotateFile({
537
+ level: 'debug',
538
+ filename: `${logDir$1}/debug-%DATE%.log`,
539
+ datePattern: 'YYYY-MM-DD',
540
+ zippedArchive: true,
541
+ maxSize: '20m',
542
+ maxFiles: '14d',
543
+ format: winston.format.combine(fileFormat$1, debugTraverse),
544
+ }));
545
+ }
546
+ const consoleFormat$1 = winston.format.combine(redactFormat(), winston.format.colorize({ all: true }), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf((info) => {
547
+ const message = `${info.timestamp} ${info.level}: ${info.message}`;
548
+ return info.level.includes('error') ? redactMessage(message) : message;
549
+ }));
550
+ let consoleLogLevel = 'info';
551
+ if (useDebugConsole) {
552
+ consoleLogLevel = 'debug';
553
+ }
554
+ // Add console transport
555
+ if (useDebugConsole) {
556
+ transports$1.push(new winston.transports.Console({
557
+ level: consoleLogLevel,
558
+ format: useConsoleJson
559
+ ? winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json())
560
+ : winston.format.combine(fileFormat$1, debugTraverse),
561
+ }));
562
+ }
563
+ else if (useConsoleJson) {
564
+ transports$1.push(new winston.transports.Console({
565
+ level: consoleLogLevel,
566
+ format: winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json()),
567
+ }));
568
+ }
569
+ else {
570
+ transports$1.push(new winston.transports.Console({
571
+ level: consoleLogLevel,
572
+ format: consoleFormat$1,
573
+ }));
574
+ }
575
+ // Create logger
576
+ const logger$1 = winston.createLogger({
577
+ level: level$1(),
578
+ levels: levels$1,
579
+ transports: transports$1,
340
580
  });
341
581
 
342
- const conversationTag = new mongoose.Schema({
343
- tag: {
344
- type: String,
345
- index: true,
346
- },
347
- user: {
348
- type: String,
349
- index: true,
350
- },
351
- description: {
352
- type: String,
353
- index: true,
354
- },
355
- count: {
356
- type: Number,
357
- default: 0,
358
- },
359
- position: {
360
- type: Number,
361
- default: 0,
362
- index: true,
363
- },
364
- }, { timestamps: true });
365
- // Create a compound index on tag and user with unique constraint.
366
- conversationTag.index({ tag: 1, user: 1 }, { unique: true });
367
-
368
- // @ts-ignore
369
- const conversationPreset = {
370
- // endpoint: [azureOpenAI, openAI, anthropic, chatGPTBrowser]
371
- endpoint: {
372
- type: String,
373
- default: null,
374
- required: true,
375
- },
376
- endpointType: {
377
- type: String,
378
- },
379
- // for azureOpenAI, openAI, chatGPTBrowser only
380
- model: {
381
- type: String,
382
- required: false,
383
- },
384
- // for bedrock only
385
- region: {
386
- type: String,
387
- required: false,
388
- },
389
- // for azureOpenAI, openAI only
390
- chatGptLabel: {
391
- type: String,
392
- required: false,
393
- },
394
- // for google only
395
- examples: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
396
- modelLabel: {
397
- type: String,
398
- required: false,
399
- },
400
- promptPrefix: {
401
- type: String,
402
- required: false,
403
- },
404
- temperature: {
405
- type: Number,
406
- required: false,
407
- },
408
- top_p: {
409
- type: Number,
410
- required: false,
411
- },
412
- // for google only
413
- topP: {
414
- type: Number,
415
- required: false,
416
- },
417
- topK: {
418
- type: Number,
419
- required: false,
420
- },
421
- maxOutputTokens: {
422
- type: Number,
423
- required: false,
424
- },
425
- maxTokens: {
426
- type: Number,
427
- required: false,
428
- },
429
- presence_penalty: {
430
- type: Number,
431
- required: false,
432
- },
433
- frequency_penalty: {
434
- type: Number,
435
- required: false,
436
- },
437
- file_ids: { type: [{ type: String }], default: undefined },
438
- // deprecated
439
- resendImages: {
440
- type: Boolean,
441
- },
442
- /* Anthropic only */
443
- promptCache: {
444
- type: Boolean,
445
- },
446
- thinking: {
447
- type: Boolean,
448
- },
449
- thinkingBudget: {
450
- type: Number,
451
- },
452
- system: {
453
- type: String,
454
- },
455
- // files
456
- resendFiles: {
457
- type: Boolean,
458
- },
459
- imageDetail: {
460
- type: String,
461
- },
462
- /* agents */
463
- agent_id: {
464
- type: String,
465
- },
466
- /* assistants */
467
- assistant_id: {
468
- type: String,
469
- },
470
- instructions: {
471
- type: String,
472
- },
473
- stop: { type: [{ type: String }], default: undefined },
474
- isArchived: {
475
- type: Boolean,
476
- default: false,
477
- },
478
- /* UI Components */
479
- iconURL: {
480
- type: String,
481
- },
482
- greeting: {
483
- type: String,
484
- },
485
- spec: {
486
- type: String,
487
- },
488
- tags: {
489
- type: [String],
490
- default: [],
491
- },
492
- tools: { type: [{ type: String }], default: undefined },
493
- maxContextTokens: {
494
- type: Number,
495
- },
496
- max_tokens: {
497
- type: Number,
498
- },
499
- useResponsesApi: {
500
- type: Boolean,
501
- },
502
- /** OpenAI Responses API / Anthropic API / Google API */
503
- web_search: {
504
- type: Boolean,
505
- },
506
- disableStreaming: {
507
- type: Boolean,
508
- },
509
- fileTokenLimit: {
510
- type: Number,
511
- },
512
- /** Reasoning models only */
513
- reasoning_effort: {
514
- type: String,
582
+ /**
583
+ * Loads and maps the Cloudflare Turnstile configuration.
584
+ *
585
+ * Expected config structure:
586
+ *
587
+ * turnstile:
588
+ * siteKey: "your-site-key-here"
589
+ * options:
590
+ * language: "auto" // "auto" or an ISO 639-1 language code (e.g. en)
591
+ * size: "normal" // Options: "normal", "compact", "flexible", or "invisible"
592
+ *
593
+ * @param config - The loaded custom configuration.
594
+ * @param configDefaults - The custom configuration default values.
595
+ * @returns The mapped Turnstile configuration.
596
+ */
597
+ function loadTurnstileConfig(config, configDefaults) {
598
+ var _a, _b;
599
+ const { turnstile: customTurnstile } = config !== null && config !== void 0 ? config : {};
600
+ const { turnstile: defaults } = configDefaults;
601
+ const loadedTurnstile = librechatDataProvider.removeNullishValues({
602
+ siteKey: (_a = customTurnstile === null || customTurnstile === void 0 ? void 0 : customTurnstile.siteKey) !== null && _a !== void 0 ? _a : defaults === null || defaults === void 0 ? void 0 : defaults.siteKey,
603
+ options: (_b = customTurnstile === null || customTurnstile === void 0 ? void 0 : customTurnstile.options) !== null && _b !== void 0 ? _b : defaults === null || defaults === void 0 ? void 0 : defaults.options,
604
+ });
605
+ const enabled = Boolean(loadedTurnstile.siteKey);
606
+ if (enabled) {
607
+ logger$1.debug('Turnstile is ENABLED with configuration:\n' + JSON.stringify(loadedTurnstile, null, 2));
608
+ }
609
+ else {
610
+ logger$1.debug('Turnstile is DISABLED (no siteKey provided).');
611
+ }
612
+ return loadedTurnstile;
613
+ }
614
+
615
+ const webSearchAuth = {
616
+ providers: {
617
+ serper: {
618
+ serperApiKey: 1,
619
+ },
620
+ searxng: {
621
+ searxngInstanceUrl: 1,
622
+ /** Optional (0) */
623
+ searxngApiKey: 0,
624
+ },
515
625
  },
516
- reasoning_summary: {
517
- type: String,
626
+ scrapers: {
627
+ firecrawl: {
628
+ firecrawlApiKey: 1,
629
+ /** Optional (0) */
630
+ firecrawlApiUrl: 0,
631
+ firecrawlVersion: 0,
632
+ },
633
+ serper: {
634
+ serperApiKey: 1,
635
+ },
518
636
  },
519
- /** Verbosity control */
520
- verbosity: {
521
- type: String,
637
+ rerankers: {
638
+ jina: {
639
+ jinaApiKey: 1,
640
+ /** Optional (0) */
641
+ jinaApiUrl: 0,
642
+ },
643
+ cohere: { cohereApiKey: 1 },
522
644
  },
523
645
  };
646
+ /**
647
+ * Extracts all unique API keys from the webSearchAuth configuration object
648
+ */
649
+ function getWebSearchKeys() {
650
+ const keysSet = new Set();
651
+ // Iterate through each category (providers, scrapers, rerankers)
652
+ for (const category of Object.keys(webSearchAuth)) {
653
+ const categoryObj = webSearchAuth[category];
654
+ // Iterate through each service within the category
655
+ for (const service of Object.keys(categoryObj)) {
656
+ const serviceObj = categoryObj[service];
657
+ // Extract the API keys from the service and add to set for deduplication
658
+ for (const key of Object.keys(serviceObj)) {
659
+ keysSet.add(key);
660
+ }
661
+ }
662
+ }
663
+ return Array.from(keysSet);
664
+ }
665
+ const webSearchKeys = getWebSearchKeys();
666
+ function loadWebSearchConfig(config) {
667
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
668
+ const serperApiKey = (_a = config === null || config === void 0 ? void 0 : config.serperApiKey) !== null && _a !== void 0 ? _a : '${SERPER_API_KEY}';
669
+ const searxngInstanceUrl = (_b = config === null || config === void 0 ? void 0 : config.searxngInstanceUrl) !== null && _b !== void 0 ? _b : '${SEARXNG_INSTANCE_URL}';
670
+ const searxngApiKey = (_c = config === null || config === void 0 ? void 0 : config.searxngApiKey) !== null && _c !== void 0 ? _c : '${SEARXNG_API_KEY}';
671
+ const firecrawlApiKey = (_d = config === null || config === void 0 ? void 0 : config.firecrawlApiKey) !== null && _d !== void 0 ? _d : '${FIRECRAWL_API_KEY}';
672
+ const firecrawlApiUrl = (_e = config === null || config === void 0 ? void 0 : config.firecrawlApiUrl) !== null && _e !== void 0 ? _e : '${FIRECRAWL_API_URL}';
673
+ const firecrawlVersion = (_f = config === null || config === void 0 ? void 0 : config.firecrawlVersion) !== null && _f !== void 0 ? _f : '${FIRECRAWL_VERSION}';
674
+ const jinaApiKey = (_g = config === null || config === void 0 ? void 0 : config.jinaApiKey) !== null && _g !== void 0 ? _g : '${JINA_API_KEY}';
675
+ const jinaApiUrl = (_h = config === null || config === void 0 ? void 0 : config.jinaApiUrl) !== null && _h !== void 0 ? _h : '${JINA_API_URL}';
676
+ const cohereApiKey = (_j = config === null || config === void 0 ? void 0 : config.cohereApiKey) !== null && _j !== void 0 ? _j : '${COHERE_API_KEY}';
677
+ const safeSearch = (_k = config === null || config === void 0 ? void 0 : config.safeSearch) !== null && _k !== void 0 ? _k : librechatDataProvider.SafeSearchTypes.MODERATE;
678
+ return {
679
+ ...config,
680
+ safeSearch,
681
+ jinaApiKey,
682
+ jinaApiUrl,
683
+ cohereApiKey,
684
+ serperApiKey,
685
+ searxngApiKey,
686
+ firecrawlApiKey,
687
+ firecrawlApiUrl,
688
+ firecrawlVersion,
689
+ searxngInstanceUrl,
690
+ };
691
+ }
524
692
 
525
- const convoSchema = new mongoose.Schema({
526
- conversationId: {
527
- type: String,
528
- unique: true,
529
- required: true,
530
- index: true,
531
- meiliIndex: true,
532
- },
533
- title: {
534
- type: String,
535
- default: 'New Chat',
536
- meiliIndex: true,
537
- },
538
- user: {
539
- type: String,
540
- index: true,
541
- meiliIndex: true,
542
- },
543
- messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
544
- agentOptions: {
545
- type: mongoose.Schema.Types.Mixed,
546
- },
547
- ...conversationPreset,
548
- agent_id: {
549
- type: String,
550
- },
551
- tags: {
552
- type: [String],
553
- default: [],
554
- meiliIndex: true,
555
- },
556
- files: {
557
- type: [String],
558
- },
559
- expiredAt: {
560
- type: Date,
561
- },
562
- }, { timestamps: true });
563
- convoSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
564
- convoSchema.index({ createdAt: 1, updatedAt: 1 });
565
- convoSchema.index({ conversationId: 1, user: 1 }, { unique: true });
693
+ /**
694
+ * Sets up Model Specs from the config (`librechat.yaml`) file.
695
+ * @param [endpoints] - The loaded custom configuration for endpoints.
696
+ * @param [modelSpecs] - The loaded custom configuration for model specs.
697
+ * @param [interfaceConfig] - The loaded interface configuration.
698
+ * @returns The processed model specs, if any.
699
+ */
700
+ function processModelSpecs(endpoints, _modelSpecs, interfaceConfig) {
701
+ var _a, _b, _c, _d;
702
+ if (!_modelSpecs) {
703
+ return undefined;
704
+ }
705
+ const list = _modelSpecs.list;
706
+ const modelSpecs = [];
707
+ const customEndpoints = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints[librechatDataProvider.EModelEndpoint.custom]) !== null && _a !== void 0 ? _a : [];
708
+ if ((interfaceConfig === null || interfaceConfig === void 0 ? void 0 : interfaceConfig.modelSelect) !== true && ((_c = (_b = _modelSpecs.addedEndpoints) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 0) {
709
+ logger$1.warn(`To utilize \`addedEndpoints\`, which allows provider/model selections alongside model specs, set \`modelSelect: true\` in the interface configuration.
566
710
 
567
- const file = new mongoose.Schema({
711
+ Example:
712
+ \`\`\`yaml
713
+ interface:
714
+ modelSelect: true
715
+ \`\`\`
716
+ `);
717
+ }
718
+ if (!list || list.length === 0) {
719
+ return undefined;
720
+ }
721
+ for (const spec of list) {
722
+ const currentEndpoint = (_d = spec.preset) === null || _d === void 0 ? void 0 : _d.endpoint;
723
+ if (!currentEndpoint) {
724
+ logger$1.warn('A model spec is missing the `endpoint` field within its `preset`. Skipping model spec...');
725
+ continue;
726
+ }
727
+ if (librechatDataProvider.EModelEndpoint[currentEndpoint] && currentEndpoint !== librechatDataProvider.EModelEndpoint.custom) {
728
+ modelSpecs.push(spec);
729
+ continue;
730
+ }
731
+ else if (currentEndpoint === librechatDataProvider.EModelEndpoint.custom) {
732
+ logger$1.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...`);
733
+ continue;
734
+ }
735
+ const normalizedName = librechatDataProvider.normalizeEndpointName(currentEndpoint);
736
+ const endpoint = customEndpoints.find((customEndpoint) => normalizedName === librechatDataProvider.normalizeEndpointName(customEndpoint.name));
737
+ if (!endpoint) {
738
+ logger$1.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.
739
+
740
+ For more information, see the documentation at https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/model_specs#endpoint`);
741
+ continue;
742
+ }
743
+ modelSpecs.push({
744
+ ...spec,
745
+ preset: {
746
+ ...spec.preset,
747
+ endpoint: normalizedName,
748
+ },
749
+ });
750
+ }
751
+ return {
752
+ ..._modelSpecs,
753
+ list: modelSpecs,
754
+ };
755
+ }
756
+
757
+ /**
758
+ * Sets up the minimum, default Assistants configuration if Azure OpenAI Assistants option is enabled.
759
+ * @returns The Assistants endpoint configuration.
760
+ */
761
+ function azureAssistantsDefaults() {
762
+ return {
763
+ capabilities: [librechatDataProvider.Capabilities.tools, librechatDataProvider.Capabilities.actions, librechatDataProvider.Capabilities.code_interpreter],
764
+ version: librechatDataProvider.defaultAssistantsVersion.azureAssistants,
765
+ };
766
+ }
767
+ /**
768
+ * Sets up the Assistants configuration from the config (`librechat.yaml`) file.
769
+ * @param config - The loaded custom configuration.
770
+ * @param assistantsEndpoint - The Assistants endpoint name.
771
+ * - The previously loaded assistants configuration from Azure OpenAI Assistants option.
772
+ * @param [prevConfig]
773
+ * @returns The Assistants endpoint configuration.
774
+ */
775
+ function assistantsConfigSetup(config, assistantsEndpoint, prevConfig = {}) {
776
+ var _a, _b, _c, _d, _e;
777
+ const assistantsConfig = (_a = config.endpoints) === null || _a === void 0 ? void 0 : _a[assistantsEndpoint];
778
+ const parsedConfig = librechatDataProvider.assistantEndpointSchema.parse(assistantsConfig);
779
+ if (((_b = assistantsConfig === null || assistantsConfig === void 0 ? void 0 : assistantsConfig.supportedIds) === null || _b === void 0 ? void 0 : _b.length) && ((_c = assistantsConfig.excludedIds) === null || _c === void 0 ? void 0 : _c.length)) {
780
+ logger$1.warn(`Configuration conflict: The '${assistantsEndpoint}' endpoint has both 'supportedIds' and 'excludedIds' defined. The 'excludedIds' will be ignored.`);
781
+ }
782
+ if ((assistantsConfig === null || assistantsConfig === void 0 ? void 0 : assistantsConfig.privateAssistants) &&
783
+ (((_d = assistantsConfig.supportedIds) === null || _d === void 0 ? void 0 : _d.length) || ((_e = assistantsConfig.excludedIds) === null || _e === void 0 ? void 0 : _e.length))) {
784
+ logger$1.warn(`Configuration conflict: The '${assistantsEndpoint}' endpoint has both 'privateAssistants' and 'supportedIds' or 'excludedIds' defined. The 'supportedIds' and 'excludedIds' will be ignored.`);
785
+ }
786
+ return {
787
+ ...prevConfig,
788
+ retrievalModels: parsedConfig.retrievalModels,
789
+ disableBuilder: parsedConfig.disableBuilder,
790
+ pollIntervalMs: parsedConfig.pollIntervalMs,
791
+ supportedIds: parsedConfig.supportedIds,
792
+ capabilities: parsedConfig.capabilities,
793
+ excludedIds: parsedConfig.excludedIds,
794
+ privateAssistants: parsedConfig.privateAssistants,
795
+ timeoutMs: parsedConfig.timeoutMs,
796
+ streamRate: parsedConfig.streamRate,
797
+ titlePrompt: parsedConfig.titlePrompt,
798
+ titleMethod: parsedConfig.titleMethod,
799
+ titleModel: parsedConfig.titleModel,
800
+ titleEndpoint: parsedConfig.titleEndpoint,
801
+ titlePromptTemplate: parsedConfig.titlePromptTemplate,
802
+ };
803
+ }
804
+
805
+ /**
806
+ * Sets up the Azure OpenAI configuration from the config (`librechat.yaml`) file.
807
+ * @param config - The loaded custom configuration.
808
+ * @returns The Azure OpenAI configuration.
809
+ */
810
+ function azureConfigSetup(config) {
811
+ var _a, _b, _c;
812
+ const azureConfig = (_a = config.endpoints) === null || _a === void 0 ? void 0 : _a[librechatDataProvider.EModelEndpoint.azureOpenAI];
813
+ if (!azureConfig) {
814
+ throw new Error('Azure OpenAI configuration is missing.');
815
+ }
816
+ const { groups, ...azureConfiguration } = azureConfig;
817
+ const { isValid, modelNames, modelGroupMap, groupMap, errors } = librechatDataProvider.validateAzureGroups(groups);
818
+ if (!isValid) {
819
+ const errorString = errors.join('\n');
820
+ const errorMessage = 'Invalid Azure OpenAI configuration:\n' + errorString;
821
+ logger$1.error(errorMessage);
822
+ throw new Error(errorMessage);
823
+ }
824
+ const assistantModels = [];
825
+ const assistantGroups = new Set();
826
+ for (const modelName of modelNames) {
827
+ librechatDataProvider.mapModelToAzureConfig({ modelName, modelGroupMap, groupMap });
828
+ const groupName = (_b = modelGroupMap === null || modelGroupMap === void 0 ? void 0 : modelGroupMap[modelName]) === null || _b === void 0 ? void 0 : _b.group;
829
+ const modelGroup = groupMap === null || groupMap === void 0 ? void 0 : groupMap[groupName];
830
+ const supportsAssistants = (modelGroup === null || modelGroup === void 0 ? void 0 : modelGroup.assistants) || ((_c = modelGroup === null || modelGroup === void 0 ? void 0 : modelGroup[modelName]) === null || _c === void 0 ? void 0 : _c.assistants);
831
+ if (supportsAssistants) {
832
+ assistantModels.push(modelName);
833
+ if (!assistantGroups.has(groupName)) {
834
+ assistantGroups.add(groupName);
835
+ }
836
+ }
837
+ }
838
+ if (azureConfiguration.assistants && assistantModels.length === 0) {
839
+ 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.');
840
+ }
841
+ if (azureConfiguration.assistants &&
842
+ process.env.ENDPOINTS &&
843
+ !process.env.ENDPOINTS.includes(librechatDataProvider.EModelEndpoint.azureAssistants)) {
844
+ logger$1.warn(`Azure Assistants are configured, but the endpoint will not be accessible as it's not included in the ENDPOINTS environment variable.
845
+ Please add the value "${librechatDataProvider.EModelEndpoint.azureAssistants}" to the ENDPOINTS list if expected.`);
846
+ }
847
+ return {
848
+ errors,
849
+ isValid,
850
+ groupMap,
851
+ modelNames,
852
+ modelGroupMap,
853
+ assistantModels,
854
+ assistantGroups: Array.from(assistantGroups),
855
+ ...azureConfiguration,
856
+ };
857
+ }
858
+
859
+ /**
860
+ * Loads custom config endpoints
861
+ * @param [config]
862
+ * @param [agentsDefaults]
863
+ */
864
+ const loadEndpoints = (config, agentsDefaults) => {
865
+ var _a;
866
+ const loadedEndpoints = {};
867
+ const endpoints = config === null || config === void 0 ? void 0 : config.endpoints;
868
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[librechatDataProvider.EModelEndpoint.azureOpenAI]) {
869
+ loadedEndpoints[librechatDataProvider.EModelEndpoint.azureOpenAI] = azureConfigSetup(config);
870
+ }
871
+ if ((_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints[librechatDataProvider.EModelEndpoint.azureOpenAI]) === null || _a === void 0 ? void 0 : _a.assistants) {
872
+ loadedEndpoints[librechatDataProvider.EModelEndpoint.azureAssistants] = azureAssistantsDefaults();
873
+ }
874
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[librechatDataProvider.EModelEndpoint.azureAssistants]) {
875
+ loadedEndpoints[librechatDataProvider.EModelEndpoint.azureAssistants] = assistantsConfigSetup(config, librechatDataProvider.EModelEndpoint.azureAssistants, loadedEndpoints[librechatDataProvider.EModelEndpoint.azureAssistants]);
876
+ }
877
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[librechatDataProvider.EModelEndpoint.assistants]) {
878
+ loadedEndpoints[librechatDataProvider.EModelEndpoint.assistants] = assistantsConfigSetup(config, librechatDataProvider.EModelEndpoint.assistants, loadedEndpoints[librechatDataProvider.EModelEndpoint.assistants]);
879
+ }
880
+ loadedEndpoints[librechatDataProvider.EModelEndpoint.agents] = agentsConfigSetup(config, agentsDefaults);
881
+ const endpointKeys = [
882
+ librechatDataProvider.EModelEndpoint.openAI,
883
+ librechatDataProvider.EModelEndpoint.google,
884
+ librechatDataProvider.EModelEndpoint.custom,
885
+ librechatDataProvider.EModelEndpoint.bedrock,
886
+ librechatDataProvider.EModelEndpoint.anthropic,
887
+ ];
888
+ endpointKeys.forEach((key) => {
889
+ const currentKey = key;
890
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints[currentKey]) {
891
+ loadedEndpoints[currentKey] = endpoints[currentKey];
892
+ }
893
+ });
894
+ if (endpoints === null || endpoints === void 0 ? void 0 : endpoints.all) {
895
+ loadedEndpoints.all = endpoints.all;
896
+ }
897
+ return loadedEndpoints;
898
+ };
899
+
900
+ function loadOCRConfig(config) {
901
+ var _a, _b, _c, _d;
902
+ if (!config)
903
+ return;
904
+ const baseURL = (_a = config === null || config === void 0 ? void 0 : config.baseURL) !== null && _a !== void 0 ? _a : '';
905
+ const apiKey = (_b = config === null || config === void 0 ? void 0 : config.apiKey) !== null && _b !== void 0 ? _b : '';
906
+ const mistralModel = (_c = config === null || config === void 0 ? void 0 : config.mistralModel) !== null && _c !== void 0 ? _c : '';
907
+ return {
908
+ apiKey,
909
+ baseURL,
910
+ mistralModel,
911
+ strategy: (_d = config === null || config === void 0 ? void 0 : config.strategy) !== null && _d !== void 0 ? _d : librechatDataProvider.OCRStrategy.MISTRAL_OCR,
912
+ };
913
+ }
914
+
915
+ /**
916
+ * Loads custom config and initializes app-wide variables.
917
+ * @function AppService
918
+ */
919
+ const AppService = async (params) => {
920
+ var _a, _b, _c, _d, _e, _f;
921
+ const { config, paths, systemTools } = params || {};
922
+ if (!config) {
923
+ throw new Error('Config is required');
924
+ }
925
+ const configDefaults = librechatDataProvider.getConfigDefaults();
926
+ const ocr = loadOCRConfig(config.ocr);
927
+ const webSearch = loadWebSearchConfig(config.webSearch);
928
+ const memory = loadMemoryConfig(config.memory);
929
+ const filteredTools = config.filteredTools;
930
+ const includedTools = config.includedTools;
931
+ const fileStrategy = ((_a = config.fileStrategy) !== null && _a !== void 0 ? _a : configDefaults.fileStrategy);
932
+ const startBalance = process.env.START_BALANCE;
933
+ const balance = (_b = config.balance) !== null && _b !== void 0 ? _b : {
934
+ enabled: ((_c = process.env.CHECK_BALANCE) === null || _c === void 0 ? void 0 : _c.toLowerCase().trim()) === 'true',
935
+ startBalance: startBalance ? parseInt(startBalance, 10) : undefined,
936
+ };
937
+ const transactions = (_d = config.transactions) !== null && _d !== void 0 ? _d : configDefaults.transactions;
938
+ const imageOutputType = (_e = config === null || config === void 0 ? void 0 : config.imageOutputType) !== null && _e !== void 0 ? _e : configDefaults.imageOutputType;
939
+ process.env.CDN_PROVIDER = fileStrategy;
940
+ const availableTools = systemTools;
941
+ const mcpConfig = config.mcpServers || null;
942
+ const registration = (_f = config.registration) !== null && _f !== void 0 ? _f : configDefaults.registration;
943
+ const interfaceConfig = await loadDefaultInterface({ config, configDefaults });
944
+ const turnstileConfig = loadTurnstileConfig(config, configDefaults);
945
+ const speech = config.speech;
946
+ const defaultConfig = {
947
+ ocr,
948
+ paths,
949
+ config,
950
+ memory,
951
+ speech,
952
+ balance,
953
+ transactions,
954
+ mcpConfig,
955
+ webSearch,
956
+ fileStrategy,
957
+ registration,
958
+ filteredTools,
959
+ includedTools,
960
+ availableTools,
961
+ imageOutputType,
962
+ interfaceConfig,
963
+ turnstileConfig,
964
+ fileStrategies: config.fileStrategies,
965
+ };
966
+ const agentsDefaults = agentsConfigSetup(config);
967
+ if (!Object.keys(config).length) {
968
+ const appConfig = {
969
+ ...defaultConfig,
970
+ endpoints: {
971
+ [librechatDataProvider.EModelEndpoint.agents]: agentsDefaults,
972
+ },
973
+ };
974
+ return appConfig;
975
+ }
976
+ const loadedEndpoints = loadEndpoints(config, agentsDefaults);
977
+ const appConfig = {
978
+ ...defaultConfig,
979
+ fileConfig: config === null || config === void 0 ? void 0 : config.fileConfig,
980
+ secureImageLinks: config === null || config === void 0 ? void 0 : config.secureImageLinks,
981
+ modelSpecs: processModelSpecs(config === null || config === void 0 ? void 0 : config.endpoints, config.modelSpecs, interfaceConfig),
982
+ endpoints: loadedEndpoints,
983
+ };
984
+ return appConfig;
985
+ };
986
+
987
+ /**
988
+ * Common role combinations
989
+ */
990
+ exports.RoleBits = void 0;
991
+ (function (RoleBits) {
992
+ /** 0001 = 1 */
993
+ RoleBits[RoleBits["VIEWER"] = librechatDataProvider.PermissionBits.VIEW] = "VIEWER";
994
+ /** 0011 = 3 */
995
+ RoleBits[RoleBits["EDITOR"] = librechatDataProvider.PermissionBits.VIEW | librechatDataProvider.PermissionBits.EDIT] = "EDITOR";
996
+ /** 0111 = 7 */
997
+ RoleBits[RoleBits["MANAGER"] = librechatDataProvider.PermissionBits.VIEW | librechatDataProvider.PermissionBits.EDIT | librechatDataProvider.PermissionBits.DELETE] = "MANAGER";
998
+ /** 1111 = 15 */
999
+ RoleBits[RoleBits["OWNER"] = librechatDataProvider.PermissionBits.VIEW | librechatDataProvider.PermissionBits.EDIT | librechatDataProvider.PermissionBits.DELETE | librechatDataProvider.PermissionBits.SHARE] = "OWNER";
1000
+ })(exports.RoleBits || (exports.RoleBits = {}));
1001
+
1002
+ async function signPayload({ payload, secret, expirationTime, }) {
1003
+ return jwt.sign(payload, secret, { expiresIn: expirationTime });
1004
+ }
1005
+ async function hashToken(str) {
1006
+ const data = new TextEncoder().encode(str);
1007
+ const hashBuffer = await node_crypto.webcrypto.subtle.digest('SHA-256', data);
1008
+ return Buffer.from(hashBuffer).toString('hex');
1009
+ }
1010
+
1011
+ // Define the Auth sub-schema with type-safety.
1012
+ const AuthSchema = new mongoose.Schema({
1013
+ authorization_type: { type: String },
1014
+ custom_auth_header: { type: String },
1015
+ type: { type: String, enum: ['service_http', 'oauth', 'none'] },
1016
+ authorization_content_type: { type: String },
1017
+ authorization_url: { type: String },
1018
+ client_url: { type: String },
1019
+ scope: { type: String },
1020
+ token_exchange_method: { type: String, enum: ['default_post', 'basic_auth_header', null] },
1021
+ }, { _id: false });
1022
+ const Action = new mongoose.Schema({
568
1023
  user: {
569
1024
  type: mongoose.Schema.Types.ObjectId,
570
1025
  ref: 'User',
571
1026
  index: true,
572
1027
  required: true,
573
1028
  },
574
- conversationId: {
575
- type: String,
576
- ref: 'Conversation',
577
- index: true,
578
- },
579
- file_id: {
1029
+ action_id: {
580
1030
  type: String,
581
1031
  index: true,
582
1032
  required: true,
583
1033
  },
584
- temp_file_id: {
585
- type: String,
586
- },
587
- bytes: {
588
- type: Number,
589
- required: true,
590
- },
591
- filename: {
592
- type: String,
593
- required: true,
594
- },
595
- filepath: {
596
- type: String,
597
- required: true,
598
- },
599
- object: {
600
- type: String,
601
- required: true,
602
- default: 'file',
603
- },
604
- embedded: {
605
- type: Boolean,
606
- },
607
1034
  type: {
608
1035
  type: String,
609
- required: true,
610
- },
611
- text: {
612
- type: String,
613
- },
614
- context: {
615
- type: String,
616
- },
617
- usage: {
618
- type: Number,
619
- required: true,
620
- default: 0,
621
- },
622
- source: {
623
- type: String,
624
- default: librechatDataProvider.FileSources.local,
625
- },
626
- model: {
627
- type: String,
1036
+ default: 'action_prototype',
628
1037
  },
629
- width: Number,
630
- height: Number,
1038
+ settings: mongoose.Schema.Types.Mixed,
1039
+ agent_id: String,
1040
+ assistant_id: String,
631
1041
  metadata: {
632
- fileIdentifier: String,
633
- },
634
- expiresAt: {
635
- type: Date,
636
- expires: 3600, // 1 hour in seconds
637
- },
638
- }, {
639
- timestamps: true,
640
- });
641
- file.index({ createdAt: 1, updatedAt: 1 });
642
-
643
- const keySchema = new mongoose.Schema({
644
- userId: {
645
- type: mongoose.Schema.Types.ObjectId,
646
- ref: 'User',
647
- required: true,
648
- },
649
- name: {
650
- type: String,
651
- required: true,
652
- },
653
- value: {
654
- type: String,
655
- required: true,
656
- },
657
- expiresAt: {
658
- type: Date,
1042
+ api_key: String,
1043
+ auth: AuthSchema,
1044
+ domain: {
1045
+ type: String,
1046
+ required: true,
1047
+ },
1048
+ privacy_policy_url: String,
1049
+ raw_spec: String,
1050
+ oauth_client_id: String,
1051
+ oauth_client_secret: String,
659
1052
  },
660
1053
  });
661
- keySchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
662
1054
 
663
- const messageSchema = new mongoose.Schema({
664
- messageId: {
1055
+ const agentSchema = new mongoose.Schema({
1056
+ id: {
665
1057
  type: String,
1058
+ index: true,
666
1059
  unique: true,
667
1060
  required: true,
668
- index: true,
669
- meiliIndex: true,
670
1061
  },
671
- conversationId: {
1062
+ name: {
672
1063
  type: String,
673
- index: true,
674
- required: true,
675
- meiliIndex: true,
676
1064
  },
677
- user: {
1065
+ description: {
678
1066
  type: String,
679
- index: true,
680
- required: true,
681
- default: null,
682
- meiliIndex: true,
683
1067
  },
684
- model: {
1068
+ instructions: {
685
1069
  type: String,
686
- default: null,
687
1070
  },
688
- endpoint: {
689
- type: String,
1071
+ avatar: {
1072
+ type: mongoose.Schema.Types.Mixed,
1073
+ default: undefined,
690
1074
  },
691
- conversationSignature: {
1075
+ provider: {
692
1076
  type: String,
1077
+ required: true,
693
1078
  },
694
- clientId: {
1079
+ model: {
695
1080
  type: String,
1081
+ required: true,
696
1082
  },
697
- invocationId: {
698
- type: Number,
1083
+ model_parameters: {
1084
+ type: Object,
699
1085
  },
700
- parentMessageId: {
1086
+ artifacts: {
701
1087
  type: String,
702
1088
  },
703
- tokenCount: {
1089
+ access_level: {
704
1090
  type: Number,
705
1091
  },
706
- summaryTokenCount: {
1092
+ recursion_limit: {
707
1093
  type: Number,
708
1094
  },
709
- sender: {
710
- type: String,
711
- meiliIndex: true,
1095
+ tools: {
1096
+ type: [String],
1097
+ default: undefined,
712
1098
  },
713
- text: {
714
- type: String,
715
- meiliIndex: true,
1099
+ tool_kwargs: {
1100
+ type: [{ type: mongoose.Schema.Types.Mixed }],
716
1101
  },
717
- summary: {
1102
+ actions: {
1103
+ type: [String],
1104
+ default: undefined,
1105
+ },
1106
+ author: {
1107
+ type: mongoose.Schema.Types.ObjectId,
1108
+ ref: 'User',
1109
+ required: true,
1110
+ },
1111
+ authorName: {
718
1112
  type: String,
1113
+ default: undefined,
719
1114
  },
720
- isCreatedByUser: {
1115
+ hide_sequential_outputs: {
721
1116
  type: Boolean,
722
- required: true,
723
- default: false,
724
1117
  },
725
- unfinished: {
1118
+ end_after_tools: {
726
1119
  type: Boolean,
727
- default: false,
728
1120
  },
729
- error: {
1121
+ /** @deprecated Use edges instead */
1122
+ agent_ids: {
1123
+ type: [String],
1124
+ },
1125
+ edges: {
1126
+ type: [{ type: mongoose.Schema.Types.Mixed }],
1127
+ default: [],
1128
+ },
1129
+ isCollaborative: {
730
1130
  type: Boolean,
731
- default: false,
1131
+ default: undefined,
732
1132
  },
733
- finish_reason: {
1133
+ conversation_starters: {
1134
+ type: [String],
1135
+ default: [],
1136
+ },
1137
+ tool_resources: {
1138
+ type: mongoose.Schema.Types.Mixed,
1139
+ default: {},
1140
+ },
1141
+ projectIds: {
1142
+ type: [mongoose.Schema.Types.ObjectId],
1143
+ ref: 'Project',
1144
+ index: true,
1145
+ },
1146
+ versions: {
1147
+ type: [mongoose.Schema.Types.Mixed],
1148
+ default: [],
1149
+ },
1150
+ category: {
734
1151
  type: String,
1152
+ trim: true,
1153
+ index: true,
1154
+ default: 'general',
735
1155
  },
736
- feedback: {
737
- type: {
738
- rating: {
739
- type: String,
740
- enum: ['thumbsUp', 'thumbsDown'],
741
- required: true,
742
- },
743
- tag: {
744
- type: mongoose.Schema.Types.Mixed,
745
- required: false,
746
- },
747
- text: {
748
- type: String,
749
- required: false,
750
- },
751
- },
1156
+ support_contact: {
1157
+ type: mongoose.Schema.Types.Mixed,
752
1158
  default: undefined,
753
- required: false,
754
1159
  },
755
- _meiliIndex: {
1160
+ is_promoted: {
756
1161
  type: Boolean,
757
- required: false,
758
- select: false,
759
1162
  default: false,
1163
+ index: true,
760
1164
  },
761
- files: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
762
- plugin: {
763
- type: {
764
- latest: {
765
- type: String,
766
- required: false,
767
- },
768
- inputs: {
769
- type: [mongoose.Schema.Types.Mixed],
770
- required: false,
771
- default: undefined,
772
- },
773
- outputs: {
774
- type: String,
775
- required: false,
776
- },
777
- },
778
- default: undefined,
1165
+ }, {
1166
+ timestamps: true,
1167
+ });
1168
+ agentSchema.index({ updatedAt: -1, _id: 1 });
1169
+
1170
+ const agentCategorySchema = new mongoose.Schema({
1171
+ value: {
1172
+ type: String,
1173
+ required: true,
1174
+ unique: true,
1175
+ trim: true,
1176
+ lowercase: true,
1177
+ index: true,
779
1178
  },
780
- plugins: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
781
- content: {
782
- type: [{ type: mongoose.Schema.Types.Mixed }],
783
- default: undefined,
784
- meiliIndex: true,
1179
+ label: {
1180
+ type: String,
1181
+ required: true,
1182
+ trim: true,
785
1183
  },
786
- thread_id: {
1184
+ description: {
787
1185
  type: String,
1186
+ trim: true,
1187
+ default: '',
788
1188
  },
789
- /* frontend components */
790
- iconURL: {
1189
+ order: {
1190
+ type: Number,
1191
+ default: 0,
1192
+ index: true,
1193
+ },
1194
+ isActive: {
1195
+ type: Boolean,
1196
+ default: true,
1197
+ index: true,
1198
+ },
1199
+ custom: {
1200
+ type: Boolean,
1201
+ default: false,
1202
+ },
1203
+ }, {
1204
+ timestamps: true,
1205
+ });
1206
+ agentCategorySchema.index({ isActive: 1, order: 1 });
1207
+ agentCategorySchema.index({ order: 1, label: 1 });
1208
+
1209
+ const assistantSchema = new mongoose.Schema({
1210
+ user: {
1211
+ type: mongoose.Schema.Types.ObjectId,
1212
+ ref: 'User',
1213
+ required: true,
1214
+ },
1215
+ assistant_id: {
791
1216
  type: String,
1217
+ index: true,
1218
+ required: true,
792
1219
  },
793
- attachments: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
794
- /*
795
- attachments: {
796
- type: [
797
- {
798
- file_id: String,
799
- filename: String,
800
- filepath: String,
801
- expiresAt: Date,
802
- width: Number,
803
- height: Number,
804
- type: String,
805
- conversationId: String,
806
- messageId: {
807
- type: String,
808
- required: true,
809
- },
810
- toolCallId: String,
811
- },
812
- ],
813
- default: undefined,
1220
+ avatar: {
1221
+ type: mongoose.Schema.Types.Mixed,
1222
+ default: undefined,
1223
+ },
1224
+ conversation_starters: {
1225
+ type: [String],
1226
+ default: [],
1227
+ },
1228
+ access_level: {
1229
+ type: Number,
1230
+ },
1231
+ file_ids: { type: [String], default: undefined },
1232
+ actions: { type: [String], default: undefined },
1233
+ append_current_datetime: {
1234
+ type: Boolean,
1235
+ default: false,
1236
+ },
1237
+ }, {
1238
+ timestamps: true,
1239
+ });
1240
+
1241
+ const balanceSchema = new mongoose.Schema({
1242
+ user: {
1243
+ type: mongoose.Schema.Types.ObjectId,
1244
+ ref: 'User',
1245
+ index: true,
1246
+ required: true,
814
1247
  },
815
- */
816
- expiredAt: {
1248
+ // 1000 tokenCredits = 1 mill ($0.001 USD)
1249
+ tokenCredits: {
1250
+ type: Number,
1251
+ default: 0,
1252
+ },
1253
+ // Automatic refill settings
1254
+ autoRefillEnabled: {
1255
+ type: Boolean,
1256
+ default: false,
1257
+ },
1258
+ refillIntervalValue: {
1259
+ type: Number,
1260
+ default: 30,
1261
+ },
1262
+ refillIntervalUnit: {
1263
+ type: String,
1264
+ enum: ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months'],
1265
+ default: 'days',
1266
+ },
1267
+ lastRefill: {
817
1268
  type: Date,
1269
+ default: Date.now,
818
1270
  },
819
- }, { timestamps: true });
820
- messageSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
821
- messageSchema.index({ createdAt: 1 });
822
- messageSchema.index({ messageId: 1, user: 1 }, { unique: true });
1271
+ // amount to add on each refill
1272
+ refillAmount: {
1273
+ type: Number,
1274
+ default: 0,
1275
+ },
1276
+ });
823
1277
 
824
- const pluginAuthSchema = new mongoose.Schema({
825
- authField: {
1278
+ const bannerSchema = new mongoose.Schema({
1279
+ bannerId: {
826
1280
  type: String,
827
1281
  required: true,
828
1282
  },
829
- value: {
1283
+ message: {
830
1284
  type: String,
831
1285
  required: true,
832
1286
  },
833
- userId: {
834
- type: String,
1287
+ displayFrom: {
1288
+ type: Date,
835
1289
  required: true,
1290
+ default: Date.now,
836
1291
  },
837
- pluginKey: {
1292
+ displayTo: {
1293
+ type: Date,
1294
+ },
1295
+ type: {
838
1296
  type: String,
1297
+ enum: ['banner', 'popup'],
1298
+ default: 'banner',
1299
+ },
1300
+ isPublic: {
1301
+ type: Boolean,
1302
+ default: false,
839
1303
  },
840
1304
  }, { timestamps: true });
841
1305
 
842
- const presetSchema = new mongoose.Schema({
843
- presetId: {
1306
+ const categoriesSchema = new mongoose.Schema({
1307
+ label: {
844
1308
  type: String,
1309
+ required: true,
845
1310
  unique: true,
1311
+ },
1312
+ value: {
1313
+ type: String,
846
1314
  required: true,
847
- index: true,
1315
+ unique: true,
848
1316
  },
849
- title: {
1317
+ });
1318
+
1319
+ const conversationTag = new mongoose.Schema({
1320
+ tag: {
850
1321
  type: String,
851
- default: 'New Chat',
852
- meiliIndex: true,
1322
+ index: true,
853
1323
  },
854
1324
  user: {
855
1325
  type: String,
856
- default: null,
1326
+ index: true,
857
1327
  },
858
- defaultPreset: {
859
- type: Boolean,
1328
+ description: {
1329
+ type: String,
1330
+ index: true,
860
1331
  },
861
- order: {
1332
+ count: {
862
1333
  type: Number,
1334
+ default: 0,
863
1335
  },
864
- ...conversationPreset,
865
- agentOptions: {
866
- type: mongoose.Schema.Types.Mixed,
867
- default: null,
1336
+ position: {
1337
+ type: Number,
1338
+ default: 0,
1339
+ index: true,
868
1340
  },
869
1341
  }, { timestamps: true });
1342
+ // Create a compound index on tag and user with unique constraint.
1343
+ conversationTag.index({ tag: 1, user: 1 }, { unique: true });
870
1344
 
871
- const projectSchema = new mongoose.Schema({
872
- name: {
1345
+ // @ts-ignore
1346
+ const conversationPreset = {
1347
+ // endpoint: [azureOpenAI, openAI, anthropic, chatGPTBrowser]
1348
+ endpoint: {
873
1349
  type: String,
1350
+ default: null,
874
1351
  required: true,
875
- index: true,
876
- },
877
- promptGroupIds: {
878
- type: [mongoose.Schema.Types.ObjectId],
879
- ref: 'PromptGroup',
880
- default: [],
881
1352
  },
882
- agentIds: {
883
- type: [String],
884
- ref: 'Agent',
885
- default: [],
1353
+ endpointType: {
1354
+ type: String,
886
1355
  },
887
- }, {
888
- timestamps: true,
889
- });
890
-
891
- const promptSchema = new mongoose.Schema({
892
- groupId: {
893
- type: mongoose.Schema.Types.ObjectId,
894
- ref: 'PromptGroup',
895
- required: true,
896
- index: true,
1356
+ // for azureOpenAI, openAI, chatGPTBrowser only
1357
+ model: {
1358
+ type: String,
1359
+ required: false,
897
1360
  },
898
- author: {
899
- type: mongoose.Schema.Types.ObjectId,
900
- ref: 'User',
901
- required: true,
1361
+ // for bedrock only
1362
+ region: {
1363
+ type: String,
1364
+ required: false,
902
1365
  },
903
- prompt: {
1366
+ // for azureOpenAI, openAI only
1367
+ chatGptLabel: {
904
1368
  type: String,
905
- required: true,
1369
+ required: false,
906
1370
  },
907
- type: {
1371
+ // for google only
1372
+ examples: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1373
+ modelLabel: {
908
1374
  type: String,
909
- enum: ['text', 'chat'],
910
- required: true,
1375
+ required: false,
911
1376
  },
912
- }, {
913
- timestamps: true,
914
- });
915
- promptSchema.index({ createdAt: 1, updatedAt: 1 });
916
-
917
- const promptGroupSchema = new mongoose.Schema({
918
- name: {
1377
+ promptPrefix: {
919
1378
  type: String,
920
- required: true,
921
- index: true,
1379
+ required: false,
922
1380
  },
923
- numberOfGenerations: {
1381
+ temperature: {
924
1382
  type: Number,
925
- default: 0,
1383
+ required: false,
926
1384
  },
927
- oneliner: {
928
- type: String,
929
- default: '',
1385
+ top_p: {
1386
+ type: Number,
1387
+ required: false,
930
1388
  },
931
- category: {
932
- type: String,
933
- default: '',
934
- index: true,
1389
+ // for google only
1390
+ topP: {
1391
+ type: Number,
1392
+ required: false,
935
1393
  },
936
- projectIds: {
937
- type: [mongoose.Schema.Types.ObjectId],
938
- ref: 'Project',
939
- index: true,
940
- default: [],
1394
+ topK: {
1395
+ type: Number,
1396
+ required: false,
941
1397
  },
942
- productionId: {
943
- type: mongoose.Schema.Types.ObjectId,
944
- ref: 'Prompt',
945
- required: true,
946
- index: true,
1398
+ maxOutputTokens: {
1399
+ type: Number,
1400
+ required: false,
947
1401
  },
948
- author: {
949
- type: mongoose.Schema.Types.ObjectId,
950
- ref: 'User',
951
- required: true,
952
- index: true,
1402
+ maxTokens: {
1403
+ type: Number,
1404
+ required: false,
953
1405
  },
954
- authorName: {
955
- type: String,
956
- required: true,
1406
+ presence_penalty: {
1407
+ type: Number,
1408
+ required: false,
957
1409
  },
958
- command: {
1410
+ frequency_penalty: {
1411
+ type: Number,
1412
+ required: false,
1413
+ },
1414
+ file_ids: { type: [{ type: String }], default: undefined },
1415
+ // deprecated
1416
+ resendImages: {
1417
+ type: Boolean,
1418
+ },
1419
+ /* Anthropic only */
1420
+ promptCache: {
1421
+ type: Boolean,
1422
+ },
1423
+ thinking: {
1424
+ type: Boolean,
1425
+ },
1426
+ thinkingBudget: {
1427
+ type: Number,
1428
+ },
1429
+ system: {
959
1430
  type: String,
960
- index: true,
961
- validate: {
962
- validator: function (v) {
963
- return v === undefined || v === null || v === '' || /^[a-z0-9-]+$/.test(v);
964
- },
965
- message: (props) => { var _a; return `${(_a = props === null || props === void 0 ? void 0 : props.value) !== null && _a !== void 0 ? _a : 'Value'} is not a valid command. Only lowercase alphanumeric characters and hyphens are allowed.`; },
966
- },
967
- maxlength: [
968
- librechatDataProvider.Constants.COMMANDS_MAX_LENGTH,
969
- `Command cannot be longer than ${librechatDataProvider.Constants.COMMANDS_MAX_LENGTH} characters`,
970
- ],
971
- }, // Casting here bypasses the type error for the command field.
972
- }, {
973
- timestamps: true,
974
- });
975
- promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
976
-
977
- /**
978
- * Uses a sub-schema for permissions. Notice we disable `_id` for this subdocument.
979
- */
980
- const rolePermissionsSchema = new mongoose.Schema({
981
- [librechatDataProvider.PermissionTypes.BOOKMARKS]: {
982
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
983
1431
  },
984
- [librechatDataProvider.PermissionTypes.PROMPTS]: {
985
- [librechatDataProvider.Permissions.SHARED_GLOBAL]: { type: Boolean },
986
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
987
- [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
1432
+ // files
1433
+ resendFiles: {
1434
+ type: Boolean,
1435
+ },
1436
+ imageDetail: {
1437
+ type: String,
988
1438
  },
989
- [librechatDataProvider.PermissionTypes.MEMORIES]: {
990
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
991
- [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
992
- [librechatDataProvider.Permissions.UPDATE]: { type: Boolean },
993
- [librechatDataProvider.Permissions.READ]: { type: Boolean },
994
- [librechatDataProvider.Permissions.OPT_OUT]: { type: Boolean },
1439
+ /* agents */
1440
+ agent_id: {
1441
+ type: String,
995
1442
  },
996
- [librechatDataProvider.PermissionTypes.AGENTS]: {
997
- [librechatDataProvider.Permissions.SHARED_GLOBAL]: { type: Boolean },
998
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
999
- [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
1443
+ /* assistants */
1444
+ assistant_id: {
1445
+ type: String,
1000
1446
  },
1001
- [librechatDataProvider.PermissionTypes.MULTI_CONVO]: {
1002
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1447
+ instructions: {
1448
+ type: String,
1003
1449
  },
1004
- [librechatDataProvider.PermissionTypes.TEMPORARY_CHAT]: {
1005
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1450
+ stop: { type: [{ type: String }], default: undefined },
1451
+ isArchived: {
1452
+ type: Boolean,
1453
+ default: false,
1006
1454
  },
1007
- [librechatDataProvider.PermissionTypes.RUN_CODE]: {
1008
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1455
+ /* UI Components */
1456
+ iconURL: {
1457
+ type: String,
1009
1458
  },
1010
- [librechatDataProvider.PermissionTypes.WEB_SEARCH]: {
1011
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1459
+ greeting: {
1460
+ type: String,
1012
1461
  },
1013
- [librechatDataProvider.PermissionTypes.PEOPLE_PICKER]: {
1014
- [librechatDataProvider.Permissions.VIEW_USERS]: { type: Boolean },
1015
- [librechatDataProvider.Permissions.VIEW_GROUPS]: { type: Boolean },
1016
- [librechatDataProvider.Permissions.VIEW_ROLES]: { type: Boolean },
1462
+ spec: {
1463
+ type: String,
1017
1464
  },
1018
- [librechatDataProvider.PermissionTypes.MARKETPLACE]: {
1019
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1465
+ tags: {
1466
+ type: [String],
1467
+ default: [],
1020
1468
  },
1021
- [librechatDataProvider.PermissionTypes.FILE_SEARCH]: {
1022
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1469
+ tools: { type: [{ type: String }], default: undefined },
1470
+ maxContextTokens: {
1471
+ type: Number,
1023
1472
  },
1024
- [librechatDataProvider.PermissionTypes.FILE_CITATIONS]: {
1025
- [librechatDataProvider.Permissions.USE]: { type: Boolean },
1473
+ max_tokens: {
1474
+ type: Number,
1026
1475
  },
1027
- }, { _id: false });
1028
- const roleSchema = new mongoose.Schema({
1029
- name: { type: String, required: true, unique: true, index: true },
1030
- permissions: {
1031
- type: rolePermissionsSchema,
1476
+ useResponsesApi: {
1477
+ type: Boolean,
1032
1478
  },
1033
- });
1034
-
1035
- const sessionSchema = new mongoose.Schema({
1036
- refreshTokenHash: {
1479
+ /** OpenAI Responses API / Anthropic API / Google API */
1480
+ web_search: {
1481
+ type: Boolean,
1482
+ },
1483
+ disableStreaming: {
1484
+ type: Boolean,
1485
+ },
1486
+ fileTokenLimit: {
1487
+ type: Number,
1488
+ },
1489
+ /** Reasoning models only */
1490
+ reasoning_effort: {
1037
1491
  type: String,
1038
- required: true,
1039
1492
  },
1040
- expiration: {
1041
- type: Date,
1042
- required: true,
1043
- expires: 0,
1493
+ reasoning_summary: {
1494
+ type: String,
1044
1495
  },
1045
- user: {
1046
- type: mongoose.Schema.Types.ObjectId,
1047
- ref: 'User',
1048
- required: true,
1496
+ /** Verbosity control */
1497
+ verbosity: {
1498
+ type: String,
1049
1499
  },
1050
- });
1500
+ };
1051
1501
 
1052
- const shareSchema = new mongoose.Schema({
1502
+ const convoSchema = new mongoose.Schema({
1053
1503
  conversationId: {
1054
1504
  type: String,
1505
+ unique: true,
1055
1506
  required: true,
1507
+ index: true,
1508
+ meiliIndex: true,
1056
1509
  },
1057
1510
  title: {
1058
1511
  type: String,
1059
- index: true,
1512
+ default: 'New Chat',
1513
+ meiliIndex: true,
1060
1514
  },
1061
1515
  user: {
1062
1516
  type: String,
1063
1517
  index: true,
1518
+ meiliIndex: true,
1064
1519
  },
1065
1520
  messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
1066
- shareId: {
1521
+ agentOptions: {
1522
+ type: mongoose.Schema.Types.Mixed,
1523
+ },
1524
+ ...conversationPreset,
1525
+ agent_id: {
1067
1526
  type: String,
1068
- index: true,
1069
1527
  },
1070
- isPublic: {
1071
- type: Boolean,
1072
- default: true,
1528
+ tags: {
1529
+ type: [String],
1530
+ default: [],
1531
+ meiliIndex: true,
1532
+ },
1533
+ files: {
1534
+ type: [String],
1535
+ },
1536
+ expiredAt: {
1537
+ type: Date,
1073
1538
  },
1074
1539
  }, { timestamps: true });
1540
+ convoSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
1541
+ convoSchema.index({ createdAt: 1, updatedAt: 1 });
1542
+ convoSchema.index({ conversationId: 1, user: 1 }, { unique: true });
1075
1543
 
1076
- const tokenSchema = new mongoose.Schema({
1077
- userId: {
1544
+ const file = new mongoose.Schema({
1545
+ user: {
1078
1546
  type: mongoose.Schema.Types.ObjectId,
1547
+ ref: 'User',
1548
+ index: true,
1079
1549
  required: true,
1080
- ref: 'user',
1081
1550
  },
1082
- email: {
1551
+ conversationId: {
1083
1552
  type: String,
1553
+ ref: 'Conversation',
1554
+ index: true,
1084
1555
  },
1085
- type: {
1556
+ file_id: {
1086
1557
  type: String,
1558
+ index: true,
1559
+ required: true,
1087
1560
  },
1088
- identifier: {
1561
+ temp_file_id: {
1089
1562
  type: String,
1090
1563
  },
1091
- token: {
1564
+ bytes: {
1565
+ type: Number,
1566
+ required: true,
1567
+ },
1568
+ filename: {
1092
1569
  type: String,
1093
1570
  required: true,
1094
1571
  },
1095
- createdAt: {
1096
- type: Date,
1572
+ filepath: {
1573
+ type: String,
1097
1574
  required: true,
1098
- default: Date.now,
1099
1575
  },
1100
- expiresAt: {
1101
- type: Date,
1576
+ object: {
1577
+ type: String,
1102
1578
  required: true,
1579
+ default: 'file',
1103
1580
  },
1104
- metadata: {
1105
- type: Map,
1106
- of: mongoose.Schema.Types.Mixed,
1581
+ embedded: {
1582
+ type: Boolean,
1107
1583
  },
1108
- });
1109
- tokenSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1110
-
1111
- const toolCallSchema = new mongoose.Schema({
1112
- conversationId: {
1584
+ type: {
1113
1585
  type: String,
1114
1586
  required: true,
1115
1587
  },
1116
- messageId: {
1588
+ text: {
1117
1589
  type: String,
1118
- required: true,
1119
1590
  },
1120
- toolId: {
1591
+ context: {
1121
1592
  type: String,
1122
- required: true,
1123
1593
  },
1124
- user: {
1125
- type: mongoose.Schema.Types.ObjectId,
1126
- ref: 'User',
1594
+ usage: {
1595
+ type: Number,
1127
1596
  required: true,
1597
+ default: 0,
1128
1598
  },
1129
- result: {
1130
- type: mongoose.Schema.Types.Mixed,
1599
+ source: {
1600
+ type: String,
1601
+ default: librechatDataProvider.FileSources.local,
1131
1602
  },
1132
- attachments: {
1133
- type: mongoose.Schema.Types.Mixed,
1603
+ model: {
1604
+ type: String,
1134
1605
  },
1135
- blockIndex: {
1136
- type: Number,
1606
+ width: Number,
1607
+ height: Number,
1608
+ metadata: {
1609
+ fileIdentifier: String,
1137
1610
  },
1138
- partIndex: {
1139
- type: Number,
1611
+ expiresAt: {
1612
+ type: Date,
1613
+ expires: 3600, // 1 hour in seconds
1140
1614
  },
1141
- }, { timestamps: true });
1142
- toolCallSchema.index({ messageId: 1, user: 1 });
1143
- toolCallSchema.index({ conversationId: 1, user: 1 });
1615
+ }, {
1616
+ timestamps: true,
1617
+ });
1618
+ file.index({ createdAt: 1, updatedAt: 1 });
1144
1619
 
1145
- const transactionSchema = new mongoose.Schema({
1146
- user: {
1620
+ const keySchema = new mongoose.Schema({
1621
+ userId: {
1147
1622
  type: mongoose.Schema.Types.ObjectId,
1148
1623
  ref: 'User',
1149
- index: true,
1150
1624
  required: true,
1151
1625
  },
1626
+ name: {
1627
+ type: String,
1628
+ required: true,
1629
+ },
1630
+ value: {
1631
+ type: String,
1632
+ required: true,
1633
+ },
1634
+ expiresAt: {
1635
+ type: Date,
1636
+ },
1637
+ });
1638
+ keySchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1639
+
1640
+ const messageSchema = new mongoose.Schema({
1641
+ messageId: {
1642
+ type: String,
1643
+ unique: true,
1644
+ required: true,
1645
+ index: true,
1646
+ meiliIndex: true,
1647
+ },
1152
1648
  conversationId: {
1153
1649
  type: String,
1154
- ref: 'Conversation',
1155
1650
  index: true,
1651
+ required: true,
1652
+ meiliIndex: true,
1156
1653
  },
1157
- tokenType: {
1654
+ user: {
1158
1655
  type: String,
1159
- enum: ['prompt', 'completion', 'credits'],
1656
+ index: true,
1160
1657
  required: true,
1658
+ default: null,
1659
+ meiliIndex: true,
1660
+ },
1661
+ model: {
1662
+ type: String,
1663
+ default: null,
1161
1664
  },
1162
- model: {
1665
+ endpoint: {
1163
1666
  type: String,
1164
1667
  },
1165
- context: {
1668
+ conversationSignature: {
1166
1669
  type: String,
1167
1670
  },
1168
- valueKey: {
1671
+ clientId: {
1169
1672
  type: String,
1170
1673
  },
1171
- rate: Number,
1172
- rawAmount: Number,
1173
- tokenValue: Number,
1174
- inputTokens: { type: Number },
1175
- writeTokens: { type: Number },
1176
- readTokens: { type: Number },
1177
- }, {
1178
- timestamps: true,
1179
- });
1180
-
1181
- // Session sub-schema
1182
- const SessionSchema = new mongoose.Schema({
1183
- refreshToken: {
1674
+ invocationId: {
1675
+ type: Number,
1676
+ },
1677
+ parentMessageId: {
1184
1678
  type: String,
1185
- default: '',
1186
1679
  },
1187
- }, { _id: false });
1188
- // Backup code sub-schema
1189
- const BackupCodeSchema = new mongoose.Schema({
1190
- codeHash: { type: String, required: true },
1191
- used: { type: Boolean, default: false },
1192
- usedAt: { type: Date, default: null },
1193
- }, { _id: false });
1194
- const userSchema = new mongoose.Schema({
1195
- name: {
1680
+ tokenCount: {
1681
+ type: Number,
1682
+ },
1683
+ summaryTokenCount: {
1684
+ type: Number,
1685
+ },
1686
+ sender: {
1196
1687
  type: String,
1688
+ meiliIndex: true,
1197
1689
  },
1198
- username: {
1690
+ text: {
1199
1691
  type: String,
1200
- lowercase: true,
1201
- default: '',
1692
+ meiliIndex: true,
1202
1693
  },
1203
- email: {
1694
+ summary: {
1204
1695
  type: String,
1205
- required: [true, "can't be blank"],
1206
- lowercase: true,
1207
- unique: true,
1208
- match: [/\S+@\S+\.\S+/, 'is invalid'],
1209
- index: true,
1210
1696
  },
1211
- emailVerified: {
1697
+ isCreatedByUser: {
1212
1698
  type: Boolean,
1213
1699
  required: true,
1214
1700
  default: false,
1215
1701
  },
1216
- password: {
1217
- type: String,
1218
- trim: true,
1219
- minlength: 8,
1220
- maxlength: 128,
1221
- select: false,
1702
+ unfinished: {
1703
+ type: Boolean,
1704
+ default: false,
1222
1705
  },
1223
- avatar: {
1706
+ error: {
1707
+ type: Boolean,
1708
+ default: false,
1709
+ },
1710
+ finish_reason: {
1224
1711
  type: String,
1712
+ },
1713
+ feedback: {
1714
+ type: {
1715
+ rating: {
1716
+ type: String,
1717
+ enum: ['thumbsUp', 'thumbsDown'],
1718
+ required: true,
1719
+ },
1720
+ tag: {
1721
+ type: mongoose.Schema.Types.Mixed,
1722
+ required: false,
1723
+ },
1724
+ text: {
1725
+ type: String,
1726
+ required: false,
1727
+ },
1728
+ },
1729
+ default: undefined,
1225
1730
  required: false,
1226
1731
  },
1227
- provider: {
1228
- type: String,
1229
- required: true,
1230
- default: 'local',
1732
+ _meiliIndex: {
1733
+ type: Boolean,
1734
+ required: false,
1735
+ select: false,
1736
+ default: false,
1231
1737
  },
1232
- role: {
1233
- type: String,
1234
- default: librechatDataProvider.SystemRoles.USER,
1738
+ files: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1739
+ plugin: {
1740
+ type: {
1741
+ latest: {
1742
+ type: String,
1743
+ required: false,
1744
+ },
1745
+ inputs: {
1746
+ type: [mongoose.Schema.Types.Mixed],
1747
+ required: false,
1748
+ default: undefined,
1749
+ },
1750
+ outputs: {
1751
+ type: String,
1752
+ required: false,
1753
+ },
1754
+ },
1755
+ default: undefined,
1235
1756
  },
1236
- googleId: {
1237
- type: String,
1238
- unique: true,
1239
- sparse: true,
1757
+ plugins: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1758
+ content: {
1759
+ type: [{ type: mongoose.Schema.Types.Mixed }],
1760
+ default: undefined,
1761
+ meiliIndex: true,
1240
1762
  },
1241
- facebookId: {
1763
+ thread_id: {
1242
1764
  type: String,
1243
- unique: true,
1244
- sparse: true,
1245
1765
  },
1246
- openidId: {
1766
+ /* frontend components */
1767
+ iconURL: {
1247
1768
  type: String,
1248
- unique: true,
1249
- sparse: true,
1250
1769
  },
1251
- samlId: {
1770
+ metadata: { type: mongoose.Schema.Types.Mixed },
1771
+ attachments: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
1772
+ /*
1773
+ attachments: {
1774
+ type: [
1775
+ {
1776
+ file_id: String,
1777
+ filename: String,
1778
+ filepath: String,
1779
+ expiresAt: Date,
1780
+ width: Number,
1781
+ height: Number,
1782
+ type: String,
1783
+ conversationId: String,
1784
+ messageId: {
1785
+ type: String,
1786
+ required: true,
1787
+ },
1788
+ toolCallId: String,
1789
+ },
1790
+ ],
1791
+ default: undefined,
1792
+ },
1793
+ */
1794
+ expiredAt: {
1795
+ type: Date,
1796
+ },
1797
+ }, { timestamps: true });
1798
+ messageSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
1799
+ messageSchema.index({ createdAt: 1 });
1800
+ messageSchema.index({ messageId: 1, user: 1 }, { unique: true });
1801
+
1802
+ const pluginAuthSchema = new mongoose.Schema({
1803
+ authField: {
1252
1804
  type: String,
1253
- unique: true,
1254
- sparse: true,
1805
+ required: true,
1255
1806
  },
1256
- ldapId: {
1807
+ value: {
1257
1808
  type: String,
1258
- unique: true,
1259
- sparse: true,
1809
+ required: true,
1260
1810
  },
1261
- githubId: {
1811
+ userId: {
1262
1812
  type: String,
1263
- unique: true,
1264
- sparse: true,
1813
+ required: true,
1265
1814
  },
1266
- discordId: {
1815
+ pluginKey: {
1267
1816
  type: String,
1268
- unique: true,
1269
- sparse: true,
1270
1817
  },
1271
- appleId: {
1818
+ }, { timestamps: true });
1819
+
1820
+ const presetSchema = new mongoose.Schema({
1821
+ presetId: {
1272
1822
  type: String,
1273
1823
  unique: true,
1274
- sparse: true,
1275
- },
1276
- plugins: {
1277
- type: Array,
1824
+ required: true,
1825
+ index: true,
1278
1826
  },
1279
- twoFactorEnabled: {
1280
- type: Boolean,
1281
- default: false,
1827
+ title: {
1828
+ type: String,
1829
+ default: 'New Chat',
1830
+ meiliIndex: true,
1282
1831
  },
1283
- totpSecret: {
1832
+ user: {
1284
1833
  type: String,
1285
- select: false,
1834
+ default: null,
1286
1835
  },
1287
- backupCodes: {
1288
- type: [BackupCodeSchema],
1289
- select: false,
1836
+ defaultPreset: {
1837
+ type: Boolean,
1290
1838
  },
1291
- refreshToken: {
1292
- type: [SessionSchema],
1839
+ order: {
1840
+ type: Number,
1293
1841
  },
1294
- expiresAt: {
1295
- type: Date,
1296
- expires: 604800, // 7 days in seconds
1842
+ ...conversationPreset,
1843
+ agentOptions: {
1844
+ type: mongoose.Schema.Types.Mixed,
1845
+ default: null,
1297
1846
  },
1298
- termsAccepted: {
1299
- type: Boolean,
1300
- default: false,
1847
+ }, { timestamps: true });
1848
+
1849
+ const projectSchema = new mongoose.Schema({
1850
+ name: {
1851
+ type: String,
1852
+ required: true,
1853
+ index: true,
1301
1854
  },
1302
- personalization: {
1303
- type: {
1304
- memories: {
1305
- type: Boolean,
1306
- default: true,
1307
- },
1308
- },
1309
- default: {},
1855
+ promptGroupIds: {
1856
+ type: [mongoose.Schema.Types.ObjectId],
1857
+ ref: 'PromptGroup',
1858
+ default: [],
1310
1859
  },
1311
- /** Field for external source identification (for consistency with TPrincipal schema) */
1312
- idOnTheSource: {
1313
- type: String,
1314
- sparse: true,
1860
+ agentIds: {
1861
+ type: [String],
1862
+ ref: 'Agent',
1863
+ default: [],
1315
1864
  },
1316
- }, { timestamps: true });
1865
+ }, {
1866
+ timestamps: true,
1867
+ });
1317
1868
 
1318
- const MemoryEntrySchema = new mongoose.Schema({
1319
- userId: {
1869
+ const promptSchema = new mongoose.Schema({
1870
+ groupId: {
1320
1871
  type: mongoose.Schema.Types.ObjectId,
1321
- ref: 'User',
1872
+ ref: 'PromptGroup',
1873
+ required: true,
1322
1874
  index: true,
1875
+ },
1876
+ author: {
1877
+ type: mongoose.Schema.Types.ObjectId,
1878
+ ref: 'User',
1323
1879
  required: true,
1324
1880
  },
1325
- key: {
1881
+ prompt: {
1326
1882
  type: String,
1327
1883
  required: true,
1328
- validate: {
1329
- validator: (v) => /^[a-z_]+$/.test(v),
1330
- message: 'Key must only contain lowercase letters and underscores',
1331
- },
1332
1884
  },
1333
- value: {
1885
+ type: {
1334
1886
  type: String,
1887
+ enum: ['text', 'chat'],
1335
1888
  required: true,
1336
1889
  },
1337
- tokenCount: {
1338
- type: Number,
1339
- default: 0,
1340
- },
1341
- updated_at: {
1342
- type: Date,
1343
- default: Date.now,
1344
- },
1890
+ }, {
1891
+ timestamps: true,
1345
1892
  });
1893
+ promptSchema.index({ createdAt: 1, updatedAt: 1 });
1346
1894
 
1347
- const groupSchema = new mongoose.Schema({
1895
+ const promptGroupSchema = new mongoose.Schema({
1348
1896
  name: {
1349
1897
  type: String,
1350
1898
  required: true,
1351
1899
  index: true,
1352
1900
  },
1353
- description: {
1901
+ numberOfGenerations: {
1902
+ type: Number,
1903
+ default: 0,
1904
+ },
1905
+ oneliner: {
1354
1906
  type: String,
1355
- required: false,
1907
+ default: '',
1356
1908
  },
1357
- email: {
1909
+ category: {
1358
1910
  type: String,
1359
- required: false,
1911
+ default: '',
1360
1912
  index: true,
1361
1913
  },
1362
- avatar: {
1363
- type: String,
1364
- required: false,
1914
+ projectIds: {
1915
+ type: [mongoose.Schema.Types.ObjectId],
1916
+ ref: 'Project',
1917
+ index: true,
1918
+ default: [],
1365
1919
  },
1366
- memberIds: [
1367
- {
1368
- type: String,
1369
- required: false,
1370
- },
1371
- ],
1372
- source: {
1920
+ productionId: {
1921
+ type: mongoose.Schema.Types.ObjectId,
1922
+ ref: 'Prompt',
1923
+ required: true,
1924
+ index: true,
1925
+ },
1926
+ author: {
1927
+ type: mongoose.Schema.Types.ObjectId,
1928
+ ref: 'User',
1929
+ required: true,
1930
+ index: true,
1931
+ },
1932
+ authorName: {
1373
1933
  type: String,
1374
- enum: ['local', 'entra'],
1375
- default: 'local',
1934
+ required: true,
1376
1935
  },
1377
- /** External ID (e.g., Entra ID) */
1378
- idOnTheSource: {
1936
+ command: {
1379
1937
  type: String,
1380
- sparse: true,
1381
1938
  index: true,
1382
- required: function () {
1383
- return this.source !== 'local';
1384
- },
1385
- },
1386
- }, { timestamps: true });
1387
- groupSchema.index({ idOnTheSource: 1, source: 1 }, {
1388
- unique: true,
1389
- partialFilterExpression: { idOnTheSource: { $exists: true } },
1390
- });
1391
- groupSchema.index({ memberIds: 1 });
1392
-
1393
- /**
1394
- * ESM-native object traversal utility
1395
- * Simplified implementation focused on the forEach use case
1396
- */
1397
- function isObject(value) {
1398
- if (value === null || typeof value !== 'object') {
1399
- return false;
1400
- }
1401
- // Treat these built-in types as leaf nodes, not objects to traverse
1402
- if (value instanceof Date)
1403
- return false;
1404
- if (value instanceof RegExp)
1405
- return false;
1406
- if (value instanceof Error)
1407
- return false;
1408
- if (value instanceof URL)
1409
- return false;
1410
- // Check for Buffer (Node.js)
1411
- if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))
1412
- return false;
1413
- // Check for TypedArrays and ArrayBuffer
1414
- if (ArrayBuffer.isView(value))
1415
- return false;
1416
- if (value instanceof ArrayBuffer)
1417
- return false;
1418
- if (value instanceof SharedArrayBuffer)
1419
- return false;
1420
- // Check for other built-in types that shouldn't be traversed
1421
- if (value instanceof Promise)
1422
- return false;
1423
- if (value instanceof WeakMap)
1424
- return false;
1425
- if (value instanceof WeakSet)
1426
- return false;
1427
- if (value instanceof Map)
1428
- return false;
1429
- if (value instanceof Set)
1430
- return false;
1431
- // Check if it's a primitive wrapper object
1432
- const stringTag = Object.prototype.toString.call(value);
1433
- if (stringTag === '[object Boolean]' ||
1434
- stringTag === '[object Number]' ||
1435
- stringTag === '[object String]') {
1436
- return false;
1437
- }
1438
- return true;
1439
- }
1440
- // Helper to safely set a property on an object or array
1441
- function setProperty(obj, key, value) {
1442
- if (Array.isArray(obj) && typeof key === 'number') {
1443
- obj[key] = value;
1444
- }
1445
- else if (!Array.isArray(obj) && typeof key === 'string') {
1446
- obj[key] = value;
1447
- }
1448
- else if (!Array.isArray(obj) && typeof key === 'number') {
1449
- // Handle numeric keys on objects
1450
- obj[key] = value;
1451
- }
1452
- }
1453
- // Helper to safely delete a property from an object
1454
- function deleteProperty(obj, key) {
1455
- if (Array.isArray(obj) && typeof key === 'number') {
1456
- // For arrays, we should use splice, but this is handled in remove()
1457
- // This function is only called for non-array deletion
1458
- return;
1459
- }
1460
- if (!Array.isArray(obj)) {
1461
- delete obj[key];
1462
- }
1463
- }
1464
- function forEach(obj, callback) {
1465
- const visited = new WeakSet();
1466
- function walk(node, path = [], parent) {
1467
- // Check for circular references
1468
- let circular = null;
1469
- if (isObject(node)) {
1470
- if (visited.has(node)) {
1471
- // Find the circular reference in the parent chain
1472
- let p = parent;
1473
- while (p) {
1474
- if (p.node === node) {
1475
- circular = p;
1476
- break;
1477
- }
1478
- p = p.parent;
1479
- }
1480
- return; // Skip circular references
1481
- }
1482
- visited.add(node);
1483
- }
1484
- const key = path.length > 0 ? path[path.length - 1] : undefined;
1485
- const isRoot = path.length === 0;
1486
- const level = path.length;
1487
- // Determine if this is a leaf node
1488
- const isLeaf = !isObject(node) ||
1489
- (Array.isArray(node) && node.length === 0) ||
1490
- Object.keys(node).length === 0;
1491
- // Create context
1492
- const context = {
1493
- node,
1494
- path: [...path],
1495
- parent,
1496
- key,
1497
- isLeaf,
1498
- notLeaf: !isLeaf,
1499
- isRoot,
1500
- notRoot: !isRoot,
1501
- level,
1502
- circular,
1503
- update(value) {
1504
- if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
1505
- setProperty(parent.node, key, value);
1506
- }
1507
- this.node = value;
1508
- },
1509
- remove() {
1510
- if (!isRoot && parent && key !== undefined && isObject(parent.node)) {
1511
- if (Array.isArray(parent.node) && typeof key === 'number') {
1512
- parent.node.splice(key, 1);
1513
- }
1514
- else {
1515
- deleteProperty(parent.node, key);
1516
- }
1517
- }
1939
+ validate: {
1940
+ validator: function (v) {
1941
+ return v === undefined || v === null || v === '' || /^[a-z0-9-]+$/.test(v);
1518
1942
  },
1519
- };
1520
- // Call the callback with the context
1521
- callback.call(context, node);
1522
- // Traverse children if not circular and is an object
1523
- if (!circular && isObject(node) && !isLeaf) {
1524
- if (Array.isArray(node)) {
1525
- for (let i = 0; i < node.length; i++) {
1526
- walk(node[i], [...path, i], context);
1527
- }
1528
- }
1529
- else {
1530
- for (const [childKey, childValue] of Object.entries(node)) {
1531
- walk(childValue, [...path, childKey], context);
1532
- }
1533
- }
1534
- }
1535
- }
1536
- walk(obj);
1537
- }
1538
- // Main traverse function that returns an object with forEach method
1539
- function traverse(obj) {
1540
- return {
1541
- forEach(callback) {
1542
- forEach(obj, callback);
1943
+ message: (props) => { var _a; return `${(_a = props === null || props === void 0 ? void 0 : props.value) !== null && _a !== void 0 ? _a : 'Value'} is not a valid command. Only lowercase alphanumeric characters and hyphens are allowed.`; },
1543
1944
  },
1544
- };
1545
- }
1945
+ maxlength: [
1946
+ librechatDataProvider.Constants.COMMANDS_MAX_LENGTH,
1947
+ `Command cannot be longer than ${librechatDataProvider.Constants.COMMANDS_MAX_LENGTH} characters`,
1948
+ ],
1949
+ }, // Casting here bypasses the type error for the command field.
1950
+ }, {
1951
+ timestamps: true,
1952
+ });
1953
+ promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
1546
1954
 
1547
- const SPLAT_SYMBOL = Symbol.for('splat');
1548
- const MESSAGE_SYMBOL = Symbol.for('message');
1549
- const CONSOLE_JSON_STRING_LENGTH = parseInt(process.env.CONSOLE_JSON_STRING_LENGTH || '', 10) || 255;
1550
- const sensitiveKeys = [
1551
- /^(sk-)[^\s]+/, // OpenAI API key pattern
1552
- /(Bearer )[^\s]+/, // Header: Bearer token pattern
1553
- /(api-key:? )[^\s]+/, // Header: API key pattern
1554
- /(key=)[^\s]+/, // URL query param: sensitive key pattern (Google)
1555
- ];
1556
- /**
1557
- * Determines if a given value string is sensitive and returns matching regex patterns.
1558
- *
1559
- * @param valueStr - The value string to check.
1560
- * @returns An array of regex patterns that match the value string.
1561
- */
1562
- function getMatchingSensitivePatterns(valueStr) {
1563
- if (valueStr) {
1564
- // Filter and return all regex patterns that match the value string
1565
- return sensitiveKeys.filter((regex) => regex.test(valueStr));
1566
- }
1567
- return [];
1568
- }
1569
- /**
1570
- * Redacts sensitive information from a console message and trims it to a specified length if provided.
1571
- * @param str - The console message to be redacted.
1572
- * @param trimLength - The optional length at which to trim the redacted message.
1573
- * @returns The redacted and optionally trimmed console message.
1574
- */
1575
- function redactMessage(str, trimLength) {
1576
- if (!str) {
1577
- return '';
1578
- }
1579
- const patterns = getMatchingSensitivePatterns(str);
1580
- patterns.forEach((pattern) => {
1581
- str = str.replace(pattern, '$1[REDACTED]');
1582
- });
1583
- return str;
1584
- }
1585
1955
  /**
1586
- * Redacts sensitive information from log messages if the log level is 'error'.
1587
- * Note: Intentionally mutates the object.
1588
- * @param info - The log information object.
1589
- * @returns The modified log information object.
1956
+ * Uses a sub-schema for permissions. Notice we disable `_id` for this subdocument.
1590
1957
  */
1591
- const redactFormat = winston.format((info) => {
1592
- if (info.level === 'error') {
1593
- // Type guard to ensure message is a string
1594
- if (typeof info.message === 'string') {
1595
- info.message = redactMessage(info.message);
1596
- }
1597
- // Handle MESSAGE_SYMBOL with type safety
1598
- const symbolValue = info[MESSAGE_SYMBOL];
1599
- if (typeof symbolValue === 'string') {
1600
- info[MESSAGE_SYMBOL] = redactMessage(symbolValue);
1601
- }
1602
- }
1603
- return info;
1958
+ const rolePermissionsSchema = new mongoose.Schema({
1959
+ [librechatDataProvider.PermissionTypes.BOOKMARKS]: {
1960
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1961
+ },
1962
+ [librechatDataProvider.PermissionTypes.PROMPTS]: {
1963
+ [librechatDataProvider.Permissions.SHARED_GLOBAL]: { type: Boolean },
1964
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1965
+ [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
1966
+ },
1967
+ [librechatDataProvider.PermissionTypes.MEMORIES]: {
1968
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1969
+ [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
1970
+ [librechatDataProvider.Permissions.UPDATE]: { type: Boolean },
1971
+ [librechatDataProvider.Permissions.READ]: { type: Boolean },
1972
+ [librechatDataProvider.Permissions.OPT_OUT]: { type: Boolean },
1973
+ },
1974
+ [librechatDataProvider.PermissionTypes.AGENTS]: {
1975
+ [librechatDataProvider.Permissions.SHARED_GLOBAL]: { type: Boolean },
1976
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1977
+ [librechatDataProvider.Permissions.CREATE]: { type: Boolean },
1978
+ },
1979
+ [librechatDataProvider.PermissionTypes.MULTI_CONVO]: {
1980
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1981
+ },
1982
+ [librechatDataProvider.PermissionTypes.TEMPORARY_CHAT]: {
1983
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1984
+ },
1985
+ [librechatDataProvider.PermissionTypes.RUN_CODE]: {
1986
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1987
+ },
1988
+ [librechatDataProvider.PermissionTypes.WEB_SEARCH]: {
1989
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1990
+ },
1991
+ [librechatDataProvider.PermissionTypes.PEOPLE_PICKER]: {
1992
+ [librechatDataProvider.Permissions.VIEW_USERS]: { type: Boolean },
1993
+ [librechatDataProvider.Permissions.VIEW_GROUPS]: { type: Boolean },
1994
+ [librechatDataProvider.Permissions.VIEW_ROLES]: { type: Boolean },
1995
+ },
1996
+ [librechatDataProvider.PermissionTypes.MARKETPLACE]: {
1997
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
1998
+ },
1999
+ [librechatDataProvider.PermissionTypes.FILE_SEARCH]: {
2000
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
2001
+ },
2002
+ [librechatDataProvider.PermissionTypes.FILE_CITATIONS]: {
2003
+ [librechatDataProvider.Permissions.USE]: { type: Boolean },
2004
+ },
2005
+ }, { _id: false });
2006
+ const roleSchema = new mongoose.Schema({
2007
+ name: { type: String, required: true, unique: true, index: true },
2008
+ permissions: {
2009
+ type: rolePermissionsSchema,
2010
+ },
1604
2011
  });
1605
- /**
1606
- * Truncates long strings, especially base64 image data, within log messages.
1607
- *
1608
- * @param value - The value to be inspected and potentially truncated.
1609
- * @param length - The length at which to truncate the value. Default: 100.
1610
- * @returns The truncated or original value.
1611
- */
1612
- const truncateLongStrings = (value, length = 100) => {
1613
- if (typeof value === 'string') {
1614
- return value.length > length ? value.substring(0, length) + '... [truncated]' : value;
1615
- }
1616
- return value;
1617
- };
1618
- /**
1619
- * An array mapping function that truncates long strings (objects converted to JSON strings).
1620
- * @param item - The item to be condensed.
1621
- * @returns The condensed item.
1622
- */
1623
- const condenseArray = (item) => {
1624
- if (typeof item === 'string') {
1625
- return truncateLongStrings(JSON.stringify(item));
1626
- }
1627
- else if (typeof item === 'object') {
1628
- return truncateLongStrings(JSON.stringify(item));
1629
- }
1630
- return item;
1631
- };
1632
- /**
1633
- * Formats log messages for debugging purposes.
1634
- * - Truncates long strings within log messages.
1635
- * - Condenses arrays by truncating long strings and objects as strings within array items.
1636
- * - Redacts sensitive information from log messages if the log level is 'error'.
1637
- * - Converts log information object to a formatted string.
1638
- *
1639
- * @param options - The options for formatting log messages.
1640
- * @returns The formatted log message.
1641
- */
1642
- const debugTraverse = winston.format.printf(({ level, message, timestamp, ...metadata }) => {
1643
- if (!message) {
1644
- return `${timestamp} ${level}`;
1645
- }
1646
- // Type-safe version of the CJS logic: !message?.trim || typeof message !== 'string'
1647
- if (typeof message !== 'string' || !message.trim) {
1648
- return `${timestamp} ${level}: ${JSON.stringify(message)}`;
1649
- }
1650
- const msgParts = [
1651
- `${timestamp} ${level}: ${truncateLongStrings(message.trim(), 150)}`,
1652
- ];
1653
- try {
1654
- if (level !== 'debug') {
1655
- return msgParts[0];
1656
- }
1657
- if (!metadata) {
1658
- return msgParts[0];
1659
- }
1660
- // Type-safe access to SPLAT_SYMBOL using bracket notation
1661
- const metadataRecord = metadata;
1662
- const splatArray = metadataRecord[SPLAT_SYMBOL];
1663
- const debugValue = Array.isArray(splatArray) ? splatArray[0] : undefined;
1664
- if (!debugValue) {
1665
- return msgParts[0];
1666
- }
1667
- if (debugValue && Array.isArray(debugValue)) {
1668
- msgParts.push(`\n${JSON.stringify(debugValue.map(condenseArray))}`);
1669
- return msgParts.join('');
1670
- }
1671
- if (typeof debugValue !== 'object') {
1672
- msgParts.push(` ${debugValue}`);
1673
- return msgParts.join('');
1674
- }
1675
- msgParts.push('\n{');
1676
- const copy = klona.klona(metadata);
1677
- try {
1678
- const traversal = traverse(copy);
1679
- traversal.forEach(function (value) {
1680
- var _a;
1681
- if (typeof (this === null || this === void 0 ? void 0 : this.key) === 'symbol') {
1682
- return;
1683
- }
1684
- let _parentKey = '';
1685
- const parent = this.parent;
1686
- if (typeof (parent === null || parent === void 0 ? void 0 : parent.key) !== 'symbol' && (parent === null || parent === void 0 ? void 0 : parent.key) !== undefined) {
1687
- _parentKey = String(parent.key);
1688
- }
1689
- const parentKey = `${parent && parent.notRoot ? _parentKey + '.' : ''}`;
1690
- const tabs = `${parent && parent.notRoot ? ' ' : ' '}`;
1691
- const currentKey = (_a = this === null || this === void 0 ? void 0 : this.key) !== null && _a !== void 0 ? _a : 'unknown';
1692
- if (this.isLeaf && typeof value === 'string') {
1693
- const truncatedText = truncateLongStrings(value);
1694
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${JSON.stringify(truncatedText)},`);
1695
- }
1696
- else if (this.notLeaf && Array.isArray(value) && value.length > 0) {
1697
- const currentMessage = `\n${tabs}// ${value.length} ${String(currentKey).replace(/s$/, '')}(s)`;
1698
- this.update(currentMessage);
1699
- msgParts.push(currentMessage);
1700
- const stringifiedArray = value.map(condenseArray);
1701
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: [${stringifiedArray}],`);
1702
- }
1703
- else if (this.isLeaf && typeof value === 'function') {
1704
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: function,`);
1705
- }
1706
- else if (this.isLeaf) {
1707
- msgParts.push(`\n${tabs}${parentKey}${currentKey}: ${value},`);
1708
- }
1709
- });
1710
- }
1711
- catch (e) {
1712
- const errorMessage = e instanceof Error ? e.message : 'Unknown error';
1713
- msgParts.push(`\n[LOGGER TRAVERSAL ERROR] ${errorMessage}`);
1714
- }
1715
- msgParts.push('\n}');
1716
- return msgParts.join('');
1717
- }
1718
- catch (e) {
1719
- const errorMessage = e instanceof Error ? e.message : 'Unknown error';
1720
- msgParts.push(`\n[LOGGER PARSING ERROR] ${errorMessage}`);
1721
- return msgParts.join('');
1722
- }
2012
+
2013
+ const sessionSchema = new mongoose.Schema({
2014
+ refreshTokenHash: {
2015
+ type: String,
2016
+ required: true,
2017
+ },
2018
+ expiration: {
2019
+ type: Date,
2020
+ required: true,
2021
+ expires: 0,
2022
+ },
2023
+ user: {
2024
+ type: mongoose.Schema.Types.ObjectId,
2025
+ ref: 'User',
2026
+ required: true,
2027
+ },
2028
+ });
2029
+
2030
+ const shareSchema = new mongoose.Schema({
2031
+ conversationId: {
2032
+ type: String,
2033
+ required: true,
2034
+ },
2035
+ title: {
2036
+ type: String,
2037
+ index: true,
2038
+ },
2039
+ user: {
2040
+ type: String,
2041
+ index: true,
2042
+ },
2043
+ messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
2044
+ shareId: {
2045
+ type: String,
2046
+ index: true,
2047
+ },
2048
+ targetMessageId: {
2049
+ type: String,
2050
+ required: false,
2051
+ index: true,
2052
+ },
2053
+ isPublic: {
2054
+ type: Boolean,
2055
+ default: true,
2056
+ },
2057
+ }, { timestamps: true });
2058
+ shareSchema.index({ conversationId: 1, user: 1, targetMessageId: 1 });
2059
+
2060
+ const tokenSchema = new mongoose.Schema({
2061
+ userId: {
2062
+ type: mongoose.Schema.Types.ObjectId,
2063
+ required: true,
2064
+ ref: 'user',
2065
+ },
2066
+ email: {
2067
+ type: String,
2068
+ },
2069
+ type: {
2070
+ type: String,
2071
+ },
2072
+ identifier: {
2073
+ type: String,
2074
+ },
2075
+ token: {
2076
+ type: String,
2077
+ required: true,
2078
+ },
2079
+ createdAt: {
2080
+ type: Date,
2081
+ required: true,
2082
+ default: Date.now,
2083
+ },
2084
+ expiresAt: {
2085
+ type: Date,
2086
+ required: true,
2087
+ },
2088
+ metadata: {
2089
+ type: Map,
2090
+ of: mongoose.Schema.Types.Mixed,
2091
+ },
1723
2092
  });
1724
- /**
1725
- * Truncates long string values in JSON log objects.
1726
- * Prevents outputting extremely long values (e.g., base64, blobs).
1727
- */
1728
- const jsonTruncateFormat = winston.format((info) => {
1729
- const truncateLongStrings = (str, maxLength) => str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
1730
- const seen = new WeakSet();
1731
- const truncateObject = (obj) => {
1732
- if (typeof obj !== 'object' || obj === null) {
1733
- return obj;
1734
- }
1735
- // Handle circular references - now with proper object type
1736
- if (seen.has(obj)) {
1737
- return '[Circular]';
1738
- }
1739
- seen.add(obj);
1740
- if (Array.isArray(obj)) {
1741
- return obj.map((item) => truncateObject(item));
1742
- }
1743
- // We know this is an object at this point
1744
- const objectRecord = obj;
1745
- const newObj = {};
1746
- Object.entries(objectRecord).forEach(([key, value]) => {
1747
- if (typeof value === 'string') {
1748
- newObj[key] = truncateLongStrings(value, CONSOLE_JSON_STRING_LENGTH);
1749
- }
1750
- else {
1751
- newObj[key] = truncateObject(value);
1752
- }
1753
- });
1754
- return newObj;
1755
- };
1756
- return truncateObject(info);
2093
+ tokenSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
2094
+
2095
+ const toolCallSchema = new mongoose.Schema({
2096
+ conversationId: {
2097
+ type: String,
2098
+ required: true,
2099
+ },
2100
+ messageId: {
2101
+ type: String,
2102
+ required: true,
2103
+ },
2104
+ toolId: {
2105
+ type: String,
2106
+ required: true,
2107
+ },
2108
+ user: {
2109
+ type: mongoose.Schema.Types.ObjectId,
2110
+ ref: 'User',
2111
+ required: true,
2112
+ },
2113
+ result: {
2114
+ type: mongoose.Schema.Types.Mixed,
2115
+ },
2116
+ attachments: {
2117
+ type: mongoose.Schema.Types.Mixed,
2118
+ },
2119
+ blockIndex: {
2120
+ type: Number,
2121
+ },
2122
+ partIndex: {
2123
+ type: Number,
2124
+ },
2125
+ }, { timestamps: true });
2126
+ toolCallSchema.index({ messageId: 1, user: 1 });
2127
+ toolCallSchema.index({ conversationId: 1, user: 1 });
2128
+
2129
+ const transactionSchema = new mongoose.Schema({
2130
+ user: {
2131
+ type: mongoose.Schema.Types.ObjectId,
2132
+ ref: 'User',
2133
+ index: true,
2134
+ required: true,
2135
+ },
2136
+ conversationId: {
2137
+ type: String,
2138
+ ref: 'Conversation',
2139
+ index: true,
2140
+ },
2141
+ tokenType: {
2142
+ type: String,
2143
+ enum: ['prompt', 'completion', 'credits'],
2144
+ required: true,
2145
+ },
2146
+ model: {
2147
+ type: String,
2148
+ },
2149
+ context: {
2150
+ type: String,
2151
+ },
2152
+ valueKey: {
2153
+ type: String,
2154
+ },
2155
+ rate: Number,
2156
+ rawAmount: Number,
2157
+ tokenValue: Number,
2158
+ inputTokens: { type: Number },
2159
+ writeTokens: { type: Number },
2160
+ readTokens: { type: Number },
2161
+ }, {
2162
+ timestamps: true,
1757
2163
  });
1758
2164
 
1759
- /**
1760
- * Determine the log directory in a cross-compatible way.
1761
- * Priority:
1762
- * 1. LIBRECHAT_LOG_DIR environment variable
1763
- * 2. If running within LibreChat monorepo (when cwd ends with /api), use api/logs
1764
- * 3. If api/logs exists relative to cwd, use that (for running from project root)
1765
- * 4. Otherwise, use logs directory relative to process.cwd()
1766
- *
1767
- * This avoids using __dirname which is not available in ESM modules
1768
- */
1769
- const getLogDirectory = () => {
1770
- if (process.env.LIBRECHAT_LOG_DIR) {
1771
- return process.env.LIBRECHAT_LOG_DIR;
1772
- }
1773
- const cwd = process.cwd();
1774
- // Check if we're running from within the api directory
1775
- if (cwd.endsWith('/api') || cwd.endsWith('\\api')) {
1776
- return path.join(cwd, 'logs');
1777
- }
1778
- // Check if api/logs exists relative to current directory (running from project root)
1779
- // We'll just use the path and let the file system create it if needed
1780
- const apiLogsPath = path.join(cwd, 'api', 'logs');
1781
- // For LibreChat project structure, use api/logs
1782
- // For external consumers, they should set LIBRECHAT_LOG_DIR
1783
- if (cwd.includes('LibreChat')) {
1784
- return apiLogsPath;
1785
- }
1786
- // Default to logs directory relative to current working directory
1787
- return path.join(cwd, 'logs');
1788
- };
2165
+ // Session sub-schema
2166
+ const SessionSchema = new mongoose.Schema({
2167
+ refreshToken: {
2168
+ type: String,
2169
+ default: '',
2170
+ },
2171
+ }, { _id: false });
2172
+ // Backup code sub-schema
2173
+ const BackupCodeSchema = new mongoose.Schema({
2174
+ codeHash: { type: String, required: true },
2175
+ used: { type: Boolean, default: false },
2176
+ usedAt: { type: Date, default: null },
2177
+ }, { _id: false });
2178
+ const userSchema = new mongoose.Schema({
2179
+ name: {
2180
+ type: String,
2181
+ },
2182
+ username: {
2183
+ type: String,
2184
+ lowercase: true,
2185
+ default: '',
2186
+ },
2187
+ email: {
2188
+ type: String,
2189
+ required: [true, "can't be blank"],
2190
+ lowercase: true,
2191
+ unique: true,
2192
+ match: [/\S+@\S+\.\S+/, 'is invalid'],
2193
+ index: true,
2194
+ },
2195
+ emailVerified: {
2196
+ type: Boolean,
2197
+ required: true,
2198
+ default: false,
2199
+ },
2200
+ password: {
2201
+ type: String,
2202
+ trim: true,
2203
+ minlength: 8,
2204
+ maxlength: 128,
2205
+ select: false,
2206
+ },
2207
+ avatar: {
2208
+ type: String,
2209
+ required: false,
2210
+ },
2211
+ provider: {
2212
+ type: String,
2213
+ required: true,
2214
+ default: 'local',
2215
+ },
2216
+ role: {
2217
+ type: String,
2218
+ default: librechatDataProvider.SystemRoles.USER,
2219
+ },
2220
+ googleId: {
2221
+ type: String,
2222
+ unique: true,
2223
+ sparse: true,
2224
+ },
2225
+ facebookId: {
2226
+ type: String,
2227
+ unique: true,
2228
+ sparse: true,
2229
+ },
2230
+ openidId: {
2231
+ type: String,
2232
+ unique: true,
2233
+ sparse: true,
2234
+ },
2235
+ samlId: {
2236
+ type: String,
2237
+ unique: true,
2238
+ sparse: true,
2239
+ },
2240
+ ldapId: {
2241
+ type: String,
2242
+ unique: true,
2243
+ sparse: true,
2244
+ },
2245
+ githubId: {
2246
+ type: String,
2247
+ unique: true,
2248
+ sparse: true,
2249
+ },
2250
+ discordId: {
2251
+ type: String,
2252
+ unique: true,
2253
+ sparse: true,
2254
+ },
2255
+ appleId: {
2256
+ type: String,
2257
+ unique: true,
2258
+ sparse: true,
2259
+ },
2260
+ plugins: {
2261
+ type: Array,
2262
+ },
2263
+ twoFactorEnabled: {
2264
+ type: Boolean,
2265
+ default: false,
2266
+ },
2267
+ totpSecret: {
2268
+ type: String,
2269
+ select: false,
2270
+ },
2271
+ backupCodes: {
2272
+ type: [BackupCodeSchema],
2273
+ select: false,
2274
+ },
2275
+ refreshToken: {
2276
+ type: [SessionSchema],
2277
+ },
2278
+ expiresAt: {
2279
+ type: Date,
2280
+ expires: 604800, // 7 days in seconds
2281
+ },
2282
+ termsAccepted: {
2283
+ type: Boolean,
2284
+ default: false,
2285
+ },
2286
+ personalization: {
2287
+ type: {
2288
+ memories: {
2289
+ type: Boolean,
2290
+ default: true,
2291
+ },
2292
+ },
2293
+ default: {},
2294
+ },
2295
+ /** Field for external source identification (for consistency with TPrincipal schema) */
2296
+ idOnTheSource: {
2297
+ type: String,
2298
+ sparse: true,
2299
+ },
2300
+ }, { timestamps: true });
1789
2301
 
1790
- const logDir$1 = getLogDirectory();
1791
- const { NODE_ENV: NODE_ENV$1, DEBUG_LOGGING: DEBUG_LOGGING$1, CONSOLE_JSON, DEBUG_CONSOLE } = process.env;
1792
- const useConsoleJson = typeof CONSOLE_JSON === 'string' && CONSOLE_JSON.toLowerCase() === 'true';
1793
- const useDebugConsole = typeof DEBUG_CONSOLE === 'string' && DEBUG_CONSOLE.toLowerCase() === 'true';
1794
- const useDebugLogging$1 = typeof DEBUG_LOGGING$1 === 'string' && DEBUG_LOGGING$1.toLowerCase() === 'true';
1795
- const levels$1 = {
1796
- error: 0,
1797
- warn: 1,
1798
- info: 2,
1799
- http: 3,
1800
- verbose: 4,
1801
- debug: 5,
1802
- activity: 6,
1803
- silly: 7,
1804
- };
1805
- winston.addColors({
1806
- info: 'green',
1807
- warn: 'italic yellow',
1808
- error: 'red',
1809
- debug: 'blue',
2302
+ const MemoryEntrySchema = new mongoose.Schema({
2303
+ userId: {
2304
+ type: mongoose.Schema.Types.ObjectId,
2305
+ ref: 'User',
2306
+ index: true,
2307
+ required: true,
2308
+ },
2309
+ key: {
2310
+ type: String,
2311
+ required: true,
2312
+ validate: {
2313
+ validator: (v) => /^[a-z_]+$/.test(v),
2314
+ message: 'Key must only contain lowercase letters and underscores',
2315
+ },
2316
+ },
2317
+ value: {
2318
+ type: String,
2319
+ required: true,
2320
+ },
2321
+ tokenCount: {
2322
+ type: Number,
2323
+ default: 0,
2324
+ },
2325
+ updated_at: {
2326
+ type: Date,
2327
+ default: Date.now,
2328
+ },
1810
2329
  });
1811
- const level$1 = () => {
1812
- const env = NODE_ENV$1 || 'development';
1813
- return env === 'development' ? 'debug' : 'warn';
1814
- };
1815
- const fileFormat$1 = winston.format.combine(redactFormat(), winston.format.timestamp({ format: () => new Date().toISOString() }), winston.format.errors({ stack: true }), winston.format.splat());
1816
- const transports$1 = [
1817
- new winston.transports.DailyRotateFile({
1818
- level: 'error',
1819
- filename: `${logDir$1}/error-%DATE%.log`,
1820
- datePattern: 'YYYY-MM-DD',
1821
- zippedArchive: true,
1822
- maxSize: '20m',
1823
- maxFiles: '14d',
1824
- format: winston.format.combine(fileFormat$1, winston.format.json()),
1825
- }),
1826
- ];
1827
- if (useDebugLogging$1) {
1828
- transports$1.push(new winston.transports.DailyRotateFile({
1829
- level: 'debug',
1830
- filename: `${logDir$1}/debug-%DATE%.log`,
1831
- datePattern: 'YYYY-MM-DD',
1832
- zippedArchive: true,
1833
- maxSize: '20m',
1834
- maxFiles: '14d',
1835
- format: winston.format.combine(fileFormat$1, debugTraverse),
1836
- }));
1837
- }
1838
- const consoleFormat$1 = winston.format.combine(redactFormat(), winston.format.colorize({ all: true }), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf((info) => {
1839
- const message = `${info.timestamp} ${info.level}: ${info.message}`;
1840
- return info.level.includes('error') ? redactMessage(message) : message;
1841
- }));
1842
- let consoleLogLevel = 'info';
1843
- if (useDebugConsole) {
1844
- consoleLogLevel = 'debug';
1845
- }
1846
- // Add console transport
1847
- if (useDebugConsole) {
1848
- transports$1.push(new winston.transports.Console({
1849
- level: consoleLogLevel,
1850
- format: useConsoleJson
1851
- ? winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json())
1852
- : winston.format.combine(fileFormat$1, debugTraverse),
1853
- }));
1854
- }
1855
- else if (useConsoleJson) {
1856
- transports$1.push(new winston.transports.Console({
1857
- level: consoleLogLevel,
1858
- format: winston.format.combine(fileFormat$1, jsonTruncateFormat(), winston.format.json()),
1859
- }));
1860
- }
1861
- else {
1862
- transports$1.push(new winston.transports.Console({
1863
- level: consoleLogLevel,
1864
- format: consoleFormat$1,
1865
- }));
1866
- }
1867
- // Create logger
1868
- const logger$1 = winston.createLogger({
1869
- level: level$1(),
1870
- levels: levels$1,
1871
- transports: transports$1,
2330
+
2331
+ const groupSchema = new mongoose.Schema({
2332
+ name: {
2333
+ type: String,
2334
+ required: true,
2335
+ index: true,
2336
+ },
2337
+ description: {
2338
+ type: String,
2339
+ required: false,
2340
+ },
2341
+ email: {
2342
+ type: String,
2343
+ required: false,
2344
+ index: true,
2345
+ },
2346
+ avatar: {
2347
+ type: String,
2348
+ required: false,
2349
+ },
2350
+ memberIds: [
2351
+ {
2352
+ type: String,
2353
+ required: false,
2354
+ },
2355
+ ],
2356
+ source: {
2357
+ type: String,
2358
+ enum: ['local', 'entra'],
2359
+ default: 'local',
2360
+ },
2361
+ /** External ID (e.g., Entra ID) */
2362
+ idOnTheSource: {
2363
+ type: String,
2364
+ sparse: true,
2365
+ index: true,
2366
+ required: function () {
2367
+ return this.source !== 'local';
2368
+ },
2369
+ },
2370
+ }, { timestamps: true });
2371
+ groupSchema.index({ idOnTheSource: 1, source: 1 }, {
2372
+ unique: true,
2373
+ partialFilterExpression: { idOnTheSource: { $exists: true } },
1872
2374
  });
2375
+ groupSchema.index({ memberIds: 1 });
1873
2376
 
1874
2377
  /**
1875
2378
  * Checks if the connected MongoDB deployment supports transactions
@@ -4766,6 +5269,68 @@ function anonymizeMessages(messages, newConvoId) {
4766
5269
  };
4767
5270
  });
4768
5271
  }
5272
+ /**
5273
+ * Filter messages up to and including the target message (branch-specific)
5274
+ * Similar to getMessagesUpToTargetLevel from fork utilities
5275
+ */
5276
+ function getMessagesUpToTarget(messages, targetMessageId) {
5277
+ var _a, _b;
5278
+ if (!messages || messages.length === 0) {
5279
+ return [];
5280
+ }
5281
+ // If only one message and it's the target, return it
5282
+ if (messages.length === 1 && ((_a = messages[0]) === null || _a === void 0 ? void 0 : _a.messageId) === targetMessageId) {
5283
+ return messages;
5284
+ }
5285
+ // Create a map of parentMessageId to children messages
5286
+ const parentToChildrenMap = new Map();
5287
+ for (const message of messages) {
5288
+ const parentId = message.parentMessageId || librechatDataProvider.Constants.NO_PARENT;
5289
+ if (!parentToChildrenMap.has(parentId)) {
5290
+ parentToChildrenMap.set(parentId, []);
5291
+ }
5292
+ (_b = parentToChildrenMap.get(parentId)) === null || _b === void 0 ? void 0 : _b.push(message);
5293
+ }
5294
+ // Find the target message
5295
+ const targetMessage = messages.find((msg) => msg.messageId === targetMessageId);
5296
+ if (!targetMessage) {
5297
+ // If target not found, return all messages for backwards compatibility
5298
+ return messages;
5299
+ }
5300
+ const visited = new Set();
5301
+ const rootMessages = parentToChildrenMap.get(librechatDataProvider.Constants.NO_PARENT) || [];
5302
+ let currentLevel = rootMessages.length > 0 ? [...rootMessages] : [targetMessage];
5303
+ const results = new Set(currentLevel);
5304
+ // Check if the target message is at the root level
5305
+ if (currentLevel.some((msg) => msg.messageId === targetMessageId) &&
5306
+ targetMessage.parentMessageId === librechatDataProvider.Constants.NO_PARENT) {
5307
+ return Array.from(results);
5308
+ }
5309
+ // Iterate level by level until the target is found
5310
+ let targetFound = false;
5311
+ while (!targetFound && currentLevel.length > 0) {
5312
+ const nextLevel = [];
5313
+ for (const node of currentLevel) {
5314
+ if (visited.has(node.messageId)) {
5315
+ continue;
5316
+ }
5317
+ visited.add(node.messageId);
5318
+ const children = parentToChildrenMap.get(node.messageId) || [];
5319
+ for (const child of children) {
5320
+ if (visited.has(child.messageId)) {
5321
+ continue;
5322
+ }
5323
+ nextLevel.push(child);
5324
+ results.add(child);
5325
+ if (child.messageId === targetMessageId) {
5326
+ targetFound = true;
5327
+ }
5328
+ }
5329
+ }
5330
+ currentLevel = nextLevel;
5331
+ }
5332
+ return Array.from(results);
5333
+ }
4769
5334
  /** Factory function that takes mongoose instance and returns the methods */
4770
5335
  function createShareMethods(mongoose) {
4771
5336
  /**
@@ -4784,6 +5349,11 @@ function createShareMethods(mongoose) {
4784
5349
  if (!(share === null || share === void 0 ? void 0 : share.conversationId) || !share.isPublic) {
4785
5350
  return null;
4786
5351
  }
5352
+ /** Filtered messages based on targetMessageId if present (branch-specific sharing) */
5353
+ let messagesToShare = share.messages;
5354
+ if (share.targetMessageId) {
5355
+ messagesToShare = getMessagesUpToTarget(share.messages, share.targetMessageId);
5356
+ }
4787
5357
  const newConvoId = anonymizeConvoId(share.conversationId);
4788
5358
  const result = {
4789
5359
  shareId: share.shareId || shareId,
@@ -4792,7 +5362,7 @@ function createShareMethods(mongoose) {
4792
5362
  createdAt: share.createdAt,
4793
5363
  updatedAt: share.updatedAt,
4794
5364
  conversationId: newConvoId,
4795
- messages: anonymizeMessages(share.messages, newConvoId),
5365
+ messages: anonymizeMessages(messagesToShare, newConvoId),
4796
5366
  };
4797
5367
  return result;
4798
5368
  }
@@ -4900,10 +5470,34 @@ function createShareMethods(mongoose) {
4900
5470
  throw new ShareServiceError('Error deleting shared links', 'BULK_DELETE_ERROR');
4901
5471
  }
4902
5472
  }
5473
+ /**
5474
+ * Delete shared links by conversation ID
5475
+ */
5476
+ async function deleteConvoSharedLink(user, conversationId) {
5477
+ if (!user || !conversationId) {
5478
+ throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS');
5479
+ }
5480
+ try {
5481
+ const SharedLink = mongoose.models.SharedLink;
5482
+ const result = await SharedLink.deleteMany({ user, conversationId });
5483
+ return {
5484
+ message: 'Shared links deleted successfully',
5485
+ deletedCount: result.deletedCount,
5486
+ };
5487
+ }
5488
+ catch (error) {
5489
+ logger$1.error('[deleteConvoSharedLink] Error deleting shared links', {
5490
+ error: error instanceof Error ? error.message : 'Unknown error',
5491
+ user,
5492
+ conversationId,
5493
+ });
5494
+ throw new ShareServiceError('Error deleting shared links', 'SHARE_DELETE_ERROR');
5495
+ }
5496
+ }
4903
5497
  /**
4904
5498
  * Create a new shared link for a conversation
4905
5499
  */
4906
- async function createSharedLink(user, conversationId) {
5500
+ async function createSharedLink(user, conversationId, targetMessageId) {
4907
5501
  if (!user || !conversationId) {
4908
5502
  throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS');
4909
5503
  }
@@ -4912,7 +5506,12 @@ function createShareMethods(mongoose) {
4912
5506
  const SharedLink = mongoose.models.SharedLink;
4913
5507
  const Conversation = mongoose.models.Conversation;
4914
5508
  const [existingShare, conversationMessages] = await Promise.all([
4915
- SharedLink.findOne({ conversationId, user, isPublic: true })
5509
+ SharedLink.findOne({
5510
+ conversationId,
5511
+ user,
5512
+ isPublic: true,
5513
+ ...(targetMessageId && { targetMessageId }),
5514
+ })
4916
5515
  .select('-_id -__v -user')
4917
5516
  .lean(),
4918
5517
  Message.find({ conversationId, user }).sort({ createdAt: 1 }).lean(),
@@ -4921,11 +5520,16 @@ function createShareMethods(mongoose) {
4921
5520
  logger$1.error('[createSharedLink] Share already exists', {
4922
5521
  user,
4923
5522
  conversationId,
5523
+ targetMessageId,
4924
5524
  });
4925
5525
  throw new ShareServiceError('Share already exists', 'SHARE_EXISTS');
4926
5526
  }
4927
5527
  else if (existingShare) {
4928
- await SharedLink.deleteOne({ conversationId, user });
5528
+ await SharedLink.deleteOne({
5529
+ conversationId,
5530
+ user,
5531
+ ...(targetMessageId && { targetMessageId }),
5532
+ });
4929
5533
  }
4930
5534
  const conversation = (await Conversation.findOne({ conversationId, user }).lean());
4931
5535
  // Check if user owns the conversation
@@ -4944,6 +5548,7 @@ function createShareMethods(mongoose) {
4944
5548
  messages: conversationMessages,
4945
5549
  title,
4946
5550
  user,
5551
+ ...(targetMessageId && { targetMessageId }),
4947
5552
  });
4948
5553
  return { shareId, conversationId };
4949
5554
  }
@@ -4955,6 +5560,7 @@ function createShareMethods(mongoose) {
4955
5560
  error: error instanceof Error ? error.message : 'Unknown error',
4956
5561
  user,
4957
5562
  conversationId,
5563
+ targetMessageId,
4958
5564
  });
4959
5565
  throw new ShareServiceError('Error creating shared link', 'SHARE_CREATE_ERROR');
4960
5566
  }
@@ -5067,6 +5673,7 @@ function createShareMethods(mongoose) {
5067
5673
  deleteSharedLink,
5068
5674
  getSharedMessages,
5069
5675
  deleteAllSharedLinks,
5676
+ deleteConvoSharedLink,
5070
5677
  };
5071
5678
  }
5072
5679
 
@@ -5089,9 +5696,11 @@ function createMethods(mongoose) {
5089
5696
  };
5090
5697
  }
5091
5698
 
5699
+ exports.AppService = AppService;
5092
5700
  exports.actionSchema = Action;
5093
5701
  exports.agentCategorySchema = agentCategorySchema;
5094
5702
  exports.agentSchema = agentSchema;
5703
+ exports.agentsConfigSetup = agentsConfigSetup;
5095
5704
  exports.assistantSchema = assistantSchema;
5096
5705
  exports.balanceSchema = balanceSchema;
5097
5706
  exports.bannerSchema = bannerSchema;
@@ -5102,15 +5711,20 @@ exports.createMethods = createMethods;
5102
5711
  exports.createModels = createModels;
5103
5712
  exports.fileSchema = file;
5104
5713
  exports.getTransactionSupport = getTransactionSupport;
5714
+ exports.getWebSearchKeys = getWebSearchKeys;
5105
5715
  exports.groupSchema = groupSchema;
5106
5716
  exports.hashToken = hashToken;
5107
5717
  exports.keySchema = keySchema;
5718
+ exports.loadDefaultInterface = loadDefaultInterface;
5719
+ exports.loadTurnstileConfig = loadTurnstileConfig;
5720
+ exports.loadWebSearchConfig = loadWebSearchConfig;
5108
5721
  exports.logger = logger$1;
5109
5722
  exports.meiliLogger = logger;
5110
5723
  exports.memorySchema = MemoryEntrySchema;
5111
5724
  exports.messageSchema = messageSchema;
5112
5725
  exports.pluginAuthSchema = pluginAuthSchema;
5113
5726
  exports.presetSchema = presetSchema;
5727
+ exports.processModelSpecs = processModelSpecs;
5114
5728
  exports.projectSchema = projectSchema;
5115
5729
  exports.promptGroupSchema = promptGroupSchema;
5116
5730
  exports.promptSchema = promptSchema;
@@ -5123,4 +5737,6 @@ exports.tokenSchema = tokenSchema;
5123
5737
  exports.toolCallSchema = toolCallSchema;
5124
5738
  exports.transactionSchema = transactionSchema;
5125
5739
  exports.userSchema = userSchema;
5740
+ exports.webSearchAuth = webSearchAuth;
5741
+ exports.webSearchKeys = webSearchKeys;
5126
5742
  //# sourceMappingURL=index.cjs.map