fix(ai): 图片创作重跑三连修——丢参考图/裂新批次/提示词矛盾
① 重跑丢参考图:「重跑这张/重跑整批」原来不带 refs → 后端收不到 reference_image_ids 落到纯文生图,出图与上传素材无关。现 GenBatch.refs 存 assetId(上传后回写;恢复批次由 tasks 接口新带的 id 还原),重跑原样带回。 ② 重跑裂新聊天记录:enqueue 每次新铸 batch_id,后端没有「补进原批次」 概念,刷新/切对话后重跑图裂成新卡、原卡失败格还挂着。现生成接口收/回 batch_id(UUID 校验,非法兜底新铸),重跑沿用原批次并打 batch_append 标记; 前端批次卡存 backendBatchId(提交回调即落,整批全失败也不丢),重跑带回。 恢复分组时 rerun 任务不计入「应出张数」→ 补图成功后失败格自然收掉; 批次状态改按「有任务在跑」判定,已有好图+补图在途也能接续轮询。 ③ 同批换装一半不换:带参考图模板「严格保留款式/不要换款」与用户要求 (如"生成现代服装的穿着")直接矛盾,模型每张随机听一边。改为只锁主体身份 (人物五官/发型/体型,商品品类/外形/Logo),用户明确要求改变的以用户要求 为最高优先级,未提及的才默认与参考图一致。 测试:apps 全量 214 过(需 --settings=airshelf.settings.test); 新增 test_rerun_with_batch_id_reuses_batch_and_marks_append。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -263,11 +263,12 @@ class StandaloneImageReferenceTests(TestCase):
|
||||
)
|
||||
prov.image_edit.assert_called_once()
|
||||
self.assertEqual(prov.image_edit.call_args.kwargs["images"], ["http://x/ref.png"])
|
||||
# 提示词必须显式钉住「保留参考图主体」+ 带上用户原话(否则图生图不真的还原上传素材)
|
||||
# 提示词必须显式钉住「参考图=主体唯一依据」+ 带上用户原话(否则图生图不真的还原上传素材);
|
||||
# 但不能再无差别「严格保留款式」——用户要求(如换现代服装)必须是最高优先级,否则同批一半不换装
|
||||
used_prompt = prov.image_edit.call_args.kwargs["prompt"]
|
||||
self.assertIn("按这件背心生成不同场景的穿搭", used_prompt)
|
||||
self.assertIn("参考图", used_prompt)
|
||||
self.assertIn("严格保留", used_prompt)
|
||||
self.assertIn("唯一依据", used_prompt)
|
||||
self.assertIn("用户要求是最高优先级", used_prompt)
|
||||
prov.image_generation.assert_not_called()
|
||||
|
||||
def test_refs_force_image_edit_model_when_volcano_selected(self):
|
||||
@@ -374,9 +375,45 @@ class ImageConversationTests(TestCase):
|
||||
# 任务 payload 落了参考图 id
|
||||
task = AITask.objects.filter(conversation_id=conv_id).first()
|
||||
self.assertEqual(task.request_payload.get("reference_image_ids"), [str(ref.id)])
|
||||
# tasks 接口把参考图解析成 {name,url} 回显
|
||||
# tasks 接口把参考图解析成 {id,name,url} 回显 —— id 供前端重跑时原样复用(否则重跑丢参考图)
|
||||
data = self.client.get(f"/api/ai/image-conversations/{conv_id}/tasks/").json()["tasks"]
|
||||
self.assertEqual(data[0]["reference_images"], [{"name": "背心参考", "url": "http://x/ref.png"}])
|
||||
self.assertEqual(data[0]["reference_images"], [{"id": str(ref.id), "name": "背心参考", "url": "http://x/ref.png"}])
|
||||
|
||||
def test_rerun_with_batch_id_reuses_batch_and_marks_append(self):
|
||||
"""重跑/补图带原批次 batch_id → 新任务沿用同一 batch_id 并打 batch_append 标记(tasks 接口回
|
||||
rerun=True)—— 守护「重跑不裂新批次卡 + 失败格计数不膨胀」;非法 batch_id 则铸新批次兜底。"""
|
||||
CreditAccount.objects.create(team=self.team, balance="100.0000")
|
||||
patch("apps.ai.tasks.generate_standalone_image_task.delay").start()
|
||||
self.addCleanup(patch.stopall)
|
||||
first = self.client.post(
|
||||
"/api/ai/generate-image/", {"prompt": "一只猫", "mode": "image", "count": 2}, format="json"
|
||||
)
|
||||
self.assertEqual(first.status_code, 202, first.content)
|
||||
conv_id = first.json()["conversation_id"]
|
||||
batch_id = first.json()["batch_id"]
|
||||
self.assertTrue(batch_id)
|
||||
# 重跑一张:带原对话 + 原 batch_id → 归回原批次
|
||||
second = self.client.post(
|
||||
"/api/ai/generate-image/",
|
||||
{"prompt": "一只猫", "mode": "image", "count": 1, "conversation_id": conv_id, "batch_id": batch_id},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(second.status_code, 202, second.content)
|
||||
self.assertEqual(second.json()["batch_id"], batch_id)
|
||||
tasks = self.client.get(f"/api/ai/image-conversations/{conv_id}/tasks/").json()["tasks"]
|
||||
self.assertEqual(len(tasks), 3)
|
||||
self.assertTrue(all(t["batch_id"] == batch_id for t in tasks))
|
||||
self.assertEqual([t["rerun"] for t in tasks], [False, False, True])
|
||||
# 非法 batch_id(不是 UUID):不沿用,铸新批次,不打 append 标记
|
||||
third = self.client.post(
|
||||
"/api/ai/generate-image/",
|
||||
{"prompt": "一只猫", "mode": "image", "count": 1, "conversation_id": conv_id, "batch_id": "not-a-uuid"},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(third.status_code, 202, third.content)
|
||||
self.assertNotEqual(third.json()["batch_id"], batch_id)
|
||||
newest = self.client.get(f"/api/ai/image-conversations/{conv_id}/tasks/").json()["tasks"][-1]
|
||||
self.assertFalse(newest["rerun"])
|
||||
|
||||
|
||||
class StandaloneCategoryTests(TestCase):
|
||||
|
||||
Reference in New Issue
Block a user