mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(site): infer model family icon from model name in AI Bridge logs (#22022)
## Problem Previously, the AI Bridge model column icon was derived from the provider field. This worked because each provider only served its own models: OpenAI interceptions always used OpenAI models, and Anthropic interceptions always used Anthropic models. With the introduction of the Copilot provider, this assumption no longer holds. Copilot can forward requests to both OpenAI and Anthropic models, so the provider field alone is not enough to determine the correct model icon. This caused Copilot interceptions to display a fallback question mark icon for the model. <img width="1337" height="365" alt="Screenshot 2026-02-10 at 09 10 34" src="https://github.com/user-attachments/assets/1efd613d-16c9-4738-8337-6ccf92e610fc" /> ## Changes * Added `AIBridgeModelIcon` component that infers the model family (Claude, OpenAI) from the model name string and renders the appropriate icon. * Updated `RequestLogsRow` to use `AIBridgeModelIcon` instead of `AIBridgeProviderIcon` in both the table row and the expanded detail view.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
||||
import { CircleQuestionMarkIcon } from "lucide-react";
|
||||
import { cn } from "utils/cn";
|
||||
|
||||
// Infers the model family from a model name string.
|
||||
// See official model naming docs:
|
||||
// - Anthropic: https://docs.anthropic.com/en/docs/about-claude/models/all-models
|
||||
// - OpenAI: https://platform.openai.com/docs/models
|
||||
function inferModelFamily(model: string): string {
|
||||
const modelFamily = model.toLowerCase();
|
||||
// Anthropic model families
|
||||
if (
|
||||
modelFamily.includes("claude") ||
|
||||
modelFamily.includes("opus") ||
|
||||
modelFamily.includes("sonnet") ||
|
||||
modelFamily.includes("haiku")
|
||||
) {
|
||||
return "claude";
|
||||
}
|
||||
// OpenAI model families (gpt-*, o1/o3/o4-*, codex, whisper)
|
||||
if (
|
||||
modelFamily.includes("gpt") ||
|
||||
modelFamily.startsWith("o1") ||
|
||||
modelFamily.startsWith("o3") ||
|
||||
modelFamily.startsWith("o4") ||
|
||||
modelFamily.includes("codex") ||
|
||||
modelFamily.includes("whisper")
|
||||
) {
|
||||
return "openai";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
export const AIBridgeModelIcon = ({
|
||||
model,
|
||||
...props
|
||||
}: {
|
||||
model: string;
|
||||
} & React.ComponentProps<"svg">) => {
|
||||
const iconClassName = "flex-shrink-0";
|
||||
const family = inferModelFamily(model);
|
||||
switch (family) {
|
||||
case "claude":
|
||||
return (
|
||||
<ExternalImage
|
||||
src="/icon/claude.svg"
|
||||
className={cn(iconClassName, props.className)}
|
||||
/>
|
||||
);
|
||||
case "openai":
|
||||
return (
|
||||
<ExternalImage
|
||||
src="/icon/openai.svg"
|
||||
className={cn(iconClassName, props.className)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<CircleQuestionMarkIcon
|
||||
className={cn(iconClassName, props.className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { type FC, Fragment, useState } from "react";
|
||||
import { cn } from "utils/cn";
|
||||
import { formatDate, humanDuration } from "utils/time";
|
||||
import { AIBridgeProviderIcon } from "../AIBridgeProviderIcon";
|
||||
import { AIBridgeModelIcon } from "../AIBridgeModelIcon";
|
||||
|
||||
type RequestLogsRowProps = {
|
||||
interception: AIBridgeInterception;
|
||||
@@ -204,8 +204,8 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
|
||||
<div className="w-full min-w-0 overflow-hidden">
|
||||
<Badge className="gap-1.5 w-full">
|
||||
<div className="flex-shrink-0 flex items-center">
|
||||
<AIBridgeProviderIcon
|
||||
provider={interception.provider}
|
||||
<AIBridgeModelIcon
|
||||
model={interception.model}
|
||||
className="size-icon-xs"
|
||||
/>
|
||||
</div>
|
||||
@@ -279,8 +279,8 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
|
||||
<dd data-chromatic="ignore">
|
||||
<Badge className="gap-2">
|
||||
<div className="flex-shrink-0 flex items-center">
|
||||
<AIBridgeProviderIcon
|
||||
provider={interception.provider}
|
||||
<AIBridgeModelIcon
|
||||
model={interception.model}
|
||||
className="size-icon-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user