生图模型可选(火山/gpt-image)+ 模特上身图提示词强化 + 多会话等改动

本轮(生图模型选择 + 火山接入):
- 工作室新增「生图模型」选择器(模特上身图/平台套图头部 chip + 图片创作底部 Pill),
  默认火山 Seedream,可切 gpt-image-2;选择写入 localStorage,下次进页面读回
- 后端 resolve_image_model 解析所选模型;enqueue_standalone_images 接 image_model
- worker 按模型能力分流:有 image_edit(gpt-image)走多图编辑;无(火山)走
  image_generation(image=参考图);新增 _ratio_to_volcano_size 让火山按比例出图
  → 内衣等敏感品类用火山可绕开 gpt-image 的 sexual 内容审核

模特上身图提示词:
- 穿戴/非穿戴分流、按 index 变化动作场景镜头、负面词尾接、多图参考序号自适应
- _product_reference_urls:商品参考真实上传图优先、排除 AI 生成图、可多张

其他(并入此前各会话未提交改动):
- 图片创作多会话(ImageConversation + migration 0019)、任务中心按类型过滤
- accounts/projects/assets/team/auth 等零散调整、相关测试
- 测试脚本(tryon_*.py)、测试清单(core/bug/*.xlsx)
This commit is contained in:
zyc
2026-06-27 14:00:28 +08:00
parent 0a519ca892
commit ce7eaefe1d
21 changed files with 1279 additions and 158 deletions
+27 -5
View File
@@ -20,6 +20,8 @@ import type {
Ledger,
LoginSession,
Invitation,
ImageConversation,
ImageConversationTask,
ModelConfig,
ModelEntity,
Notification,
@@ -135,7 +137,7 @@ export const api = {
return request<AuthPayload>("/api/auth/login/", { method: "POST", body: JSON.stringify(payload) });
},
me() {
return request<{ user: User; team: Team }>("/api/auth/me/");
return request<{ user: User; team: Team; role?: string }>("/api/auth/me/");
},
updateProfile(payload: { name?: string; phone?: string; email?: string }) {
return request<{ user: User; team: Team }>("/api/auth/me/", { method: "PATCH", body: JSON.stringify(payload) });
@@ -456,7 +458,9 @@ export const api = {
// 服务端分页+过滤的资产列表(各页按需懒加载,不再前端取全量再切片)。
assetsPage(params: {
tab?: string; category?: string; source?: string; asset_type?: string;
product?: string; q?: string; ordering?: string; page?: number; pageSize?: number;
product?: string; origin_task?: string; q?: string; ordering?: string; page?: number; pageSize?: number;
// in_library: "all" 看全部(含未入库,任务中心用)/ "false" 只看未入库 / 省略=只看已入库
in_library?: string;
meta?: Record<string, string>;
} = {}) {
const qs = new URLSearchParams();
@@ -585,9 +589,27 @@ export const api = {
// 以便 全部/已完成/失败 三个 tab 数按真实总量算。
return request<Paginated<AITask>>("/api/ai/tasks/?page_size=200&task_type=person_image,product_image");
},
// 异步生图:提交后秒级返回任务列表(慢出图在 worker 跑),再用 generateImageStatus 轮询取结果
submitGenerateImage(payload: { prompt: string; mode?: "image" | "model" | "cover"; count?: number; product_id?: string; reference_product?: boolean; model_id?: string; model_entity_id?: string; ratio?: string; image_model?: string }) {
return request<{ tasks: { id: string; status: string }[] }>("/api/ai/generate-image/", { method: "POST", body: JSON.stringify(payload) });
// 异步生图:提交后秒级返回任务列表(慢出图在 worker 跑),再用 generateImageStatus 轮询取结果
// 带 conversation_id 则归属该对话;不带则后端自动开一条新对话并回传其 id。
submitGenerateImage(payload: { prompt: string; mode?: "image" | "model" | "cover"; count?: number; product_id?: string; reference_product?: boolean; model_id?: string; model_entity_id?: string; ratio?: string; image_model?: string; conversation_id?: string; reference_image_ids?: string[] }) {
return request<{ conversation_id: string; tasks: { id: string; status: string }[] }>("/api/ai/generate-image/", { method: "POST", body: JSON.stringify(payload) });
},
// 图片创作对话 CRUD —— 左栏会话列表 / 新对话 / 切换 / 重命名 / 删除
listConversations(mode: "image" | "model" | "cover" = "image") {
return request<Paginated<ImageConversation>>(`/api/ai/image-conversations/?mode=${mode}&page_size=100`);
},
createConversation(payload: { mode?: "image" | "model" | "cover"; title?: string; product?: string | null }) {
return request<ImageConversation>("/api/ai/image-conversations/", { method: "POST", body: JSON.stringify(payload) });
},
renameConversation(id: string, title: string) {
return request<ImageConversation>(`/api/ai/image-conversations/${id}/`, { method: "PATCH", body: JSON.stringify({ title }) });
},
deleteConversation(id: string) {
return request<void>(`/api/ai/image-conversations/${id}/`, { method: "DELETE" });
},
// 切换对话时回填:该对话历次生成的任务 + 成图
conversationTasks(id: string) {
return request<{ conversation_id: string; tasks: ImageConversationTask[] }>(`/api/ai/image-conversations/${id}/tasks/`);
},
generateImageStatus(ids: string[]) {
return request<{ tasks: { id: string; status: string; error_message: string; assets: Asset[] }[] }>(`/api/ai/generate-image/?ids=${encodeURIComponent(ids.join(","))}`);