fix(grok): support upstream model sync for API keys

This commit is contained in:
Heatherm Huang
2026-07-13 11:46:52 +08:00
parent a1930ea6f2
commit b0441ca5aa
2 changed files with 88 additions and 0 deletions
@@ -131,6 +131,8 @@ func (s *AccountTestService) buildUpstreamModelsRequest(ctx context.Context, acc
switch {
case account.Platform == PlatformAntigravity:
return s.buildAntigravityAPIKeyModelsRequest(ctx, account)
case account.IsGrok():
return s.buildGrokUpstreamModelsRequest(ctx, account)
case account.IsOpenAI():
return s.buildOpenAIUpstreamModelsRequest(ctx, account)
case account.IsGemini():
@@ -144,6 +146,36 @@ func (s *AccountTestService) buildUpstreamModelsRequest(ctx context.Context, acc
}
}
func (s *AccountTestService) buildGrokUpstreamModelsRequest(ctx context.Context, account *Account) (*http.Request, error) {
if account.Type != AccountTypeAPIKey {
return nil, newUpstreamModelSyncUnsupportedError(
fmt.Sprintf("Unsupported Grok account type for upstream model sync: %s", account.Type), nil,
)
}
apiKey := strings.TrimSpace(account.GetCredential("api_key"))
if apiKey == "" {
return nil, newUpstreamModelSyncConfigError("No Grok API key is available", nil)
}
baseURL := strings.TrimSpace(account.GetCredential("base_url"))
if baseURL == "" {
baseURL = "https://api.x.ai"
}
normalizedBaseURL, err := s.validateUpstreamBaseURL(baseURL)
if err != nil {
return nil, newUpstreamModelSyncConfigError("Invalid Grok base URL", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, buildOpenAIModelsURL(normalizedBaseURL), nil)
if err != nil {
return nil, newUpstreamModelSyncConfigError("Invalid Grok model list URL", err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
account.ApplyHeaderOverrides(req.Header)
return req, nil
}
func (s *AccountTestService) buildAnthropicUpstreamModelsRequest(ctx context.Context, account *Account) (*http.Request, error) {
if account.IsBedrock() || account.Type == AccountTypeServiceAccount {
return nil, newUpstreamModelSyncUnsupportedError(
@@ -177,6 +177,18 @@ func TestBuildUpstreamModelsRequestsForAPIKeyAccounts(t *testing.T) {
require.Equal(t, "https://openai.example.com/v1/models", openAIReq.URL.String())
require.Equal(t, "Bearer openai-key", openAIReq.Header.Get("Authorization"))
grokReq, err := svc.buildUpstreamModelsRequest(ctx, &Account{
Platform: PlatformGrok,
Type: AccountTypeAPIKey,
Credentials: map[string]any{
"api_key": "xai-key",
"base_url": "https://xai.example.com/v1",
},
})
require.NoError(t, err)
require.Equal(t, "https://xai.example.com/v1/models", grokReq.URL.String())
require.Equal(t, "Bearer xai-key", grokReq.Header.Get("Authorization"))
geminiReq, err := svc.buildGeminiUpstreamModelsRequest(ctx, &Account{
Platform: PlatformGemini,
Type: AccountTypeAPIKey,
@@ -202,6 +214,22 @@ func TestBuildUpstreamModelsRequestsForAPIKeyAccounts(t *testing.T) {
require.Equal(t, "antigravity-key", antigravityReq.Header.Get("x-api-key"))
}
func TestBuildUpstreamModelsRequestRejectsGrokOAuth(t *testing.T) {
t.Parallel()
svc := &AccountTestService{cfg: upstreamModelSyncTestConfig()}
_, err := svc.buildUpstreamModelsRequest(context.Background(), &Account{
Platform: PlatformGrok,
Type: AccountTypeOAuth,
})
require.Error(t, err)
var syncErr *UpstreamModelSyncError
require.True(t, errors.As(err, &syncErr))
require.Equal(t, UpstreamModelSyncErrorUnsupported, syncErr.Kind)
require.Contains(t, syncErr.SafeMessage(), "Unsupported Grok account type")
}
func TestBuildAntigravityAPIKeyModelsRequestRejectsOfficialCloudCodeBase(t *testing.T) {
t.Parallel()
@@ -265,6 +293,34 @@ func TestFetchUpstreamSupportedModelsParsesOpenAIResponse(t *testing.T) {
require.Equal(t, "Bearer openai-key", upstream.lastReq.Header.Get("Authorization"))
}
func TestFetchUpstreamSupportedModelsParsesGrokAPIKeyResponse(t *testing.T) {
t.Parallel()
upstream := &httpUpstreamRecorder{resp: &http.Response{
StatusCode: http.StatusOK,
Header: http.Header{"Content-Type": []string{"application/json"}},
Body: io.NopCloser(strings.NewReader(`{"data":[{"id":"grok-4.5"},{"id":"grok-4.5"},{"id":"grok-imagine"}]}`)),
}}
svc := &AccountTestService{
httpUpstream: upstream,
cfg: upstreamModelSyncTestConfig(),
}
models, err := svc.FetchUpstreamSupportedModels(context.Background(), &Account{
ID: 9,
Platform: PlatformGrok,
Type: AccountTypeAPIKey,
Credentials: map[string]any{
"api_key": "xai-key",
"base_url": "https://xai.example.com/v1",
},
})
require.NoError(t, err)
require.Equal(t, []string{"grok-4.5", "grok-imagine"}, models)
require.Equal(t, "https://xai.example.com/v1/models", upstream.lastReq.URL.String())
require.Equal(t, "Bearer xai-key", upstream.lastReq.Header.Get("Authorization"))
}
func TestFetchUpstreamSupportedModelsDoesNotExposeUpstreamBody(t *testing.T) {
t.Parallel()