⚙️ refactor: Enhance Logging, Navigation And Error Handling (#5910)

* refactor: Ensure Axios Errors are less Verbose if No Response

* refactor: Improve error handling in logAxiosError function

* fix: Prevent ModelSelect from rendering for Agent Endpoints

* refactor: Enhance logging functions with type parameter for better clarity

* refactor: Update buildDefaultConvo function to use optional endpoint parameter since we pass a default value for undefined

* refactor: Replace console logs with logger warnings and errors in useNavigateToConvo hook, and handle removed endpoint edge case

* chore: import order
This commit is contained in:
Danny Avila 2025-02-16 11:47:01 -05:00 committed by GitHub
parent 93dd365fda
commit a65647a7de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 57 additions and 56 deletions

View file

@ -5,40 +5,32 @@ const { logger } = require('~/config');
*
* @param {Object} options - The options object.
* @param {string} options.message - The custom message to be logged.
* @param {Error} options.error - The Axios error object.
* @param {import('axios').AxiosError} options.error - The Axios error object.
*/
const logAxiosError = ({ message, error }) => {
const timedOutMessage = 'Cannot read properties of undefined (reading \'status\')';
if (error.response) {
logger.error(
`${message} The request was made and the server responded with a status code that falls out of the range of 2xx: ${
error.message ? error.message : ''
}. Error response data:\n`,
{
headers: error.response?.headers,
status: error.response?.status,
data: error.response?.data,
},
);
} else if (error.request) {
logger.error(
`${message} The request was made but no response was received: ${
error.message ? error.message : ''
}. Error Request:\n`,
{
request: error.request,
},
);
} else if (error?.message?.includes(timedOutMessage)) {
logger.error(
`${message}\nThe request either timed out or was unsuccessful. Error message:\n`,
error,
);
} else {
logger.error(
`${message}\nSomething happened in setting up the request. Error message:\n`,
error,
);
try {
if (error.response?.status) {
const { status, headers, data } = error.response;
logger.error(`${message} The server responded with status ${status}: ${error.message}`, {
status,
headers,
data,
});
} else if (error.request) {
const { method, url } = error.config || {};
logger.error(
`${message} No response received for ${method ? method.toUpperCase() : ''} ${url || ''}: ${error.message}`,
{ requestInfo: { method, url } },
);
} else if (error?.message?.includes('Cannot read properties of undefined (reading \'status\')')) {
logger.error(
`${message} It appears the request timed out or was unsuccessful: ${error.message}`,
);
} else {
logger.error(`${message} An error occurred while setting up the request: ${error.message}`);
}
} catch (err) {
logger.error(`Error in logAxiosError: ${err.message}`);
}
};

View file

@ -1,8 +1,13 @@
import { useRecoilState } from 'recoil';
import { Settings2 } from 'lucide-react';
import { Root, Anchor } from '@radix-ui/react-popover';
import { useState, useEffect, useMemo } from 'react';
import { tConvoUpdateSchema, EModelEndpoint, isParamEndpoint } from 'librechat-data-provider';
import { Root, Anchor } from '@radix-ui/react-popover';
import {
EModelEndpoint,
isParamEndpoint,
isAgentsEndpoint,
tConvoUpdateSchema,
} from 'librechat-data-provider';
import type { TPreset, TInterfaceConfig } from 'librechat-data-provider';
import { EndpointSettings, SaveAsPresetDialog, AlternativeSettings } from '~/components/Endpoints';
import { PluginStoreDialog, TooltipAnchor } from '~/components';
@ -42,7 +47,6 @@ export default function HeaderOptions({
if (endpoint && noSettings[endpoint]) {
setShowPopover(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [endpoint, noSettings]);
const saveAsPreset = () => {
@ -67,7 +71,7 @@ export default function HeaderOptions({
<div className="my-auto lg:max-w-2xl xl:max-w-3xl">
<span className="flex w-full flex-col items-center justify-center gap-0 md:order-none md:m-auto md:gap-2">
<div className="z-[61] flex w-full items-center justify-center gap-2">
{interfaceConfig?.modelSelect === true && (
{interfaceConfig?.modelSelect === true && !isAgentsEndpoint(endpoint) && (
<ModelSelect
conversation={conversation}
setOption={setOption}

View file

@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { QueryKeys, EModelEndpoint, LocalStorageKeys, Constants } from 'librechat-data-provider';
import type { TConversation, TEndpointsConfig, TModelsConfig } from 'librechat-data-provider';
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField, logger } from '~/utils';
import store from '~/store';
const useNavigateToConvo = (index = 0) => {
@ -20,7 +20,7 @@ const useNavigateToConvo = (index = 0) => {
invalidateMessages = false,
) => {
if (!conversation) {
console.log('Conversation not provided');
logger.warn('conversation', 'Conversation not provided to `navigateToConvo`');
return;
}
hasSetConversation.current = true;
@ -34,10 +34,10 @@ const useNavigateToConvo = (index = 0) => {
}
let convo = { ...conversation };
if (!convo.endpoint) {
/* undefined endpoint edge case */
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
if (!convo.endpoint || !endpointsConfig?.[convo.endpoint]) {
/* undefined/removed endpoint edge case */
const modelsConfig = queryClient.getQueryData<TModelsConfig>([QueryKeys.models]);
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
const defaultEndpoint = getDefaultEndpoint({
convoSetup: conversation,
endpointsConfig,
@ -51,10 +51,10 @@ const useNavigateToConvo = (index = 0) => {
const models = modelsConfig?.[defaultEndpoint ?? ''] ?? [];
convo = buildDefaultConvo({
models,
conversation,
endpoint: defaultEndpoint,
lastConversationSetup: conversation,
models,
});
}
clearAllConversations(true);
@ -68,7 +68,7 @@ const useNavigateToConvo = (index = 0) => {
invalidateMessages?: boolean,
) => {
if (!conversation) {
console.log('Conversation not provided');
logger.warn('conversation', 'Conversation not provided to `navigateToConvo`');
return;
}
// set conversation to the new conversation
@ -78,7 +78,7 @@ const useNavigateToConvo = (index = 0) => {
lastSelectedTools =
JSON.parse(localStorage.getItem(LocalStorageKeys.LAST_TOOLS) ?? '') ?? [];
} catch (e) {
// console.error(e);
logger.error('conversation', 'Error parsing last selected tools', e);
}
const hasTools = (conversation.tools?.length ?? 0) > 0;
navigateToConvo(

View file

@ -8,14 +8,14 @@ import type { TConversation } from 'librechat-data-provider';
import { getLocalStorageItems } from './localStorage';
const buildDefaultConvo = ({
models,
conversation,
endpoint = null,
models,
lastConversationSetup,
}: {
conversation: TConversation;
endpoint: EModelEndpoint | null;
models: string[];
conversation: TConversation;
endpoint?: EModelEndpoint | null;
lastConversationSetup: TConversation | null;
}): TConversation => {
const { lastSelectedModel, lastSelectedTools } = getLocalStorageItems();
@ -33,7 +33,7 @@ const buildDefaultConvo = ({
const model = lastConversationSetup?.model ?? lastSelectedModel?.[endpoint] ?? '';
const secondaryModel: string | null =
endpoint === EModelEndpoint.gptPlugins
? lastConversationSetup?.agentOptions?.model ?? lastSelectedModel?.secondaryModel ?? null
? (lastConversationSetup?.agentOptions?.model ?? lastSelectedModel?.secondaryModel ?? null)
: null;
let possibleModels: string[], secondaryModels: string[];

View file

@ -4,12 +4,17 @@ const loggerFilter = import.meta.env.VITE_LOGGER_FILTER || '';
type LogFunction = (...args: unknown[]) => void;
const createLogFunction = (consoleMethod: LogFunction): LogFunction => {
const createLogFunction = (
consoleMethod: LogFunction,
type?: 'log' | 'warn' | 'error' | 'info' | 'debug' | 'dir',
): LogFunction => {
return (...args: unknown[]) => {
if (isDevelopment || isLoggerEnabled) {
const tag = typeof args[0] === 'string' ? args[0] : '';
if (shouldLog(tag)) {
if (tag && args.length > 1) {
if (tag && typeof args[1] === 'string' && type === 'error') {
consoleMethod(`[${tag}] ${args[1]}`, ...args.slice(2));
} else if (tag && args.length > 1) {
consoleMethod(`[${tag}]`, ...args.slice(1));
} else {
consoleMethod(...args);
@ -20,12 +25,12 @@ const createLogFunction = (consoleMethod: LogFunction): LogFunction => {
};
const logger = {
log: createLogFunction(console.log),
warn: createLogFunction(console.warn),
error: createLogFunction(console.error),
info: createLogFunction(console.info),
debug: createLogFunction(console.debug),
dir: createLogFunction(console.dir),
log: createLogFunction(console.log, 'log'),
dir: createLogFunction(console.dir, 'dir'),
warn: createLogFunction(console.warn, 'warn'),
info: createLogFunction(console.info, 'info'),
error: createLogFunction(console.error, 'error'),
debug: createLogFunction(console.debug, 'debug'),
};
function shouldLog(tag: string): boolean {