Skip to content

Commit 2f3ee7d

Browse files
committed
feat: add xAI (Grok) provider support
- Add xAI to AIProvider type definition - Implement API key detection for xai- prefix format - Add xAI provider display name and console URL - Add Grok model detection and categorization - Add Rocket icon for xAI category in ModelSelector - Support all xAI Grok models (grok-4.20 and grok-4-1-fast variants) This enables users to select and use xAI's Grok models for text-to-cypher functionality with proper API key validation and UI integration.
1 parent 708b28a commit 2f3ee7d

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

app/settings/ModelSelector.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState, useEffect, useRef, useCallback } from "react";
22
import { cn } from "@/lib/utils";
33
import { formatModelDisplayName } from "@/lib/ai-provider-utils";
4-
import { Search, Check, Sparkles, Zap, Brain, Globe, Server, Cpu, MessageSquare, ChevronRight } from "lucide-react";
4+
import { Search, Check, Sparkles, Zap, Brain, Globe, Server, Cpu, MessageSquare, ChevronRight, Rocket } from "lucide-react";
55
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
66
import Input from "../components/ui/Input";
77

@@ -21,6 +21,7 @@ const PROVIDER_DISPLAY_NAMES: Record<string, string> = {
2121
ollama: "Ollama",
2222
groq: "Groq",
2323
cohere: "Cohere",
24+
xai: "xAI",
2425
};
2526

2627
// Get icon for provider category
@@ -39,6 +40,8 @@ const getCategoryIcon = (category: string) => {
3940
return <Cpu className={className} />;
4041
case "Cohere":
4142
return <MessageSquare className={className} />;
43+
case "xAI":
44+
return <Rocket className={className} />;
4245
default:
4346
return <Sparkles className={className} />;
4447
}
@@ -84,6 +87,8 @@ const categorizeModels = (models: string[]) => {
8487
categoryName = "Groq";
8588
} else if (model.includes("command") || model.includes("cohere")) {
8689
categoryName = "Cohere";
90+
} else if (model.includes("grok")) {
91+
categoryName = "xAI";
8792
} else {
8893
categoryName = "Other";
8994
}

lib/ai-provider-utils.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Utility functions for AI provider detection and model management
33
*/
44

5-
export type AIProvider = "openai" | "anthropic" | "gemini" | "ollama" | "groq" | "cohere" | "unknown";
5+
export type AIProvider = "openai" | "anthropic" | "gemini" | "ollama" | "groq" | "cohere" | "xai" | "unknown";
66

77
/**
88
* Detects the AI provider based on the API key format
@@ -44,6 +44,11 @@ export function detectProviderFromApiKey(apiKey: string | undefined): AIProvider
4444
return "groq";
4545
}
4646

47+
// xAI: starts with "xai-"
48+
if (trimmedKey.startsWith("xai-")) {
49+
return "xai";
50+
}
51+
4752
// Cohere: typically a long alphanumeric string starting with specific patterns
4853
// No reliable prefix detection for Cohere, handled by model selection
4954

@@ -70,6 +75,8 @@ export function getProviderDisplayName(provider: AIProvider): string {
7075
return "Groq";
7176
case "cohere":
7277
return "Cohere";
78+
case "xai":
79+
return "xAI";
7380
default:
7481
return "Unknown";
7582
}
@@ -120,6 +127,11 @@ export function getProviderApiKeyInfo(provider: AIProvider): {
120127
url: "https://dashboard.cohere.com/api-keys",
121128
description: "Get your Cohere API key from the Cohere Dashboard",
122129
};
130+
case "xai":
131+
return {
132+
url: "https://console.x.ai/",
133+
description: "Get your xAI API key from the xAI Console",
134+
};
123135
default:
124136
return null;
125137
}
@@ -140,7 +152,7 @@ export function detectProviderFromModel(model: string): AIProvider {
140152
const doubleSeparatorIndex = model.indexOf("::");
141153
if (doubleSeparatorIndex !== -1) {
142154
const prefix = model.substring(0, doubleSeparatorIndex);
143-
const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere"];
155+
const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere", "xai"];
144156
const matched = knownProviders.find(p => p === prefix);
145157
if (matched) return matched;
146158
}
@@ -149,7 +161,7 @@ export function detectProviderFromModel(model: string): AIProvider {
149161
const singleSeparatorIndex = model.indexOf(":");
150162
if (singleSeparatorIndex !== -1) {
151163
const prefix = model.substring(0, singleSeparatorIndex);
152-
const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere"];
164+
const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere", "xai"];
153165
const matched = knownProviders.find(p => p === prefix);
154166
if (matched) return matched;
155167
}
@@ -161,6 +173,7 @@ export function detectProviderFromModel(model: string): AIProvider {
161173
if (model.includes("llama") || model.includes("mixtral") || model.includes("phi") || model.includes("deepseek")) return "ollama";
162174
if (model.includes("groq")) return "groq";
163175
if (model.includes("command") || model.includes("cohere")) return "cohere";
176+
if (model.includes("grok")) return "xai";
164177

165178
return "unknown";
166179
}
@@ -185,7 +198,7 @@ export function formatModelDisplayName(modelValue: string): string {
185198
withoutPrefix = modelValue.substring(doubleSepIndex + 2);
186199
} else {
187200
// Remove legacy single-colon provider prefix (e.g., "anthropic:claude-3-5-sonnet")
188-
const knownPrefixes = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere"];
201+
const knownPrefixes = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere", "xai"];
189202
const singleSepIndex = modelValue.indexOf(":");
190203
if (singleSepIndex !== -1) {
191204
const prefix = modelValue.substring(0, singleSepIndex);

0 commit comments

Comments
 (0)