From d6a57c2fa74afc52fde88d057ce6b6113604b258 Mon Sep 17 00:00:00 2001 From: ddcat666 <26564566+ddcat666@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:51:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(release):=20=E5=8F=91=E5=B8=83=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=20-=20=E4=BF=AE=E5=A4=8D=20v1.6.1=20=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E6=B5=8B=E8=AF=95=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 兑换码购买链接默认改为空,避免非法占位文案导致外观保存失败;迁移清单补上 upload_reservations;按首尾帧后的实测体积上调 Agent 工具 schema 预算;Grok 生图测试覆盖 response_format 开关的两种返回。 --- CHANGELOG.md | 3 + backend/cmd/migrate-sqlite-postgres/main.go | 1 + backend/internal/app/appearance.go | 2 +- .../cloud_agent_tool_schema_budget_test.go | 7 +- backend/internal/app/provider_test.go | 83 +++++++++++-------- 5 files changed, 59 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a380bda6f..ebcbc4c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,11 @@ - 技能管理支持后台维护一级分类与平台子分类策展;分片上传先预留配额并原子提交容量;资源交付按内容识别上传类型并统一重试。 - 修复 Agent 视频生成首尾帧设置、审批暂停与恢复、排队任务执行前重查自定义渠道开关、欢迎页外观与 SEO 标题、Gmail 别名注册唯一性、API 调用日志 UTF-8 截断等问题。 - 新增 3D 预演台赞助用户致谢,详见 README。 +- 兑换码购买链接默认留空,未配置时不展示入口;Grok 生图按 response_format 开关在 b64_json 与 url 之间切换。 - 升级注意:数据库迁移升至 v47(新增上传配额预留与技能分类策展表),启动时自动执行。 +## v1.6.0 + - 角色库与音频工作台完善素材选择、生成参数和同步;音频生成支持 IndexTTS2 情感控制,并保留多条音频结果。 - 画布 Agent 补齐角色卡、音频协议和任务恢复;修复关闭思考被误判为开启,以及审批中修改图片参数时容量检查自锁导致审批一直卡住。 - 修复账号存储配额准入、Live2D 预览和后台用户筛选。 diff --git a/backend/cmd/migrate-sqlite-postgres/main.go b/backend/cmd/migrate-sqlite-postgres/main.go index 08c996c6a..b7f92b9dc 100644 --- a/backend/cmd/migrate-sqlite-postgres/main.go +++ b/backend/cmd/migrate-sqlite-postgres/main.go @@ -408,6 +408,7 @@ func migrations() []tableMigration { migrateTable[model.UserOSSSetting]("user_oss_settings"), migrateTable[model.StorageLocation]("storage_locations"), migrateTable[model.UserDailyUploadUsage]("user_daily_upload_usages"), + migrateTable[model.UploadReservation]("upload_reservations"), migrateTable[model.Skill]("skills"), migrateTable[model.SkillLibraryCategory]("skill_library_categories"), migrateTable[model.SkillVersion]("skill_versions"), diff --git a/backend/internal/app/appearance.go b/backend/internal/app/appearance.go index 2c30167dd..030ed89c4 100644 --- a/backend/internal/app/appearance.go +++ b/backend/internal/app/appearance.go @@ -48,7 +48,7 @@ const ( defaultAppearanceVideoURL = "https://boss-shjd.biliapi.net/updream/aniforge/video/video_bbcb00bd-650d-4249-9346-5cd21fd2484c_m1hc-u0-1pu13x-3v1s.mp4" defaultAppearancePosterURL = "https://i0.hdslb.com/bfs/aitool/aniforge/image/02933f26-5f1b-49ff-a811-b7f95ee5e5b8_m1hc-u0-sau.jpg" defaultAppearanceHeroTitle = "让一个故事,\n从文字走向银幕。" - defaultRedeemPurchaseURL = "暂未配置" + defaultRedeemPurchaseURL = "" // 默认不配置购买链接,前端为空时不展示入口 ) type AppearanceSetting struct { diff --git a/backend/internal/app/cloud_agent_tool_schema_budget_test.go b/backend/internal/app/cloud_agent_tool_schema_budget_test.go index 6cf13db4c..8bccc13ec 100644 --- a/backend/internal/app/cloud_agent_tool_schema_budget_test.go +++ b/backend/internal/app/cloud_agent_tool_schema_budget_test.go @@ -56,9 +56,10 @@ func TestCloudAgentToolSchemaStaysCompact(t *testing.T) { // 之后能力注册表加入角色卡(图片/视频/音频接受 character 输入)到 28,979 字节;再新增 // canvas_create_character(角色卡打包创建,设定字段已压成一行描述)后为 25 个工具、29,938 // 字节;本轮加入 previs_scene_create 与 previs_apply_patch 后为 27 个工具、34,171 字节, - // 因此显式上调到 35,000,并保留约2.4%的余量。新增工具或字段时请重新测量并有意识地 + // 因此显式上调到 35,000;视频生成支持 Agent 设置首尾帧(#696)后为 35,050 字节, + // 再显式上调到 36,000,保留约2.7%的余量。新增工具或字段时请重新测量并有意识地 // 调整这个数字,而不是让 schema 悄悄膨胀(它每一步都要发、还在前缀最前面)。 - if len(raw) > 35000 { - t.Fatalf("平台工具 schema 体积 %d 字节超出预算 35000:请压缩描述或显式调整预算", len(raw)) + if len(raw) > 36000 { + t.Fatalf("平台工具 schema 体积 %d 字节超出预算 36000:请压缩描述或显式调整预算", len(raw)) } } diff --git a/backend/internal/app/provider_test.go b/backend/internal/app/provider_test.go index 12b744b5c..96c4e35a2 100644 --- a/backend/internal/app/provider_test.go +++ b/backend/internal/app/provider_test.go @@ -1378,40 +1378,57 @@ func TestRunOpenAIImageTaskUsesMultipartEditContract(t *testing.T) { func TestRunGrokImageTaskUsesJSONEditContract(t *testing.T) { t.Setenv("CANVAS_ALLOW_PRIVATE_UPSTREAMS", "true") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/v1/images/edits" { - t.Errorf("path = %q, want /v1/images/edits", r.URL.Path) - } - if contentType := r.Header.Get("Content-Type"); !strings.HasPrefix(contentType, "application/json") { - t.Errorf("Content-Type = %q, want application/json", contentType) - } - var body grokImageRequest - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - t.Fatalf("decode request body: %v", err) - } - if body.Model != "grok-imagine-image-quality" || body.N != 1 || body.ResponseFormat != "url" { - t.Fatalf("request body = %#v", body) - } - if body.Image == nil || body.Image.URL != testReferenceImageDataURL { - t.Fatalf("image = %#v", body.Image) - } - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write([]byte(`{"data":[{"url":"https://example.com/result.png"}]}`)) - })) - defer server.Close() + // response_format 开关(#692):支持时请求 b64_json,关闭时回落 url。 + for _, tc := range []struct { + name string + responseFormat bool + wantFormat string + reply string + wantDataURL string + }{ + {name: "b64_json", responseFormat: true, wantFormat: "b64_json", reply: `{"data":[{"b64_json":"aGVsbG8="}]}`, wantDataURL: "data:image/png;base64,aGVsbG8="}, + {name: "url", responseFormat: false, wantFormat: "url", reply: `{"data":[{"url":"https://example.com/result.png"}]}`, wantDataURL: "https://example.com/result.png"}, + } { + t.Run(tc.name, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/v1/images/edits" { + t.Errorf("path = %q, want /v1/images/edits", r.URL.Path) + } + if contentType := r.Header.Get("Content-Type"); !strings.HasPrefix(contentType, "application/json") { + t.Errorf("Content-Type = %q, want application/json", contentType) + } + var body grokImageRequest + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + t.Errorf("decode request body: %v", err) + } + if body.Model != "grok-imagine-image-quality" || body.N != 1 || body.ResponseFormat != tc.wantFormat { + t.Errorf("request body = %#v", body) + } + if body.Image == nil || body.Image.URL != testReferenceImageDataURL { + t.Errorf("image = %#v", body.Image) + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(tc.reply)) + })) + defer server.Close() - result, err := runImageTask(context.Background(), canvasGenerationInput{ - Mode: "image", - Prompt: "edit the reference", - Config: providerConfig{BaseURL: server.URL, APIKey: "key", Model: "grok-imagine-image-quality", InterfaceType: "grok-image"}, - ReferenceImages: []providerMedia{{DataURL: testReferenceImageDataURL}}, - }) - if err != nil { - t.Fatalf("runImageTask() error = %v", err) - } - images, _ := result["images"].([]map[string]string) - if len(images) != 1 || images[0]["dataUrl"] != "https://example.com/result.png" { - t.Fatalf("images = %#v", result["images"]) + profile := DefaultImageCapabilityConfig("grok-image", "grok-imagine-image-quality") + profile.ResponseFormat.Supported = tc.responseFormat + result, err := runImageTask(context.Background(), canvasGenerationInput{ + Mode: "image", + Prompt: "edit the reference", + Config: providerConfig{BaseURL: server.URL, APIKey: "key", Model: "grok-imagine-image-quality", InterfaceType: "grok-image"}, + ImageCapability: profile, + ReferenceImages: []providerMedia{{DataURL: testReferenceImageDataURL}}, + }) + if err != nil { + t.Fatalf("runImageTask() error = %v", err) + } + images, _ := result["images"].([]map[string]string) + if len(images) != 1 || images[0]["dataUrl"] != tc.wantDataURL { + t.Fatalf("images = %#v", result["images"]) + } + }) } }