feat: 开发编辑工作台
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"""图生图 / 图像编辑请求响应模型(多模型)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
# 支持的模型白名单
|
||||
SUPPORTED_MODELS: frozenset[str] = frozenset(
|
||||
{
|
||||
"wanx2.1-imageedit",
|
||||
"wan2.6-image",
|
||||
"qwen-image-edit",
|
||||
"qwen-image-edit-plus",
|
||||
"qwen-image-edit-plus-2025-10-30",
|
||||
}
|
||||
)
|
||||
|
||||
# 万相 2.1 通用图像编辑支持的 function 白名单
|
||||
# description_edit :文本指令编辑(无掩码,即“图生图”)
|
||||
# description_edit_with_mask :局部重绘(需 mask)
|
||||
# stylization_local :局部风格化
|
||||
# stylization_all :整体风格化
|
||||
ImageEditFunction = Literal[
|
||||
"description_edit",
|
||||
"description_edit_with_mask",
|
||||
"stylization_local",
|
||||
"stylization_all",
|
||||
]
|
||||
|
||||
# 单张 base64 图片(编码后)上限,超出直接拒绝,避免无谓占用内存。
|
||||
_MAX_BASE64_LENGTH = 15 * 1024 * 1024
|
||||
|
||||
|
||||
class ImageEditRequest(BaseModel):
|
||||
"""图生图请求。图片以 data URL(data:image/...;base64,...)或公网 URL 传入。"""
|
||||
|
||||
base_image: str = Field(..., description="待编辑原图:data URL 或公网 http(s) URL")
|
||||
prompt: str = Field(..., description="编辑指令,例如“把背景换成纯白色摄影棚”")
|
||||
model: str = Field(
|
||||
default="wanx2.1-imageedit",
|
||||
description="模型 code,见 SUPPORTED_MODELS",
|
||||
)
|
||||
mask_image: str | None = Field(
|
||||
default=None, description="掩码图(仅 function=description_edit_with_mask 时使用)"
|
||||
)
|
||||
function: ImageEditFunction = Field(
|
||||
default="description_edit", description="编辑能力类型,见 ImageEditFunction"
|
||||
)
|
||||
n: int = Field(default=1, ge=1, le=4, description="生成数量")
|
||||
size: str = Field(default="", description="输出尺寸,如 1024*1024;留空由模型决定")
|
||||
seed: int | None = Field(default=None, ge=0, description="随机种子,可选")
|
||||
style: str = Field(default="", description="输出风格,如 auto/photography/anime 等,可选")
|
||||
prompt_extend: bool = Field(default=True, description="是否自动扩写提示词(官方示例默认开启)")
|
||||
strength: float | None = Field(
|
||||
default=None,
|
||||
ge=0.0,
|
||||
le=1.0,
|
||||
description=(
|
||||
"修改幅度 0.0~1.0,官方默认 0.5;值越大改动越明显、越偏离原图。"
|
||||
"新增文字、大幅修改等场景建议 0.8 左右"
|
||||
),
|
||||
)
|
||||
|
||||
@field_validator("prompt")
|
||||
@classmethod
|
||||
def prompt_not_empty(cls, value: str) -> str:
|
||||
text = (value or "").strip()
|
||||
if not text:
|
||||
raise ValueError("prompt 不能为空")
|
||||
return text
|
||||
|
||||
@field_validator("model")
|
||||
@classmethod
|
||||
def model_whitelist(cls, value: str) -> str:
|
||||
model = (value or "").strip()
|
||||
if model not in SUPPORTED_MODELS:
|
||||
raise ValueError(f"不支持的模型「{model}」,可选:{sorted(SUPPORTED_MODELS)}")
|
||||
return model
|
||||
|
||||
@field_validator("base_image")
|
||||
@classmethod
|
||||
def base_image_valid(cls, value: str) -> str:
|
||||
image = (value or "").strip()
|
||||
if not image:
|
||||
raise ValueError("base_image 不能为空")
|
||||
if image.startswith("data:image/"):
|
||||
if len(image) > _MAX_BASE64_LENGTH:
|
||||
raise ValueError("图片过大,请压缩后重试")
|
||||
elif not image.startswith(("http://", "https://")):
|
||||
raise ValueError("base_image 仅支持 data URL 或公网 http(s) URL")
|
||||
return image
|
||||
|
||||
@field_validator("mask_image")
|
||||
@classmethod
|
||||
def mask_image_valid(cls, value: str | None) -> str | None:
|
||||
if value is None or not value.strip():
|
||||
return None
|
||||
image = value.strip()
|
||||
if image.startswith("data:image/"):
|
||||
if len(image) > _MAX_BASE64_LENGTH:
|
||||
raise ValueError("mask_image 图片过大")
|
||||
elif not image.startswith(("http://", "https://")):
|
||||
raise ValueError("mask_image 仅支持 data URL 或公网 http(s) URL")
|
||||
return image
|
||||
|
||||
@field_validator("style")
|
||||
@classmethod
|
||||
def normalize_style(cls, value: str) -> str:
|
||||
return (value or "").strip()
|
||||
|
||||
|
||||
class ImageEditResult(BaseModel):
|
||||
url: str = Field(..., description="生成图片 URL(有效期 24 小时)")
|
||||
image_base64: str = Field(
|
||||
default="",
|
||||
description="生成图片的 data URL(服务端代理下载,便于前端直接合成/导出,规避跨域画布污染)",
|
||||
)
|
||||
|
||||
|
||||
class ImageEditResponse(BaseModel):
|
||||
task_id: str = ""
|
||||
results: list[ImageEditResult] = Field(default_factory=list)
|
||||
image_count: int = 0
|
||||
request_id: str = ""
|
||||
Reference in New Issue
Block a user