Skip to content

Commit 9f3bf97

Browse files
committed
🔧 refactor: Consolidate Logging, Model Selection & Actions Optimizations, Minor Fixes (#6553)
* 🔧 feat: Enhance logging configuration for production and debug environments * 🔒 feat: Implement encryption and decryption functions for sensitive values in ActionService with URL encoding/decoding * refactor: optimize action service for agent tools * refactor: optimize action processing for Assistants API * fix: handle case where agent is not found in loadAgent function * refactor: improve error handling in API calls by throwing new Error with logAxiosError output * chore: bump @librechat/agents to 2.3.95, fixes "Invalid tool call structure: No preceding AIMessage with tool_call_ids" * refactor: enhance error logging in logAxiosError function to include response status * refactor: remove unused useModelSelection hook from Endpoint * refactor: add support for assistants in useSelectorEffects hook * refactor: replace string easing with imported easings in Landing component * chore: remove duplicate translation * refactor: update model selection logic and improve localization for UI elements * refactor: replace endpoint value checks with helper functions for agents and assistants * refactor: optimize display value logic and utilize useMemo for performance improvements * refactor: clean up imports and optimize display/icon value logic in endpoint components, fix spec selection * refactor: enhance error logging in axios utility to include stack traces for better debugging * refactor: update logging configuration to use DEBUG_LOGGING and streamline log level handling * refactor: adjust className for export menu button to improve layout consistency and remove unused title prop from ShareButton * refactor: update import path for logAxiosError utility to improve module organization and clarity * refactor: implement debounced search value setter in ModelSelectorContext for improved performance
1 parent ae1c5ca commit 9f3bf97

26 files changed

+16330
-16495
lines changed

‎api/config/meiliLogger.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ require('winston-daily-rotate-file');
44

55
const logDir = path.join(__dirname, '..', 'logs');
66

7-
const { NODE_ENV } = process.env;
7+
const { NODE_ENV, DEBUG_LOGGING = false } = process.env;
8+
9+
const useDebugLogging =
10+
(typeof DEBUG_LOGGING === 'string' && DEBUG_LOGGING?.toLowerCase() === 'true') ||
11+
DEBUG_LOGGING === true;
812

913
const levels = {
1014
error: 0,
@@ -36,9 +40,10 @@ const fileFormat = winston.format.combine(
3640
winston.format.splat(),
3741
);
3842

43+
const logLevel = useDebugLogging ? 'debug' : 'error';
3944
const transports = [
4045
new winston.transports.DailyRotateFile({
41-
level: 'debug',
46+
level: logLevel,
4247
filename: `${logDir}/meiliSync-%DATE%.log`,
4348
datePattern: 'YYYY-MM-DD',
4449
zippedArchive: true,
@@ -48,14 +53,6 @@ const transports = [
4853
}),
4954
];
5055

51-
// if (NODE_ENV !== 'production') {
52-
// transports.push(
53-
// new winston.transports.Console({
54-
// format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
55-
// }),
56-
// );
57-
// }
58-
5956
const consoleFormat = winston.format.combine(
6057
winston.format.colorize({ all: true }),
6158
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),

‎api/config/winston.js

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { redactFormat, redactMessage, debugTraverse, jsonTruncateFormat } = requi
55

66
const logDir = path.join(__dirname, '..', 'logs');
77

8-
const { NODE_ENV, DEBUG_LOGGING = true, DEBUG_CONSOLE = false, CONSOLE_JSON = false } = process.env;
8+
const { NODE_ENV, DEBUG_LOGGING = true, CONSOLE_JSON = false, DEBUG_CONSOLE = false } = process.env;
99

1010
const useConsoleJson =
1111
(typeof CONSOLE_JSON === 'string' && CONSOLE_JSON?.toLowerCase() === 'true') ||
@@ -15,6 +15,10 @@ const useDebugConsole =
1515
(typeof DEBUG_CONSOLE === 'string' && DEBUG_CONSOLE?.toLowerCase() === 'true') ||
1616
DEBUG_CONSOLE === true;
1717

18+
const useDebugLogging =
19+
(typeof DEBUG_LOGGING === 'string' && DEBUG_LOGGING?.toLowerCase() === 'true') ||
20+
DEBUG_LOGGING === true;
21+
1822
const levels = {
1923
error: 0,
2024
warn: 1,
@@ -57,28 +61,9 @@ const transports = [
5761
maxFiles: '14d',
5862
format: fileFormat,
5963
}),
60-
// new winston.transports.DailyRotateFile({
61-
// level: 'info',
62-
// filename: `${logDir}/info-%DATE%.log`,
63-
// datePattern: 'YYYY-MM-DD',
64-
// zippedArchive: true,
65-
// maxSize: '20m',
66-
// maxFiles: '14d',
67-
// }),
6864
];
6965

70-
// if (NODE_ENV !== 'production') {
71-
// transports.push(
72-
// new winston.transports.Console({
73-
// format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
74-
// }),
75-
// );
76-
// }
77-
78-
if (
79-
(typeof DEBUG_LOGGING === 'string' && DEBUG_LOGGING?.toLowerCase() === 'true') ||
80-
DEBUG_LOGGING === true
81-
) {
66+
if (useDebugLogging) {
8267
transports.push(
8368
new winston.transports.DailyRotateFile({
8469
level: 'debug',
@@ -107,10 +92,16 @@ const consoleFormat = winston.format.combine(
10792
}),
10893
);
10994

95+
// Determine console log level
96+
let consoleLogLevel = 'info';
97+
if (useDebugConsole) {
98+
consoleLogLevel = 'debug';
99+
}
100+
110101
if (useDebugConsole) {
111102
transports.push(
112103
new winston.transports.Console({
113-
level: 'debug',
104+
level: consoleLogLevel,
114105
format: useConsoleJson
115106
? winston.format.combine(fileFormat, jsonTruncateFormat(), winston.format.json())
116107
: winston.format.combine(fileFormat, debugTraverse),
@@ -119,14 +110,14 @@ if (useDebugConsole) {
119110
} else if (useConsoleJson) {
120111
transports.push(
121112
new winston.transports.Console({
122-
level: 'info',
113+
level: consoleLogLevel,
123114
format: winston.format.combine(fileFormat, jsonTruncateFormat(), winston.format.json()),
124115
}),
125116
);
126117
} else {
127118
transports.push(
128119
new winston.transports.Console({
129-
level: 'info',
120+
level: consoleLogLevel,
130121
format: consoleFormat,
131122
}),
132123
);

‎api/models/Agent.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ const loadAgent = async ({ req, agent_id }) => {
4646
id: agent_id,
4747
});
4848

49+
if (!agent) {
50+
return null;
51+
}
52+
4953
if (agent.author.toString() === req.user.id) {
5054
return agent;
5155
}

‎api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@langchain/google-genai": "^0.1.11",
5151
"@langchain/google-vertexai": "^0.2.2",
5252
"@langchain/textsplitters": "^0.1.0",
53-
"@librechat/agents": "^2.3.94",
53+
"@librechat/agents": "^2.3.95",
5454
"@librechat/data-schemas": "*",
5555
"@waylaidwanderer/fetch-event-source": "^3.0.1",
5656
"axios": "^1.8.2",

‎api/server/services/ActionService.js

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const {
1313
actionDomainSeparator,
1414
} = require('librechat-data-provider');
1515
const { refreshAccessToken } = require('~/server/services/TokenService');
16-
const { isActionDomainAllowed } = require('~/server/services/domains');
1716
const { logger, getFlowStateManager, sendEvent } = require('~/config');
1817
const { encryptV2, decryptV2 } = require('~/server/utils/crypto');
1918
const { getActions, deleteActions } = require('~/models/Action');
@@ -130,6 +129,7 @@ async function loadActionSets(searchParams) {
130129
* @param {string | undefined} [params.name] - The name of the tool.
131130
* @param {string | undefined} [params.description] - The description for the tool.
132131
* @param {import('zod').ZodTypeAny | undefined} [params.zodSchema] - The Zod schema for tool input validation/definition
132+
* @param {{ oauth_client_id?: string; oauth_client_secret?: string; }} params.encrypted - The encrypted values for the action.
133133
* @returns { Promise<typeof tool | { _call: (toolInput: Object | string) => unknown}> } An object with `_call` method to execute the tool input.
134134
*/
135135
async function createActionTool({
@@ -140,17 +140,8 @@ async function createActionTool({
140140
zodSchema,
141141
name,
142142
description,
143+
encrypted,
143144
}) {
144-
const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain);
145-
if (!isDomainAllowed) {
146-
return null;
147-
}
148-
const encrypted = {
149-
oauth_client_id: action.metadata.oauth_client_id,
150-
oauth_client_secret: action.metadata.oauth_client_secret,
151-
};
152-
action.metadata = await decryptMetadata(action.metadata);
153-
154145
/** @type {(toolInput: Object | string, config: GraphRunnableConfig) => Promise<unknown>} */
155146
const _call = async (toolInput, config) => {
156147
try {
@@ -308,9 +299,8 @@ async function createActionTool({
308299
}
309300
return response.data;
310301
} catch (error) {
311-
const logMessage = `API call to ${action.metadata.domain} failed`;
312-
logAxiosError({ message: logMessage, error });
313-
throw error;
302+
const message = `API call to ${action.metadata.domain} failed:`;
303+
return logAxiosError({ message, error });
314304
}
315305
};
316306

@@ -327,6 +317,27 @@ async function createActionTool({
327317
};
328318
}
329319

320+
/**
321+
* Encrypts a sensitive value.
322+
* @param {string} value
323+
* @returns {Promise<string>}
324+
*/
325+
async function encryptSensitiveValue(value) {
326+
// Encode API key to handle special characters like ":"
327+
const encodedValue = encodeURIComponent(value);
328+
return await encryptV2(encodedValue);
329+
}
330+
331+
/**
332+
* Decrypts a sensitive value.
333+
* @param {string} value
334+
* @returns {Promise<string>}
335+
*/
336+
async function decryptSensitiveValue(value) {
337+
const decryptedValue = await decryptV2(value);
338+
return decodeURIComponent(decryptedValue);
339+
}
340+
330341
/**
331342
* Encrypts sensitive metadata values for an action.
332343
*
@@ -339,17 +350,19 @@ async function encryptMetadata(metadata) {
339350
// ServiceHttp
340351
if (metadata.auth && metadata.auth.type === AuthTypeEnum.ServiceHttp) {
341352
if (metadata.api_key) {
342-
encryptedMetadata.api_key = await encryptV2(metadata.api_key);
353+
encryptedMetadata.api_key = await encryptSensitiveValue(metadata.api_key);
343354
}
344355
}
345356

346357
// OAuth
347358
else if (metadata.auth && metadata.auth.type === AuthTypeEnum.OAuth) {
348359
if (metadata.oauth_client_id) {
349-
encryptedMetadata.oauth_client_id = await encryptV2(metadata.oauth_client_id);
360+
encryptedMetadata.oauth_client_id = await encryptSensitiveValue(metadata.oauth_client_id);
350361
}
351362
if (metadata.oauth_client_secret) {
352-
encryptedMetadata.oauth_client_secret = await encryptV2(metadata.oauth_client_secret);
363+
encryptedMetadata.oauth_client_secret = await encryptSensitiveValue(
364+
metadata.oauth_client_secret,
365+
);
353366
}
354367
}
355368

@@ -368,17 +381,19 @@ async function decryptMetadata(metadata) {
368381
// ServiceHttp
369382
if (metadata.auth && metadata.auth.type === AuthTypeEnum.ServiceHttp) {
370383
if (metadata.api_key) {
371-
decryptedMetadata.api_key = await decryptV2(metadata.api_key);
384+
decryptedMetadata.api_key = await decryptSensitiveValue(metadata.api_key);
372385
}
373386
}
374387

375388
// OAuth
376389
else if (metadata.auth && metadata.auth.type === AuthTypeEnum.OAuth) {
377390
if (metadata.oauth_client_id) {
378-
decryptedMetadata.oauth_client_id = await decryptV2(metadata.oauth_client_id);
391+
decryptedMetadata.oauth_client_id = await decryptSensitiveValue(metadata.oauth_client_id);
379392
}
380393
if (metadata.oauth_client_secret) {
381-
decryptedMetadata.oauth_client_secret = await decryptV2(metadata.oauth_client_secret);
394+
decryptedMetadata.oauth_client_secret = await decryptSensitiveValue(
395+
metadata.oauth_client_secret,
396+
);
382397
}
383398
}
384399

‎api/server/services/Files/Code/crud.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ async function getCodeOutputDownloadStream(fileIdentifier, apiKey) {
3232
const response = await axios(options);
3333
return response;
3434
} catch (error) {
35-
logAxiosError({
36-
message: `Error downloading code environment file stream: ${error.message}`,
37-
error,
38-
});
39-
throw new Error(`Error downloading file: ${error.message}`);
35+
throw new Error(
36+
logAxiosError({
37+
message: `Error downloading code environment file stream: ${error.message}`,
38+
error,
39+
}),
40+
);
4041
}
4142
}
4243

@@ -89,11 +90,12 @@ async function uploadCodeEnvFile({ req, stream, filename, apiKey, entity_id = ''
8990

9091
return `${fileIdentifier}?entity_id=${entity_id}`;
9192
} catch (error) {
92-
logAxiosError({
93-
message: `Error uploading code environment file: ${error.message}`,
94-
error,
95-
});
96-
throw new Error(`Error uploading code environment file: ${error.message}`);
93+
throw new Error(
94+
logAxiosError({
95+
message: `Error uploading code environment file: ${error.message}`,
96+
error,
97+
}),
98+
);
9799
}
98100
}
99101

‎api/server/services/Files/MistralOCR/crud.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const FormData = require('form-data');
55
const { FileSources, envVarRegex, extractEnvVariable } = require('librechat-data-provider');
66
const { loadAuthValues } = require('~/server/services/Tools/credentials');
77
const { logger, createAxiosInstance } = require('~/config');
8-
const { logAxiosError } = require('~/utils');
8+
const { logAxiosError } = require('~/utils/axios');
99

1010
const axios = createAxiosInstance();
1111

@@ -194,8 +194,7 @@ const uploadMistralOCR = async ({ req, file, file_id, entity_id }) => {
194194
};
195195
} catch (error) {
196196
const message = 'Error uploading document to Mistral OCR API';
197-
logAxiosError({ error, message });
198-
throw new Error(message);
197+
throw new Error(logAxiosError({ error, message }));
199198
}
200199
};
201200

‎api/server/services/Files/MistralOCR/crud.spec.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ const mockAxios = {
2929

3030
jest.mock('axios', () => mockAxios);
3131
jest.mock('fs');
32-
jest.mock('~/utils', () => ({
33-
logAxiosError: jest.fn(),
34-
}));
3532
jest.mock('~/config', () => ({
3633
logger: {
3734
error: jest.fn(),
@@ -494,9 +491,6 @@ describe('MistralOCR Service', () => {
494491
}),
495492
).rejects.toThrow('Error uploading document to Mistral OCR API');
496493
expect(fs.createReadStream).toHaveBeenCalledWith('/tmp/upload/file.pdf');
497-
498-
const { logAxiosError } = require('~/utils');
499-
expect(logAxiosError).toHaveBeenCalled();
500494
});
501495

502496
it('should handle single page documents without page numbering', async () => {

‎api/server/services/Runs/methods.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ async function retrieveRun({ thread_id, run_id, timeout, openai }) {
5555
return response.data;
5656
} catch (error) {
5757
const message = '[retrieveRun] Failed to retrieve run data:';
58-
logAxiosError({ message, error });
59-
throw error;
58+
throw new Error(logAxiosError({ message, error }));
6059
}
6160
}
6261

‎api/server/services/TokenService.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,12 @@ const refreshAccessToken = async ({
9393
return response.data;
9494
} catch (error) {
9595
const message = 'Error refreshing OAuth tokens';
96-
logAxiosError({
97-
message,
98-
error,
99-
});
100-
throw new Error(message);
96+
throw new Error(
97+
logAxiosError({
98+
message,
99+
error,
100+
}),
101+
);
101102
}
102103
};
103104

@@ -156,11 +157,12 @@ const getAccessToken = async ({
156157
return response.data;
157158
} catch (error) {
158159
const message = 'Error exchanging OAuth code';
159-
logAxiosError({
160-
message,
161-
error,
162-
});
163-
throw new Error(message);
160+
throw new Error(
161+
logAxiosError({
162+
message,
163+
error,
164+
}),
165+
);
164166
}
165167
};
166168

0 commit comments

Comments
 (0)