fix(dashboard): label usage-by-provider column as Provider / API Key
This commit is contained in:
@@ -280,7 +280,7 @@ const ENDPOINT_COLUMNS = [
|
||||
];
|
||||
|
||||
const PROVIDER_COLUMNS = [
|
||||
{ field: "provider", label: "Provider" },
|
||||
{ field: "provider", label: "Provider / API Key" },
|
||||
{ field: "requests", label: "Requests", align: "right" },
|
||||
{ field: "lastUsed", label: "Last Used", align: "right" },
|
||||
];
|
||||
@@ -321,10 +321,13 @@ export default function UsageStats({
|
||||
|
||||
// Resolve a provider key (built-in id, alias, or custom node id like
|
||||
// "openai-compatible-chat-096baf9a-...") to a human-friendly name.
|
||||
// Prefers the connected-providers state (nodeName > connection name)
|
||||
// and falls back to the module-level registry lookup.
|
||||
// Prefers the canonical provider name from the registry so e.g. "commandcode"
|
||||
// shows "Command Code" instead of a connection/account name; falls back to
|
||||
// the connected-providers state (nodeName > connection name) for custom nodes.
|
||||
const getProviderLabel = useCallback((providerId) => {
|
||||
if (!providerId) return "Unknown Provider";
|
||||
const known = getProviderByAlias(providerId) || AI_PROVIDERS[providerId] || FREE_PROVIDERS[providerId];
|
||||
if (known?.name) return known.name;
|
||||
const match = providers.find((p) => p.provider === providerId || p.id === providerId);
|
||||
if (match) return match.nodeName || match.name || providerId;
|
||||
return resolveProviderLabel(providerId);
|
||||
@@ -591,12 +594,55 @@ export default function UsageStats({
|
||||
};
|
||||
}
|
||||
case "provider": {
|
||||
// Detail rows = the API keys used per provider (grouped from byApiKey),
|
||||
// so expanding a provider shows its keys instead of repeating the provider.
|
||||
// byApiKey[].provider is the display name (node name for custom nodes);
|
||||
// map display names back to provider ids via the connected-providers state.
|
||||
const displayToId = {};
|
||||
for (const p of providers) {
|
||||
if (p.nodeName) displayToId[p.nodeName] = p.provider;
|
||||
if (p.name && !displayToId[p.name]) displayToId[p.name] = p.provider;
|
||||
}
|
||||
const keyGroupsByProvider = {};
|
||||
for (const ak of Object.values(sortData(stats.byApiKey, {}, sortBy, sortOrder))) {
|
||||
const pid = displayToId[ak.provider] || ak.provider || "unknown";
|
||||
const kname = ak.keyName || ak.apiKeyMasked || "Unknown Key";
|
||||
const bucket = keyGroupsByProvider[pid] || (keyGroupsByProvider[pid] = {});
|
||||
const g = bucket[kname] || (bucket[kname] = {
|
||||
requests: 0,
|
||||
promptTokens: 0,
|
||||
completionTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
cost: 0,
|
||||
inputCost: 0,
|
||||
cachedCost: 0,
|
||||
outputCost: 0,
|
||||
lastUsed: null,
|
||||
keyName: kname,
|
||||
});
|
||||
g.requests += ak.requests || 0;
|
||||
g.promptTokens += ak.promptTokens || 0;
|
||||
g.completionTokens += ak.completionTokens || 0;
|
||||
g.cachedTokens += ak.cachedTokens || 0;
|
||||
g.totalTokens += ak.totalTokens || 0;
|
||||
g.cost += ak.cost || 0;
|
||||
g.inputCost += ak.inputCost || 0;
|
||||
g.cachedCost += ak.cachedCost || 0;
|
||||
g.outputCost += ak.outputCost || 0;
|
||||
if (ak.lastUsed && (!g.lastUsed || new Date(ak.lastUsed) > new Date(g.lastUsed)))
|
||||
g.lastUsed = ak.lastUsed;
|
||||
}
|
||||
const groupedData = groupDataByKey(
|
||||
sortData(stats.byProvider, {}, sortBy, sortOrder),
|
||||
"provider",
|
||||
).map((group) => ({
|
||||
...group,
|
||||
items: Object.values(keyGroupsByProvider[group.groupKey] || {}),
|
||||
}));
|
||||
return {
|
||||
columns: PROVIDER_COLUMNS,
|
||||
groupedData: groupDataByKey(
|
||||
sortData(stats.byProvider, {}, sortBy, sortOrder),
|
||||
"provider",
|
||||
),
|
||||
groupedData,
|
||||
storageKey: "usage-stats:expanded-providers",
|
||||
emptyMessage: "No usage recorded yet.",
|
||||
renderSummaryCells: (group) => (
|
||||
@@ -612,7 +658,7 @@ export default function UsageStats({
|
||||
renderDetailCells: (item) => (
|
||||
<>
|
||||
<td className="px-6 py-3 font-medium">
|
||||
{getProviderLabel(item.provider || item.key)}
|
||||
{item.keyName}
|
||||
</td>
|
||||
<td className="px-6 py-3 text-right">{fmt(item.requests)}</td>
|
||||
<td className="px-6 py-3 text-right text-text-muted whitespace-nowrap">
|
||||
@@ -664,7 +710,7 @@ export default function UsageStats({
|
||||
};
|
||||
}
|
||||
}
|
||||
}, [stats, tableView, sortBy, sortOrder, getProviderLabel]);
|
||||
}, [stats, tableView, sortBy, sortOrder, getProviderLabel, providers]);
|
||||
|
||||
if (!stats && !loading)
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user