FN-7801: infer xAI icon for Grok model IDs in ProviderIcon
Fix ProviderIcon so Grok model-shaped provider strings (e.g. grok-4.5, grok-cli/grok-4-fast) render the xAI brand icon instead of falling back to the generic Cpu icon. - ProviderIcon now falls back to inferProviderIconKey(provider) when an exact providerConfig lookup misses, before defaulting to the Cpu icon - Added a changeset (patch) documenting the fix for dashboard provider icon surfaces - Added regression tests covering exact xAI/Grok keys, inferred Grok model-id strings, non-Grok inferred providers (e.g. gpt-5.5), and confirming unknown/empty providers still fall back to Cpu without the xAI icon Files changed: .changeset/fn-7801-grok-xai-logo.md | 7 ++++++ packages/dashboard/app/components/ProviderIcon.tsx | 11 ++++++++- .../app/components/__tests__/ProviderIcon.test.tsx | 27 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7801 Fusion-Task-Lineage: e81f15f6-d295-4f6d-b21f-7cf58854457f Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7801-grok-xai-logo.md
Normal file
7
.changeset/fn-7801-grok-xai-logo.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Show the xAI logo for Grok model IDs across dashboard provider surfaces.
|
||||||
|
category: fix
|
||||||
|
dev: ProviderIcon now falls back through inferProviderIconKey before the generic CPU icon.
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Cpu } from "lucide-react";
|
import { Cpu } from "lucide-react";
|
||||||
|
|
||||||
|
import { inferProviderIconKey } from "../utils/providerIconKey";
|
||||||
|
|
||||||
function LlamaCppIcon({ size, color, label = "llama.cpp" }: { size: number; color: string; label?: string }) {
|
function LlamaCppIcon({ size, color, label = "llama.cpp" }: { size: number; color: string; label?: string }) {
|
||||||
return <Cpu size={size} color={color} aria-label={label} data-testid="llama-cpp-icon" />;
|
return <Cpu size={size} color={color} aria-label={label} data-testid="llama-cpp-icon" />;
|
||||||
}
|
}
|
||||||
@@ -829,7 +831,14 @@ const providerConfig: Record<
|
|||||||
|
|
||||||
export function ProviderIcon({ provider, size = "sm" }: ProviderIconProps) {
|
export function ProviderIcon({ provider, size = "sm" }: ProviderIconProps) {
|
||||||
const normalizedProvider = provider.toLowerCase();
|
const normalizedProvider = provider.toLowerCase();
|
||||||
const config = providerConfig[normalizedProvider];
|
const directConfig = providerConfig[normalizedProvider];
|
||||||
|
/*
|
||||||
|
FNXC:ProviderIcons 2026-07-10-00:00:
|
||||||
|
FN-7801: dashboard surfaces can pass model-id-shaped provider strings such as grok-4.5 or grok-cli/grok-4-fast, which strict providerConfig lookups miss.
|
||||||
|
Fall through to the shared inferProviderIconKey normalizer before the Cpu default so Grok/xAI and other provider families keep their brand icons, while exact keys and genuinely unknown providers retain existing behavior.
|
||||||
|
*/
|
||||||
|
const inferredConfig = directConfig ? undefined : providerConfig[inferProviderIconKey(provider)];
|
||||||
|
const config = directConfig ?? inferredConfig;
|
||||||
const IconComponent = config?.component;
|
const IconComponent = config?.component;
|
||||||
const color = config?.color ?? "var(--text-muted)";
|
const color = config?.color ?? "var(--text-muted)";
|
||||||
const label = config?.label;
|
const label = config?.label;
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ describe("ProviderIcon", () => {
|
|||||||
|
|
||||||
it("renders Cpu icon as fallback for unknown providers", () => {
|
it("renders Cpu icon as fallback for unknown providers", () => {
|
||||||
render(<ProviderIcon provider="unknown" />);
|
render(<ProviderIcon provider="unknown" />);
|
||||||
|
expect(screen.queryByTestId("xai-icon")).not.toBeInTheDocument();
|
||||||
// Cpu icon from lucide-react renders as an svg without our custom data-testid
|
// Cpu icon from lucide-react renders as an svg without our custom data-testid
|
||||||
const icon = screen.getByText((_, element) => {
|
const icon = screen.getByText((_, element) => {
|
||||||
return element?.tagName.toLowerCase() === "svg" &&
|
return element?.tagName.toLowerCase() === "svg" &&
|
||||||
@@ -132,6 +133,7 @@ describe("ProviderIcon", () => {
|
|||||||
|
|
||||||
it("renders Cpu icon as fallback for empty provider", () => {
|
it("renders Cpu icon as fallback for empty provider", () => {
|
||||||
render(<ProviderIcon provider="" />);
|
render(<ProviderIcon provider="" />);
|
||||||
|
expect(screen.queryByTestId("xai-icon")).not.toBeInTheDocument();
|
||||||
const icon = screen.getByText((_, element) => {
|
const icon = screen.getByText((_, element) => {
|
||||||
return element?.tagName.toLowerCase() === "svg" &&
|
return element?.tagName.toLowerCase() === "svg" &&
|
||||||
element?.parentElement?.getAttribute("data-provider") === "";
|
element?.parentElement?.getAttribute("data-provider") === "";
|
||||||
@@ -418,6 +420,31 @@ describe("ProviderIcon", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// xAI provider tests
|
// xAI provider tests
|
||||||
|
it.each(["xai", "grok", "grok-cli"])("renders xAI brand icon for exact %s provider key", (provider) => {
|
||||||
|
render(<ProviderIcon provider={provider} />);
|
||||||
|
expect(screen.getByTestId("xai-icon")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
["grok-4.5", "grok-4.5"],
|
||||||
|
["grok-4-fast", "grok-4-fast"],
|
||||||
|
["grok-cli/grok-4-fast", "grok-cli/grok-4-fast"],
|
||||||
|
["xai-grok-4", "xai-grok-4"],
|
||||||
|
["Grok-4.5", "grok-4.5"],
|
||||||
|
])("renders xAI brand icon for inferred Grok model/provider string %s", (provider, expectedDataProvider) => {
|
||||||
|
render(<ProviderIcon provider={provider} />);
|
||||||
|
const icon = screen.getByTestId("xai-icon").parentElement;
|
||||||
|
expect(icon).toHaveStyle({ color: "var(--text)" });
|
||||||
|
expect(icon).toHaveAttribute("data-provider", expectedDataProvider);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("infers non-Grok provider icons from model-shaped ids", () => {
|
||||||
|
render(<ProviderIcon provider="gpt-5.5" />);
|
||||||
|
const icon = screen.getByTestId("openai-icon").parentElement;
|
||||||
|
expect(icon).toHaveStyle({ color: "var(--provider-openai)" });
|
||||||
|
expect(icon).toHaveAttribute("data-provider", "gpt-5.5");
|
||||||
|
});
|
||||||
|
|
||||||
it("renders xAI brand icon for xai provider", () => {
|
it("renders xAI brand icon for xai provider", () => {
|
||||||
render(<ProviderIcon provider="xai" />);
|
render(<ProviderIcon provider="xai" />);
|
||||||
expect(screen.getByTestId("xai-icon")).toBeInTheDocument();
|
expect(screen.getByTestId("xai-icon")).toBeInTheDocument();
|
||||||
|
|||||||
Reference in New Issue
Block a user