@@ -64,6 +71,45 @@ export function DrawerHeader({
);
}
+/**
+ * Model and Provider display with logo
+ */
+function ModelProviderSection({
+ model,
+ providerLogo,
+ providerName,
+}: {
+ model: string;
+ providerLogo?: string;
+ providerName?: string;
+}) {
+ return (
+
+ {providerLogo && (
+

{
+ const target = e.target as HTMLImageElement;
+ target.style.display = "none";
+ }}
+ />
+ )}
+
+
+ {model}
+
+ {providerName && (
+
+ {providerName}
+
+ )}
+
+
+ );
+}
+
/**
* Request ID display with copy button
*/
@@ -93,6 +139,7 @@ function RequestIdSection({ requestId, onCopy }: { requestId: string; onCopy: ()
/**
* Navigation controls (previous, next, close)
+ * Shows keyboard shortcuts with bounding boxes for visibility
*/
function NavigationSection({
onPrevious,
@@ -103,18 +150,32 @@ function NavigationSection({
onNext: () => void;
onClose: () => void;
}) {
+ const keyboardShortcutStyle = {
+ border: "1px solid #d9d9d9",
+ borderRadius: 4,
+ padding: "0 4px",
+ fontSize: 12,
+ fontFamily: "monospace",
+ marginLeft: 4,
+ background: "#fafafa",
+ };
+
return (
-
- } onClick={onPrevious} />
-
-
- } onClick={onNext} />
-
+
+
-
} onClick={onClose} />
+
+ } onClick={onClose} />
+
);
}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/JsonViewer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/JsonViewer.tsx
index 1f1d935973..6463abf89d 100644
--- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/JsonViewer.tsx
+++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/JsonViewer.tsx
@@ -1,53 +1,22 @@
import { Typography } from "antd";
import { JsonView, defaultStyles } from "react-json-view-lite";
import "react-json-view-lite/dist/index.css";
-import {
- JSON_MAX_HEIGHT,
- FONT_SIZE_SMALL,
- COLOR_BG_LIGHT,
- SPACING_LARGE,
- FONT_FAMILY_MONO,
- VIEW_MODE_JSON,
-} from "./constants";
+import { JSON_MAX_HEIGHT, COLOR_BG_LIGHT, SPACING_LARGE } from "./constants";
const { Text } = Typography;
-export type ViewMode = "formatted" | "json";
-
interface JsonViewerProps {
data: any;
- mode: ViewMode;
+ mode: "formatted";
}
/**
- * Displays JSON data in either formatted tree view or raw JSON format.
- * Formatted view uses an interactive tree, JSON view shows raw stringified output.
+ * Displays JSON data in formatted tree view.
+ * Uses an interactive tree component for easy navigation.
*/
-export function JsonViewer({ data, mode }: JsonViewerProps) {
+export function JsonViewer({ data }: JsonViewerProps) {
if (!data) return
No data;
- if (mode === VIEW_MODE_JSON) {
- return (
-
- {JSON.stringify(data, null, 2)}
-
- );
- }
-
- // Formatted tree view
return (
(TAB_REQUEST);
- const [jsonViewMode, setJsonViewMode] = useState
(VIEW_MODE_FORMATTED);
// Keyboard navigation
const { selectNextLog, selectPreviousLog } = useKeyboardNavigation({
@@ -162,8 +160,9 @@ export function LogDetailsDrawer({
)}
{/* Request Details Section */}
-
-
+
+
+
{logEntry.model}
{logEntry.custom_llm_provider || "-"}
{logEntry.call_type}
@@ -183,6 +182,7 @@ export function LogDetailsDrawer({
)}
+
{/* Metrics Section */}
@@ -193,31 +193,19 @@ export function LogDetailsDrawer({
{/* Configuration Info Message - Show when data is missing */}
- {/* Request/Response JSON - Using Tabs with View Toggle */}
+ {/* Request/Response JSON - Collapsible */}
copyToClipboard(JSON.stringify(data, null, 2), label)}
getRawRequest={getRawRequest}
getFormattedResponse={getFormattedResponse}
/>
{/* Guardrail Data - Show only if present */}
- {hasGuardrailData && (
-
-
-
- )}
+ {hasGuardrailData && }
{/* Vector Store Request Data - Show only if present */}
- {hasVectorStoreData && (
-
-
-
- )}
+ {hasVectorStoreData && }
{/* Metadata Card - Only show if there's metadata */}
{logEntry.metadata && Object.keys(logEntry.metadata).length > 0 && (
@@ -251,8 +239,8 @@ function ErrorDescription({ errorInfo }: { errorInfo: any }) {
function TagsSection({ tags }: { tags: Record }) {
return (
-
-
+
+
Tags
@@ -286,8 +274,9 @@ function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata:
metadata.additional_usage_values.cache_read_input_tokens > 0);
return (
-
-
+
+
+
)}
- {metadata?.litellm_overhead_time_ms !== undefined && (
+ {metadata?.litellm_overhead_time_ms !== undefined && metadata.litellm_overhead_time_ms !== null && (
{metadata.litellm_overhead_time_ms.toFixed(2)} ms
@@ -331,30 +320,26 @@ function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata:
+
);
}
interface RequestResponseSectionProps {
- activeTab: typeof TAB_REQUEST | typeof TAB_RESPONSE;
- jsonViewMode: ViewMode;
hasResponse: boolean;
- onTabChange: (key: typeof TAB_REQUEST | typeof TAB_RESPONSE) => void;
- onViewModeChange: (mode: ViewMode) => void;
onCopy: (data: any, label: string) => void;
getRawRequest: () => any;
getFormattedResponse: () => any;
}
function RequestResponseSection({
- activeTab,
- jsonViewMode,
hasResponse,
- onTabChange,
- onViewModeChange,
onCopy,
getRawRequest,
getFormattedResponse,
}: RequestResponseSectionProps) {
+ const [activeTab, setActiveTab] = useState(TAB_REQUEST);
+ const [isOpen, setIsOpen] = useState(true);
+
const handleCopy = () => {
const data = activeTab === TAB_REQUEST ? getRawRequest() : getFormattedResponse();
const label = activeTab === TAB_REQUEST ? "Request" : "Response";
@@ -362,98 +347,109 @@ function RequestResponseSection({
};
return (
-
- onTabChange(key as typeof TAB_REQUEST | typeof TAB_RESPONSE)}
- tabBarExtraContent={
-
- {/* View Mode Toggle */}
- onViewModeChange(e.target.value)}>
- Formatted
- JSON
-
-
- {/* Copy Button */}
- }
- onClick={handleCopy}
- disabled={activeTab === TAB_RESPONSE && !hasResponse}
- >
- Copy
-
-
- }
+
+
setIsOpen(keys.includes("request-response"))}
+ expandIcon={({ isActive }) => }
+ bordered={false}
items={[
{
- key: TAB_REQUEST,
- label: "Request",
+ key: "request-response",
+ label: Request & Response,
children: (
-
-
-
- ),
- },
- {
- key: TAB_RESPONSE,
- label: "Response",
- children: (
-
- {hasResponse ? (
-
- ) : (
-
- Response data not available
-
- )}
-
+ setActiveTab(key as typeof TAB_REQUEST | typeof TAB_RESPONSE)}
+ tabBarExtraContent={
+ }
+ onClick={handleCopy}
+ disabled={activeTab === TAB_RESPONSE && !hasResponse}
+ >
+ Copy
+
+ }
+ items={[
+ {
+ key: TAB_REQUEST,
+ label: "Request",
+ children: (
+
+
+
+ ),
+ },
+ {
+ key: TAB_RESPONSE,
+ label: "Response",
+ children: (
+
+ {hasResponse ? (
+
+ ) : (
+
+ Response data not available
+
+ )}
+
+ ),
+ },
+ ]}
+ />
),
},
]}
- style={{ padding: `0 ${SPACING_XLARGE}px` }}
+ styles={{
+ header: {
+ padding: "16px",
+ borderBottom: "1px solid #f0f0f0",
+ },
+ body: {
+ padding: 0,
+ },
+ }}
/>
-
+
);
}
function MetadataSection({ metadata, onCopy }: { metadata: Record; onCopy: (data: string) => void }) {
return (
- }
- onClick={() => onCopy(JSON.stringify(metadata, null, 2))}
- >
- Copy
-
- }
- >
-
+ }
+ onClick={() => onCopy(JSON.stringify(metadata, null, 2))}
+ >
+ Copy
+
+ }
>
- {JSON.stringify(metadata, null, 2)}
-
-
+
+ {JSON.stringify(metadata, null, 2)}
+
+
+
);
}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/TokenFlow.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/TokenFlow.tsx
index a9a30e55f1..5eec3c0a5c 100644
--- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/TokenFlow.tsx
+++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/TokenFlow.tsx
@@ -1,5 +1,4 @@
import { Typography } from "antd";
-import { COLOR_SECONDARY, FONT_FAMILY_MONO, FONT_SIZE_MEDIUM, SPACING_SMALL, SPACING_MEDIUM } from "./constants";
const { Text } = Typography;
@@ -10,18 +9,14 @@ interface TokenFlowProps {
}
/**
- * Displays token usage in a flow format: "prompt → completion (Σ total)"
- * Makes it easy to see the relationship between input, output, and total tokens.
+ * Displays token usage in LiteLLM format: "12 (9 prompt tokens + 3 completion tokens)"
+ * Shows total with breakdown of prompt and completion tokens.
*/
export function TokenFlow({ prompt = 0, completion = 0, total = 0 }: TokenFlowProps) {
return (
-
- {prompt.toLocaleString()}
- →
- {completion.toLocaleString()}
-
- (Σ {total.toLocaleString()})
-
+
+ {total.toLocaleString()} ({prompt.toLocaleString()} prompt tokens + {completion.toLocaleString()} completion
+ tokens)
);
}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts
index e1222ce00a..91f5ff8f11 100644
--- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts
+++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts
@@ -9,14 +9,10 @@ export const API_BASE_MAX_WIDTH = 200;
export const JSON_MAX_HEIGHT = 400;
export const METADATA_MAX_HEIGHT = 300;
-// Tab keys
+// Tab keys (kept for backwards compatibility if needed)
export const TAB_REQUEST = "request" as const;
export const TAB_RESPONSE = "response" as const;
-// View modes
-export const VIEW_MODE_FORMATTED = "formatted" as const;
-export const VIEW_MODE_JSON = "json" as const;
-
// Keyboard shortcuts
export const KEY_ESCAPE = "Escape";
export const KEY_J_LOWER = "j";