diff --git a/coderd/x/chatd/chatprovider/chatprovider.go b/coderd/x/chatd/chatprovider/chatprovider.go
index 5862138b0c..a973848a18 100644
--- a/coderd/x/chatd/chatprovider/chatprovider.go
+++ b/coderd/x/chatd/chatprovider/chatprovider.go
@@ -655,6 +655,9 @@ func ResolveModelWithProviderHint(modelName, providerHint string) (provider stri
if isChatModelForProvider(fantasyopenai.Name, normalized) {
return fantasyopenai.Name, modelName, nil
}
+ if isChatModelForProvider(fantasygoogle.Name, normalized) {
+ return fantasygoogle.Name, modelName, nil
+ }
return "", "", xerrors.Errorf("unknown model %q", modelName)
}
diff --git a/coderd/x/chatd/chatprovider/chatprovider_test.go b/coderd/x/chatd/chatprovider/chatprovider_test.go
index fea015db73..9b808d9820 100644
--- a/coderd/x/chatd/chatprovider/chatprovider_test.go
+++ b/coderd/x/chatd/chatprovider/chatprovider_test.go
@@ -11,6 +11,7 @@ import (
"charm.land/fantasy"
fantasyanthropic "charm.land/fantasy/providers/anthropic"
fantasybedrock "charm.land/fantasy/providers/bedrock"
+ fantasygoogle "charm.land/fantasy/providers/google"
fantasyopenai "charm.land/fantasy/providers/openai"
fantasyopenaicompat "charm.land/fantasy/providers/openaicompat"
fantasyopenrouter "charm.land/fantasy/providers/openrouter"
@@ -1622,6 +1623,34 @@ func TestResolveModelWithProviderHint(t *testing.T) {
wantProvider: fantasyvercel.Name,
wantModel: "claude-4-5-sonnet",
},
+ {
+ name: "BareGeminiModelResolvesToGoogle",
+ modelName: "gemini-3.5-flash",
+ providerHint: "",
+ wantProvider: fantasygoogle.Name,
+ wantModel: "gemini-3.5-flash",
+ },
+ {
+ name: "BareGemmaModelResolvesToGoogle",
+ modelName: "gemma-3-27b",
+ providerHint: "",
+ wantProvider: fantasygoogle.Name,
+ wantModel: "gemma-3-27b",
+ },
+ {
+ name: "GoogleHintWithGeminiModel",
+ modelName: "gemini-2.5-pro",
+ providerHint: fantasygoogle.Name,
+ wantProvider: fantasygoogle.Name,
+ wantModel: "gemini-2.5-pro",
+ },
+ {
+ name: "CanonicalGoogleRefResolvesToGoogle",
+ modelName: "google/gemini-3.5-flash",
+ providerHint: "",
+ wantProvider: fantasygoogle.Name,
+ wantModel: "gemini-3.5-flash",
+ },
}
for _, tt := range tests {
diff --git a/site/src/pages/AIBridgePage/icons/AIBridgeModelIcon.tsx b/site/src/pages/AIBridgePage/icons/AIBridgeModelIcon.tsx
index ea17396309..121ecda7da 100644
--- a/site/src/pages/AIBridgePage/icons/AIBridgeModelIcon.tsx
+++ b/site/src/pages/AIBridgePage/icons/AIBridgeModelIcon.tsx
@@ -6,6 +6,7 @@ import { cn } from "#/utils/cn";
// See official model naming docs:
// - Anthropic: https://docs.anthropic.com/en/docs/about-claude/models/all-models
// - OpenAI: https://platform.openai.com/docs/models
+// - Google: https://ai.google.dev/gemini-api/docs/models
function inferModelFamily(model: string): string {
const modelFamily = model.toLowerCase();
// Anthropic model families
@@ -28,6 +29,10 @@ function inferModelFamily(model: string): string {
) {
return "openai";
}
+ // Google model families (gemini-*, gemma-*)
+ if (modelFamily.includes("gemini") || modelFamily.includes("gemma")) {
+ return "gemini";
+ }
return "unknown";
}
@@ -55,6 +60,13 @@ export const AIBridgeModelIcon = ({
className={cn(iconClassName, className)}
/>
);
+ case "gemini":
+ return (
+
+ );
default:
return (
);
+ case "google":
+ return (
+
+ );
+ case "azure":
+ return (
+
+ );
+ case "bedrock":
+ return (
+
+ );
case "copilot":
return (
);
+ case "vercel":
+ return (
+
+ );
default:
return (
{
+ it.each([
+ ["anthropic", "Anthropic"],
+ ["openai", "OpenAI"],
+ ["google", "Google"],
+ ["azure", "Azure OpenAI"],
+ ["bedrock", "AWS Bedrock"],
+ ["copilot", "GitHub Copilot"],
+ ["openai-compat", "OpenAI-compatible"],
+ ["openrouter", "OpenRouter"],
+ ["vercel", "Vercel"],
+ ])("maps known provider %s to %s", (input, expected) => {
+ expect(getProviderDisplayName(input)).toBe(expected);
+ });
+
+ it("capitalizes unknown provider names", () => {
+ expect(getProviderDisplayName("custom")).toBe("Custom");
+ expect(getProviderDisplayName("some-provider")).toBe("Some-provider");
+ });
+
+ it("returns Unknown for empty string", () => {
+ expect(getProviderDisplayName("")).toBe("Unknown");
+ });
+});
diff --git a/site/src/pages/AIBridgePage/utils.ts b/site/src/pages/AIBridgePage/utils.ts
index 3585d4468e..08424f0341 100644
--- a/site/src/pages/AIBridgePage/utils.ts
+++ b/site/src/pages/AIBridgePage/utils.ts
@@ -18,9 +18,25 @@ export const getProviderDisplayName = (provider: string) => {
return "Anthropic";
case "openai":
return "OpenAI";
+ case "google":
+ return "Google";
+ case "azure":
+ return "Azure OpenAI";
+ case "bedrock":
+ return "AWS Bedrock";
case "copilot":
- return "Github";
- default:
- return "Unknown";
+ return "GitHub Copilot";
+ case "openai-compat":
+ return "OpenAI-compatible";
+ case "openrouter":
+ return "OpenRouter";
+ case "vercel":
+ return "Vercel";
+ default: {
+ if (!provider) {
+ return "Unknown";
+ }
+ return provider.charAt(0).toUpperCase() + provider.slice(1);
+ }
}
};