fix(ai): 平台套图改走商品主图参考 image_edit · 模特上身图锁商品+模特双图
- 平台套图(cover)以前纯文生图,不参考真实商品主图 → 出图与商品对不上; 现以商品主图为参考图1走 image_edit,锁包装一致性,有模特则参考图2=模特 - 新增 build_platform_cover_prompt_refs;model_url 解析对 cover 模式同样生效 - 补回归测试:cover 传商品主图、model 传商品主图+模特图、无主图回落文生图 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -93,3 +93,78 @@ class NormalizeDraftTests(SimpleTestCase):
|
||||
self.assertEqual(len(draft["segments"]), 2)
|
||||
self.assertEqual(draft["segments"][0]["narration"], "口播1")
|
||||
self.assertEqual(draft["segments"][1]["visual"], "画面2")
|
||||
|
||||
|
||||
from io import BytesIO
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.accounts.models import Team, User
|
||||
from apps.ai.services import enqueue_standalone_images
|
||||
from apps.assets.models import Asset, AssetFile
|
||||
from apps.billing.models import CreditAccount
|
||||
from apps.products.models import Product
|
||||
|
||||
|
||||
class StandaloneImageReferenceTests(TestCase):
|
||||
"""独立生图(平台套图 / 模特上身图)必须把商品真实主图(+ 模特图)作为参考图走 image_edit,
|
||||
而不是纯文生图——回归保护 #图片生成没参考商品主图# 这个 bug。
|
||||
(图像默认模型由迁移 seed 的 tokenssr:gpt-image-2 提供,get_image_provider 全程 mock。)"""
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username="owner", password="pass")
|
||||
self.team = Team.objects.create(name="T", owner=self.user)
|
||||
CreditAccount.objects.create(team=self.team, balance="100.0000")
|
||||
# 商品 + 主图(带可访问 preview_url)
|
||||
self.product = Product.objects.create(team=self.team, created_by=self.user, title="南卡 Lite Pro")
|
||||
cover = Asset.objects.create(
|
||||
team=self.team, created_by=self.user, name="主图", asset_type=Asset.Type.IMAGE,
|
||||
source=Asset.Source.UPLOAD, category=Asset.Category.PRODUCT_IMAGE,
|
||||
)
|
||||
AssetFile.objects.create(asset=cover, object_key="c.png", bucket="b", content_type="image/png", preview_url="http://x/cover.png", is_primary=True)
|
||||
self.product.cover_asset = cover
|
||||
self.product.save(update_fields=["cover_asset"])
|
||||
|
||||
def _patch_provider(self):
|
||||
# image_edit 返回值 + 媒体落库链路全部 mock,聚焦验证「传了哪些参考图」
|
||||
provider = patch("apps.ai.services.get_image_provider").start()
|
||||
prov = provider.return_value
|
||||
prov.image_edit.return_value = {"data": [{"url": "http://x/out.png"}]}
|
||||
prov.image_generation.return_value = {"data": [{"url": "http://x/out.png"}]}
|
||||
prov.extract_first_media_url.return_value = "http://x/out.png"
|
||||
media = patch("apps.ai.services.VolcanoArkProvider.media_to_bytes").start()
|
||||
media.return_value = (BytesIO(b"img"), "image/png")
|
||||
store = patch("apps.ai.services.TosStorage").start()
|
||||
stored = store.return_value.upload_fileobj.return_value
|
||||
stored.object_key, stored.bucket, stored.content_type, stored.size_bytes = "o.png", "b", "image/png", 3
|
||||
self.addCleanup(patch.stopall)
|
||||
return prov
|
||||
|
||||
def test_cover_mode_references_product_main_image(self):
|
||||
prov = self._patch_provider()
|
||||
enqueue_standalone_images(team=self.team, user=self.user, prompt="平台套图", mode="cover", count=1, product_id=str(self.product.id), ratio="4:5")
|
||||
prov.image_edit.assert_called_once()
|
||||
self.assertEqual(prov.image_edit.call_args.kwargs["images"], ["http://x/cover.png"])
|
||||
prov.image_generation.assert_not_called()
|
||||
|
||||
def test_model_tryon_combines_product_and_model_images(self):
|
||||
prov = self._patch_provider()
|
||||
# 模特资产(PERSON)带 preview_url
|
||||
model_asset = Asset.objects.create(
|
||||
team=self.team, created_by=self.user, name="模特", asset_type=Asset.Type.IMAGE,
|
||||
source=Asset.Source.AI_GENERATED, category=Asset.Category.PERSON,
|
||||
)
|
||||
AssetFile.objects.create(asset=model_asset, object_key="m.png", bucket="b", content_type="image/png", preview_url="http://x/model.png", is_primary=True)
|
||||
enqueue_standalone_images(team=self.team, user=self.user, prompt="上身图", mode="model", count=1, product_id=str(self.product.id), model_id=str(model_asset.id), ratio="4:5")
|
||||
prov.image_edit.assert_called_once()
|
||||
self.assertEqual(prov.image_edit.call_args.kwargs["images"], ["http://x/cover.png", "http://x/model.png"])
|
||||
prov.image_generation.assert_not_called()
|
||||
|
||||
def test_cover_mode_falls_back_to_t2i_without_main_image(self):
|
||||
prov = self._patch_provider()
|
||||
self.product.cover_asset = None
|
||||
self.product.save(update_fields=["cover_asset"])
|
||||
enqueue_standalone_images(team=self.team, user=self.user, prompt="平台套图", mode="cover", count=1, product_id=str(self.product.id), ratio="4:5")
|
||||
prov.image_generation.assert_called_once()
|
||||
prov.image_edit.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user