mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-09-03 06:35:00 +08:00
Merge pull request #4668 from oscarbrey/fix/grok-imagine-video-1.5-ga
fix(xai): support Grok Imagine Video 1.5 GA
This commit is contained in:
@@ -1648,6 +1648,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) {
|
||||
{ID: "gpt-image-2", Object: "model", OwnedBy: "openai", Type: "openai"},
|
||||
{ID: "grok-imagine-image", Object: "model", OwnedBy: "xai", Type: "openai"},
|
||||
{ID: "grok-imagine-video", Object: "model", OwnedBy: "xai", Type: "openai"},
|
||||
{ID: "grok-imagine-video-1.5", Object: "model", OwnedBy: "xai", Type: "openai"},
|
||||
{ID: "grok-imagine-video-1.5-preview", Object: "model", OwnedBy: "xai", Type: "openai"},
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
@@ -1747,6 +1748,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) {
|
||||
"gpt-image-2": false,
|
||||
"grok-imagine-image": false,
|
||||
"grok-imagine-video": false,
|
||||
"grok-imagine-video-1.5": false,
|
||||
"grok-imagine-video-1.5-preview": false,
|
||||
}
|
||||
for _, model := range resp.Models {
|
||||
|
||||
@@ -286,7 +286,7 @@ func applyCodexClientModelMetadata(entry map[string]any, id string, model map[st
|
||||
|
||||
func applyCodexClientVisibilityOverride(entry map[string]any, id string) {
|
||||
switch strings.TrimSpace(id) {
|
||||
case "grok-imagine-image-quality", "gpt-image-1.5", "gpt-image-2", "grok-imagine-image", "grok-imagine-video", "grok-imagine-video-1.5-preview":
|
||||
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":
|
||||
entry["visibility"] = "hide"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
codexBuiltinImage15ModelID = "gpt-image-1.5"
|
||||
codexBuiltinImageModelID = "gpt-image-2"
|
||||
xaiBuiltinImageModelID = "grok-imagine-image"
|
||||
xaiBuiltinImageQualityModelID = "grok-imagine-image-quality"
|
||||
xaiBuiltinVideoModelID = "grok-imagine-video"
|
||||
xaiBuiltinVideo15PreviewModelID = "grok-imagine-video-1.5-preview"
|
||||
codexBuiltinImage15ModelID = "gpt-image-1.5"
|
||||
codexBuiltinImageModelID = "gpt-image-2"
|
||||
xaiBuiltinImageModelID = "grok-imagine-image"
|
||||
xaiBuiltinImageQualityModelID = "grok-imagine-image-quality"
|
||||
xaiBuiltinVideoModelID = "grok-imagine-video"
|
||||
xaiBuiltinVideo15ModelID = "grok-imagine-video-1.5"
|
||||
xaiBuiltinVideo15PreviewID = "grok-imagine-video-1.5-preview"
|
||||
)
|
||||
|
||||
// staticModelsJSON mirrors the top-level structure of models.json.
|
||||
@@ -120,7 +121,7 @@ func WithCodexBuiltins(models []*ModelInfo) []*ModelInfo {
|
||||
// WithXAIBuiltins injects hard-coded xAI image/video model definitions that should
|
||||
// not depend on remote models.json updates.
|
||||
func WithXAIBuiltins(models []*ModelInfo) []*ModelInfo {
|
||||
return upsertModelInfos(models, xaiBuiltinImageModelInfo(), xaiBuiltinImageQualityModelInfo(), xaiBuiltinVideoModelInfo(), xaiBuiltinVideo15PreviewModelInfo())
|
||||
return upsertModelInfos(models, xaiBuiltinImageModelInfo(), xaiBuiltinImageQualityModelInfo(), xaiBuiltinVideoModelInfo(), xaiBuiltinVideo15ModelInfo(), xaiBuiltinVideo15PreviewModelInfo())
|
||||
}
|
||||
|
||||
func normalizeAntigravityCapabilityModelID(modelID string) string {
|
||||
@@ -194,16 +195,29 @@ func xaiBuiltinVideoModelInfo() *ModelInfo {
|
||||
}
|
||||
}
|
||||
|
||||
func xaiBuiltinVideo15ModelInfo() *ModelInfo {
|
||||
return &ModelInfo{
|
||||
ID: xaiBuiltinVideo15ModelID,
|
||||
Object: "model",
|
||||
Created: 1735689600, // 2025-01-01
|
||||
OwnedBy: "xai",
|
||||
Type: "xai",
|
||||
DisplayName: "Grok Imagine Video 1.5",
|
||||
Name: xaiBuiltinVideo15ModelID,
|
||||
Description: "xAI Grok video generation model.",
|
||||
}
|
||||
}
|
||||
|
||||
func xaiBuiltinVideo15PreviewModelInfo() *ModelInfo {
|
||||
return &ModelInfo{
|
||||
ID: xaiBuiltinVideo15PreviewModelID,
|
||||
ID: xaiBuiltinVideo15PreviewID,
|
||||
Object: "model",
|
||||
Created: 1735689600, // 2025-01-01
|
||||
OwnedBy: "xai",
|
||||
Type: "xai",
|
||||
DisplayName: "Grok Imagine Video 1.5 Preview",
|
||||
Name: xaiBuiltinVideo15PreviewModelID,
|
||||
Description: "xAI Grok preview video generation model.",
|
||||
Name: xaiBuiltinVideo15PreviewID,
|
||||
Description: "Compatibility alias for the xAI Grok video generation model.",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,19 +35,29 @@ func TestGeminiVertexModelsUseFlashLiteReleaseID(t *testing.T) {
|
||||
t.Fatalf("Vertex models do not contain %q", releaseID)
|
||||
}
|
||||
|
||||
func TestWithXAIBuiltinsIncludesVideoPreviewModel(t *testing.T) {
|
||||
func TestWithXAIBuiltinsIncludesVideo15GAAndPreviewAlias(t *testing.T) {
|
||||
models := WithXAIBuiltins(nil)
|
||||
foundGA := false
|
||||
foundPreviewAlias := false
|
||||
|
||||
for _, model := range models {
|
||||
if model == nil {
|
||||
continue
|
||||
}
|
||||
if model.ID == xaiBuiltinVideo15PreviewModelID {
|
||||
return
|
||||
if model.ID == xaiBuiltinVideo15ModelID {
|
||||
foundGA = true
|
||||
}
|
||||
if model.ID == xaiBuiltinVideo15PreviewID {
|
||||
foundPreviewAlias = true
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf("expected xAI builtin model %s", xaiBuiltinVideo15PreviewModelID)
|
||||
if !foundGA {
|
||||
t.Fatalf("expected xAI builtin model %s", xaiBuiltinVideo15ModelID)
|
||||
}
|
||||
if !foundPreviewAlias {
|
||||
t.Fatalf("expected xAI builtin compatibility alias %s", xaiBuiltinVideo15PreviewID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAntigravityWebSearchModelForRequiresRequestedModelCapability(t *testing.T) {
|
||||
|
||||
@@ -32,7 +32,8 @@ const (
|
||||
xaiVideosExtensionsAPI = "/v1/videos/extensions"
|
||||
defaultOpenAIVideosModel = "sora-2"
|
||||
defaultXAIVideosModel = "grok-imagine-video"
|
||||
xaiVideos15PreviewModel = "grok-imagine-video-1.5-preview"
|
||||
xaiVideos15Model = "grok-imagine-video-1.5"
|
||||
xaiVideos15PreviewAlias = "grok-imagine-video-1.5-preview"
|
||||
xaiVideosHandlerType = "openai-video"
|
||||
defaultVideosSeconds = "4"
|
||||
defaultVideosSize = "720x1280"
|
||||
@@ -45,12 +46,12 @@ const defaultVideoAuthBindingTTL = 3 * time.Hour
|
||||
var videoAuthBindings = newVideoAuthBindingStore()
|
||||
|
||||
type xaiVideoCreateMetadata struct {
|
||||
Model string
|
||||
UpstreamModel string
|
||||
Prompt string
|
||||
Seconds string
|
||||
Size string
|
||||
CreatedAt int64
|
||||
Model string
|
||||
RoutingModel string
|
||||
Prompt string
|
||||
Seconds string
|
||||
Size string
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
type videoAuthBinding struct {
|
||||
@@ -147,7 +148,7 @@ func videosModelBase(model string) string {
|
||||
func isXAIVideosModel(model string) bool {
|
||||
prefix, baseModel := imagesModelParts(model)
|
||||
baseModel = strings.ToLower(strings.TrimSpace(baseModel))
|
||||
if baseModel != defaultXAIVideosModel && baseModel != xaiVideos15PreviewModel {
|
||||
if baseModel != defaultXAIVideosModel && baseModel != xaiVideos15Model && baseModel != xaiVideos15PreviewAlias {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -199,8 +200,23 @@ func canonicalXAIVideosModel(model string) string {
|
||||
switch videosModelBase(model) {
|
||||
case defaultXAIVideosModel:
|
||||
return defaultXAIVideosModel
|
||||
case xaiVideos15PreviewModel:
|
||||
return xaiVideos15PreviewModel
|
||||
case xaiVideos15Model, xaiVideos15PreviewAlias:
|
||||
return xaiVideos15Model
|
||||
}
|
||||
return defaultXAIVideosModel
|
||||
}
|
||||
|
||||
func routingXAIVideosModel(model string) string {
|
||||
if isSoraVideosModel(model) {
|
||||
return defaultXAIVideosModel
|
||||
}
|
||||
switch videosModelBase(model) {
|
||||
case defaultXAIVideosModel:
|
||||
return defaultXAIVideosModel
|
||||
case xaiVideos15Model:
|
||||
return xaiVideos15Model
|
||||
case xaiVideos15PreviewAlias:
|
||||
return xaiVideos15PreviewAlias
|
||||
}
|
||||
return defaultXAIVideosModel
|
||||
}
|
||||
@@ -298,11 +314,11 @@ func (h *OpenAIAPIHandler) bindVideoAuthIDAndModelFromPayload(payload []byte, au
|
||||
if videoID == "" {
|
||||
return
|
||||
}
|
||||
videoAuthBindings.setWithModel(videoID, authID, canonicalXAIVideosModel(model), h.videoAuthBindingTTL())
|
||||
videoAuthBindings.setWithModel(videoID, authID, routingXAIVideosModel(model), h.videoAuthBindingTTL())
|
||||
}
|
||||
|
||||
func (h *OpenAIAPIHandler) bindVideoAuthID(videoID string, authID string, model string) {
|
||||
videoAuthBindings.setWithModel(videoID, authID, canonicalXAIVideosModel(model), h.videoAuthBindingTTL())
|
||||
videoAuthBindings.setWithModel(videoID, authID, routingXAIVideosModel(model), h.videoAuthBindingTTL())
|
||||
}
|
||||
|
||||
func (h *OpenAIAPIHandler) contextWithVideoAuthBinding(ctx context.Context, videoID string) context.Context {
|
||||
@@ -374,12 +390,12 @@ func buildXAIVideosCreateRequest(rawJSON []byte, model string) ([]byte, xaiVideo
|
||||
}
|
||||
|
||||
meta := xaiVideoCreateMetadata{
|
||||
Model: responseVideosModel(model),
|
||||
UpstreamModel: videoModel,
|
||||
Prompt: prompt,
|
||||
Seconds: seconds,
|
||||
Size: size,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
Model: responseVideosModel(model),
|
||||
RoutingModel: routingXAIVideosModel(model),
|
||||
Prompt: prompt,
|
||||
Seconds: seconds,
|
||||
Size: size,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
return req, meta, nil
|
||||
}
|
||||
@@ -732,7 +748,9 @@ func (h *OpenAIAPIHandler) handleXAIVideosNativePost(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
h.collectXAIVideosNative(c, rawJSON, videoModel, true)
|
||||
routingModel := routingXAIVideosModel(videoModel)
|
||||
rawJSON, _ = sjson.SetBytes(rawJSON, "model", canonicalXAIVideosModel(videoModel))
|
||||
h.collectXAIVideosNative(c, rawJSON, routingModel, true)
|
||||
}
|
||||
|
||||
func (h *OpenAIAPIHandler) XAIVideosRetrieve(c *gin.Context) {
|
||||
@@ -995,12 +1013,12 @@ func (h *OpenAIAPIHandler) collectXAIVideosCreate(c *gin.Context, xaiReq []byte,
|
||||
cliCtx = handlers.WithSelectedAuthIDCallback(cliCtx, func(authID string) {
|
||||
selectedAuthID = authID
|
||||
})
|
||||
upstreamModel := strings.TrimSpace(meta.UpstreamModel)
|
||||
if upstreamModel == "" {
|
||||
upstreamModel = meta.Model
|
||||
routingModel := strings.TrimSpace(meta.RoutingModel)
|
||||
if routingModel == "" {
|
||||
routingModel = routingXAIVideosModel(meta.Model)
|
||||
}
|
||||
stopKeepAlive := h.StartNonStreamingKeepAlive(c, cliCtx)
|
||||
resp, upstreamHeaders, errMsg := h.ExecuteWithAuthManager(cliCtx, xaiVideosHandlerType, upstreamModel, xaiReq, "")
|
||||
resp, upstreamHeaders, errMsg := h.ExecuteWithAuthManager(cliCtx, xaiVideosHandlerType, routingModel, xaiReq, "")
|
||||
stopKeepAlive()
|
||||
if errMsg != nil {
|
||||
h.WriteErrorResponse(c, errMsg)
|
||||
@@ -1020,7 +1038,7 @@ func (h *OpenAIAPIHandler) collectXAIVideosCreate(c *gin.Context, xaiReq []byte,
|
||||
return
|
||||
}
|
||||
|
||||
h.bindVideoAuthIDFromPayload(out, selectedAuthID)
|
||||
h.bindVideoAuthIDAndModelFromPayload(out, selectedAuthID, routingModel)
|
||||
handlers.WriteUpstreamHeaders(c.Writer.Header(), upstreamHeaders)
|
||||
_, _ = c.Writer.Write(out)
|
||||
cliCancel(nil)
|
||||
|
||||
@@ -63,11 +63,12 @@ func performVideosRouteRequest(t *testing.T, method string, routePath string, re
|
||||
}
|
||||
|
||||
type videoAuthCaptureExecutor struct {
|
||||
mu sync.Mutex
|
||||
requestID string
|
||||
contentURL string
|
||||
authIDs []string
|
||||
models []string
|
||||
mu sync.Mutex
|
||||
requestID string
|
||||
contentURL string
|
||||
authIDs []string
|
||||
models []string
|
||||
payloadModels []string
|
||||
}
|
||||
|
||||
func (e *videoAuthCaptureExecutor) Identifier() string { return "xai" }
|
||||
@@ -80,6 +81,7 @@ func (e *videoAuthCaptureExecutor) Execute(_ context.Context, auth *coreauth.Aut
|
||||
e.mu.Lock()
|
||||
e.authIDs = append(e.authIDs, authID)
|
||||
e.models = append(e.models, req.Model)
|
||||
e.payloadModels = append(e.payloadModels, strings.TrimSpace(gjson.GetBytes(req.Payload, "model").String()))
|
||||
e.mu.Unlock()
|
||||
|
||||
requestID := strings.TrimSpace(gjson.GetBytes(req.Payload, "request_id").String())
|
||||
@@ -126,6 +128,14 @@ func (e *videoAuthCaptureExecutor) Models() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *videoAuthCaptureExecutor) PayloadModels() []string {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
out := make([]string, len(e.payloadModels))
|
||||
copy(out, e.payloadModels)
|
||||
return out
|
||||
}
|
||||
|
||||
func resetVideoAuthBindingsForTest(t *testing.T) {
|
||||
t.Helper()
|
||||
previous := videoAuthBindings
|
||||
@@ -170,6 +180,10 @@ func TestVideosModelValidationAllowsXAIVideoModel(t *testing.T) {
|
||||
"xai/grok-imagine-video",
|
||||
"x-ai/grok-imagine-video",
|
||||
"grok/grok-imagine-video",
|
||||
"grok-imagine-video-1.5",
|
||||
"xai/grok-imagine-video-1.5",
|
||||
"x-ai/grok-imagine-video-1.5",
|
||||
"grok/grok-imagine-video-1.5",
|
||||
"grok-imagine-video-1.5-preview",
|
||||
"xai/grok-imagine-video-1.5-preview",
|
||||
"x-ai/grok-imagine-video-1.5-preview",
|
||||
@@ -188,6 +202,9 @@ func TestVideosModelValidationAllowsXAIVideoModel(t *testing.T) {
|
||||
if isSupportedVideosModel("codex/grok-imagine-video") {
|
||||
t.Fatal("expected codex/grok-imagine-video to be rejected")
|
||||
}
|
||||
if isSupportedVideosModel("codex/grok-imagine-video-1.5") {
|
||||
t.Fatal("expected codex/grok-imagine-video-1.5 to be rejected")
|
||||
}
|
||||
if isSupportedVideosModel("codex/grok-imagine-video-1.5-preview") {
|
||||
t.Fatal("expected codex/grok-imagine-video-1.5-preview to be rejected")
|
||||
}
|
||||
@@ -240,7 +257,26 @@ func TestBuildXAIVideosCreateRequest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildXAIVideosCreateRequestAllowsPreviewModel(t *testing.T) {
|
||||
func TestBuildXAIVideosCreateRequestAllowsVideo15Model(t *testing.T) {
|
||||
rawJSON := []byte(`{"model":"xai/grok-imagine-video-1.5","prompt":"a cat playing piano","seconds":"8"}`)
|
||||
|
||||
req, meta, err := buildXAIVideosCreateRequest(rawJSON, "xai/grok-imagine-video-1.5")
|
||||
if err != nil {
|
||||
t.Fatalf("buildXAIVideosCreateRequest() error = %v", err)
|
||||
}
|
||||
|
||||
if got := gjson.GetBytes(req, "model").String(); got != xaiVideos15Model {
|
||||
t.Fatalf("model = %q, want %s", got, xaiVideos15Model)
|
||||
}
|
||||
if meta.Model != xaiVideos15Model {
|
||||
t.Fatalf("meta model = %q, want %s", meta.Model, xaiVideos15Model)
|
||||
}
|
||||
if meta.RoutingModel != xaiVideos15Model {
|
||||
t.Fatalf("routing model = %q, want %s", meta.RoutingModel, xaiVideos15Model)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildXAIVideosCreateRequestNormalizesVideo15PreviewAlias(t *testing.T) {
|
||||
rawJSON := []byte(`{"model":"xai/grok-imagine-video-1.5-preview","prompt":"a cat playing piano","seconds":"8"}`)
|
||||
|
||||
req, meta, err := buildXAIVideosCreateRequest(rawJSON, "xai/grok-imagine-video-1.5-preview")
|
||||
@@ -248,11 +284,14 @@ func TestBuildXAIVideosCreateRequestAllowsPreviewModel(t *testing.T) {
|
||||
t.Fatalf("buildXAIVideosCreateRequest() error = %v", err)
|
||||
}
|
||||
|
||||
if got := gjson.GetBytes(req, "model").String(); got != xaiVideos15PreviewModel {
|
||||
t.Fatalf("model = %q, want %s", got, xaiVideos15PreviewModel)
|
||||
if got := gjson.GetBytes(req, "model").String(); got != xaiVideos15Model {
|
||||
t.Fatalf("model = %q, want %s", got, xaiVideos15Model)
|
||||
}
|
||||
if meta.Model != xaiVideos15PreviewModel {
|
||||
t.Fatalf("meta model = %q, want %s", meta.Model, xaiVideos15PreviewModel)
|
||||
if meta.Model != xaiVideos15Model {
|
||||
t.Fatalf("meta model = %q, want %s", meta.Model, xaiVideos15Model)
|
||||
}
|
||||
if meta.RoutingModel != xaiVideos15PreviewAlias {
|
||||
t.Fatalf("routing model = %q, want %s", meta.RoutingModel, xaiVideos15PreviewAlias)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,9 +773,9 @@ func TestXAIVideosNativeCreateBindsRetrieveToSelectedAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) {
|
||||
func TestXAIVideosNativeRetrieveUsesCanonicalBoundModel(t *testing.T) {
|
||||
resetVideoAuthBindingsForTest(t)
|
||||
executor := &videoAuthCaptureExecutor{requestID: "video-xai-preview-bound"}
|
||||
executor := &videoAuthCaptureExecutor{requestID: "video-xai-1.5-bound"}
|
||||
manager := coreauth.NewManager(nil, &coreauth.RoundRobinSelector{}, nil)
|
||||
manager.RegisterExecutor(executor)
|
||||
|
||||
@@ -744,8 +783,8 @@ func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) {
|
||||
authID string
|
||||
model string
|
||||
}{
|
||||
{authID: "video-xai-preview-default-auth", model: defaultXAIVideosModel},
|
||||
{authID: "video-xai-preview-auth", model: xaiVideos15PreviewModel},
|
||||
{authID: "video-xai-1.5-default-auth", model: defaultXAIVideosModel},
|
||||
{authID: "video-xai-1.5-auth", model: xaiVideos15Model},
|
||||
}
|
||||
for _, entry := range authModels {
|
||||
auth := &coreauth.Auth{
|
||||
@@ -768,7 +807,7 @@ func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) {
|
||||
base := apihandlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager)
|
||||
handler := NewOpenAIAPIHandler(base)
|
||||
|
||||
createResp := performVideosEndpointRequest(t, http.MethodPost, xaiVideosGenerationsAPI, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.XAIVideosGenerations)
|
||||
createResp := performVideosEndpointRequest(t, http.MethodPost, xaiVideosGenerationsAPI, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5","prompt":"make a video"}`), handler.XAIVideosGenerations)
|
||||
if createResp.Code != http.StatusOK {
|
||||
t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
|
||||
}
|
||||
@@ -786,22 +825,142 @@ func TestXAIVideosNativeRetrieveUsesBoundModel(t *testing.T) {
|
||||
if len(authIDs) != 2 {
|
||||
t.Fatalf("authIDs = %v, want two calls", authIDs)
|
||||
}
|
||||
if authIDs[0] != "video-xai-preview-auth" || authIDs[1] != authIDs[0] {
|
||||
t.Fatalf("authIDs = %v, want both calls to use video-xai-preview-auth", authIDs)
|
||||
if authIDs[0] != "video-xai-1.5-auth" || authIDs[1] != authIDs[0] {
|
||||
t.Fatalf("authIDs = %v, want both calls to use video-xai-1.5-auth", authIDs)
|
||||
}
|
||||
models := executor.Models()
|
||||
if len(models) != 2 {
|
||||
t.Fatalf("models = %v, want two calls", models)
|
||||
}
|
||||
if models[0] != xaiVideos15PreviewModel || models[1] != xaiVideos15PreviewModel {
|
||||
t.Fatalf("models = %v, want both calls to use %s", models, xaiVideos15PreviewModel)
|
||||
if models[0] != xaiVideos15Model || models[1] != xaiVideos15Model {
|
||||
t.Fatalf("models = %v, want both calls to use %s", models, xaiVideos15Model)
|
||||
}
|
||||
payloadModels := executor.PayloadModels()
|
||||
if len(payloadModels) != 2 || payloadModels[0] != xaiVideos15Model {
|
||||
t.Fatalf("payload models = %v, want create payload model %s", payloadModels, xaiVideos15Model)
|
||||
}
|
||||
binding, ok := videoAuthBindings.getBinding(videoID)
|
||||
if !ok {
|
||||
t.Fatal("video auth binding was not stored")
|
||||
}
|
||||
if binding.authID != "video-xai-preview-auth" || binding.model != xaiVideos15PreviewModel {
|
||||
t.Fatalf("binding = {authID:%q model:%q}, want {authID:%q model:%q}", binding.authID, binding.model, "video-xai-preview-auth", xaiVideos15PreviewModel)
|
||||
if binding.authID != "video-xai-1.5-auth" || binding.model != xaiVideos15Model {
|
||||
t.Fatalf("binding = {authID:%q model:%q}, want {authID:%q model:%q}", binding.authID, binding.model, "video-xai-1.5-auth", xaiVideos15Model)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVideosCreatePreviewAliasUsesPreviewAuthWithGAPayload(t *testing.T) {
|
||||
resetVideoAuthBindingsForTest(t)
|
||||
executor := &videoAuthCaptureExecutor{requestID: "video-openai-preview-alias"}
|
||||
handler := newVideoSingleModelAuthTestHandler(t, executor, "video-openai-preview-auth", xaiVideos15PreviewAlias)
|
||||
|
||||
createResp := performVideosEndpointRequest(t, http.MethodPost, openAIVideosPath, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.VideosCreate)
|
||||
if createResp.Code != http.StatusOK {
|
||||
t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
|
||||
}
|
||||
videoID := gjson.GetBytes(createResp.Body.Bytes(), "id").String()
|
||||
if got := gjson.GetBytes(createResp.Body.Bytes(), "model").String(); got != xaiVideos15Model {
|
||||
t.Fatalf("response model = %q, want %s", got, xaiVideos15Model)
|
||||
}
|
||||
|
||||
retrieveResp := performVideosRouteRequest(t, http.MethodGet, openAIVideosPath+"/:video_id", openAIVideosPath+"/"+videoID, "", nil, handler.VideosRetrieve)
|
||||
if retrieveResp.Code != http.StatusOK {
|
||||
t.Fatalf("retrieve status = %d, want %d: %s", retrieveResp.Code, http.StatusOK, retrieveResp.Body.String())
|
||||
}
|
||||
|
||||
assertPreviewAliasRouting(t, executor, videoID, "video-openai-preview-auth")
|
||||
}
|
||||
|
||||
func TestVideosCreatePreviewAliasUsesDefaultXAIModelsWithGAPayload(t *testing.T) {
|
||||
resetVideoAuthBindingsForTest(t)
|
||||
executor := &videoAuthCaptureExecutor{requestID: "video-openai-preview-default-models"}
|
||||
handler := newVideoAuthTestHandler(t, executor, "video-openai-preview-default-auth", registry.GetXAIModels())
|
||||
|
||||
createResp := performVideosEndpointRequest(t, http.MethodPost, openAIVideosPath, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.VideosCreate)
|
||||
if createResp.Code != http.StatusOK {
|
||||
t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
|
||||
}
|
||||
videoID := gjson.GetBytes(createResp.Body.Bytes(), "id").String()
|
||||
if got := gjson.GetBytes(createResp.Body.Bytes(), "model").String(); got != xaiVideos15Model {
|
||||
t.Fatalf("response model = %q, want %s", got, xaiVideos15Model)
|
||||
}
|
||||
|
||||
retrieveResp := performVideosRouteRequest(t, http.MethodGet, openAIVideosPath+"/:video_id", openAIVideosPath+"/"+videoID, "", nil, handler.VideosRetrieve)
|
||||
if retrieveResp.Code != http.StatusOK {
|
||||
t.Fatalf("retrieve status = %d, want %d: %s", retrieveResp.Code, http.StatusOK, retrieveResp.Body.String())
|
||||
}
|
||||
|
||||
assertPreviewAliasRouting(t, executor, videoID, "video-openai-preview-default-auth")
|
||||
}
|
||||
|
||||
func TestXAIVideosNativePreviewAliasUsesPreviewAuthWithGAPayload(t *testing.T) {
|
||||
resetVideoAuthBindingsForTest(t)
|
||||
executor := &videoAuthCaptureExecutor{requestID: "video-native-preview-alias"}
|
||||
handler := newVideoSingleModelAuthTestHandler(t, executor, "video-native-preview-auth", xaiVideos15PreviewAlias)
|
||||
|
||||
createResp := performVideosEndpointRequest(t, http.MethodPost, xaiVideosGenerationsAPI, "application/json", strings.NewReader(`{"model":"grok-imagine-video-1.5-preview","prompt":"make a video"}`), handler.XAIVideosGenerations)
|
||||
if createResp.Code != http.StatusOK {
|
||||
t.Fatalf("create status = %d, want %d: %s", createResp.Code, http.StatusOK, createResp.Body.String())
|
||||
}
|
||||
videoID := gjson.GetBytes(createResp.Body.Bytes(), "request_id").String()
|
||||
|
||||
retrieveResp := performVideosRouteRequest(t, http.MethodGet, videosPath+"/:request_id", videosPath+"/"+videoID, "", nil, handler.XAIVideosRetrieve)
|
||||
if retrieveResp.Code != http.StatusOK {
|
||||
t.Fatalf("retrieve status = %d, want %d: %s", retrieveResp.Code, http.StatusOK, retrieveResp.Body.String())
|
||||
}
|
||||
|
||||
assertPreviewAliasRouting(t, executor, videoID, "video-native-preview-auth")
|
||||
}
|
||||
|
||||
func newVideoSingleModelAuthTestHandler(t *testing.T, executor *videoAuthCaptureExecutor, authID string, model string) *OpenAIAPIHandler {
|
||||
t.Helper()
|
||||
|
||||
return newVideoAuthTestHandler(t, executor, authID, []*registry.ModelInfo{{ID: model}})
|
||||
}
|
||||
|
||||
func newVideoAuthTestHandler(t *testing.T, executor *videoAuthCaptureExecutor, authID string, models []*registry.ModelInfo) *OpenAIAPIHandler {
|
||||
t.Helper()
|
||||
|
||||
manager := coreauth.NewManager(nil, &coreauth.RoundRobinSelector{}, nil)
|
||||
manager.RegisterExecutor(executor)
|
||||
auth := &coreauth.Auth{
|
||||
ID: authID,
|
||||
Provider: "xai",
|
||||
Status: coreauth.StatusActive,
|
||||
}
|
||||
if _, errRegister := manager.Register(context.Background(), auth); errRegister != nil {
|
||||
t.Fatalf("manager.Register(%s): %v", authID, errRegister)
|
||||
}
|
||||
registry.GetGlobalRegistry().RegisterClient(authID, auth.Provider, models)
|
||||
manager.RefreshSchedulerEntry(authID)
|
||||
t.Cleanup(func() {
|
||||
registry.GetGlobalRegistry().UnregisterClient(authID)
|
||||
})
|
||||
|
||||
base := apihandlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager)
|
||||
return NewOpenAIAPIHandler(base)
|
||||
}
|
||||
|
||||
func assertPreviewAliasRouting(t *testing.T, executor *videoAuthCaptureExecutor, videoID string, authID string) {
|
||||
t.Helper()
|
||||
|
||||
authIDs := executor.AuthIDs()
|
||||
if len(authIDs) != 2 || authIDs[0] != authID || authIDs[1] != authID {
|
||||
t.Fatalf("authIDs = %v, want both calls to use %s", authIDs, authID)
|
||||
}
|
||||
models := executor.Models()
|
||||
if len(models) != 2 || models[0] != xaiVideos15PreviewAlias || models[1] != xaiVideos15PreviewAlias {
|
||||
t.Fatalf("models = %v, want both calls to route with %s", models, xaiVideos15PreviewAlias)
|
||||
}
|
||||
payloadModels := executor.PayloadModels()
|
||||
if len(payloadModels) != 2 || payloadModels[0] != xaiVideos15Model {
|
||||
t.Fatalf("payload models = %v, want create payload model %s", payloadModels, xaiVideos15Model)
|
||||
}
|
||||
binding, ok := videoAuthBindings.getBinding(videoID)
|
||||
if !ok {
|
||||
t.Fatal("video auth binding was not stored")
|
||||
}
|
||||
if binding.authID != authID || binding.model != xaiVideos15PreviewAlias {
|
||||
t.Fatalf("binding = {authID:%q model:%q}, want {authID:%q model:%q}", binding.authID, binding.model, authID, xaiVideos15PreviewAlias)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user