From 469627c7922cfbc67a251e7e89f937e96229fd68 Mon Sep 17 00:00:00 2001 From: MasterChiefCN <49179444+MasterChiefCN@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:05:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(creation):=20=E5=88=9B=E4=BD=9C=E4=B8=8E?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E9=85=8D=E7=BD=AE=20-=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=99=84=E4=BB=B6=E9=A2=84=E8=A7=88=E9=9A=90=E8=97=8F=E5=92=8C?= =?UTF-8?q?=20MiniMax=202K=20=E8=83=BD=E5=8A=9B=E6=A0=A1=E9=AA=8C=20(#433)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/service/logical_models_test.go | 35 +++++++++++++++++++ backend/internal/service/model_router.go | 5 +-- backend/internal/service/provider_test.go | 33 +++++++++++++++++ docs/content/docs/progress/pending-test.mdx | 11 ++++++ web/src/styles/globals.css | 8 ++--- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/backend/internal/service/logical_models_test.go b/backend/internal/service/logical_models_test.go index 5af096129..46cf15bb0 100644 --- a/backend/internal/service/logical_models_test.go +++ b/backend/internal/service/logical_models_test.go @@ -65,6 +65,41 @@ func TestMatchCapabilityTreatsVideoResolutionSuffixAsEquivalent(t *testing.T) { } } +func TestVideoResolutionAliasesAcrossProductAndPricedRoutes(t *testing.T) { + product := CapabilitySpec{ + Version: 1, Capability: "video", + Options: map[string]OptionConstraint{"vquality": {Values: []any{"768P", "2K"}}}, + } + route := capabilitySpecWithPriceTiers(product, model.ChannelModel{ + PriceTiers: []model.ChannelModelPriceTier{ + {Resolution: "768P", Enabled: true, PriceConfigured: true}, + {Resolution: "2K", Enabled: true, PriceConfigured: true}, + }, + }) + if err := validateProductSpecWithinRoutes(product, []CapabilitySpec{route}); err != nil { + t.Fatalf("2K product rejected against priced route: %v", err) + } + for _, resolution := range []string{"2K", "2k", "1440P", "1440"} { + intent := ModelRequestIntent{Capability: "video", Options: map[string]any{ + "vquality": normalizeModelRequestOption("vquality", resolution), + }} + for name, spec := range map[string]CapabilitySpec{"product": product, "route": route} { + if match := MatchCapability(spec, intent); !match.Matched { + t.Errorf("%s rejected %s: %#v", name, resolution, match) + } + } + } + unsupported := CapabilitySpec{Version: 1, Capability: "video", Options: map[string]OptionConstraint{ + "vquality": {Values: []any{"4K"}}, + }} + if err := validateProductSpecWithinRoutes(unsupported, []CapabilitySpec{route}); err == nil { + t.Fatal("2K route accepted unsupported 4K product") + } + if capabilityOptionValuesEqual("quality", "2K", "1440p") { + t.Fatal("video aliases must not affect image quality") + } +} + func TestValidateProductSpecWithinRoutesRejectsUnsupportedCapabilityValue(t *testing.T) { routes := []CapabilitySpec{ { diff --git a/backend/internal/service/model_router.go b/backend/internal/service/model_router.go index d294d909f..f37e82b2a 100644 --- a/backend/internal/service/model_router.go +++ b/backend/internal/service/model_router.go @@ -358,8 +358,9 @@ func capabilityOptionValuesEqual(name string, candidate any, value any) bool { left := normalizedScalar(candidate) right := normalizedScalar(value) if canonicalCapabilityOptionName(name) == "vquality" { - left = strings.TrimSuffix(left, "p") - right = strings.TrimSuffix(right, "p") + // Compare using the same aliases as request intents and price tiers. + left = strings.TrimSuffix(normalizedScalar(normalizeModelRequestOption(name, left)), "p") + right = strings.TrimSuffix(normalizedScalar(normalizeModelRequestOption(name, right)), "p") } return left == right } diff --git a/backend/internal/service/provider_test.go b/backend/internal/service/provider_test.go index 05d80d236..a82288e62 100644 --- a/backend/internal/service/provider_test.go +++ b/backend/internal/service/provider_test.go @@ -2706,6 +2706,39 @@ func TestRunMiniMaxVideoTaskCreatesPollsAndDownloads(t *testing.T) { } } +func TestRunMiniMaxVideoTaskSendsOfficial2KResolution(t *testing.T) { + t.Setenv("CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS", "127.0.0.1") + for _, quality := range []string{"2K", "1440p"} { + t.Run(quality, func(t *testing.T) { + requested := false + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != "/v2/video_generation" { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) + } + requested = true + var body miniMaxVideoRequest + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + t.Errorf("decode request: %v", err) + } + if body.Resolution != "2K" { + t.Errorf("resolution = %q, want official API value 2K", body.Resolution) + } + // End at submission: this test inspects the wire payload without polling. + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{}`)) + })) + defer server.Close() + _, err := runMiniMaxVideoTask(context.Background(), canvasGenerationInput{ + Mode: "video", Prompt: "make it move", + Config: providerConfig{BaseURL: server.URL, APIKey: "test-key", Model: "MiniMax-H3", VQuality: quality}, + }) + if !requested || err == nil || !strings.Contains(err.Error(), "没有返回任务 ID") { + t.Fatalf("expected submission without task ID, requested=%v err=%v", requested, err) + } + }) + } +} + func TestRunMiniMaxVideoTaskUsesExplicitReferenceRoles(t *testing.T) { t.Setenv("CANVAS_ALLOW_PRIVATE_UPSTREAMS", "true") var server *httptest.Server diff --git a/docs/content/docs/progress/pending-test.mdx b/docs/content/docs/progress/pending-test.mdx index 24d796608..67fd0b4e1 100644 --- a/docs/content/docs/progress/pending-test.mdx +++ b/docs/content/docs/progress/pending-test.mdx @@ -5,6 +5,17 @@ description: 当前项目已实现功能的手工验证清单与未确认边界 # 待测试 +## 创作页面附件缩略图 + +- 聊天消息与分镜简报附件的放大图标样式排除 `.cached-resource-image-shell`,避免缓存图片容器被误设为透明和绝对定位;悬停与键盘聚焦仍显示放大图标。 +- [ ] 在真实创作会话中确认远端图片加载后无需悬停即可显示,移开鼠标后保持可见,点击可放大;同时确认分镜简报附件、本地图片和视频预览正常。 + +## MiniMax H3 分辨率能力与价格档 + +- 视频能力比较复用请求意图和价格档的分辨率别名归一规则,修复 MiniMax H3 供应线路配置 2K 价格档后,前台模型声明 2K 时误报「创作端参数超出供应线路能力:vquality」。内部价格档仍使用归一值,MiniMax 请求保持官方参数 `resolution: "2K"`。 +- [x] 回归测试在修复前复现相同保存校验错误,修复后通过;相关能力匹配、线路覆盖和 MiniMax 专项测试通过。模拟 HTTP 请求验证 2K 与内部 1440p 均发送 2K,未声明的 4K 仍被拒绝,图片 quality 参数不受视频别名影响。 +- [ ] 在实际 `/admin/channels` 配置 768P / 2K 价格档后,于 `/admin/models` 保存包含 2K 的模型能力,确认创作端可选并正确报价;未调用真实收费生成接口或部署服务。 + ## 管理后台系统性能 - 管理后台「系统配置」新增独立「系统性能」页面,集中展示主机与 Go 进程、数据目录所在磁盘、系统/进程内存、数据库连接池与 Schema、PostgreSQL 统计、Redis 运行状态和运行时缓存 key 数量;连接地址、账号、密码、DSN 与 Redis URL 不返回前端。 diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css index c39c928a5..770fb42fe 100644 --- a/web/src/styles/globals.css +++ b/web/src/styles/globals.css @@ -11318,10 +11318,10 @@ html:not(.dark) .creation-user-message-reference { background: rgba(28, 32, 37, .creation-user-message-attachments button > .cached-resource-image-shell { display: block; width: 100%; height: 100%; } .creation-user-message-attachments button > .cached-resource-image-shell > img { display: block; width: 100%; height: 100%; object-fit: cover; } .creation-user-message-attachments button:focus-visible { outline: 2px solid var(--creation-text); outline-offset: 2px; } -.creation-user-message-attachments button > span { position: absolute; right: 3px; bottom: 3px; display: grid; width: 17px; height: 17px; place-items: center; border-radius: 5px; background: rgba(0, 0, 0, .68); color: #fff; opacity: 0; transition: opacity 140ms ease; } -.creation-user-message-attachments button:hover > span, -.creation-user-message-attachments button:focus-visible > span { opacity: 1; } -.creation-user-message-attachments button > span svg { width: 10px; height: 10px; } +.creation-user-message-attachments button > span:not(.cached-resource-image-shell) { position: absolute; right: 3px; bottom: 3px; display: grid; width: 17px; height: 17px; place-items: center; border-radius: 5px; background: rgba(0, 0, 0, .68); color: #fff; opacity: 0; transition: opacity 140ms ease; } +.creation-user-message-attachments button:hover > span:not(.cached-resource-image-shell), +.creation-user-message-attachments button:focus-visible > span:not(.cached-resource-image-shell) { opacity: 1; } +.creation-user-message-attachments button > span:not(.cached-resource-image-shell) svg { width: 10px; height: 10px; } .creation-assistant-message { max-width: 900px; margin: 0 0 48px; } .creation-message-heading { gap: 7px; color: var(--creation-faint); font-size: 11px; }