diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index a9d9f65ac22..b01239278f8 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -221,3 +221,51 @@ const formatMessage = (message: any): string => { } return String(message); }; + +// Add this new component for displaying request/response with copy buttons +export const RequestResponsePanel = ({ request, response }: { request: any; response: any }) => { + const requestStr = typeof request === 'object' ? JSON.stringify(request, null, 2) : String(request || '{}'); + const responseStr = typeof response === 'object' ? JSON.stringify(response, null, 2) : String(response || '{}'); + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + }; + + return ( +
+
+
+

Request

+ +
+
{requestStr}
+
+ +
+
+

Response

+ +
+
{responseStr}
+
+
+ ); +}; diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index 9c3c9690741..d8a0bc44223 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -8,6 +8,8 @@ import { DataTable } from "./table"; import { columns, LogEntry } from "./columns"; import { Row } from "@tanstack/react-table"; import { prefetchLogDetails } from "./prefetch"; +import { RequestResponsePanel } from "./columns"; + interface SpendLogsTableProps { accessToken: string | null; token: string | null; @@ -579,141 +581,109 @@ function RequestViewer({ row }: { row: Row }) { {/* Combined Info Card */}
-

Request Details

+

Request Details

-
-
- Request ID: - {row.original.request_id} -
-
- Api Key: - {row.original.api_key} -
-
- Team ID: - {row.original.team_id} -
-
- Model: - {row.original.model} -
-
- Custom LLM Provider: - {row.original.custom_llm_provider} -
-
- Api Base: - {row.original.api_base} -
-
- Call Type: - {row.original.call_type} -
-
- Spend: - {row.original.spend} -
-
- Total Tokens: - {row.original.total_tokens} -
-
- Prompt Tokens: - {row.original.prompt_tokens} -
-
- Completion Tokens: - {row.original.completion_tokens} -
-
- Start Time: - {row.original.startTime} -
-
- End Time: - {row.original.endTime} -
-
- Cache Hit: - {row.original.cache_hit} -
-
- Cache Key: - {row.original.cache_key} -
- {row?.original?.requester_ip_address && ( +
+
- Request IP Address: - {row?.original?.requester_ip_address} + Request ID: + {row.original.request_id} +
+
+ Model: + {row.original.model} +
+
+ Provider: + {row.original.custom_llm_provider || "-"} +
+
+ Start Time: + {row.original.startTime} +
+
+ End Time: + {row.original.endTime} +
+
+
+
+ Tokens: + {row.original.total_tokens} ({row.original.prompt_tokens}+{row.original.completion_tokens}) +
+
+ Cost: + ${Number(row.original.spend || 0).toFixed(6)} +
+
+ Cache Hit: + {row.original.cache_hit} +
+ {row?.original?.requester_ip_address && ( +
+ IP Address: + {row?.original?.requester_ip_address} +
+ )} +
+ Status: + + {(row.original.metadata?.status || "Success").toLowerCase() !== "failure" ? "Success" : "Failure"} +
- )} -
-
- - {/* Request Card */} -
-
-

Request Tags

-
-
-          {JSON.stringify(formatData(row.original.request_tags), null, 2)}
-        
-
- - {/* Request Card */} -
-
-

Request

- {/*
- - -
*/} -
-
-          {JSON.stringify(formatData(row.original.messages), null, 2)}
-        
-
- - {/* Response Card */} -
-
-

Response

-
- {/* - */}
-
-          {JSON.stringify(formatData(row.original.response), null, 2)}
-        
- {/* Metadata Card */} - {row.original.metadata && - Object.keys(row.original.metadata).length > 0 && ( -
-
-

Metadata

- {/*
- -
*/} -
-
-              {JSON.stringify(row.original.metadata, null, 2)}
-            
+ {/* Request/Response Panel */} + + + {/* Tags Card - Only show if there are tags */} + {row.original.request_tags && Object.keys(row.original.request_tags).length > 0 && ( +
+
+

Request Tags

- )} +
+
+ {Object.entries(row.original.request_tags).map(([key, value]) => ( + + {key}: {String(value)} + + ))} +
+
+
+ )} + + {/* Metadata Card - Only show if there's metadata */} + {row.original.metadata && Object.keys(row.original.metadata).length > 0 && ( +
+
+

Metadata

+ +
+
+            {JSON.stringify(row.original.metadata, null, 2)}
+          
+
+ )}
); } \ No newline at end of file