From a0ba303b02afa8eb94c678c71b5bfe0358a66b34 Mon Sep 17 00:00:00 2001 From: ddcat666 <26564566+ddcat666@users.noreply.github.com> Date: Sun, 27 Sep 2026 20:09:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(video):=20=E8=A7=86=E9=A2=91=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=20-=20=E8=A7=84=E8=8C=83=E5=8C=96=E6=95=B0=E5=AD=97?= =?UTF-8?q?=E6=B8=85=E6=99=B0=E5=BA=A6=E4=BB=B7=E6=A0=BC=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/app/model_router.go | 7 ++++++ backend/internal/app/model_router_test.go | 15 +++++++++++ backend/internal/app/task_creation_test.go | 29 ++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/backend/internal/app/model_router.go b/backend/internal/app/model_router.go index c55e162ac..5a70e4b5c 100644 --- a/backend/internal/app/model_router.go +++ b/backend/internal/app/model_router.go @@ -133,6 +133,13 @@ func normalizeModelRequestOption(name string, value any) any { case "4k", "2160", "2160p": return "2160p" default: + // Providers expose additional resolutions such as 768P and 960P. + // Canonicalize numeric P values generically so billing selectors match + // regardless of whether the client sends "960" or "960P". + pixels := strings.TrimSuffix(strings.ToLower(strings.TrimSpace(resolution)), "p") + if numeric, err := strconv.Atoi(pixels); err == nil && numeric > 0 { + return strconv.Itoa(numeric) + "p" + } return value } } diff --git a/backend/internal/app/model_router_test.go b/backend/internal/app/model_router_test.go index 77448beb2..68574b82f 100644 --- a/backend/internal/app/model_router_test.go +++ b/backend/internal/app/model_router_test.go @@ -62,6 +62,21 @@ func TestModelRequestIntentNormalizesVideoResolution(t *testing.T) { } } +func TestModelRequestIntentNormalizesNumericVideoResolution(t *testing.T) { + for value, want := range map[string]string{ + "768": "768p", "768p": "768p", "768P": "768p", + "960": "960p", "960P": "960p", + } { + intent := ModelRequestIntentFromTaskInput(map[string]any{ + "mode": "video", + "config": map[string]any{"vquality": value}, + }, "canvas_video", "image_to_video") + if got := intent.Options["vquality"]; got != want { + t.Errorf("vquality %q normalized to %#v, want %s", value, got, want) + } + } +} + func TestModelRequestIntentNormalizesImageSpecificationValues(t *testing.T) { input := map[string]any{ "mode": "image", diff --git a/backend/internal/app/task_creation_test.go b/backend/internal/app/task_creation_test.go index 1f07875d8..6cb7a838e 100644 --- a/backend/internal/app/task_creation_test.go +++ b/backend/internal/app/task_creation_test.go @@ -172,6 +172,35 @@ func TestResolveSystemChannelModelSelectionRebuildsAuthoritativeExecutionSpec(t } } +func TestResolveSystemChannelModelSelectionMatchesMiniMax768PTier(t *testing.T) { + profile := DefaultModelCapabilityConfigForModel(string(model.ChannelInterfaceMiniMaxVideo), "MiniMax-H3") + svc, _, channel, channelModel := createSystemChannelSelectionFixture(t, "video", model.ChannelInterfaceMiniMaxVideo, profile, []model.ChannelModelPriceTier{ + newSelectionPriceTier("tier-768p", `{"vquality":"768p"}`, "MiniMax-H3", "per_second"), + newSelectionPriceTier("tier-2k", `{"vquality":"1440p"}`, "MiniMax-H3", "per_second"), + }) + + resolved, err := svc.resolveSystemChannelModelSelection(map[string]any{ + "mode": "video", + "referenceImages": []any{map[string]any{"storageKey": "resource:test"}}, + "config": map[string]any{ + "channelId": channel.ID, + "model": channelModel.ModelKey, + "vquality": "768", + "videoSeconds": "15", + }, + }, "canvas_video", "image_to_video") + if err != nil { + t.Fatalf("resolveSystemChannelModelSelection() error = %v", err) + } + config := resolved["config"].(map[string]any) + if config["priceTierId"] != "tier-768p" { + t.Fatalf("priceTierId = %#v, want tier-768p", config["priceTierId"]) + } + if config["vquality"] != "768" { + t.Fatalf("execution vquality = %#v, want original provider-facing value 768", config["vquality"]) + } +} + func TestResolveSystemChannelModelSelectionAppliesServerDefaults(t *testing.T) { profile := DefaultModelCapabilityConfigForModel(string(model.ChannelInterfaceGrokImage), "grok-image") svc, _, channel, channelModel := createSystemChannelSelectionFixture(t, "image", model.ChannelInterfaceGrokImage, profile, []model.ChannelModelPriceTier{