-
Notifications
You must be signed in to change notification settings - Fork 24
feat: add xAI (Grok) provider support #1576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import { useState, useEffect, useRef, useCallback } from "react"; | ||
| import { cn } from "@/lib/utils"; | ||
| import { formatModelDisplayName } from "@/lib/ai-provider-utils"; | ||
| import { Search, Check, Sparkles, Zap, Brain, Globe, Server, Cpu, MessageSquare, ChevronRight } from "lucide-react"; | ||
| import { Search, Check, Sparkles, Zap, Brain, Globe, Server, Cpu, MessageSquare, ChevronRight, Rocket } from "lucide-react"; | ||
| import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; | ||
| import Input from "../components/ui/Input"; | ||
|
|
||
|
|
@@ -21,6 +21,7 @@ const PROVIDER_DISPLAY_NAMES: Record<string, string> = { | |
| ollama: "Ollama", | ||
| groq: "Groq", | ||
| cohere: "Cohere", | ||
| xai: "xAI", | ||
| }; | ||
|
|
||
| // Get icon for provider category | ||
|
|
@@ -39,6 +40,8 @@ const getCategoryIcon = (category: string) => { | |
| return <Cpu className={className} />; | ||
| case "Cohere": | ||
| return <MessageSquare className={className} />; | ||
| case "xAI": | ||
| return <Rocket className={className} />; | ||
| default: | ||
| return <Sparkles className={className} />; | ||
| } | ||
|
|
@@ -84,6 +87,8 @@ const categorizeModels = (models: string[]) => { | |
| categoryName = "Groq"; | ||
| } else if (model.includes("command") || model.includes("cohere")) { | ||
| categoryName = "Cohere"; | ||
| } else if (model.includes("grok")) { | ||
| categoryName = "xAI"; | ||
| } else { | ||
|
Comment on lines
87
to
92
|
||
| categoryName = "Other"; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
| * Utility functions for AI provider detection and model management | ||
| */ | ||
|
|
||
| export type AIProvider = "openai" | "anthropic" | "gemini" | "ollama" | "groq" | "cohere" | "unknown"; | ||
| export type AIProvider = "openai" | "anthropic" | "gemini" | "ollama" | "groq" | "cohere" | "xai" | "unknown"; | ||
|
|
||
| /** | ||
| * Detects the AI provider based on the API key format | ||
|
|
@@ -44,6 +44,11 @@ export function detectProviderFromApiKey(apiKey: string | undefined): AIProvider | |
| return "groq"; | ||
| } | ||
|
|
||
| // xAI: starts with "xai-" | ||
| if (trimmedKey.startsWith("xai-")) { | ||
| return "xai"; | ||
| } | ||
|
|
||
| // Cohere: typically a long alphanumeric string starting with specific patterns | ||
| // No reliable prefix detection for Cohere, handled by model selection | ||
|
|
||
|
|
@@ -70,6 +75,8 @@ export function getProviderDisplayName(provider: AIProvider): string { | |
| return "Groq"; | ||
| case "cohere": | ||
| return "Cohere"; | ||
| case "xai": | ||
| return "xAI"; | ||
| default: | ||
| return "Unknown"; | ||
| } | ||
|
|
@@ -120,6 +127,11 @@ export function getProviderApiKeyInfo(provider: AIProvider): { | |
| url: "https://dashboard.cohere.com/api-keys", | ||
| description: "Get your Cohere API key from the Cohere Dashboard", | ||
| }; | ||
| case "xai": | ||
| return { | ||
| url: "https://console.x.ai/", | ||
| description: "Get your xAI API key from the xAI Console", | ||
| }; | ||
| default: | ||
| return null; | ||
| } | ||
|
|
@@ -140,7 +152,7 @@ export function detectProviderFromModel(model: string): AIProvider { | |
| const doubleSeparatorIndex = model.indexOf("::"); | ||
| if (doubleSeparatorIndex !== -1) { | ||
| const prefix = model.substring(0, doubleSeparatorIndex); | ||
| const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere"]; | ||
| const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere", "xai"]; | ||
| const matched = knownProviders.find(p => p === prefix); | ||
| if (matched) return matched; | ||
| } | ||
|
|
@@ -149,7 +161,7 @@ export function detectProviderFromModel(model: string): AIProvider { | |
| const singleSeparatorIndex = model.indexOf(":"); | ||
| if (singleSeparatorIndex !== -1) { | ||
| const prefix = model.substring(0, singleSeparatorIndex); | ||
| const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere"]; | ||
| const knownProviders: AIProvider[] = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere", "xai"]; | ||
| const matched = knownProviders.find(p => p === prefix); | ||
| if (matched) return matched; | ||
|
Comment on lines
152
to
166
|
||
| } | ||
|
|
@@ -161,6 +173,7 @@ export function detectProviderFromModel(model: string): AIProvider { | |
| if (model.includes("llama") || model.includes("mixtral") || model.includes("phi") || model.includes("deepseek")) return "ollama"; | ||
| if (model.includes("groq")) return "groq"; | ||
| if (model.includes("command") || model.includes("cohere")) return "cohere"; | ||
| if (model.includes("grok")) return "xai"; | ||
|
|
||
| return "unknown"; | ||
| } | ||
|
|
@@ -185,7 +198,7 @@ export function formatModelDisplayName(modelValue: string): string { | |
| withoutPrefix = modelValue.substring(doubleSepIndex + 2); | ||
| } else { | ||
| // Remove legacy single-colon provider prefix (e.g., "anthropic:claude-3-5-sonnet") | ||
| const knownPrefixes = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere"]; | ||
| const knownPrefixes = ["openai", "anthropic", "gemini", "ollama", "groq", "cohere", "xai"]; | ||
| const singleSepIndex = modelValue.indexOf(":"); | ||
| if (singleSepIndex !== -1) { | ||
| const prefix = modelValue.substring(0, singleSepIndex); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
categorizeModelsre-implements provider detection heuristics that largely overlap withdetectProviderFromModelinlib/ai-provider-utils.ts. This duplication means future provider additions/heuristic tweaks can easily get out of sync between backend validation (model/API key mismatch) and UI grouping. Consider importing and usingdetectProviderFromModel(plusgetProviderDisplayName/ the existing mapping) to derive categories instead of maintaining a separate heuristic set here.