History

Merge pull request #4668 from oscarbrey/fix/grok-imagine-video-1.5-ga

fix(xai): support Grok Imagine Video 1.5 GA

Luis Pater committed 3 months ago

Commit 32f10692 parents f27be00 84232746 files · +263 −60Browse files

Unified · Split

6 files
  1. internal/api/server_test.go +2 −0
  2. internal/client/codex/models/models.go +1 −1
  3. internal/registry/model_definitions.go +24 −10
  4. internal/registry/model_definitions_test.go +14 −4
  5. sdk/api/handlers/openai/openai_videos_handlers.go +42 −24
  6. sdk/api/handlers/openai/openai_videos_handlers_test.go +180 −21
internal/api/server_test.go+2−0
@@ -1648,6 +1648,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) { · 1647 unmodified lines
16481648 {ID: "gpt-image-2", Object: "model", OwnedBy: "openai", Type: "openai"},
16491649 {ID: "grok-imagine-image", Object: "model", OwnedBy: "xai", Type: "openai"},
16501650 {ID: "grok-imagine-video", Object: "model", OwnedBy: "xai", Type: "openai"},
1651+ {ID: "grok-imagine-video-1.5", Object: "model", OwnedBy: "xai", Type: "openai"},
16511652 {ID: "grok-imagine-video-1.5-preview", Object: "model", OwnedBy: "xai", Type: "openai"},
16521653 })
16531654 t.Cleanup(func() {
@@ -1747,6 +1748,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) { · 93 unmodified lines
17471748 "gpt-image-2": false,
17481749 "grok-imagine-image": false,
17491750 "grok-imagine-video": false,
1751+ "grok-imagine-video-1.5": false,
17501752 "grok-imagine-video-1.5-preview": false,
17511753 }
17521754 for _, model := range resp.Models {
internal/client/codex/models/models.go+1−1
@@ -286,7 +286,7 @@ func applyCodexClientModelMetadata(entry map[string]any, id string, model map[st · 285 unmodified lines
286286
287287 func applyCodexClientVisibilityOverride(entry map[string]any, id string) {
288288 switch strings.TrimSpace(id) {
289− case "grok-imagine-image-quality", "gpt-image-1.5", "gpt-image-2", "grok-imagine-image", "grok-imagine-video", "grok-imagine-video-1.5-preview":
289+ case "grok-imagine-image-quality", "gpt-image-1.5", "gpt-image-2", "grok-imagine-image", "grok-imagine-video", "grok-imagine-video-1.5", "grok-imagine-video-1.5-preview":
290290 entry["visibility"] = "hide"
291291 }
292292 }
internal/registry/model_definitions.go+24−10
@@ -7,12 +7,13 @@ import ( · 6 unmodified lines
77 )
88
99 const (
10− codexBuiltinImage15ModelID = "gpt-image-1.5"
11− codexBuiltinImageModelID = "gpt-image-2"
12− xaiBuiltinImageModelID = "grok-imagine-image"
13− xaiBuiltinImageQualityModelID = "grok-imagine-image-quality"
14− xaiBuiltinVideoModelID = "grok-imagine-video"
15− xaiBuiltinVideo15PreviewModelID = "grok-imagine-video-1.5-preview"
10+ codexBuiltinImage15ModelID = "gpt-image-1.5"
11+ codexBuiltinImageModelID = "gpt-image-2"
12+ xaiBuiltinImageModelID = "grok-imagine-image"
13+ xaiBuiltinImageQualityModelID = "grok-imagine-image-quality"
14+ xaiBuiltinVideoModelID = "grok-imagine-video"
15+ xaiBuiltinVideo15ModelID = "grok-imagine-video-1.5"
16+ xaiBuiltinVideo15PreviewID = "grok-imagine-video-1.5-preview"
1617 )
1718
1819 // staticModelsJSON mirrors the top-level structure of models.json.
@@ -120,7 +121,7 @@ func WithCodexBuiltins(models []*ModelInfo) []*ModelInfo { · 101 unmodified lines
120121 // WithXAIBuiltins injects hard-coded xAI image/video model definitions that should
121122 // not depend on remote models.json updates.
122123 func WithXAIBuiltins(models []*ModelInfo) []*ModelInfo {
123− return upsertModelInfos(models, xaiBuiltinImageModelInfo(), xaiBuiltinImageQualityModelInfo(), xaiBuiltinVideoModelInfo(), xaiBuiltinVideo15PreviewModelInfo())
124+ return upsertModelInfos(models, xaiBuiltinImageModelInfo(), xaiBuiltinImageQualityModelInfo(), xaiBuiltinVideoModelInfo(), xaiBuiltinVideo15ModelInfo(), xaiBuiltinVideo15PreviewModelInfo())
124125 }
125126
126127 func normalizeAntigravityCapabilityModelID(modelID string) string {
@@ -194,16 +195,29 @@ func xaiBuiltinVideoModelInfo() *ModelInfo { · 67 unmodified lines
194195 }
195196 }
196197
198+func xaiBuiltinVideo15ModelInfo() *ModelInfo {
199+ return &ModelInfo{
200+ ID: xaiBuiltinVideo15ModelID,
201+ Object: "model",
202+ Created: 1735689600, // 2025-01-01
203+ OwnedBy: "xai",
204+ Type: "xai",
205+ DisplayName: "Grok Imagine Video 1.5",
206+ Name: xaiBuiltinVideo15ModelID,
207+ Description: "xAI Grok video generation model.",
208+ }
209+}
210+
197211 func xaiBuiltinVideo15PreviewModelInfo() *ModelInfo {
198212 return &ModelInfo{
199− ID: xaiBuiltinVideo15PreviewModelID,
213+ ID: xaiBuiltinVideo15PreviewID,
200214 Object: "model",
201215 Created: 1735689600, // 2025-01-01
202216 OwnedBy: "xai",
203217 Type: "xai",
204218 DisplayName: "Grok Imagine Video 1.5 Preview",
205− Name: xaiBuiltinVideo15PreviewModelID,
206− Description: "xAI Grok preview video generation model.",
219+ Name: xaiBuiltinVideo15PreviewID,
220+ Description: "Compatibility alias for the xAI Grok video generation model.",
207221 }
208222 }
209223
internal/registry/model_definitions_test.go+14−4
@@ -35,19 +35,29 @@ func TestGeminiVertexModelsUseFlashLiteReleaseID(t *testing.T) { · 34 unmodified lines
3535 t.Fatalf("Vertex models do not contain %q", releaseID)
3636 }
3737
38−func TestWithXAIBuiltinsIncludesVideoPreviewModel(t *testing.T) {
38+func TestWithXAIBuiltinsIncludesVideo15GAAndPreviewAlias(t *testing.T) {
3939 models := WithXAIBuiltins(nil)
40+ foundGA := false
41+ foundPreviewAlias := false
4042
4143 for _, model := range models {
4244 if model == nil {
4345 continue
4446 }
45− if model.ID == xaiBuiltinVideo15PreviewModelID {
46− return
47+ if model.ID == xaiBuiltinVideo15ModelID {
48+ foundGA = true
49+ }
50+ if model.ID == xaiBuiltinVideo15PreviewID {
51+ foundPreviewAlias = true
4752 }
4853 }
4954
50− t.Fatalf("expected xAI builtin model %s", xaiBuiltinVideo15PreviewModelID)
55+ if !foundGA {
56+ t.Fatalf("expected xAI builtin model %s", xaiBuiltinVideo15ModelID)
57+ }
58+ if !foundPreviewAlias {
59+ t.Fatalf("expected xAI builtin compatibility alias %s", xaiBuiltinVideo15PreviewID)
60+ }
5161 }
5262
5363 func TestAntigravityWebSearchModelForRequiresRequestedModelCapability(t *testing.T) {
sdk/api/handlers/openai/openai_videos_handlers.go+42−24
@@ -32,7 +32,8 @@ const ( · 31 unmodified lines
3232 xaiVideosExtensionsAPI = "/v1/videos/extensions"
3333 defaultOpenAIVideosModel = "sora-2"
3434 defaultXAIVideosModel = "grok-imagine-video"
35− xaiVideos15PreviewModel = "grok-imagine-video-1.5-preview"
35+ xaiVideos15Model = "grok-imagine-video-1.5"
36+ xaiVideos15PreviewAlias = "grok-imagine-video-1.5-preview"
3637 xaiVideosHandlerType = "openai-video"
3738 defaultVideosSeconds = "4"
3839 defaultVideosSize = "720x1280"
@@ -45,12 +46,12 @@ const defaultVideoAuthBindingTTL = 3 * time.Hour · 6 unmodified lines
4546 var videoAuthBindings = newVideoAuthBindingStore()
4647
4748 type xaiVideoCreateMetadata struct {
48− Model string
49− UpstreamModel string
50− Prompt string
51− Seconds string
52− Size string
53− CreatedAt int64
49+ Model string
50+ RoutingModel string
51+ Prompt string
52+ Seconds string
53+ Size string
54+ CreatedAt int64
5455 }
5556
5657 type videoAuthBinding struct {
@@ -147,7 +148,7 @@ func videosModelBase(model string) string { · 90 unmodified lines
147148 func isXAIVideosModel(model string) bool {
148149 prefix, baseModel := imagesModelParts(model)
149150 baseModel = strings.ToLower(strings.TrimSpace(baseModel))
150− if baseModel != defaultXAIVideosModel && baseModel != xaiVideos15PreviewModel {
151+ if baseModel != defaultXAIVideosModel && baseModel != xaiVideos15Model && baseModel != xaiVideos15PreviewAlias {
151152 return false
152153 }
153154
@@ -199,8 +200,23 @@ func canonicalXAIVideosModel(model string) string { · 45 unmodified lines
199200 switch videosModelBase(model) {
200201 case defaultXAIVideosModel:
201202 return defaultXAIVideosModel
202− case xaiVideos15PreviewModel:
203− return xaiVideos15PreviewModel
203+ case xaiVideos15Model, xaiVideos15PreviewAlias:
204+ return xaiVideos15Model
205+ }
206+ return defaultXAIVideosModel
207+}
208+
209+func routingXAIVideosModel(model string) string {
210+ if isSoraVideosModel(model) {
211+ return defaultXAIVideosModel
212+ }
213+ switch videosModelBase(model) {
214+ case defaultXAIVideosModel:
215+ return defaultXAIVideosModel
216+ case xaiVideos15Model:
217+ return xaiVideos15Model
218+ case xaiVideos15PreviewAlias:
219+ return xaiVideos15PreviewAlias
204220 }
205221 return defaultXAIVideosModel
206222 }
@@ -298,11 +314,11 @@ func (h *OpenAIAPIHandler) bindVideoAuthIDAndModelFromPayload(payload []byte, au · 91 unmodified lines
298314 if videoID == "" {
299315 return
300316 }
301− videoAuthBindings.setWithModel(videoID, authID, canonicalXAIVideosModel(model), h.videoAuthBindingTTL())
317+ videoAuthBindings.setWithModel(videoID, authID, routingXAIVideosModel(model), h.videoAuthBindingTTL())
302318 }
303319
304320 func (h *OpenAIAPIHandler) bindVideoAuthID(videoID string, authID string, model string) {
305− videoAuthBindings.setWithModel(videoID, authID, canonicalXAIVideosModel(model), h.videoAuthBindingTTL())
321+ videoAuthBindings.setWithModel(videoID, authID, routingXAIVideosModel(model), h.videoAuthBindingTTL())
306322 }
307323
308324 func (h *OpenAIAPIHandler) contextWithVideoAuthBinding(ctx context.Context, videoID string) context.Context {
@@ -374,12 +390,12 @@ func buildXAIVideosCreateRequest(rawJSON []byte, model string) ([]byte, xaiVideo · 65 unmodified lines
374390 }
375391
376392 meta := xaiVideoCreateMetadata{
377− Model: responseVideosModel(model),
378− UpstreamModel: videoModel,
379− Prompt: prompt,
380− Seconds: seconds,
381− Size: size,
382− CreatedAt: time.Now().Unix(),
393+ Model: responseVideosModel(model),
394+ RoutingModel: routingXAIVideosModel(model),
395+ Prompt: prompt,
396+ Seconds: seconds,
397+ Size: size,
398+ CreatedAt: time.Now().Unix(),
383399 }
384400 return req, meta, nil
385401 }
@@ -732,7 +748,9 @@ func (h *OpenAIAPIHandler) handleXAIVideosNativePost(c *gin.Context) { · 346 unmodified lines
732748 return
733749 }
734750
735− h.collectXAIVideosNative(c, rawJSON, videoModel, true)
751+ routingModel := routingXAIVideosModel(videoModel)
752+ rawJSON, _ = sjson.SetBytes(rawJSON, "model", canonicalXAIVideosModel(videoModel))
753+ h.collectXAIVideosNative(c, rawJSON, routingModel, true)
736754 }
737755
738756 func (h *OpenAIAPIHandler) XAIVideosRetrieve(c *gin.Context) {
@@ -995,12 +1013,12 @@ func (h *OpenAIAPIHandler) collectXAIVideosCreate(c *gin.Context, xaiReq []byte, · 256 unmodified lines
9951013 cliCtx = handlers.WithSelectedAuthIDCallback(cliCtx, func(authID string) {
9961014 selectedAuthID = authID
9971015 })
998− upstreamModel := strings.TrimSpace(meta.UpstreamModel)
999− if upstreamModel == "" {
1000− upstreamModel = meta.Model
1016+ routingModel := strings.TrimSpace(meta.RoutingModel)
1017+ if routingModel == "" {
1018+ routingModel = routingXAIVideosModel(meta.Model)
10011019 }
10021020 stopKeepAlive := h.StartNonStreamingKeepAlive(c, cliCtx)
1003− resp, upstreamHeaders, errMsg := h.ExecuteWithAuthManager(cliCtx, xaiVideosHandlerType, upstreamModel, xaiReq, "")
1021+ resp, upstreamHeaders, errMsg := h.ExecuteWithAuthManager(cliCtx, xaiVideosHandlerType, routingModel, xaiReq, "")
10041022 stopKeepAlive()
10051023 if errMsg != nil {
10061024 h.WriteErrorResponse(c, errMsg)
@@ -1020,7 +1038,7 @@ func (h *OpenAIAPIHandler) collectXAIVideosCreate(c *gin.Context, xaiReq []byte, · 13 unmodified lines
10201038 return
10211039 }
10221040
1023− h.bindVideoAuthIDFromPayload(out, selectedAuthID)
1041+ h.bindVideoAuthIDAndModelFromPayload(out, selectedAuthID, routingModel)
10241042 handlers.WriteUpstreamHeaders(c.Writer.Header(), upstreamHeaders)
10251043 _, _ = c.Writer.Write(out)
10261044 cliCancel(nil)
sdk/api/handlers/openai/openai_videos_handlers_test.go+180−21
@@ -63,11 +63,12 @@ func performVideosRouteRequest(t *testing.T, method string, routePath string, re · 62 unmodified lines
6363 }
6464
6565 type videoAuthCaptureExecutor struct {
66− mu sync.Mutex
67− requestID string
68− contentURL string
69− authIDs []string
70− models []string
66+ mu sync.Mutex
67+ requestID string
68+ contentURL string
69+ authIDs []string
70+ models []string
71+ payloadModels []string
7172 }
7273
7374 func (e *videoAuthCaptureExecutor) Identifier() string { return "xai" }
@@ -80,6 +81,7 @@ func (e *videoAuthCaptureExecutor) Execute(_ context.Context, auth *coreauth.Aut · 6 unmodified lines
8081 e.mu.Lock()
8182 e.authIDs = append(e.authIDs, authID)
8283 e.models = append(e.models, req.Model)
84+ e.payloadModels = append(e.payloadModels, strings.TrimSpace(gjson.GetBytes(req.Payload, "model").String()))
8385 e.mu.Unlock()
8486
8587 requestID := strings.TrimSpace(gjson.GetBytes(req.Payload, "request_id").String())
@@ -126,6 +128,14 @@ func (e *videoAuthCaptureExecutor) Models() []string { · 40 unmodified lines
126128 return out
127129 }
128130
131+func (e *videoAuthCaptureExecutor) PayloadModels() []string {
132+ e.mu.Lock()
133+ defer e.mu.Unlock()
134+ out := make([]string, len(e.payloadModels))
135+ copy(out, e.payloadModels)
136+ return out
137+}
138+
129139 func resetVideoAuthBindingsForTest(t *testing.T) {
130140 t.Helper()
131141 previous := videoAuthBindings
@@ -170,6 +180,10 @@ func TestVideosModelValidationAllowsXAIVideoModel(t *testing.T) { · 38 unmodified lines
170180 "xai/grok-imagine-video",
171181 "x-ai/grok-imagine-video",
172182 "grok/grok-imagine-video",
183+ "grok-imagine-video-1.5",
184+ "xai/grok-imagine-video-1.5",
185+ "x-ai/grok-imagine-video-1.5",
186+ "grok/grok-imagine-video-1.5",
173187 "grok-imagine-video-1.5-preview",
174188 "xai/grok-imagine-video-1.5-preview",
175189 "x-ai/grok-imagine-video-1.5-preview",
@@ -188,6 +202,9 @@ func TestVideosModelValidationAllowsXAIVideoModel(t *testing.T) { · 12 unmodified lines
188202 if isSupportedVideosModel("codex/grok-imagine-video") {
189203 t.Fatal("expected codex/grok-imagine-video to be rejected")
190204 }
205+ if isSupportedVideosModel("codex/grok-imagine-video-1.5") {
206+ t.Fatal("expected codex/grok-imagine-video-1.5 to be rejected")
207+ }
191208 if isSupportedVideosModel("codex/grok-imagine-video-1.5-preview") {
192209 t.Fatal("expected codex/grok-imagine-video-1.5-preview to be rejected")
193210 }
@@ -240,7 +257,26 @@ func TestBuildXAIVideosCreateRequest(t *testing.T) { · 46 unmodified lines
240257 }
241258 }
242259
243−func TestBuildXAIVideosCreateRequestAllowsPreviewModel(t *testing.T) {
260+func TestBuildXAIVideosCreateRequestAllowsVideo15Model(t *testing.T) {
261+ rawJSON := []byte(`{"model":"xai/grok-imagine-video-1.5","prompt":"a cat playing piano","seconds":"8"}`)
262+
263+ req, meta, err := buildXAIVideosCreateRequest(rawJSON, "xai/grok-imagine-video-1.5")
264+ if err != nil {
265+ t.Fatalf("buildXAIVideosCreateRequest() error = %v", err)
266+ }
267+
268+ if got := gjson.GetBytes(req, "model").String(); got != xaiVideos15Model {
269+ t.Fatalf("model = %q, want %s", got, xaiVideos15Model)
270+ }
271+ if meta.Model != xaiVideos15Model {
272+ t.Fatalf("meta model = %q, want %s", meta.Model, xaiVideos15Model)
273+ }
274+ if meta.RoutingModel != xaiVideos15Model {
275+ t.Fatalf("routing model = %q, want %s", meta.RoutingModel, xaiVideos15Model)
276+ }
277+}
278+
279+func TestBuildXAIVideosCreateRequestNormalizesVideo15PreviewAlias(t *testing.T) {
244280 rawJSON := []byte(`{"model":"xai/grok-imagine-video-1.5-preview","prompt":"a cat playing piano","seconds":"8"}`)
245281
246282 req, meta, err := buildXAIVideosCreateRequest(rawJSON, "xai/grok-imagine-video-1.5-preview")
@@ -248,11 +284,14 @@ func TestBuildXAIVideosCreateRequestAllowsPreviewModel(t *testing.T) { · 1 unmodified line
248284 t.Fatalf("buildXAIVideosCreateRequest() error = %v", err)
249285 }
250286
251− if got := gjson.GetBytes(req, "model").String(); got != xaiVideos15PreviewModel {
252− t.Fatalf("model = %q, want %s", got, xaiVideos15PreviewModel)
287+ if got := gjson.GetBytes(req, "model").String(); got != xaiVideos15Model {
288+ t.Fatalf("model = %q, want %s", got, xaiVideos15Model)
289+ }
290+ if meta.Model != xaiVideos15Model {
291+ t.Fatalf("meta model = %q, want %s", meta.Model, xaiVideos15Model)
253292 }
254− if meta.Model != xaiVideos15PreviewModel {
255− t.Fatalf("meta model = %q, want %s", meta.Model, xaiVideos15PreviewModel)
293+ if meta.RoutingModel != xaiVideos15PreviewAlias {
294+ t.Fatalf("routing model = %q, want %s", meta.RoutingModel, xaiVideos15PreviewAlias)
256295 }
257296 }
258297
@@ -734,9 +773,9 @@ func TestXAIVideosNativeCreateBindsRetrieveToSelectedAuth(t *testing.T) { · 475 unmodified lines
734773 }
735774 }
736775
737−func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) {
776+func TestXAIVideosNativeRetrieveUsesCanonicalBoundModel(t *testing.T) {
738777 resetVideoAuthBindingsForTest(t)
739− executor := &videoAuthCaptureExecutor{requestID: "video-xai-preview-bound"}
778+ executor := &videoAuthCaptureExecutor{requestID: "video-xai-1.5-bound"}
740779 manager := coreauth.NewManager(nil, &coreauth.RoundRobinSelector{}, nil)
741780 manager.RegisterExecutor(executor)
742781
@@ -744,8 +783,8 @@ func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) { · 1 unmodified line
744783 authID string
745784 model string
746785 }{
747− {authID: "video-xai-preview-default-auth", model: defaultXAIVideosModel},
748− {authID: "video-xai-preview-auth", model: xaiVideos15PreviewModel},
786+ {authID: "video-xai-1.5-default-auth", model: defaultXAIVideosModel},
787+ {authID: "video-xai-1.5-auth", model: xaiVideos15Model},
749788 }
750789 for _, entry := range authModels {
751790 auth := &coreauth.Auth{
@@ -768,7 +807,7 @@ func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) { · 16 unmodified lines
768807 base := apihandlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager)
769808 handler := NewOpenAIAPIHandler(base)
770809
771− createResp := performVideosEndpointRequest(t, http.MethodPost, xaiVideosGenerationsAPI, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.XAIVideosGenerations)
810+ createResp := performVideosEndpointRequest(t, http.MethodPost, xaiVideosGenerationsAPI, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5","prompt":"make a video"}`), handler.XAIVideosGenerations)
772811 if createResp.Code != http.StatusOK {
773812 t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
774813 }
@@ -786,22 +825,142 @@ func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) { · 11 unmodified lines
786825 if len(authIDs) != 2 {
787826 t.Fatalf("authIDs = %v, want two calls", authIDs)
788827 }
789− if authIDs[0] != "video-xai-preview-auth" || authIDs[1] != authIDs[0] {
790− t.Fatalf("authIDs = %v, want both calls to use video-xai-preview-auth", authIDs)
828+ if authIDs[0] != "video-xai-1.5-auth" || authIDs[1] != authIDs[0] {
829+ t.Fatalf("authIDs = %v, want both calls to use video-xai-1.5-auth", authIDs)
791830 }
792831 models := executor.Models()
793832 if len(models) != 2 {
794833 t.Fatalf("models = %v, want two calls", models)
795834 }
796− if models[0] != xaiVideos15PreviewModel || models[1] != xaiVideos15PreviewModel {
797− t.Fatalf("models = %v, want both calls to use %s", models, xaiVideos15PreviewModel)
835+ if models[0] != xaiVideos15Model || models[1] != xaiVideos15Model {
836+ t.Fatalf("models = %v, want both calls to use %s", models, xaiVideos15Model)
837+ }
838+ payloadModels := executor.PayloadModels()
839+ if len(payloadModels) != 2 || payloadModels[0] != xaiVideos15Model {
840+ t.Fatalf("payload models = %v, want create payload model %s", payloadModels, xaiVideos15Model)
841+ }
842+ binding, ok := videoAuthBindings.getBinding(videoID)
843+ if !ok {
844+ t.Fatal("video auth binding was not stored")
845+ }
846+ if binding.authID != "video-xai-1.5-auth" || binding.model != xaiVideos15Model {
847+ t.Fatalf("binding = {authID:%q model:%q}, want {authID:%q model:%q}", binding.authID, binding.model, "video-xai-1.5-auth", xaiVideos15Model)
848+ }
849+}
850+
851+func TestVideosCreatePreviewAliasUsesPreviewAuthWithGAPayload(t *testing.T) {
852+ resetVideoAuthBindingsForTest(t)
853+ executor := &videoAuthCaptureExecutor{requestID: "video-openai-preview-alias"}
854+ handler := newVideoSingleModelAuthTestHandler(t, executor, "video-openai-preview-auth", xaiVideos15PreviewAlias)
855+
856+ createResp := performVideosEndpointRequest(t, http.MethodPost, openAIVideosPath, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.VideosCreate)
857+ if createResp.Code != http.StatusOK {
858+ t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
859+ }
860+ videoID := gjson.GetBytes(createResp.Body.Bytes(), "id").String()
861+ if got := gjson.GetBytes(createResp.Body.Bytes(), "model").String(); got != xaiVideos15Model {
862+ t.Fatalf("response model = %q, want %s", got, xaiVideos15Model)
863+ }
864+
865+ retrieveResp := performVideosRouteRequest(t, http.MethodGet, openAIVideosPath+"/:video_id", openAIVideosPath+"/"+videoID, "", nil, handler.VideosRetrieve)
866+ if retrieveResp.Code != http.StatusOK {
867+ t.Fatalf("retrieve status = %d, want %d: %s", retrieveResp.Code, http.StatusOK, retrieveResp.Body.String())
868+ }
869+
870+ assertPreviewAliasRouting(t, executor, videoID, "video-openai-preview-auth")
871+}
872+
873+func TestVideosCreatePreviewAliasUsesDefaultXAIModelsWithGAPayload(t *testing.T) {
874+ resetVideoAuthBindingsForTest(t)
875+ executor := &videoAuthCaptureExecutor{requestID: "video-openai-preview-default-models"}
876+ handler := newVideoAuthTestHandler(t, executor, "video-openai-preview-default-auth", registry.GetXAIModels())
877+
878+ createResp := performVideosEndpointRequest(t, http.MethodPost, openAIVideosPath, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.VideosCreate)
879+ if createResp.Code != http.StatusOK {
880+ t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
881+ }
882+ videoID := gjson.GetBytes(createResp.Body.Bytes(), "id").String()
883+ if got := gjson.GetBytes(createResp.Body.Bytes(), "model").String(); got != xaiVideos15Model {
884+ t.Fatalf("response model = %q, want %s", got, xaiVideos15Model)
885+ }
886+
887+ retrieveResp := performVideosRouteRequest(t, http.MethodGet, openAIVideosPath+"/:video_id", openAIVideosPath+"/"+videoID, "", nil, handler.VideosRetrieve)
888+ if retrieveResp.Code != http.StatusOK {
889+ t.Fatalf("retrieve status = %d, want %d: %s", retrieveResp.Code, http.StatusOK, retrieveResp.Body.String())
890+ }
891+
892+ assertPreviewAliasRouting(t, executor, videoID, "video-openai-preview-default-auth")
893+}
894+
895+func TestXAIVideosNativePreviewAliasUsesPreviewAuthWithGAPayload(t *testing.T) {
896+ resetVideoAuthBindingsForTest(t)
897+ executor := &videoAuthCaptureExecutor{requestID: "video-native-preview-alias"}
898+ handler := newVideoSingleModelAuthTestHandler(t, executor, "video-native-preview-auth", xaiVideos15PreviewAlias)
899+
900+ createResp := performVideosEndpointRequest(t, http.MethodPost, xaiVideosGenerationsAPI, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.XAIVideosGenerations)
901+ if createResp.Code != http.StatusOK {
902+ t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
903+ }
904+ videoID := gjson.GetBytes(createResp.Body.Bytes(), "request_id").String()
905+
906+ retrieveResp := performVideosRouteRequest(t, http.MethodGet, videosPath+"/:request_id", videosPath+"/"+videoID, "", nil, handler.XAIVideosRetrieve)
907+ if retrieveResp.Code != http.StatusOK {
908+ t.Fatalf("retrieve status = %d, want %d: %s", retrieveResp.Code, http.StatusOK, retrieveResp.Body.String())
909+ }
910+
911+ assertPreviewAliasRouting(t, executor, videoID, "video-native-preview-auth")
912+}
913+
914+func newVideoSingleModelAuthTestHandler(t *testing.T, executor *videoAuthCaptureExecutor, authID string, model string) *OpenAIAPIHandler {
915+ t.Helper()
916+
917+ return newVideoAuthTestHandler(t, executor, authID, []*registry.ModelInfo{{ID: model}})
918+}
919+
920+func newVideoAuthTestHandler(t *testing.T, executor *videoAuthCaptureExecutor, authID string, models []*registry.ModelInfo) *OpenAIAPIHandler {
921+ t.Helper()
922+
923+ manager := coreauth.NewManager(nil, &coreauth.RoundRobinSelector{}, nil)
924+ manager.RegisterExecutor(executor)
925+ auth := &coreauth.Auth{
926+ ID: authID,
927+ Provider: "xai",
928+ Status: coreauth.StatusActive,
929+ }
930+ if _, errRegister := manager.Register(context.Background(), auth); errRegister != nil {
931+ t.Fatalf("manager.Register(%s): %v", authID, errRegister)
932+ }
933+ registry.GetGlobalRegistry().RegisterClient(authID, auth.Provider, models)
934+ manager.RefreshSchedulerEntry(authID)
935+ t.Cleanup(func() {
936+ registry.GetGlobalRegistry().UnregisterClient(authID)
937+ })
938+
939+ base := apihandlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager)
940+ return NewOpenAIAPIHandler(base)
941+}
942+
943+func assertPreviewAliasRouting(t *testing.T, executor *videoAuthCaptureExecutor, videoID string, authID string) {
944+ t.Helper()
945+
946+ authIDs := executor.AuthIDs()
947+ if len(authIDs) != 2 || authIDs[0] != authID || authIDs[1] != authID {
948+ t.Fatalf("authIDs = %v, want both calls to use %s", authIDs, authID)
949+ }
950+ models := executor.Models()
951+ if len(models) != 2 || models[0] != xaiVideos15PreviewAlias || models[1] != xaiVideos15PreviewAlias {
952+ t.Fatalf("models = %v, want both calls to route with %s", models, xaiVideos15PreviewAlias)
953+ }
954+ payloadModels := executor.PayloadModels()
955+ if len(payloadModels) != 2 || payloadModels[0] != xaiVideos15Model {
956+ t.Fatalf("payload models = %v, want create payload model %s", payloadModels, xaiVideos15Model)
798957 }
799958 binding, ok := videoAuthBindings.getBinding(videoID)
800959 if !ok {
801960 t.Fatal("video auth binding was not stored")
802961 }
803− if binding.authID != "video-xai-preview-auth" || binding.model != xaiVideos15PreviewModel {
804− t.Fatalf("binding = {authID:%q model:%q}, want {authID:%q model:%q}", binding.authID, binding.model, "video-xai-preview-auth", xaiVideos15PreviewModel)
962+ if binding.authID != authID || binding.model != xaiVideos15PreviewAlias {
963+ t.Fatalf("binding = {authID:%q model:%q}, want {authID:%q model:%q}", binding.authID, binding.model, authID, xaiVideos15PreviewAlias)
805964 }
806965 }
807966

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t