diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index ed9b878854a..218f1caa9ae 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -1,12 +1,11 @@ import moment from "moment"; -import { useQuery, useQueries, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import { useState, useRef, useEffect } from "react"; -import { uiSpendLogsCall, uiSpendLogDetailsCall } from "../networking"; +import { uiSpendLogsCall } from "../networking"; import { DataTable } from "./table"; import { columns, LogEntry } from "./columns"; -import { RequestViewer } from "./request_viewer"; -import { prefetchLogDetails } from "./prefetch"; +import { Row } from "@tanstack/react-table"; interface SpendLogsTableProps { accessToken: string | null; @@ -55,8 +54,6 @@ export default function SpendLogsTable({ const [selectedKeyHash, setSelectedKeyHash] = useState(""); const [selectedFilter, setSelectedFilter] = useState("Team ID"); - const queryClient = useQueryClient(); - // Close dropdown when clicking outside useEffect(() => { function handleClickOutside(event: MouseEvent) { @@ -85,7 +82,6 @@ export default function SpendLogsTable({ document.removeEventListener("mousedown", handleClickOutside); }, []); - // Update the logs query to use the imported prefetchLogDetails const logs = useQuery({ queryKey: [ "logs", @@ -109,21 +105,13 @@ export default function SpendLogsTable({ }; } - console.log("Fetching logs with params:", { - startTime, - endTime, - selectedTeamId, - selectedKeyHash, - currentPage, - pageSize - }); - + // Convert times to UTC before formatting const formattedStartTime = moment(startTime).utc().format("YYYY-MM-DD HH:mm:ss"); const formattedEndTime = isCustomDate ? moment(endTime).utc().format("YYYY-MM-DD HH:mm:ss") : moment().utc().format("YYYY-MM-DD HH:mm:ss"); - const response = await uiSpendLogsCall( + return await uiSpendLogsCall( accessToken, selectedKeyHash || undefined, selectedTeamId || undefined, @@ -133,62 +121,30 @@ export default function SpendLogsTable({ currentPage, pageSize ); - - console.log("Received logs response:", response); - - // Update prefetchLogDetails call with new parameters - prefetchLogDetails(response.data, formattedStartTime, accessToken, queryClient); - - return response; }, enabled: !!accessToken && !!token && !!userRole && !!userID, refetchInterval: 5000, refetchIntervalInBackground: true, }); - // Move useQueries before the early return - const logDetailsQueries = useQueries({ - queries: logs.data?.data?.map((log) => ({ - queryKey: ["logDetails", log.request_id, moment(startTime).utc().format("YYYY-MM-DD HH:mm:ss")], - queryFn: () => uiSpendLogDetailsCall(accessToken!, log.request_id, moment(startTime).utc().format("YYYY-MM-DD HH:mm:ss")), - staleTime: 10 * 60 * 1000, - cacheTime: 10 * 60 * 1000, - enabled: !!log.request_id, - })) || [] - }); - if (!accessToken || !token || !userRole || !userID) { - console.log("got None values for one of accessToken, token, userRole, userID"); + console.log( + "got None values for one of accessToken, token, userRole, userID", + ); return null; } - // Consolidate log details from queries - const logDetails: Record = {}; - logDetailsQueries.forEach((q, index) => { - const log = logs.data?.data[index]; - if (log && q.data) { - logDetails[log.request_id] = q.data; - } - }); - - // Modify the filtered data to include log details const filteredData = - logs.data?.data - ?.filter((log) => { - const matchesSearch = - !searchTerm || - log.request_id.includes(searchTerm) || - log.model.includes(searchTerm) || - (log.user && log.user.includes(searchTerm)); - - return matchesSearch; - }) - .map(log => ({ - ...log, - // Include messages/response from cached details - messages: logDetails[log.request_id]?.messages || [], - response: logDetails[log.request_id]?.response || {}, - })) || []; + logs.data?.data?.filter((log) => { + const matchesSearch = + !searchTerm || + log.request_id.includes(searchTerm) || + log.model.includes(searchTerm) || + (log.user && log.user.includes(searchTerm)); + + // No need for additional filtering since we're now handling this in the API call + return matchesSearch; + }) || []; // Add this function to handle manual refresh const handleRefresh = () => { @@ -573,3 +529,155 @@ export default function SpendLogsTable({ ); } + +function RequestViewer({ row }: { row: Row }) { + const formatData = (input: any) => { + if (typeof input === "string") { + try { + return JSON.parse(input); + } catch { + return input; + } + } + return input; + }; + + return ( +
+ {/* Combined Info Card */} +
+
+

Request Details

+
+
+
+ Request ID: + {row.original.request_id} +
+
+ Api Key: + {row.original.api_key} +
+
+ Team ID: + {row.original.team_id} +
+
+ Model: + {row.original.model} +
+
+ 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 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)}
+            
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/ui/litellm-dashboard/src/components/view_logs/request_viewer.tsx b/ui/litellm-dashboard/src/components/view_logs/request_viewer.tsx deleted file mode 100644 index 63af1843005..00000000000 --- a/ui/litellm-dashboard/src/components/view_logs/request_viewer.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { Row } from "@tanstack/react-table"; -import { LogEntry } from "./columns"; - -export function RequestViewer({ row }: { row: Row }) { - const formatData = (input: any) => { - if (typeof input === "string") { - try { - return JSON.parse(input); - } catch { - return input; - } - } - return input; - }; - - return ( -
- {/* Combined Info Card */} -
-
-

Request Details

-
-
-
- Request ID: - {row.original.request_id} -
-
- Api Key: - {row.original.api_key} -
-
- Team ID: - {row.original.team_id} -
-
- Model: - {row.original.model} -
-
- 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 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)}
-              
-
- )} -
- ); - } - \ No newline at end of file