fix(release): 发布检查 - 修复 v1.6.1 后端测试失败

兑换码购买链接默认改为空,避免非法占位文案导致外观保存失败;迁移清单补上 upload_reservations;按首尾帧后的实测体积上调 Agent 工具 schema 预算;Grok 生图测试覆盖 response_format 开关的两种返回。
This commit is contained in:
ddcat666
2026-10-06 14:51:43 +08:00
parent b4408669fc
commit d6a57c2fa7
5 changed files with 59 additions and 37 deletions

View File

@@ -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 预览和后台用户筛选。

View File

@@ -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"),

View File

@@ -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 {

View File

@@ -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))
}
}

View File

@@ -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"])
}
})
}
}