From 05e483c02eee226fd95dc980de24e38bb1e37b37 Mon Sep 17 00:00:00 2001 From: decolua Date: Sat, 13 Jun 2026 11:43:18 +0700 Subject: [PATCH] fix(provider-topology): update label assignment to include nodeName Co-authored-by: Cursor --- .../usage/components/ProviderTopology.js | 2 +- src/shared/components/ModelSelectModal.js | 2 +- src/shared/components/UsageStats.js | 18 ++++++++++++++---- tests/unit/mimo-free.test.js | 5 ++--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js b/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js index 6ecb6df8..3827a0f8 100644 --- a/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js +++ b/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js @@ -149,7 +149,7 @@ function buildLayout(providers, activeSet, lastSet, errorSet) { const error = !active && errorSet.has(p.provider?.toLowerCase()); const nodeId = `provider-${p.provider}`; const data = { - label: (config.name !== p.provider ? config.name : null) || p.name || p.provider, + label: (config.name !== p.provider ? config.name : null) || p.nodeName || p.name || p.provider, color: config.color || "#6b7280", imageUrl: getProviderImageUrl(p.provider), textIcon: config.textIcon || (p.provider || "?").slice(0, 2).toUpperCase(), diff --git a/src/shared/components/ModelSelectModal.js b/src/shared/components/ModelSelectModal.js index 6abb8867..0e965f8d 100644 --- a/src/shared/components/ModelSelectModal.js +++ b/src/shared/components/ModelSelectModal.js @@ -208,7 +208,7 @@ export default function ModelSelectModal({ // Find connection object to get prefix synchronously without waiting for providerNodes fetch const connection = activeProviders.find(p => p.provider === providerId); const matchedNode = providerNodes.find(node => node.id === providerId); - const displayName = connection?.name || matchedNode?.name || providerInfo.name; + const displayName = matchedNode?.name || connection?.name || providerInfo.name; const nodePrefix = connection?.providerSpecificData?.prefix || matchedNode?.prefix || providerId; // Aliases are stored using the raw providerId as key (e.g. "openai-compatible-chat-/glm-4.7"), diff --git a/src/shared/components/UsageStats.js b/src/shared/components/UsageStats.js index 8585cd74..950a7af9 100644 --- a/src/shared/components/UsageStats.js +++ b/src/shared/components/UsageStats.js @@ -211,9 +211,16 @@ export default function UsageStats({ period: periodProp, setPeriod: setPeriodPro // Fetch connected providers once, deduplicate by provider type // Always include noAuth free providers (e.g. opencode) regardless of connections useEffect(() => { - fetch("/api/providers") - .then((r) => r.ok ? r.json() : null) - .then((d) => { + Promise.all([ + fetch("/api/providers").then((r) => r.ok ? r.json() : null), + fetch("/api/provider-nodes").then((r) => r.ok ? r.json() : null), + ]) + .then(([d, nodesData]) => { + // Build node name lookup for custom providers + const nodeNameMap = {}; + for (const node of (nodesData?.nodes || [])) { + nodeNameMap[node.id] = node.name; + } const seen = new Set(); const unique = (d?.connections || []).filter((c) => { if (c.isActive === false) return false; @@ -221,7 +228,10 @@ export default function UsageStats({ period: periodProp, setPeriod: setPeriodPro if (seen.has(c.provider)) return false; seen.add(c.provider); return true; - }); + }).map((c) => ({ + ...c, + nodeName: nodeNameMap[c.provider] || null, + })); const noAuthProviders = Object.values(FREE_PROVIDERS) .filter((p) => p.noAuth && !seen.has(p.id) && isLLMProvider(p.id)) .map((p) => ({ provider: p.id, name: p.name })); diff --git a/tests/unit/mimo-free.test.js b/tests/unit/mimo-free.test.js index d6a04a89..ae4633bf 100644 --- a/tests/unit/mimo-free.test.js +++ b/tests/unit/mimo-free.test.js @@ -235,9 +235,8 @@ describe("MiMo Free provider registration", () => { expect(PROVIDERS.mmf?.noAuth).toBe(true); }); - it("exposes the mmf model catalog", () => { - expect(Array.isArray(PROVIDER_MODELS.mmf)).toBe(true); - expect(PROVIDER_MODELS.mmf.some((m) => m.id === "mimo-auto")).toBe(true); + it("exposes only mimo-auto (the sole free-channel model)", () => { + expect(PROVIDER_MODELS.mmf.map((m) => m.id)).toEqual(["mimo-auto"]); }); it("maps the mimo-free alias to mmf", () => {