fix(grok): route OAuth media through official API

This commit is contained in:
Heatherm Huang
2026-07-13 17:08:55 +08:00
parent 55ed0ab0da
commit bb73416733
4 changed files with 175 additions and 2 deletions
+37 -1
View File
@@ -1251,6 +1251,9 @@ func (a *Account) GetOpenAIRefreshToken() string {
return a.GetCredential("refresh_token")
}
// GetGrokBaseURL selects the upstream used by Grok text and Responses traffic.
// Grok media traffic has a different transport contract and must use
// GetGrokMediaBaseURL instead.
func (a *Account) GetGrokBaseURL() string {
if !a.IsGrok() {
return ""
@@ -1271,12 +1274,45 @@ func (a *Account) GetGrokBaseURL() string {
return xai.DefaultBaseURL
}
// GetGrokMediaBaseURL selects the upstream used by Grok Imagine APIs.
//
// OAuth text requests need the CLI subscription proxy, but that proxy has a
// smaller request-body limit than the official Imagine API. Media requests can
// contain large base64 inputs, so default OAuth accounts must use api.x.ai.
// API-key accounts and explicit unsafe development overrides retain their
// configured base URL.
func (a *Account) GetGrokMediaBaseURL() string {
if !a.IsGrok() {
return ""
}
if !a.IsGrokOAuth() {
return a.GetGrokBaseURL()
}
baseURL := a.GetCredential("base_url")
if strings.TrimSpace(baseURL) == "" || isOfficialGrokAPIBaseURL(baseURL) || isOfficialGrokCLIBaseURL(baseURL) {
return xai.DefaultBaseURL
}
if _, err := xai.ValidateTrustedBaseURL(baseURL); err == nil {
return baseURL
}
return xai.DefaultBaseURL
}
func isOfficialGrokAPIBaseURL(raw string) bool {
return isOfficialGrokBaseURL(raw, xai.DefaultBaseURL)
}
func isOfficialGrokCLIBaseURL(raw string) bool {
return isOfficialGrokBaseURL(raw, xai.DefaultCLIBaseURL)
}
func isOfficialGrokBaseURL(raw, expected string) bool {
parsed, err := url.Parse(strings.TrimSpace(raw))
if err != nil || parsed == nil || parsed.Opaque != "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" {
return false
}
defaultURL, err := url.Parse(xai.DefaultBaseURL)
defaultURL, err := url.Parse(expected)
if err != nil {
return false
}
@@ -306,3 +306,104 @@ func TestGetGrokBaseURLAllowsExplicitOAuthOverrideWhenUnsafeOverridesEnabled(t *
require.Equal(t, "https://custom.example.com/v1", account.GetGrokBaseURL())
}
func TestGetGrokMediaBaseURLSeparatesOAuthMediaFromCLIProxy(t *testing.T) {
tests := []struct {
name string
account Account
expected string
}{
{
name: "oauth without base_url uses official media API",
account: Account{
Type: AccountTypeOAuth,
Platform: PlatformGrok,
Credentials: map[string]any{},
},
expected: xai.DefaultBaseURL,
},
{
name: "oauth stored CLI proxy uses official media API",
account: Account{
Type: AccountTypeOAuth,
Platform: PlatformGrok,
Credentials: map[string]any{
"base_url": xai.DefaultCLIBaseURL,
},
},
expected: xai.DefaultBaseURL,
},
{
name: "oauth stored CLI proxy variant uses official media API",
account: Account{
Type: AccountTypeOAuth,
Platform: PlatformGrok,
Credentials: map[string]any{
"base_url": "HTTPS://CLI-CHAT-PROXY.GROK.COM:443/%76%31/",
},
},
expected: xai.DefaultBaseURL,
},
{
name: "oauth legacy official API remains on official media API",
account: Account{
Type: AccountTypeOAuth,
Platform: PlatformGrok,
Credentials: map[string]any{
"base_url": xai.DefaultBaseURL,
},
},
expected: xai.DefaultBaseURL,
},
{
name: "oauth untrusted custom base_url is pinned to official media API",
account: Account{
Type: AccountTypeOAuth,
Platform: PlatformGrok,
Credentials: map[string]any{
"base_url": "https://custom.example.com/v1",
},
},
expected: xai.DefaultBaseURL,
},
{
name: "API key retains its configured media API",
account: Account{
Type: AccountTypeAPIKey,
Platform: PlatformGrok,
Credentials: map[string]any{
"base_url": "https://grok.example.com/v1",
},
},
expected: "https://grok.example.com/v1",
},
{
name: "non-Grok account has no Grok media base URL",
account: Account{
Type: AccountTypeOAuth,
Platform: PlatformOpenAI,
Credentials: map[string]any{},
},
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.expected, tt.account.GetGrokMediaBaseURL())
})
}
}
func TestGetGrokMediaBaseURLAllowsExplicitOAuthOverrideWhenUnsafeOverridesEnabled(t *testing.T) {
t.Setenv(xai.EnvAllowUnsafeURLOverrides, "true")
account := Account{
Type: AccountTypeOAuth,
Platform: PlatformGrok,
Credentials: map[string]any{
"base_url": "https://custom.example.com/v1",
},
}
require.Equal(t, "https://custom.example.com/v1", account.GetGrokMediaBaseURL())
}
+1 -1
View File
@@ -308,7 +308,7 @@ func (s *OpenAIGatewayService) ForwardGrokMedia(
if err != nil {
return nil, err
}
targetURL, err := endpoint.upstreamURL(account.GetGrokBaseURL(), requestID)
targetURL, err := endpoint.upstreamURL(account.GetGrokMediaBaseURL(), requestID)
if err != nil {
return nil, err
}
@@ -611,6 +611,42 @@ func TestForwardGrokMediaVideoGenerationPreservesImageToVideoModel(t *testing.T)
require.Equal(t, VideoBillingDefaultDurationSeconds, result.VideoDurationSeconds)
}
func TestForwardGrokMediaOAuthImageToVideoUsesOfficialAPIForLargeBody(t *testing.T) {
gin.SetMode(gin.TestMode)
recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder)
imageData := strings.Repeat("A", 2*1024*1024)
body := []byte(`{"model":"grok-imagine-video-1.5","prompt":"animate","image":{"image_url":"data:image/png;base64,` + imageData + `"}}`)
c.Request = httptest.NewRequest(http.MethodPost, "/v1/videos/generations", bytes.NewReader(body))
c.Request.Header.Set("Content-Type", "application/json")
account := &Account{
ID: 66,
Name: "grok-oauth",
Platform: PlatformGrok,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "oauth-access-token",
"base_url": xai.DefaultCLIBaseURL,
},
}
upstream := &httpUpstreamRecorder{resp: &http.Response{
StatusCode: http.StatusOK,
Header: http.Header{
"Content-Type": []string{"application/json"},
},
Body: io.NopCloser(strings.NewReader(`{"request_id":"video-request-oauth"}`)),
}}
svc := &OpenAIGatewayService{httpUpstream: upstream}
_, err := svc.ForwardGrokMedia(context.Background(), c, account, GrokMediaEndpointVideosGenerations, "", body, "application/json")
require.NoError(t, err)
require.Equal(t, xai.DefaultBaseURL+"/videos/generations", upstream.lastReq.URL.String())
require.Equal(t, "data:image/png;base64,"+imageData, gjson.GetBytes(upstream.lastBody, "image.image_url").String())
}
func TestForwardGrokMediaVideoStatusUsesGETWithoutBody(t *testing.T) {
t.Setenv(xai.EnvAllowUnsafeURLOverrides, "true")
gin.SetMode(gin.TestMode)