fix(grok): drop account-test ZDR path and align media CLI headers

Remove optional upload_url / fake connectivity-only success from admin video
tests. Stamp Grok CLI headers only on the CLI proxy so OAuth media against
api.x.ai can complete and preview video like the gateway path.
This commit is contained in:
IanShaw027
2026-08-08 09:45:12 +08:00
parent 77e39fe608
commit 12db0f906a
3 changed files with 19 additions and 85 deletions
@@ -1067,10 +1067,8 @@ type TestAccountRequest struct {
Mode string `json:"mode"`
// Optional media for Grok (and future) real generation tests.
// ImageDataURL / AudioDataURL are data:<mime>;base64,... payloads.
// VideoUploadURL is a public HTTPS PUT URL for ZDR video output.
ImageDataURL string `json:"image_data_url"`
AudioDataURL string `json:"audio_data_url"`
VideoUploadURL string `json:"video_upload_url"`
ImageDataURL string `json:"image_data_url"`
AudioDataURL string `json:"audio_data_url"`
}
type SyncFromCRSRequest struct {
@@ -1101,9 +1099,8 @@ func (h *AccountHandler) Test(c *gin.Context) {
_ = c.ShouldBindJSON(&req)
opts := service.AccountTestOptions{
ImageDataURL: req.ImageDataURL,
AudioDataURL: req.AudioDataURL,
VideoUploadURL: req.VideoUploadURL,
ImageDataURL: req.ImageDataURL,
AudioDataURL: req.AudioDataURL,
}
// Use AccountTestService to test the account with SSE streaming
@@ -66,11 +66,9 @@ type TestEvent struct {
// AccountTestOptions carries optional media for admin connectivity tests.
// ImageDataURL / AudioDataURL are full data URLs (data:<mime>;base64,...).
// VideoUploadURL is an optional public HTTPS PUT URL for ZDR video output.
type AccountTestOptions struct {
ImageDataURL string
AudioDataURL string
VideoUploadURL string
ImageDataURL string
AudioDataURL string
}
func firstAccountTestOptions(opts []AccountTestOptions) AccountTestOptions {
@@ -137,22 +135,6 @@ func normalizeGrokAccountTestMode(mode string) string {
}
}
// isGrokVideoZDRUploadURLRequired reports when xAI rejects video create because
// Zero Data Retention teams must supply output.upload_url. For admin connectivity
// probes we treat this as a successful reachability signal (endpoint + auth work).
func isGrokVideoZDRUploadURLRequired(statusCode int, body []byte) bool {
if statusCode != http.StatusBadRequest && statusCode != http.StatusUnprocessableEntity {
return false
}
msg := strings.ToLower(string(body))
if !strings.Contains(msg, "upload_url") {
return false
}
return strings.Contains(msg, "zero data retention") ||
strings.Contains(msg, "zdr") ||
strings.Contains(msg, "must provide output.upload_url")
}
// AccountTestService handles account testing operations
type AccountTestService struct {
accountRepo AccountRepository
@@ -277,7 +259,7 @@ func createTestPayload(modelID string) (map[string]any, error) {
// All account types use full Claude Code client characteristics, only auth header differs
// modelID is optional - if empty, defaults to claude.DefaultTestModel
// mode is optional - "compact" routes OpenAI accounts to the /responses/compact probe path
// opts is optional media (image/audio data URLs, video upload_url for ZDR).
// opts is optional media (image/audio data URLs for real generation / STT).
func (s *AccountTestService) TestAccountConnection(c *gin.Context, accountID int64, modelID string, prompt string, mode string, opts ...AccountTestOptions) error {
ctx := c.Request.Context()
testOpts := firstAccountTestOptions(opts)
@@ -962,7 +944,10 @@ func (s *AccountTestService) applyGrokTestRequestHeaders(req *http.Request, acco
req.Header.Set("Accept", accept)
}
req.Header.Set("Authorization", "Bearer "+authToken)
if account.IsGrokOAuth() {
// Match gateway media/voice: CLI identity headers only on the CLI chat proxy.
// api.x.ai media (images/videos) rejects or mistreats OAuth when CLI headers
// are stamped on the official API host (e.g. ZDR upload_url false positives).
if account.IsGrokOAuth() && req.URL != nil && isGrokCLIProxyTarget(req.URL.String()) {
applyGrokCLIHeaders(req.Header)
}
account.ApplyHeaderOverrides(req.Header)
@@ -1253,13 +1238,6 @@ func (s *AccountTestService) testGrokVideoGeneration(c *gin.Context, ctx context
payload["image"] = grokMediaImageObject(normalized)
s.sendEvent(c, TestEvent{Type: "content", Text: "using uploaded first-frame / reference image\n"})
}
if uploadURL := strings.TrimSpace(opts.VideoUploadURL); uploadURL != "" {
if err := validateAccountTestPublicUploadURL(uploadURL); err != nil {
return s.sendErrorAndEnd(c, err.Error())
}
payload["output"] = map[string]any{"upload_url": uploadURL}
s.sendEvent(c, TestEvent{Type: "content", Text: "using client-provided output.upload_url for ZDR video output\n"})
}
payloadBytes, _ := json.Marshal(payload)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(payloadBytes))
@@ -1280,25 +1258,6 @@ func (s *AccountTestService) testGrokVideoGeneration(c *gin.Context, ctx context
return s.sendErrorAndEnd(c, fmt.Sprintf("Failed to read Grok video response: %s", err.Error()))
}
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted && resp.StatusCode != http.StatusCreated {
// ZDR teams require a client-hosted output.upload_url that xAI can PUT the
// finished MP4 into. Admin account tests do not expose a public upload sink
// (would need HTTPS public base + PUT receiver, like a media upload ticket).
// Reaching this validation still proves auth + /videos/generations routing.
// Mark success for connectivity, but state clearly that no video was produced.
if isGrokVideoZDRUploadURLRequired(resp.StatusCode, body) {
s.sendEvent(c, TestEvent{
Type: "content",
Text: "" +
"NO VIDEO GENERATED — this is connectivity-only for ZDR accounts.\n" +
"xAI Zero Data Retention requires payload field output.upload_url (public HTTPS PUT URL);\n" +
"this admin probe does not host that receiver, so generation cannot complete here.\n" +
"What worked: auth token + POST /v1/videos/generations reached xAI and returned the ZDR rule.\n" +
"To actually generate video: call gateway /v1/videos/generations with a client-provided output.upload_url, or use a non-ZDR team.\n",
})
s.sendEvent(c, TestEvent{Type: "status", Text: "Connectivity OK (no video file produced)"})
s.sendEvent(c, TestEvent{Type: "test_complete", Success: true})
return nil
}
return s.sendErrorAndEnd(c, fmt.Sprintf("Grok videos API returned %d: %s", resp.StatusCode, string(body)))
}
@@ -1392,8 +1351,7 @@ func (s *AccountTestService) emitGrokVideoResult(c *gin.Context, ctx context.Con
defer func() { _ = resp.Body.Close() }()
body, _ := io.ReadAll(io.LimitReader(resp.Body, 64<<20)) // 64 MiB cap for admin preview
if resp.StatusCode != http.StatusOK {
// Fall back to reporting status-only success if content is unavailable
// (e.g. ZDR already PUT to client upload_url).
// Fall back to status URL when binary content is unavailable.
if videoURL != "" {
s.sendEvent(c, TestEvent{Type: "content", Text: "video completed; content download unavailable, reported url=" + videoURL + "\n"})
s.sendEvent(c, TestEvent{Type: "video", VideoURL: videoURL, MimeType: "video/mp4"})
@@ -1895,24 +1853,6 @@ func decodeAccountTestDataURL(raw string) (data []byte, mime string, err error)
return decoded, mime, nil
}
func validateAccountTestPublicUploadURL(raw string) error {
raw = strings.TrimSpace(raw)
if raw == "" {
return fmt.Errorf("upload_url is empty")
}
u, err := url.Parse(raw)
if err != nil || u == nil {
return fmt.Errorf("upload_url is not a valid URL")
}
if !strings.EqualFold(u.Scheme, "https") {
return fmt.Errorf("upload_url must be https (xAI requires a public HTTPS PUT URL)")
}
if strings.TrimSpace(u.Host) == "" {
return fmt.Errorf("upload_url host is required")
}
return nil
}
func sttFilenameForMIME(mime string) string {
switch strings.ToLower(strings.TrimSpace(mime)) {
case "audio/mpeg", "audio/mp3":
@@ -502,10 +502,10 @@ func TestAccountTestService_GrokExplicitImageModeDefaultsModel(t *testing.T) {
require.Contains(t, rec.Body.String(), `"type":"test_complete"`)
}
func TestAccountTestService_GrokVideoZDRUploadURLRequiredCountsAsConnectivityOK(t *testing.T) {
func TestAccountTestService_GrokVideoUpstreamErrorIsNotMaskedAsSuccess(t *testing.T) {
gin.SetMode(gin.TestMode)
account := &Account{
ID: 21, Name: "grok-oauth-video-zdr", Platform: PlatformGrok,
ID: 21, Name: "grok-oauth-video-err", Platform: PlatformGrok,
Type: AccountTypeOAuth, Status: StatusActive, Schedulable: true, Concurrency: 1,
Credentials: map[string]any{
"access_token": "grok-access-token",
@@ -518,7 +518,7 @@ func TestAccountTestService_GrokVideoZDRUploadURLRequiredCountsAsConnectivityOK(
StatusCode: http.StatusBadRequest,
Header: http.Header{"Content-Type": []string{"application/json"}},
Body: io.NopCloser(strings.NewReader(
`{"code":"invalid-argument","error":"Zero Data Retention teams must provide output.upload_url for video generation."}`,
`{"code":"invalid-argument","error":"bad video request"}`,
)),
}}
svc := &AccountTestService{
@@ -532,14 +532,11 @@ func TestAccountTestService_GrokVideoZDRUploadURLRequiredCountsAsConnectivityOK(
err := svc.TestAccountConnection(c, account.ID, "grok-imagine-video", "bounce ball", AccountTestModeGrokVideo)
require.NoError(t, err)
require.Error(t, err)
require.Equal(t, "https://api.x.ai/v1/videos/generations", upstream.lastReq.URL.String())
require.Contains(t, rec.Body.String(), "NO VIDEO GENERATED")
require.Contains(t, rec.Body.String(), "output.upload_url")
require.Contains(t, rec.Body.String(), "Connectivity OK")
require.Contains(t, rec.Body.String(), `"type":"test_complete"`)
require.Contains(t, rec.Body.String(), `"success":true`)
require.NotContains(t, rec.Body.String(), `"type":"error"`)
require.Contains(t, rec.Body.String(), `"type":"error"`)
require.Contains(t, rec.Body.String(), "Grok videos API returned 400")
require.NotContains(t, rec.Body.String(), `"success":true`)
}
type grokRealtimeTestConn struct {