From c61dc6de463e0eb0a394926af063c1b472033213 Mon Sep 17 00:00:00 2001 From: luulam Date: Mon, 17 Aug 2026 14:24:13 +0700 Subject: [PATCH] fix(dashboard): show provider names instead of node ids in Usage by Provider The Usage by Provider table (and related provider cells) rendered raw provider keys, so requests routed through a custom OpenAI-compatible node appeared as "openai-compatible-chat-096baf9a-6433-4f22-b079-20371092555a" instead of the node's display name. - UsageStats: add getProviderLabel() resolving a provider key (built-in id, alias, or custom node id) to a friendly name via the providers state (nodeName > connection name) and AI_PROVIDERS/getProviderByAlias fallback - Apply the label in group headers, detail rows, badges, and recent requests (raw key kept as hover title) - UsageTable: accept providerLabel prop, use it for provider group headers --- .../dashboard/usage/components/UsageTable.js | 10 +++++-- src/shared/components/UsageStats.js | 28 +++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/app/(dashboard)/dashboard/usage/components/UsageTable.js b/src/app/(dashboard)/dashboard/usage/components/UsageTable.js index 9da3c3e6..f249d41e 100644 --- a/src/app/(dashboard)/dashboard/usage/components/UsageTable.js +++ b/src/app/(dashboard)/dashboard/usage/components/UsageTable.js @@ -105,6 +105,7 @@ export default function UsageTable({ renderDetailCells, renderSummaryCells, emptyMessage, + providerLabel, }) { const [expanded, setExpanded] = useState(new Set()); @@ -198,8 +199,13 @@ export default function UsageTable({ chevron_right - 0 ? "text-primary" : ""}`}> - {group.groupKey} + 0 ? "text-primary" : ""}`} + title={group.groupKey} + > + {tableType === "provider" && providerLabel + ? providerLabel(group.groupKey) + : group.groupKey} diff --git a/src/shared/components/UsageStats.js b/src/shared/components/UsageStats.js index ff5286a3..b5b75062 100644 --- a/src/shared/components/UsageStats.js +++ b/src/shared/components/UsageStats.js @@ -2,7 +2,7 @@ import { useState, useEffect, useMemo, useCallback, useRef } from "react"; import { useSearchParams, useRouter } from "next/navigation"; -import { FREE_PROVIDERS, AI_PROVIDERS } from "@/shared/constants/providers"; +import { FREE_PROVIDERS, AI_PROVIDERS, getProviderByAlias } from "@/shared/constants/providers"; // Keep providers without serviceKinds (default LLM) or with "llm" in serviceKinds function isLLMProvider(id) { @@ -104,7 +104,7 @@ function RecentRequests({ requests = [] }) { className="py-1.5 font-mono truncate max-w-[80px] text-text-muted" title={r.provider} > - {r.provider || "—"} + {getProviderLabel(r.provider) || "—"} @@ -310,6 +310,17 @@ export default function UsageStats({ const [tableView, setTableView] = useState("provider"); const [viewMode, setViewMode] = useState("costs"); const [providers, setProviders] = useState([]); + + // Resolve a provider key (built-in id, alias, or custom node id like + // "openai-compatible-chat-096baf9a-...") to a human-friendly name. + const getProviderLabel = useCallback((providerId) => { + if (!providerId) return "Unknown Provider"; + const match = providers.find((p) => p.provider === providerId || p.id === providerId); + if (match) return match.nodeName || match.name || providerId; + // Fallback: known built-in provider (by id, alias, or display prefix) + const known = getProviderByAlias(providerId) || AI_PROVIDERS[providerId] || FREE_PROVIDERS[providerId]; + return known?.name || known?.uiAlias || known?.alias || providerId; + }, [providers]); const [periodLocal, setPeriodLocal] = useState("today"); const isInitialLoad = useRef(true); const hasLoadedStats = useRef(false); @@ -458,7 +469,7 @@ export default function UsageStats({ variant={item.pending > 0 ? "primary" : "neutral"} size="sm" > - {item.provider} + {getProviderLabel(item.provider)} {fmt(item.requests)} @@ -522,7 +533,7 @@ export default function UsageStats({ variant={item.pending > 0 ? "primary" : "neutral"} size="sm" > - {item.provider} + {getProviderLabel(item.provider)} {fmt(item.requests)} @@ -560,7 +571,7 @@ export default function UsageStats({ {item.rawModel} - {item.provider} + {getProviderLabel(item.provider)} {fmt(item.requests)} @@ -593,7 +604,7 @@ export default function UsageStats({ renderDetailCells: (item) => ( <> - {item.provider || item.key} + {getProviderLabel(item.provider || item.key)} {fmt(item.requests)} @@ -633,7 +644,7 @@ export default function UsageStats({ {item.rawModel} - {item.provider} + {getProviderLabel(item.provider)} {fmt(item.requests)} @@ -645,7 +656,7 @@ export default function UsageStats({ }; } } - }, [stats, tableView, sortBy, sortOrder]); + }, [stats, tableView, sortBy, sortOrder, getProviderLabel]); if (!stats && !loading) return ( @@ -752,6 +763,7 @@ export default function UsageStats({ renderSummaryCells={activeTableConfig.renderSummaryCells} renderDetailCells={activeTableConfig.renderDetailCells} emptyMessage={activeTableConfig.emptyMessage} + providerLabel={getProviderLabel} /> )}