mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-17 04:14:32 +00:00
feat(cliproxy): add xAI Grok provider
This commit is contained in:
1 parent
1b67c7840b
commit
54a2b4759b
37 files changed
+510
-54
No files matched your search
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<title>xAI Grok</title>
|
||||
<path d="M6.469 8.776 16.512 23h-4.464L2.005 8.776H6.47Zm-.004 7.9 2.233 3.164L6.467 23H2l4.465-6.324ZM22 2.582V23h-3.659V7.764L22 2.582ZM22 1l-9.952 14.095-2.233-3.163L17.533 1H22Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Add Account Dialog Component
|
||||
* Uses /start-url to get OAuth URL + polls for completion via management API.
|
||||
* For Device Code flows (ghcp, qwen, kiro): Uses /start endpoint which spawns CLIProxy
|
||||
* For verification-code flows: Uses /start endpoint which spawns CLIProxy
|
||||
* binary and emits WebSocket events. DeviceCodeDialog handles user code display.
|
||||
* Shows auth URL + callback paste field. Polling auto-closes on success.
|
||||
* For Kiro: Also shows "Import from IDE" option.
|
||||
|
||||
@@ -17,6 +17,7 @@ const WIZARD_PROVIDER_ORDER: CLIProxyProvider[] = [
|
||||
'claude',
|
||||
'gemini',
|
||||
'codex',
|
||||
'xai',
|
||||
'cursor',
|
||||
'gitlab',
|
||||
'codebuddy',
|
||||
|
||||
@@ -382,6 +382,68 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
||||
},
|
||||
],
|
||||
},
|
||||
xai: {
|
||||
provider: 'xai',
|
||||
displayName: 'xAI (Grok)',
|
||||
defaultModel: 'grok-build-0.1',
|
||||
models: [
|
||||
{
|
||||
id: 'grok-build-0.1',
|
||||
name: 'Grok Build 0.1',
|
||||
description: 'Fast coding model for agentic software engineering workflows',
|
||||
presetMapping: {
|
||||
default: 'grok-build-0.1',
|
||||
opus: 'grok-4.5',
|
||||
sonnet: 'grok-build-0.1',
|
||||
haiku: 'grok-composer-2.5-fast',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'grok-4.5',
|
||||
name: 'Grok 4.5',
|
||||
description: 'Frontier model for coding, engineering, and agentic workflows',
|
||||
},
|
||||
{
|
||||
id: 'grok-4.3',
|
||||
name: 'Grok 4.3',
|
||||
description: 'General-purpose Grok model with a one-million-token context window',
|
||||
extendedContext: true,
|
||||
},
|
||||
{
|
||||
id: 'grok-4.20-0309-reasoning',
|
||||
name: 'Grok 4.20 0309 Reasoning',
|
||||
description: 'Reasoning model with a two-million-token context window',
|
||||
extendedContext: true,
|
||||
},
|
||||
{
|
||||
id: 'grok-4.20-0309-non-reasoning',
|
||||
name: 'Grok 4.20 0309 Non Reasoning',
|
||||
description: 'Non-reasoning model with a two-million-token context window',
|
||||
extendedContext: true,
|
||||
},
|
||||
{
|
||||
id: 'grok-4.20-multi-agent-0309',
|
||||
name: 'Grok 4.20 Multi Agent 0309',
|
||||
description: 'Multi-agent model with a two-million-token context window',
|
||||
extendedContext: true,
|
||||
},
|
||||
{
|
||||
id: 'grok-3-mini',
|
||||
name: 'Grok 3 Mini',
|
||||
description: 'Compact reasoning model',
|
||||
},
|
||||
{
|
||||
id: 'grok-3-mini-fast',
|
||||
name: 'Grok 3 Mini Fast',
|
||||
description: 'Faster compact reasoning model',
|
||||
},
|
||||
{
|
||||
id: 'grok-composer-2.5-fast',
|
||||
name: 'Grok Composer 2.5 Fast',
|
||||
description: 'Fast Composer model for lightweight coding tasks',
|
||||
},
|
||||
],
|
||||
},
|
||||
qwen: {
|
||||
provider: 'qwen',
|
||||
displayName: 'Qwen',
|
||||
|
||||
@@ -98,6 +98,7 @@ export const PROVIDER_ASSETS: Partial<Record<ProviderVisualId, string>> = {
|
||||
gemini: '/assets/providers/gemini-color.svg',
|
||||
agy: '/assets/providers/agy.png',
|
||||
codex: '/assets/providers/codex-color.svg',
|
||||
xai: '/assets/providers/xai.svg',
|
||||
qwen: '/assets/providers/qwen-color.svg',
|
||||
iflow: '/assets/providers/iflow.png',
|
||||
kiro: '/assets/providers/kiro.png',
|
||||
@@ -128,6 +129,7 @@ export const PROVIDER_FALLBACK_VISUALS: Record<ProviderVisualId, ProviderFallbac
|
||||
gemini: { textClass: 'text-blue-600', letter: 'G' },
|
||||
claude: { textClass: 'text-orange-600', letter: 'C' },
|
||||
codex: { textClass: 'text-emerald-600', letter: 'X' },
|
||||
xai: { textClass: 'text-slate-900', letter: 'X' },
|
||||
agy: { textClass: 'text-violet-600', letter: 'A' },
|
||||
qwen: { textClass: 'text-cyan-600', letter: 'Q' },
|
||||
iflow: { textClass: 'text-indigo-600', letter: 'i' },
|
||||
@@ -265,6 +267,7 @@ export const PROVIDER_COLORS: Record<string, string> = {
|
||||
gemini: '#4285F4',
|
||||
agy: '#f3722c',
|
||||
codex: '#10a37f',
|
||||
xai: '#111827',
|
||||
openai: '#111827',
|
||||
vertex: '#4285F4',
|
||||
iflow: '#f94144',
|
||||
@@ -370,6 +373,7 @@ const DEVICE_CODE_PROVIDER_INSTRUCTIONS: Readonly<Partial<Record<CLIProxyProvide
|
||||
qwen: 'Sign in with your Qwen account to authorize access.',
|
||||
kiro: 'Sign in with your selected Kiro auth provider to continue.',
|
||||
kimi: 'Sign in with your Kimi account and finish the device authorization.',
|
||||
xai: 'Sign in with your xAI account and finish the Grok device authorization.',
|
||||
});
|
||||
|
||||
/** Check if the add-account UI should use the verification-code auth dialog. */
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { MODEL_CATALOGS } from '@/lib/model-catalogs';
|
||||
|
||||
describe('xAI model catalog defaults', () => {
|
||||
it('mirrors the CLIProxyAPI text catalog and default tier routing', () => {
|
||||
const catalog = MODEL_CATALOGS.xai;
|
||||
const ids = catalog.models.map((model) => model.id);
|
||||
const defaultModel = catalog.models.find((model) => model.id === catalog.defaultModel);
|
||||
|
||||
expect(catalog.displayName).toBe('xAI (Grok)');
|
||||
expect(catalog.defaultModel).toBe('grok-build-0.1');
|
||||
expect(ids).toEqual([
|
||||
'grok-build-0.1',
|
||||
'grok-4.5',
|
||||
'grok-4.3',
|
||||
'grok-4.20-0309-reasoning',
|
||||
'grok-4.20-0309-non-reasoning',
|
||||
'grok-4.20-multi-agent-0309',
|
||||
'grok-3-mini',
|
||||
'grok-3-mini-fast',
|
||||
'grok-composer-2.5-fast',
|
||||
]);
|
||||
expect(defaultModel?.presetMapping).toEqual({
|
||||
default: 'grok-build-0.1',
|
||||
opus: 'grok-4.5',
|
||||
sonnet: 'grok-build-0.1',
|
||||
haiku: 'grok-composer-2.5-fast',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
CLIPROXY_PROVIDER_SECTIONS,
|
||||
CLIPROXY_PROVIDERS,
|
||||
CORE_CLIPROXY_PROVIDERS,
|
||||
formatRequestedUpstreamModelRules,
|
||||
getProviderDescription,
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
PROVIDER_COLORS,
|
||||
variantUsesPlusExtraProvider,
|
||||
} from '@/lib/provider-config';
|
||||
import { PROVIDERS as WIZARD_PROVIDERS } from '@/components/setup/wizard/constants';
|
||||
|
||||
describe('provider model mapping helpers', () => {
|
||||
it('parses requested=upstream rules into stored upstream+alias pairs', () => {
|
||||
@@ -61,6 +63,7 @@ describe('provider presentation metadata', () => {
|
||||
expect(CLIPROXY_PROVIDER_SECTIONS.map((section) => section.id)).toEqual(['core', 'plus-extra']);
|
||||
expect(CORE_CLIPROXY_PROVIDERS).toContain('gemini');
|
||||
expect(CORE_CLIPROXY_PROVIDERS).toContain('kimi');
|
||||
expect(CORE_CLIPROXY_PROVIDERS).toContain('xai');
|
||||
expect(PLUS_EXTRA_CLIPROXY_PROVIDERS).toEqual([
|
||||
'kiro',
|
||||
'ghcp',
|
||||
@@ -76,6 +79,12 @@ describe('provider presentation metadata', () => {
|
||||
expect(isPlusExtraProvider('gemini')).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps every non-deprecated canonical provider available in the setup wizard', () => {
|
||||
expect(WIZARD_PROVIDERS.map((provider) => provider.id).sort()).toEqual(
|
||||
CLIPROXY_PROVIDERS.filter((provider) => provider !== 'ghcp').sort()
|
||||
);
|
||||
});
|
||||
|
||||
it('groups provider-backed data by shared section metadata', () => {
|
||||
const grouped = groupProvidersBySection(
|
||||
[
|
||||
@@ -96,6 +105,7 @@ describe('provider presentation metadata', () => {
|
||||
expect(isDeviceCodeProvider('cursor')).toBe(false);
|
||||
expect(isDeviceCodeProvider('ghcp')).toBe(true);
|
||||
expect(isDeviceCodeProvider('qwen')).toBe(true);
|
||||
expect(isDeviceCodeProvider('xai')).toBe(true);
|
||||
});
|
||||
|
||||
it('detects plus-extra providers inside composite variants', () => {
|
||||
@@ -120,6 +130,7 @@ describe('provider presentation metadata', () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
['xai', 'xAI (Grok)', 'Grok coding and reasoning models', '/assets/providers/xai.svg'],
|
||||
['cursor', 'Cursor', 'Cursor browser-authenticated provider', '/assets/sidebar/cursor.svg'],
|
||||
['gitlab', 'GitLab Duo', 'GitLab Duo with OAuth or PAT auth', '/assets/providers/gitlab.svg'],
|
||||
[
|
||||
@@ -153,10 +164,15 @@ describe('provider presentation metadata', () => {
|
||||
textClass: 'text-rose-600',
|
||||
letter: 'K',
|
||||
});
|
||||
expect(getProviderFallbackVisual('xai')).toEqual({
|
||||
textClass: 'text-slate-900',
|
||||
letter: 'X',
|
||||
});
|
||||
|
||||
expect(PROVIDER_COLORS.cursor).toBe('#111827');
|
||||
expect(PROVIDER_COLORS.gitlab).toBe('#FC6D26');
|
||||
expect(PROVIDER_COLORS.codebuddy).toBe('#2563EB');
|
||||
expect(PROVIDER_COLORS.kilo).toBe('#E11D48');
|
||||
expect(PROVIDER_COLORS.xai).toBe('#111827');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user