Skip to content

Commit 95c6a30

Browse files
authored
🤖 feat: display mux gateway models with provider icon and suffix (#790)
- Add mux.svg icon (black/white M logo matching other provider styles) - Parse mux-gateway:provider/model format to extract inner provider - Show dual icons: mux icon + inner provider icon (e.g., Anthropic) - Append "(mux gateway)" suffix to display name - Model name formatting uses existing formatModelDisplayName() Example: mux-gateway:anthropic/claude-opus-4-5 displays as: [mux] [anthropic] Opus 4.5 (mux gateway)
1 parent 179e0c0 commit 95c6a30

File tree

2 files changed

+62
-23
lines changed

2 files changed

+62
-23
lines changed

src/browser/assets/icons/mux.svg

Lines changed: 1 addition & 0 deletions
Loading

src/browser/components/Messages/ModelDisplay.tsx

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from "react";
22
import AnthropicIcon from "@/browser/assets/icons/anthropic.svg?react";
33
import OpenAIIcon from "@/browser/assets/icons/openai.svg?react";
44
import AWSIcon from "@/browser/assets/icons/aws.svg?react";
5+
import MuxIcon from "@/browser/assets/icons/mux.svg?react";
56
import { TooltipWrapper, Tooltip } from "@/browser/components/Tooltip";
67
import { formatModelDisplayName } from "@/common/utils/ai/modelDisplay";
78

@@ -11,46 +12,83 @@ interface ModelDisplayProps {
1112
showTooltip?: boolean;
1213
}
1314

15+
/**
16+
* Parse a model string into provider and model name.
17+
* Handles mux-gateway format: "mux-gateway:inner-provider/model-name"
18+
* Returns: { provider, modelName, isMuxGateway, innerProvider }
19+
*/
20+
function parseModelString(modelString: string): {
21+
provider: string;
22+
modelName: string;
23+
isMuxGateway: boolean;
24+
innerProvider: string;
25+
} {
26+
const [provider, rest] = modelString.includes(":")
27+
? modelString.split(":", 2)
28+
: ["", modelString];
29+
30+
// Handle mux-gateway format: mux-gateway:anthropic/claude-sonnet-4-5
31+
if (provider === "mux-gateway" && rest.includes("/")) {
32+
const [innerProvider, modelName] = rest.split("/", 2);
33+
return { provider, modelName, isMuxGateway: true, innerProvider };
34+
}
35+
36+
return { provider, modelName: rest, isMuxGateway: false, innerProvider: "" };
37+
}
38+
39+
/** Get icon component for a provider name */
40+
function getProviderIcon(provider: string): React.ReactNode {
41+
switch (provider) {
42+
case "anthropic":
43+
return <AnthropicIcon />;
44+
case "openai":
45+
return <OpenAIIcon />;
46+
case "bedrock":
47+
return <AWSIcon />;
48+
case "mux-gateway":
49+
return <MuxIcon />;
50+
default:
51+
return null;
52+
}
53+
}
54+
1455
/**
1556
* Display a model name with its provider icon.
1657
* Supports format "provider:model-name" (e.g., "anthropic:claude-sonnet-4-5")
58+
* Also supports mux-gateway: "mux-gateway:anthropic/claude-sonnet-4-5"
59+
* -> Shows mux icon + inner provider icon + model name + "(mux gateway)"
1760
*
1861
* Uses standard inline layout for natural text alignment.
1962
* Icon is 1em (matches font size) with vertical-align: middle.
2063
*/
2164
export const ModelDisplay: React.FC<ModelDisplayProps> = ({ modelString, showTooltip = true }) => {
22-
const [provider, modelName] = modelString.includes(":")
23-
? modelString.split(":", 2)
24-
: ["", modelString];
65+
const { provider, modelName, isMuxGateway, innerProvider } = parseModelString(modelString);
2566

26-
// Map provider names to their icons
27-
const getProviderIcon = () => {
28-
switch (provider) {
29-
case "anthropic":
30-
return <AnthropicIcon />;
31-
case "openai":
32-
return <OpenAIIcon />;
33-
case "bedrock":
34-
return <AWSIcon />;
35-
default:
36-
return null;
37-
}
38-
};
39-
40-
const providerIcon = getProviderIcon();
67+
// For mux-gateway, show the inner provider's icon (the model's actual provider)
68+
const providerIcon = isMuxGateway ? getProviderIcon(innerProvider) : getProviderIcon(provider);
69+
const muxIcon = isMuxGateway ? getProviderIcon("mux-gateway") : null;
4170
const displayName = formatModelDisplayName(modelName);
71+
const suffix = isMuxGateway ? " (mux gateway)" : "";
72+
73+
const iconClass =
74+
"mr-[0.3em] inline-block h-[1.1em] w-[1.1em] align-[-0.19em] [&_svg]:block [&_svg]:h-full [&_svg]:w-full [&_svg_.st0]:fill-current [&_svg_circle]:!fill-current [&_svg_path]:!fill-current [&_svg_rect]:!fill-current";
4275

4376
const content = (
4477
<span className="inline normal-case" data-model-display>
78+
{muxIcon && (
79+
<span className={iconClass} data-model-icon="mux">
80+
{muxIcon}
81+
</span>
82+
)}
4583
{providerIcon && (
46-
<span
47-
className="mr-[0.3em] inline-block h-[1.1em] w-[1.1em] align-[-0.19em] [&_svg]:block [&_svg]:h-full [&_svg]:w-full [&_svg_.st0]:fill-current [&_svg_circle]:!fill-current [&_svg_path]:!fill-current [&_svg_rect]:!fill-current"
48-
data-model-icon
49-
>
84+
<span className={iconClass} data-model-icon>
5085
{providerIcon}
5186
</span>
5287
)}
53-
<span className="inline">{displayName}</span>
88+
<span className="inline">
89+
{displayName}
90+
{suffix}
91+
</span>
5492
</span>
5593
);
5694

0 commit comments

Comments
 (0)