mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
(UI) - View Logs Page - Refinement (#8087)
* working refetch interval * ui show provider logo in SpendLogs Table * fix padding * improve time range filter * ui fix diff minutes * fix refresh button placement
This commit is contained in:
parent
46b44f3a7f
commit
892581ffc3
4 changed files with 134 additions and 59 deletions
|
|
@ -5,15 +5,9 @@ model_list:
|
|||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
|
||||
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["prometheus"]
|
||||
prometheus_initialize_budget_metrics: true
|
||||
guardrails:
|
||||
- guardrail_name: "bedrock-pre-guard"
|
||||
litellm_params:
|
||||
guardrail: bedrock # supported values: "aporia", "bedrock", "lakera"
|
||||
mode: "during_call"
|
||||
guardrailIdentifier: gf3sc1mzinjw
|
||||
guardrailVersion: "DRAFT"
|
||||
callbacks: ["datadog"]
|
||||
|
||||
general_settings:
|
||||
store_prompts_in_spend_logs: true
|
||||
|
||||
|
|
|
|||
|
|
@ -1740,6 +1740,8 @@ async def ui_view_spend_logs( # noqa: PLR0915
|
|||
# Calculate total pages
|
||||
total_pages = (total_records + page_size - 1) // page_size
|
||||
|
||||
verbose_proxy_logger.debug("data= %s", json.dumps(data, indent=4, default=str))
|
||||
|
||||
return {
|
||||
"data": data,
|
||||
"total": total_records,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { getCountryFromIP } from "./ip_lookup";
|
|||
import moment from "moment";
|
||||
import React from "react";
|
||||
import { CountryCell } from "./country_cell";
|
||||
import { getProviderLogoAndName } from "../provider_info_helpers";
|
||||
|
||||
export type LogEntry = {
|
||||
request_id: string;
|
||||
|
|
@ -18,6 +19,7 @@ export type LogEntry = {
|
|||
startTime: string;
|
||||
endTime: string;
|
||||
user?: string;
|
||||
custom_llm_provider?: string;
|
||||
metadata?: Record<string, any>;
|
||||
cache_hit: string;
|
||||
cache_key?: string;
|
||||
|
|
@ -106,7 +108,26 @@ export const columns: ColumnDef<LogEntry>[] = [
|
|||
{
|
||||
header: "Model",
|
||||
accessorKey: "model",
|
||||
cell: (info: any) => <span>{String(info.getValue() || "")}</span>,
|
||||
cell: (info: any) => {
|
||||
const row = info.row.original;
|
||||
const provider = row.custom_llm_provider;
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
{provider && (
|
||||
<img
|
||||
src={getProviderLogoAndName(provider).logo}
|
||||
alt=""
|
||||
className="w-4 h-4"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{String(info.getValue() || "")}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Tokens",
|
||||
|
|
|
|||
|
|
@ -106,13 +106,15 @@ export default function SpendLogsTable({
|
|||
}
|
||||
|
||||
const formattedStartTime = moment(startTime).format("YYYY-MM-DD HH:mm:ss");
|
||||
const formattedEndTime = moment(endTime).format("YYYY-MM-DD HH:mm:ss");
|
||||
const formattedEndTime = isCustomDate
|
||||
? moment(endTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
: moment().format("YYYY-MM-DD HH:mm:ss");
|
||||
|
||||
return await uiSpendLogsCall(
|
||||
accessToken,
|
||||
selectedKeyHash || undefined,
|
||||
selectedTeamId || undefined,
|
||||
undefined, // This parameter might be setting a default min_spend
|
||||
undefined,
|
||||
formattedStartTime,
|
||||
formattedEndTime,
|
||||
currentPage,
|
||||
|
|
@ -120,6 +122,8 @@ export default function SpendLogsTable({
|
|||
);
|
||||
},
|
||||
enabled: !!accessToken && !!token && !!userRole && !!userID,
|
||||
refetchInterval: 5000,
|
||||
refetchIntervalInBackground: true,
|
||||
});
|
||||
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
|
|
@ -141,10 +145,37 @@ export default function SpendLogsTable({
|
|||
return matchesSearch;
|
||||
}) || [];
|
||||
|
||||
// Add this function to handle manual refresh
|
||||
const handleRefresh = () => {
|
||||
logs.refetch();
|
||||
};
|
||||
|
||||
// Add this function to format the time range display
|
||||
const getTimeRangeDisplay = () => {
|
||||
if (isCustomDate) {
|
||||
return `${moment(startTime).format('MMM D, h:mm A')} - ${moment(endTime).format('MMM D, h:mm A')}`;
|
||||
}
|
||||
|
||||
const now = moment();
|
||||
const start = moment(startTime);
|
||||
const diffMinutes = now.diff(start, 'minutes');
|
||||
|
||||
if (diffMinutes <= 15) return 'Last 15 Minutes';
|
||||
if (diffMinutes <= 60) return 'Last Hour';
|
||||
|
||||
const diffHours = now.diff(start, 'hours');
|
||||
if (diffHours <= 4) return 'Last 4 Hours';
|
||||
if (diffHours <= 24) return 'Last 24 Hours';
|
||||
if (diffHours <= 168) return 'Last 7 Days';
|
||||
return `${start.format('MMM D')} - ${now.format('MMM D')}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<h1 className="text-xl font-semibold mb-4">Traces</h1>
|
||||
<div className="w-full p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h1 className="text-xl font-semibold">Request Logs</h1>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div className="border-b px-6 py-4">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between space-y-4 md:space-y-0">
|
||||
|
|
@ -310,13 +341,78 @@ export default function SpendLogsTable({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative" ref={quickSelectRef}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative" ref={quickSelectRef}>
|
||||
<button
|
||||
onClick={() => setQuickSelectOpen(!quickSelectOpen)}
|
||||
className="px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
{getTimeRangeDisplay()}
|
||||
</button>
|
||||
|
||||
{quickSelectOpen && (
|
||||
<div className="absolute right-0 mt-2 w-64 bg-white rounded-lg shadow-lg border p-2 z-50">
|
||||
<div className="space-y-1">
|
||||
{[
|
||||
{ label: "Last 15 Minutes", value: 15, unit: "minutes" },
|
||||
{ label: "Last Hour", value: 1, unit: "hours" },
|
||||
{ label: "Last 4 Hours", value: 4, unit: "hours" },
|
||||
{ label: "Last 24 Hours", value: 24, unit: "hours" },
|
||||
{ label: "Last 7 Days", value: 7, unit: "days" },
|
||||
].map((option) => (
|
||||
<button
|
||||
key={option.label}
|
||||
className={`w-full px-3 py-2 text-left text-sm hover:bg-gray-50 rounded-md ${
|
||||
getTimeRangeDisplay() === option.label ? 'bg-blue-50 text-blue-600' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
setEndTime(moment().format("YYYY-MM-DDTHH:mm"));
|
||||
setStartTime(
|
||||
moment()
|
||||
.subtract(option.value, option.unit as any)
|
||||
.format("YYYY-MM-DDTHH:mm")
|
||||
);
|
||||
setQuickSelectOpen(false);
|
||||
setIsCustomDate(false);
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
<div className="border-t my-2" />
|
||||
<button
|
||||
className={`w-full px-3 py-2 text-left text-sm hover:bg-gray-50 rounded-md ${
|
||||
isCustomDate ? 'bg-blue-50 text-blue-600' : ''
|
||||
}`}
|
||||
onClick={() => setIsCustomDate(!isCustomDate)}
|
||||
>
|
||||
Custom Range
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setQuickSelectOpen(!quickSelectOpen)}
|
||||
onClick={handleRefresh}
|
||||
className="px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2"
|
||||
title="Refresh data"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
className={`w-4 h-4 ${logs.isFetching ? 'animate-spin' : ''}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
@ -325,49 +421,11 @@ export default function SpendLogsTable({
|
|||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
||||
/>
|
||||
</svg>
|
||||
Time Range
|
||||
<span>Refresh</span>
|
||||
</button>
|
||||
|
||||
{quickSelectOpen && (
|
||||
<div className="absolute left-0 mt-2 w-64 bg-white rounded-lg shadow-lg border p-2 z-50">
|
||||
<div className="space-y-1">
|
||||
{[
|
||||
{ label: "Last 15 Minutes", value: 15, unit: "minutes" },
|
||||
{ label: "Last Hour", value: 1, unit: "hours" },
|
||||
{ label: "Last 4 Hours", value: 4, unit: "hours" },
|
||||
{ label: "Last 24 Hours", value: 24, unit: "hours" },
|
||||
{ label: "Last 7 Days", value: 7, unit: "days" },
|
||||
].map((option) => (
|
||||
<button
|
||||
key={option.label}
|
||||
className="w-full px-3 py-2 text-left text-sm hover:bg-gray-50 rounded-md"
|
||||
onClick={() => {
|
||||
setEndTime(moment().format("YYYY-MM-DDTHH:mm"));
|
||||
setStartTime(
|
||||
moment()
|
||||
.subtract(option.value, option.unit as any)
|
||||
.format("YYYY-MM-DDTHH:mm")
|
||||
);
|
||||
setQuickSelectOpen(false);
|
||||
setIsCustomDate(false);
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
<div className="border-t my-2" />
|
||||
<button
|
||||
className="w-full px-3 py-2 text-left text-sm hover:bg-gray-50 rounded-md"
|
||||
onClick={() => setIsCustomDate(!isCustomDate)}
|
||||
>
|
||||
Custom Range
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isCustomDate && (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue