fix(FN-7786): show partial Command Center costs

Fusion-Task-Id: FN-7786
This commit is contained in:
gsxdsm
2026-07-10 23:24:00 -07:00
parent 122e404494
commit fbea66d0f2
9 changed files with 72 additions and 20 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Show partial estimated costs across all Command Center cost views when some model pricing is unavailable.
category: fix
dev: Priced subtotals use a trailing plus sign; entirely unpriced usage remains unavailable.

View File

@@ -715,6 +715,18 @@ describe("CommandCenter shell", () => {
expect(liveMetricValue("command-center-live-tokens")).toBe("1,234,567,890"); expect(liveMetricValue("command-center-live-tokens")).toBe("1,234,567,890");
}); });
it("shows the priced subtotal in Overview when some usage has unknown pricing", async () => {
mockOverviewApi({
tokens: {
...tokenFixture(1_500),
cost: { usd: 911.39004125, unavailable: true, stale: false },
},
});
render(<CommandCenter />);
expect(await screen.findByTestId("command-center-stat-tokens")).toHaveTextContent("$911.39+");
});
it("live-polls token totals for the Overview card, live strip, and model charts", async () => { it("live-polls token totals for the Overview card, live strip, and model charts", async () => {
vi.useFakeTimers(); vi.useFakeTimers();
let resolvePoll: ((value: ReturnType<typeof tokenFixture>) => void) | null = null; let resolvePoll: ((value: ReturnType<typeof tokenFixture>) => void) | null = null;

View File

@@ -54,7 +54,7 @@ type OrgChartDragState = {
}; };
function costSortValue(cost: CostResult): number { function costSortValue(cost: CostResult): number {
return cost.unavailable || cost.usd === null ? -1 : cost.usd; return cost.usd ?? -1;
} }
function agentLabel(agent: TeamAgentSummary, unknownLabel: string): string { function agentLabel(agent: TeamAgentSummary, unknownLabel: string): string {

View File

@@ -38,7 +38,7 @@ The Tokens detail area is the full-fidelity by-model source of truth: every grou
*/ */
function costSortValue(cost: CostResult): number { function costSortValue(cost: CostResult): number {
return cost.unavailable || cost.usd === null ? -1 : cost.usd; return cost.usd ?? -1;
} }
function modelGroupIdentity(group: TokenGroupSummary): string { function modelGroupIdentity(group: TokenGroupSummary): string {
@@ -287,16 +287,12 @@ export function TokensArea({ range, projectId }: { range: DateRange; projectId?:
<td>{formatCount(g.cachedTokens)}</td> <td>{formatCount(g.cachedTokens)}</td>
<td>{formatCount(g.totalTokens)}</td> <td>{formatCount(g.totalTokens)}</td>
<td> <td>
{g.cost.unavailable || g.cost.usd === null ? ( <span
<span className={g.cost.usd === null ? "cc-unavailable" : undefined}
className="cc-unavailable" title={g.cost.unavailable ? t("commandCenter.tokens.costUnavailable", "No pricing for some or all usage") : undefined}
title={t("commandCenter.tokens.costUnavailable", "No pricing for this model")} >
> {formatCost(g.cost.usd, g.cost.unavailable)}
— </span>
</span>
) : (
formatCost(g.cost.usd, g.cost.unavailable)
)}
</td> </td>
</tr> </tr>
))} ))}

View File

@@ -14,7 +14,7 @@ import { WorkflowIcon } from "../../WorkflowIcon";
type SortKey = "workflow" | "tokens" | "cost" | "filesChanged" | "tasksCompleted" | "tasksInProgress" | "tasksInReview"; type SortKey = "workflow" | "tokens" | "cost" | "filesChanged" | "tasksCompleted" | "tasksInProgress" | "tasksInReview";
function costSortValue(cost: CostResult): number { function costSortValue(cost: CostResult): number {
return cost.unavailable || cost.usd === null ? -1 : cost.usd; return cost.usd ?? -1;
} }
function workflowLabel(workflow: WorkflowSummary, unknownLabel: string): string { function workflowLabel(workflow: WorkflowSummary, unknownLabel: string): string {

View File

@@ -180,6 +180,24 @@ afterEach(() => {
}); });
describe("TokensArea provider model icons", () => { describe("TokensArea provider model icons", () => {
it("renders a priced subtotal when the aggregate also contains unpriced usage", async () => {
apiMock.mockResolvedValue({
...tokenFixture(),
cost: { usd: 11.25, unavailable: true, stale: false },
groups: [
{
...tokenFixture().groups[0],
cost: { usd: 3.5, unavailable: true, stale: false },
},
],
});
render(<TokensArea range={range7d} />);
expect(await screen.findByTestId("cc-tokens-cost")).toHaveTextContent("$11.25+");
expect(screen.getByTestId("cc-tokens-row-claude-sonnet-4-5")).toHaveTextContent("$3.50+");
});
it("renders current OpenAI Codex priced costs as dollars instead of the unavailable sentinel", async () => { it("renders current OpenAI Codex priced costs as dollars instead of the unavailable sentinel", async () => {
const usage = { inputTokens: 1_000_000, outputTokens: 200_000, cachedTokens: 500_000, cacheWriteTokens: 100_000 }; const usage = { inputTokens: 1_000_000, outputTokens: 200_000, cachedTokens: 500_000, cacheWriteTokens: 100_000 };
const cost = costFor(usage, { provider: "openai-codex", model: "gpt-5.5" }); const cost = costFor(usage, { provider: "openai-codex", model: "gpt-5.5" });

View File

@@ -112,8 +112,7 @@ describe("WorkflowArea", () => {
await screen.findByTestId("cc-area-workflows"); await screen.findByTestId("cc-area-workflows");
expect(mocks.api).toHaveBeenCalledWith("/command-center/workflows?from=2026-06-08", undefined); expect(mocks.api).toHaveBeenCalledWith("/command-center/workflows?from=2026-06-08", undefined);
expect(screen.getByTestId("cc-workflows-total-tokens").textContent).toContain("1,650"); expect(screen.getByTestId("cc-workflows-total-tokens").textContent).toContain("1,650");
expect(screen.getByTestId("cc-workflows-total-cost").textContent).toContain("—"); expect(screen.getByTestId("cc-workflows-total-cost").textContent).toContain("$4.25+");
expect(screen.getByTestId("cc-workflows-total-cost").textContent).not.toContain("$0");
expect(screen.getByTestId("cc-workflows-tokens-chart").textContent).toContain("Coding"); expect(screen.getByTestId("cc-workflows-tokens-chart").textContent).toContain("Coding");
const table = screen.getByTestId("cc-workflows-table"); const table = screen.getByTestId("cc-workflows-table");

View File

@@ -59,7 +59,7 @@ import { ActivityArea } from "../ActivityArea";
import { EcosystemArea } from "../EcosystemArea"; import { EcosystemArea } from "../EcosystemArea";
import { useAnalyticsArea } from "../useAnalyticsArea"; import { useAnalyticsArea } from "../useAnalyticsArea";
import { ConfirmDialogProvider } from "../../../../hooks/useConfirm"; import { ConfirmDialogProvider } from "../../../../hooks/useConfirm";
import { rangeQuery } from "../areaShared"; import { formatCost, rangeQuery } from "../areaShared";
import { defaultPresets, rangeFromPreset } from "../../DateRangePicker"; import { defaultPresets, rangeFromPreset } from "../../DateRangePicker";
import { import {
activityFixture, activityFixture,
@@ -80,6 +80,15 @@ import {
tokenFixture, tokenFixture,
} from "./areas.test-harness"; } from "./areas.test-harness";
describe("Command Center cost formatting", () => {
it("shows priced subtotals when only part of the usage has known pricing", () => {
expect(formatCost(911.39004125, true)).toBe("$911.39+");
expect(formatCost(12.5, false)).toBe("$12.50");
expect(formatCost(0, false)).toBe("$0.00");
expect(formatCost(null, true)).toBe("—");
});
});
beforeEach(() => { beforeEach(() => {
apiMock.mockReset(); apiMock.mockReset();
backfillGithubSourceIssueClosedAtMock.mockReset(); backfillGithubSourceIssueClosedAtMock.mockReset();
@@ -1394,11 +1403,16 @@ describe("TeamArea", () => {
}); });
it("renders a large comma-grouped total unchanged in the team total tokens stat", async () => { it("renders a large comma-grouped total unchanged in the team total tokens stat", async () => {
apiMock.mockResolvedValue(populatedTeamFixture(1_234_567_890)); const fixture = populatedTeamFixture(1_234_567_890);
apiMock.mockResolvedValue({
...fixture,
totals: { ...fixture.totals, cost: { usd: 4.25, unavailable: true, stale: false } },
});
render(<TeamArea range={range7d} />); render(<TeamArea range={range7d} />);
await screen.findByTestId("cc-area-team"); await screen.findByTestId("cc-area-team");
expect(screen.getByTestId("cc-team-total-tokens").textContent).toContain("1,234,567,890"); expect(screen.getByTestId("cc-team-total-tokens").textContent).toContain("1,234,567,890");
expect(screen.getByTestId("cc-team-total-cost")).toHaveTextContent("$4.25+");
}); });
it("keeps the team pie safe for single-item and non-finite data", async () => { it("keeps the team pie safe for single-item and non-finite data", async () => {

View File

@@ -45,12 +45,18 @@ export function formatDurationMs(ms: number | null): string {
return `${seconds}s`; return `${seconds}s`;
} }
/** Format a USD cost result, returning the unavailable sentinel "—" when unknown. */ /**
* Format a USD cost result for every Command Center cost surface.
*
* FNXC:CommandCenterCost 2026-07-10-23:20:
* Cost aggregation deliberately preserves the priced subtotal when some usage has unknown pricing. Overview, Tokens, Team, and Workflows must display that subtotal with a trailing `+`; show `—` only when no usage can be priced, and keep an exact priced zero distinct from unavailable data.
*/
export function formatCost(usd: number | null, unavailable: boolean): string { export function formatCost(usd: number | null, unavailable: boolean): string {
if (unavailable || usd === null || !Number.isFinite(usd)) { if (usd === null || !Number.isFinite(usd)) {
return "—"; return "—";
} }
return `$${usd.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; const formatted = `$${usd.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
return unavailable ? `${formatted}+` : formatted;
} }
/** True when the picker's custom range is invalid (from after to). */ /** True when the picker's custom range is invalid (from after to). */