FN-7217: map GLM models to Z.ai icons

Command Center token analytics now render standalone GLM model rows with Z.ai provider icons.

- Infer Z.ai provider icons for standalone glm-* model ids without matching unrelated text.
- Cover GLM icon rendering in Overview token bars and Tokens Area bars/table rows.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-7217-zai-glm-icons.md                |  7 +++
 .../__tests__/CommandCenter.test.tsx               | 39 ++++++++++++++++
 .../areas/__tests__/TokensArea.test.tsx            | 53 ++++++++++++++++++++++
 .../app/utils/__tests__/providerIconKey.test.ts    |  4 ++
 packages/dashboard/app/utils/providerIconKey.ts    |  7 ++-
 5 files changed, 109 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7217

Fusion-Task-Lineage: e784413e-2dcf-4b38-ba01-5f9d825e07b8

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-28 21:00:00 -07:00
parent f3d9bfb27b
commit 0a8a86cb05
5 changed files with 109 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Show Z.ai icons for GLM model rows in Command Center token analytics.
category: fix
dev: Maps standalone glm-* model labels through the shared provider-icon inference helper.

View File

@@ -106,6 +106,31 @@ function tokenFixture(totalTokens = 1_500) {
};
}
function glmOverviewTokenFixture() {
const groups = [
makeTokenGroup("glm-5.1", 1_400),
makeTokenGroup("gpt-4o", 1_200),
makeTokenGroup("claude-sonnet", 1_000),
];
const totals = groups.reduce(
(acc, group) => ({
inputTokens: acc.inputTokens + group.inputTokens,
outputTokens: acc.outputTokens + group.outputTokens,
cachedTokens: acc.cachedTokens + group.cachedTokens,
cacheWriteTokens: acc.cacheWriteTokens + group.cacheWriteTokens,
totalTokens: acc.totalTokens + group.totalTokens,
nTasks: acc.nTasks + group.nTasks,
}),
{ inputTokens: 0, outputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, totalTokens: 0, nTasks: 0 },
);
return {
...tokenFixture(totals.totalTokens),
totals,
groups,
};
}
function manyModelTokenFixture() {
const groups = [
makeTokenGroup("model-01", 2_000),
@@ -639,6 +664,20 @@ describe("CommandCenter shell", () => {
}
});
it("renders standalone GLM model rows with the Z.ai icon in Overview token bars", async () => {
mockOverviewApi({ tokens: glmOverviewTokenFixture() });
render(<CommandCenter />);
const overviewTokenChart = await screen.findByTestId("command-center-overview-chart-tokens");
const glmBarLabel = within(overviewTokenChart).getByText("glm-5.1").closest(".cc-bar-label");
expect(glmBarLabel).toBeTruthy();
expect(providerIconIn(overviewTokenChart, "zai")).toBeTruthy();
expect(providerIconIn(overviewTokenChart, "openai")).toBeTruthy();
expect(providerIconIn(overviewTokenChart, "anthropic")).toBeTruthy();
expect(screen.getByRole("img", { name: "zai glm-5.1: 1,400" })).toBeTruthy();
expect(providerIconIn(screen.getByTestId("cc-overview-pie"), "zai")).toBeNull();
});
it("renders Overview providerless and unknown model icons without touching pie labels", async () => {
mockOverviewApi({
tokens: {

View File

@@ -83,6 +83,36 @@ function tokenFixture() {
};
}
function glmMixedProviderTokenFixture() {
const groups = [
makeTokenGroup("glm-5.1", 2_400),
makeTokenGroup("glm-4.5-air", 2_200),
makeTokenGroup("glm-5v-turbo", 2_000),
makeTokenGroup("gpt-4o-mini", 1_800),
makeTokenGroup("claude-sonnet-4-5", 1_600),
makeTokenGroup("custom-model-v1", 1_400),
makeTokenGroup(null, 1_200),
];
const totals = groups.reduce(
(acc, group) => ({
inputTokens: acc.inputTokens + group.inputTokens,
outputTokens: acc.outputTokens + group.outputTokens,
cachedTokens: acc.cachedTokens + group.cachedTokens,
cacheWriteTokens: acc.cacheWriteTokens + group.cacheWriteTokens,
totalTokens: acc.totalTokens + group.totalTokens,
nTasks: acc.nTasks + group.nTasks,
}),
{ inputTokens: 0, outputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, totalTokens: 0, nTasks: 0 },
);
return {
...tokenFixture(),
totals,
cost: { usd: null, unavailable: true, stale: false },
groups,
};
}
function manyModelTokenFixture() {
const groups = [
makeTokenGroup("claude-sonnet-4-5", 2_000),
@@ -152,6 +182,29 @@ describe("TokensArea provider model icons", () => {
expect(screen.getByTestId("cc-tokens-pie")).toHaveTextContent("(unknown)");
});
it("renders standalone GLM model rows with Z.ai icons across bars and table", async () => {
apiMock.mockResolvedValue(glmMixedProviderTokenFixture());
render(<TokensArea range={range7d} />);
const table = await screen.findByTestId("cc-tokens-table");
const byModelChart = screen.getByRole("list", { name: "Tokens by model" });
for (const label of ["glm-5.1", "glm-4.5-air", "glm-5v-turbo"]) {
const row = screen.getByTestId(`cc-tokens-row-${label}`);
expect(within(row).getByText(label)).toBeTruthy();
expect(within(row).getByTestId("provider-icon-zai")).toHaveAttribute("data-provider", "zai");
const barLabel = within(byModelChart).getByText(label).closest(".cc-bar-label");
expect(barLabel?.firstElementChild).toHaveAttribute("data-testid", "provider-icon-zai");
expect(barLabel?.firstElementChild).toHaveAttribute("data-provider", "zai");
}
expect(within(screen.getByTestId("cc-tokens-row-gpt-4o-mini")).getByTestId("provider-icon-openai")).toBeTruthy();
expect(within(screen.getByTestId("cc-tokens-row-claude-sonnet-4-5")).getByTestId("provider-icon-anthropic")).toBeTruthy();
expect(within(screen.getByTestId("cc-tokens-row-custom-model-v1")).getByTestId("provider-icon-custom-model-v1")).toBeTruthy();
expect(within(screen.getByTestId("cc-tokens-row-unknown")).getByTestId("provider-icon-")).toBeTruthy();
expect(table.querySelectorAll('.provider-icon[data-provider="zai"]').length).toBe(3);
});
it("renders every analytics model group in detail bar, pie, and table even beyond the old cap", async () => {
apiMock.mockResolvedValue(manyModelTokenFixture());
render(<TokensArea range={range7d} />);

View File

@@ -13,6 +13,9 @@ describe("inferProviderIconKey", () => {
["minimax-text-01", "minimax"],
["zhipu-glm-4", "zai"],
["zai/glm-4.5", "zai"],
["glm-5.1", "zai"],
["glm-4.5-air", "zai"],
["glm-5v-turbo", "zai"],
["kimi-k2", "kimi"],
["moonshot-v1", "kimi"],
["amazon-bedrock-claude", "anthropic"],
@@ -27,6 +30,7 @@ describe("inferProviderIconKey", () => {
it("returns unknown ids unchanged so ProviderIcon can render its fallback", () => {
expect(inferProviderIconKey("custom-model-v1")).toBe("custom-model-v1");
expect(inferProviderIconKey("not-a-glmish-model")).toBe("not-a-glmish-model");
expect(inferProviderIconKey("")).toBe("");
});
});

View File

@@ -1,7 +1,12 @@
/*
FNXC:ProviderIcons 2026-06-25-00:00:
Command Center model analytics group rows by model id only, so dashboard model-name surfaces must infer the provider icon from the model/provider text before handing off to ProviderIcon's fallback-safe renderer.
FNXC:ProviderIcons 2026-06-28-20:46:
Standalone GLM model ids such as glm-5.1, glm-4.5-air, and glm-5v-turbo are Z.ai/Zhipu-family models even when the analytics label omits zai/zhipu. Match GLM only at model-id segment boundaries so unrelated words do not hijack the Z.ai provider icon.
*/
const GLM_MODEL_SEGMENT_PATTERN = /(?:^|[/:_-])glm(?:$|[\d._:-]|-[\da-z])/;
export function inferProviderIconKey(modelOrProviderName: string): string {
const normalized = modelOrProviderName.toLowerCase();
@@ -21,7 +26,7 @@ export function inferProviderIconKey(modelOrProviderName: string): string {
if (normalized.includes("minimax")) {
return "minimax";
}
if (normalized.includes("zai") || normalized.includes("zhipu")) {
if (normalized.includes("zai") || normalized.includes("zhipu") || GLM_MODEL_SEGMENT_PATTERN.test(normalized)) {
return "zai";
}
if (normalized.includes("kimi") || normalized.includes("moonshot")) {