mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
rollback changes to view logs page
This commit is contained in:
parent
aee90f1dfe
commit
4fde1afd75
2 changed files with 170 additions and 217 deletions
|
|
@ -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<PaginatedResponse>({
|
||||
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<string, any> = {};
|
||||
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({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RequestViewer({ row }: { row: Row<LogEntry> }) {
|
||||
const formatData = (input: any) => {
|
||||
if (typeof input === "string") {
|
||||
try {
|
||||
return JSON.parse(input);
|
||||
} catch {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-gray-50 space-y-6">
|
||||
{/* Combined Info Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="p-4 border-b">
|
||||
<h3 className="text-lg font-medium ">Request Details</h3>
|
||||
</div>
|
||||
<div className="space-y-2 p-4 ">
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Request ID:</span>
|
||||
<span>{row.original.request_id}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Api Key:</span>
|
||||
<span>{row.original.api_key}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Team ID:</span>
|
||||
<span>{row.original.team_id}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Model:</span>
|
||||
<span>{row.original.model}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Api Base:</span>
|
||||
<span>{row.original.api_base}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Call Type:</span>
|
||||
<span>{row.original.call_type}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Spend:</span>
|
||||
<span>{row.original.spend}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Total Tokens:</span>
|
||||
<span>{row.original.total_tokens}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Prompt Tokens:</span>
|
||||
<span>{row.original.prompt_tokens}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Completion Tokens:</span>
|
||||
<span>{row.original.completion_tokens}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Start Time:</span>
|
||||
<span>{row.original.startTime}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">End Time:</span>
|
||||
<span>{row.original.endTime}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Cache Hit:</span>
|
||||
<span>{row.original.cache_hit}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Cache Key:</span>
|
||||
<span>{row.original.cache_key}</span>
|
||||
</div>
|
||||
{row?.original?.requester_ip_address && (
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Request IP Address:</span>
|
||||
<span>{row?.original?.requester_ip_address}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Request Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Request Tags</h3>
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm">
|
||||
{JSON.stringify(formatData(row.original.request_tags), null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Request Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Request</h3>
|
||||
{/* <div>
|
||||
<button className="mr-2 px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
Expand
|
||||
</button>
|
||||
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
JSON
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm">
|
||||
{JSON.stringify(formatData(row.original.messages), null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Response Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Response</h3>
|
||||
<div>
|
||||
{/* <button className="mr-2 px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
Expand
|
||||
</button>
|
||||
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
JSON
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm">
|
||||
{JSON.stringify(formatData(row.original.response), null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Metadata Card */}
|
||||
{row.original.metadata &&
|
||||
Object.keys(row.original.metadata).length > 0 && (
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Metadata</h3>
|
||||
{/* <div>
|
||||
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
JSON
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm ">
|
||||
{JSON.stringify(row.original.metadata, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
import { Row } from "@tanstack/react-table";
|
||||
import { LogEntry } from "./columns";
|
||||
|
||||
export function RequestViewer({ row }: { row: Row<LogEntry> }) {
|
||||
const formatData = (input: any) => {
|
||||
if (typeof input === "string") {
|
||||
try {
|
||||
return JSON.parse(input);
|
||||
} catch {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-gray-50 space-y-6">
|
||||
{/* Combined Info Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="p-4 border-b">
|
||||
<h3 className="text-lg font-medium ">Request Details</h3>
|
||||
</div>
|
||||
<div className="space-y-2 p-4 ">
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Request ID:</span>
|
||||
<span>{row.original.request_id}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Api Key:</span>
|
||||
<span>{row.original.api_key}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Team ID:</span>
|
||||
<span>{row.original.team_id}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Model:</span>
|
||||
<span>{row.original.model}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Api Base:</span>
|
||||
<span>{row.original.api_base}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Call Type:</span>
|
||||
<span>{row.original.call_type}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Spend:</span>
|
||||
<span>{row.original.spend}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Total Tokens:</span>
|
||||
<span>{row.original.total_tokens}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Prompt Tokens:</span>
|
||||
<span>{row.original.prompt_tokens}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Completion Tokens:</span>
|
||||
<span>{row.original.completion_tokens}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Start Time:</span>
|
||||
<span>{row.original.startTime}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">End Time:</span>
|
||||
<span>{row.original.endTime}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Cache Hit:</span>
|
||||
<span>{row.original.cache_hit}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Cache Key:</span>
|
||||
<span>{row.original.cache_key}</span>
|
||||
</div>
|
||||
{row?.original?.requester_ip_address && (
|
||||
<div className="flex">
|
||||
<span className="font-medium w-1/3">Request IP Address:</span>
|
||||
<span>{row?.original?.requester_ip_address}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Request Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Request Tags</h3>
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm">
|
||||
{JSON.stringify(formatData(row.original.request_tags), null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Request Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Request</h3>
|
||||
{/* <div>
|
||||
<button className="mr-2 px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
Expand
|
||||
</button>
|
||||
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
JSON
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm">
|
||||
{JSON.stringify(formatData(row.original.messages), null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Response Card */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Response</h3>
|
||||
<div>
|
||||
{/* <button className="mr-2 px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
Expand
|
||||
</button>
|
||||
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
JSON
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm">
|
||||
{JSON.stringify(formatData(row.original.response), null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Metadata Card */}
|
||||
{row.original.metadata &&
|
||||
Object.keys(row.original.metadata).length > 0 && (
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h3 className="text-lg font-medium">Metadata</h3>
|
||||
{/* <div>
|
||||
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
|
||||
JSON
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
<pre className="p-4 text-wrap overflow-auto text-sm ">
|
||||
{JSON.stringify(row.original.metadata, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue