diff --git a/ui/litellm-dashboard/src/components/activity_metrics.test.tsx b/ui/litellm-dashboard/src/components/activity_metrics.test.tsx index 2b3e74d6fe6..1bd7655b5e3 100644 --- a/ui/litellm-dashboard/src/components/activity_metrics.test.tsx +++ b/ui/litellm-dashboard/src/components/activity_metrics.test.tsx @@ -1,7 +1,8 @@ import { fireEvent, render, screen } from "@testing-library/react"; import React from "react"; import { beforeAll, describe, expect, it, vi } from "vitest"; -import { ActivityMetrics, formatKeyLabel, processActivityData } from "./activity_metrics"; +import { ActivityMetrics, formatKeyLabel, processActivityData, ResponseTimeTooltip } from "./activity_metrics"; +import type { ChartTooltipProps } from "@/components/shared/charts"; import { Team } from "./key_team_helpers/key_list"; import { DailyData, KeyMetricWithMetadata, ModelActivityData } from "./UsagePage/types"; @@ -1541,6 +1542,17 @@ describe("ActivityMetrics response time", () => { expect(screen.getAllByText(/^\d+(\.\d+)?(ms|s)$/).length).toBeGreaterThan(1); }); + it("labels the chart tooltip with the readable series name and a formatted duration", () => { + const payload = [ + { dataKey: "metrics.avg_response_time_ms", value: 1500, color: "#f59e0b", payload: timedModel.daily_data[0] }, + ] as NonNullable; + render(); + + expect(screen.getByText("Avg Response Time Ms")).toBeInTheDocument(); + expect(screen.getByText("1.50s")).toBeInTheDocument(); + expect(screen.queryByText("metrics.avg_response_time_ms")).not.toBeInTheDocument(); + }); + it("shows a dash and no response time chart when the model has no timed requests", () => { render(); diff --git a/ui/litellm-dashboard/src/components/activity_metrics.tsx b/ui/litellm-dashboard/src/components/activity_metrics.tsx index b1df2b598e2..95315f8ec27 100644 --- a/ui/litellm-dashboard/src/components/activity_metrics.tsx +++ b/ui/litellm-dashboard/src/components/activity_metrics.tsx @@ -1,4 +1,13 @@ -import { AreaChart, BarChart, CustomLegend, CustomTooltip, LineChart } from "@/components/shared/charts"; +import { + AreaChart, + BarChart, + type ChartTooltipProps, + CustomLegend, + CustomTooltip, + formatCategoryName, + LineChart, + ValueTooltip, +} from "@/components/shared/charts"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { resolveTeamAliasFromTeamID } from "@/utils/teamUtils"; import { Card, CardContent } from "@/components/ui/card"; @@ -19,6 +28,15 @@ interface ActivityMetricsProps { const modelAverageResponseTimeMs = (metrics: ModelActivityData): number | null => averageResponseTimeMs(metrics.total_response_time_ms ?? 0, metrics.total_timed_requests ?? 0); +export const ResponseTimeTooltip = ({ active, payload, label }: ChartTooltipProps) => ( + ({ ...item, name: formatCategoryName(String(item.dataKey ?? "")) }))} + label={label} + valueFormatter={formatResponseTime} + /> +); + const ModelSection = ({ modelName, metrics, @@ -182,6 +200,7 @@ const ModelSection = ({ categories={["metrics.avg_response_time_ms"]} colors={["amber"]} valueFormatter={formatResponseTime} + customTooltip={ResponseTimeTooltip} connectNulls={true} showLegend={false} />