From 858b96bccc22c4c92b25f6e8553216dc9f279240 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 28 Feb 2025 11:32:05 -0800 Subject: [PATCH] show traceback on error viewer --- .../src/components/view_logs/ErrorViewer.tsx | 119 ++++++++++++++++++ .../src/components/view_logs/index.tsx | 6 + 2 files changed, 125 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/view_logs/ErrorViewer.tsx diff --git a/ui/litellm-dashboard/src/components/view_logs/ErrorViewer.tsx b/ui/litellm-dashboard/src/components/view_logs/ErrorViewer.tsx new file mode 100644 index 00000000000..0b0fcb6316e --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/ErrorViewer.tsx @@ -0,0 +1,119 @@ +import React from 'react'; + +interface ErrorViewerProps { + errorInfo: { + error_class?: string; + error_message?: string; + status_code?: string | number; + traceback?: string; + llm_provider?: string; + error_code?: string; + }; +} + +export const ErrorViewer: React.FC = ({ errorInfo }) => { + // Parse traceback into frames + const parseTraceback = (traceback: string) => { + if (!traceback) return []; + + // Extract file paths, line numbers and code from traceback + const fileLineRegex = /File "([^"]+)", line (\d+)/g; + const matches = [...traceback.matchAll(fileLineRegex)]; + + // Create simplified frames + return matches.map(match => { + const filePath = match[1]; + const lineNumber = match[2]; + const fileName = filePath.split('/').pop() || filePath; + + // Extract the context around this frame + const matchIndex = match.index || 0; + const nextMatchIndex = traceback.indexOf('File "', matchIndex + 1); + const frameContent = nextMatchIndex > -1 + ? traceback.substring(matchIndex, nextMatchIndex).trim() + : traceback.substring(matchIndex).trim(); + + // Try to extract the code line + const lines = frameContent.split('\n'); + let code = ''; + if (lines.length > 1) { + code = lines[lines.length - 1].trim(); + } + + return { + filePath, + fileName, + lineNumber, + code, + inFunction: frameContent.includes(' in ') + ? frameContent.split(' in ')[1].split('\n')[0] + : '' + }; + }); + }; + + const tracebackFrames = errorInfo.traceback ? parseTraceback(errorInfo.traceback) : []; + + return ( +
+
+

+ + + + Error Details +

+
+ +
+
+
+ Type: + {errorInfo.error_class || "Unknown Error"} +
+
+ Message: + {errorInfo.error_message || "Unknown error occurred"} +
+
+ + {errorInfo.traceback && ( +
+
+

Traceback

+ +
+ +
+ {tracebackFrames.map((frame, index) => ( +
+
+ {frame.lineNumber} + {frame.fileName} + in + {frame.inFunction || frame.fileName} +
+ {frame.code && ( +
+ {frame.code} +
+ )} +
+ ))} +
+
+ )} +
+
+ ); +}; \ No newline at end of file diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index e313c120f4f..4aad28633ab 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -9,6 +9,7 @@ import { columns, LogEntry } from "./columns"; import { Row } from "@tanstack/react-table"; import { prefetchLogDetails } from "./prefetch"; import { RequestResponsePanel } from "./columns"; +import { ErrorViewer } from './ErrorViewer'; interface SpendLogsTableProps { accessToken: string | null; @@ -708,6 +709,11 @@ function RequestViewer({ row }: { row: Row }) { + + {/* Error Card - Only show for failures */} + {hasError && errorInfo && } + + {/* Tags Card - Only show if there are tags */} {row.original.request_tags && Object.keys(row.original.request_tags).length > 0 && (