Skip to content

Commit 39be1ec

Browse files
committed
feat: add grok subscription support
1 parent fd9c945 commit 39be1ec

92 files changed

Lines changed: 3277 additions & 197 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,50 @@ Simple Mode is designed for individual developers or internal teams who want qui
581581

582582
---
583583

584+
## Grok / xAI OAuth Support
585+
586+
Sub2API supports Grok subscription accounts through xAI OAuth and forwards OpenAI-compatible Responses traffic to xAI.
587+
588+
### Supported Scope
589+
590+
- Platform name: `grok`
591+
- Account type: OAuth subscription accounts
592+
- Gateway target: `${XAI_BASE_URL:-https://api.x.ai/v1}/responses`
593+
- Initial models: `grok-4.3`, `grok-build-0.1`, `grok-4.20-0309-reasoning`, `grok-4.20-0309-non-reasoning`, and `grok-4.20-multi-agent-0309`
594+
- Out of scope for this provider: image, video, TTS, transcription, browser automation, cookies, and Grok web scraping
595+
596+
### OAuth Configuration
597+
598+
The Grok OAuth flow uses PKCE and does not require committing private secrets. The default client details follow the public xAI OAuth flow used by compatible clients, and every value can be overridden by environment variable:
599+
600+
| Variable | Default |
601+
|----------|---------|
602+
| `XAI_OAUTH_CLIENT_ID` | Public xAI OAuth client ID |
603+
| `XAI_OAUTH_SCOPE` | `openid profile email offline_access grok-cli:access api:access` |
604+
| `XAI_OAUTH_REDIRECT_URI` | `http://127.0.0.1:56121/callback` |
605+
| `XAI_OAUTH_AUTHORIZE_URL` | `https://auth.x.ai/oauth2/authorize` |
606+
| `XAI_OAUTH_TOKEN_URL` | `https://auth.x.ai/oauth2/token` |
607+
| `XAI_BASE_URL` | `https://api.x.ai/v1` |
608+
609+
Administrators can create or reauthorize Grok accounts from the dashboard, or use the admin API:
610+
611+
| Endpoint | Purpose |
612+
|----------|---------|
613+
| `POST /api/v1/admin/grok/oauth/auth-url` | Generate an xAI OAuth authorization URL |
614+
| `POST /api/v1/admin/grok/oauth/exchange-code` | Exchange a callback URL, query string, or code for OAuth credentials |
615+
| `POST /api/v1/admin/grok/oauth/refresh-token` | Validate or refresh a Grok refresh token |
616+
| `POST /api/v1/admin/grok/accounts/:id/refresh` | Refresh an existing Grok account |
617+
618+
Credential storage reuses the existing account JSON fields: `access_token`, `refresh_token`, `token_type`, `expires_at`, optional `email`, optional `subscription_tier`, and `entitlement_status`.
619+
620+
### Usage And Quota Display
621+
622+
xAI quota is passive. Sub2API does not invent subscription quota values; it records whitelisted xAI rate-limit headers from successful or rate-limited upstream responses when xAI sends them. Before the first usable upstream response, the dashboard shows quota as unknown and still displays local Sub2API usage stats.
623+
624+
`401` responses mark the account as needing reauthorization. `403` responses are treated as entitlement or subscription-tier failures instead of token-refresh loops. `429` responses use `Retry-After` or a short cooldown to temporarily remove the account from scheduling.
625+
626+
---
627+
584628
## Antigravity Support
585629

586630
Sub2API supports [Antigravity](https://antigravity.so/) accounts. After authorization, dedicated endpoints are available for Claude and Gemini models.

‎backend/cmd/server/wire.go‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func provideCleanup(
9494
openaiOAuth *service.OpenAIOAuthService,
9595
geminiOAuth *service.GeminiOAuthService,
9696
antigravityOAuth *service.AntigravityOAuthService,
97+
grokOAuth *service.GrokOAuthService,
9798
openAIGateway *service.OpenAIGatewayService,
9899
scheduledTestRunner *service.ScheduledTestRunnerService,
99100
backupSvc *service.BackupService,
@@ -222,6 +223,12 @@ func provideCleanup(
222223
antigravityOAuth.Stop()
223224
return nil
224225
}},
226+
{"GrokOAuthService", func() error {
227+
if grokOAuth != nil {
228+
grokOAuth.Stop()
229+
}
230+
return nil
231+
}},
225232
{"OpenAIWSPool", func() error {
226233
if openAIGateway != nil {
227234
openAIGateway.CloseOpenAIWSPool()

‎backend/cmd/server/wire_gen.go‎

Lines changed: 17 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎backend/cmd/server/wire_gen_test.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func TestProvideCleanup_WithMinimalDependencies_NoPanic(t *testing.T) {
7474
openAIOAuthSvc,
7575
geminiOAuthSvc,
7676
antigravityOAuthSvc,
77+
nil, // grokOAuth
7778
nil, // openAIGateway
7879
nil, // scheduledTestRunner
7980
nil, // backupSvc

‎backend/ent/schema/user_platform_quota.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (UserPlatformQuota) Fields() []ent.Field {
4141
// 注意:平台列表的单一权威源为 service.AllowedQuotaPlatforms;
4242
// 此处为 ent 构建期约束,需与 service.AllowedQuotaPlatforms 保持同步。
4343
switch s {
44-
case "anthropic", "openai", "gemini", "antigravity":
44+
case "anthropic", "openai", "gemini", "antigravity", "grok":
4545
return nil
4646
default:
4747
return fmt.Errorf("platform %q is not allowed", s)

‎backend/go.sum‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
164164
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
165165
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
166166
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
167+
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
168+
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
167169
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
168170
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
169171
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=

‎backend/internal/domain/constants.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const (
2222
PlatformOpenAI = "openai"
2323
PlatformGemini = "gemini"
2424
PlatformAntigravity = "antigravity"
25+
PlatformGrok = "grok"
2526
)
2627

2728
// Account type constants

‎backend/internal/handler/admin/channel_handler.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ var platformToLiteLLMProvider = map[string]string{
509509
service.PlatformOpenAI: "openai",
510510
service.PlatformGemini: "google",
511511
service.PlatformAntigravity: "anthropic",
512+
service.PlatformGrok: "xai",
512513
}
513514

514515
// SyncPricingModels 返回 LiteLLM 定价目录中指定平台的最新模型列表

0 commit comments

Comments
 (0)