生成记录接口兼容图片类型|CHAT对话生成视频/图片任务API+celery异步模块完成

This commit is contained in:
2026-05-27 13:11:55 +08:00
parent d61dcdc8db
commit b8eda53c0b
30 changed files with 3392 additions and 45 deletions
+418
View File
@@ -0,0 +1,418 @@
from pydantic import BaseModel, ConfigDict, Field
from app.schemas.common import NaiveDatetimeOptional
class GenerationAIReference(BaseModel):
"""AI生成任务参考素材。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"url": "https://example.com/reference.png",
"type": "image",
"name": "参考图.png",
}
}
)
url: str = Field(
...,
description="参考素材地址,可以是图片地址或视频地址",
examples=["https://example.com/reference.png"],
)
type: str = Field(
...,
description="参考素材类型:image=图片,video=视频",
examples=["image"],
)
name: str | None = Field(
None,
description="参考素材名称,前端展示用,可为空",
examples=["参考图.png"],
)
class GenerationAITaskCreate(BaseModel):
"""创建AI图片/视频生成任务请求体。"""
model_config = ConfigDict(
json_schema_extra={
"examples": [
{
"project_id": None,
"gen_type": "image",
"original_prompt": "生成一张赛博朋克风格的城市夜景",
"engine_id": None,
"media_references": [
{
"url": "https://example.com/reference.png",
"type": "image",
"name": "参考图.png",
}
],
"idempotency_key": "frontend-submit-uuid-001",
"image_size": "2K",
"image_proportion": "1:1",
"image_px": "2048x2048",
"duration": None,
"aspect_ratio": None,
"resolution": None,
},
{
"project_id": None,
"gen_type": "video",
"original_prompt": "生成一段海边日落的电影感视频",
"engine_id": None,
"media_references": None,
"idempotency_key": "frontend-submit-uuid-002",
"image_size": None,
"image_proportion": None,
"image_px": None,
"duration": 4,
"aspect_ratio": "16:9",
"resolution": "480p",
},
]
}
)
# 新 chat 生成任务不绑定 project_id。为了兼容旧前端误传,保留可选字段但后端不使用。
project_id: str | None = Field(
None,
description="兼容旧前端字段。当前 /generation-ai 任务不绑定项目,后端不使用该字段,可传 null 或不传",
examples=[None],
)
gen_type: str = Field(
...,
description="生成类型:image=图片生成,video=视频生成",
examples=["image"],
)
original_prompt: str = Field(
...,
min_length=1,
max_length=5000,
description="用户原始提示词,不能为空,最长5000字符",
examples=["生成一张赛博朋克风格的城市夜景"],
)
engine_id: str | None = Field(
None,
description="图片/视频引擎ID;为空则使用当前激活且优先级最高的引擎",
examples=[None],
)
media_references: list[GenerationAIReference] | None = Field(
None,
description="参考素材列表。可以传图片/视频参考素材;为空表示不使用参考素材",
)
idempotency_key: str | None = Field(
None,
max_length=64,
description=(
"幂等键,用于防止前端重复提交、网络重试导致重复创建任务和重复扣费。"
"同一用户、同一 idempotency_key、同一 generation_mode 下重复请求会返回已有任务。"
"建议前端每次点击生成时生成 UUID;同一次请求失败重试时复用同一个 UUID。"
),
examples=["frontend-submit-uuid-001"],
)
# image params
image_size: str | None = Field(
None,
description="图片分辨率档位,例如:1K、2K。仅图片生成或视频首帧参数需要使用;为空则使用引擎默认值",
examples=["2K"],
)
image_proportion: str | None = Field(
None,
description="图片比例,例如:1:1、16:9、9:16。仅图片生成或视频首帧参数需要使用;为空则使用默认值",
examples=["1:1"],
)
image_px: str | None = Field(
None,
description="图片像素尺寸,例如:2048x2048。为空时后端根据 image_size 和 image_proportion 自动匹配",
examples=["2048x2048"],
)
# video params
duration: int | None = Field(
None,
description="视频时长,单位秒。仅视频生成使用;为空则使用默认时长",
examples=[4],
)
aspect_ratio: str | None = Field(
None,
description="视频比例,例如:16:9、9:16、1:1。仅视频生成使用;为空则使用默认比例",
examples=["16:9"],
)
resolution: str | None = Field(
None,
description="视频分辨率,例如:480p、720p、1080p。仅视频生成使用;为空则使用默认分辨率",
examples=["480p"],
)
class GenerationAITaskOut(BaseModel):
"""AI生成任务详情响应体。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"id": "0019e0a44895b6d837d",
"project_id": None,
"gen_type": "image",
"generation_mode": "chatapi_async",
"pipeline_stage": "done",
"status": "completed",
"original_prompt": "生成一张赛博朋克风格的城市夜景",
"optimized_prompt": None,
"duration": None,
"aspect_ratio": None,
"resolution": None,
"image_size": "2K",
"image_proportion": "1:1",
"image_px": "2048x2048",
"media_references": None,
"provider_task_id": "provider_task_xxx",
"seedance_task_id": "provider_task_xxx",
"remote_result_url": None,
"image_url": "https://example.com/result.png",
"video_url": None,
"engine_id": "engine_xxx",
"engine_snapshot": {
"engine_type": "image",
"id": "engine_xxx",
"name": "图片生成引擎",
"provider": "provider_name",
"model_name": "model_name",
"supported_models": [],
"default_size": "2K",
"selected_size": "2K",
"selected_proportion": "1:1",
"selected_px": "2048x2048",
},
"credits_cost": 10.0,
"text_credits_cost": 0.0,
"text_tokens_used": 0,
"image_tokens_used": 0,
"video_tokens_used": 0,
"retry_count": 0,
"poll_count": 3,
"error_message": None,
"created_at": "2026-05-27T10:12:00",
"generated_at": "2026-05-27T10:15:30",
}
}
)
id: str = Field(..., description="生成任务ID")
project_id: str | None = Field(
None,
description="项目ID。当前 /generation-ai 任务不绑定项目,通常为 null",
)
gen_type: str = Field(..., description="生成类型:image=图片,video=视频")
generation_mode: str | None = Field(
None,
description="生成模式。当前异步Chat生成任务一般为 chatapi_async",
)
pipeline_stage: str | None = Field(
None,
description=(
"任务流水线阶段,例如:queued=已入队,creating_provider_task=创建第三方任务中,"
"waiting_remote=等待第三方生成,result_ready=远程结果已就绪,downloading=下载中,done=完成"
),
)
status: str = Field(
...,
description="任务状态,例如:generating=生成中,completed=已完成,failed=失败",
)
original_prompt: str = Field(..., description="用户原始提示词")
optimized_prompt: str | None = Field(None, description="优化后的提示词,可能为空")
duration: int | None = Field(None, description="视频时长,单位秒。图片任务通常为空")
aspect_ratio: str | None = Field(None, description="视频比例,例如 16:9。图片任务通常为空")
resolution: str | None = Field(None, description="视频分辨率,例如 480p。图片任务通常为空")
image_size: str | None = Field(None, description="图片分辨率档位,例如 2K")
image_proportion: str | None = Field(None, description="图片比例,例如 1:1")
image_px: str | None = Field(None, description="图片像素尺寸,例如 2048x2048")
media_references: list[dict] | None = Field(
None,
description="参考素材列表。由创建任务时传入的 media_references 转换而来",
)
provider_task_id: str | None = Field(None, description="第三方服务商任务ID")
seedance_task_id: str | None = Field(
None,
description="兼容旧前端/旧服务命名的第三方任务ID字段",
)
remote_result_url: str | None = Field(
None,
description="第三方远程结果地址。当前接口可能隐藏或为空,最终展示优先使用 image_url/video_url",
)
image_url: str | None = Field(None, description="最终图片地址。图片任务完成后通常有值")
video_url: str | None = Field(None, description="最终视频地址。视频任务完成后通常有值")
engine_id: str | None = Field(None, description="本次任务使用的生成引擎ID")
engine_snapshot: dict | None = Field(
None,
description="生成任务创建时的引擎快照,用于前端展示当时使用的模型、比例、尺寸等信息",
)
credits_cost: float = Field(0.0, description="本次任务总消耗积分")
text_credits_cost: float = Field(0.0, description="文本优化或文本处理消耗积分")
text_tokens_used: int = Field(0, description="文本 token 使用量")
image_tokens_used: int = Field(0, description="图片 token 使用量")
video_tokens_used: int = Field(0, description="视频 token 使用量")
retry_count: int = Field(0, description="任务重试次数")
poll_count: int = Field(0, description="轮询第三方任务状态次数")
error_message: str | None = Field(None, description="错误信息。成功任务一般为 null")
created_at: NaiveDatetimeOptional = Field(None, description="任务创建时间")
generated_at: NaiveDatetimeOptional = Field(None, description="任务生成完成时间")
class GenerationAITaskListOut(BaseModel):
"""AI生成任务列表响应体。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"total": 1,
"items": [
{
"id": "0019e0a44895b6d837d",
"project_id": None,
"gen_type": "image",
"generation_mode": "chatapi_async",
"pipeline_stage": "done",
"status": "completed",
"original_prompt": "生成一张赛博朋克风格的城市夜景",
"optimized_prompt": None,
"duration": None,
"aspect_ratio": None,
"resolution": None,
"image_size": "2K",
"image_proportion": "1:1",
"image_px": "2048x2048",
"media_references": None,
"provider_task_id": "provider_task_xxx",
"seedance_task_id": "provider_task_xxx",
"remote_result_url": None,
"image_url": "https://example.com/result.png",
"video_url": None,
"engine_id": "engine_xxx",
"engine_snapshot": {},
"credits_cost": 10.0,
"text_credits_cost": 0.0,
"text_tokens_used": 0,
"image_tokens_used": 0,
"video_tokens_used": 0,
"retry_count": 0,
"poll_count": 3,
"error_message": None,
"created_at": "2026-05-27T10:12:00",
"generated_at": "2026-05-27T10:15:30",
}
],
}
}
)
total: int = Field(..., description="符合筛选条件的任务总数")
items: list[GenerationAITaskOut] = Field(
default_factory=list,
description="当前分页的任务列表",
)
class GenerationAIRetryOut(BaseModel):
"""AI生成任务重试响应体。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"id": "0019e0a44895b6d837d",
"status": "generating",
"pipeline_stage": "queued",
"message": "任务已重新投递",
}
}
)
id: str = Field(..., description="被重试的任务ID")
status: str = Field(..., description="重试后的任务状态")
pipeline_stage: str | None = Field(None, description="重试后的任务流水线阶段")
message: str = Field(..., description="操作结果提示信息")
class GenerationAIHistoryDayGroupOut(BaseModel):
"""AI生成历史按天分组响应项。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"generated_date": "2026-05-27",
"total": 18,
"items": [],
}
}
)
generated_date: str = Field(..., description="生成日期,格式:YYYY-MM-DD")
total: int = Field(..., description="当前生成日期下的生成成功记录总数")
items: list[GenerationAITaskOut] = Field(
default_factory=list,
description="当前生成日期下倒序前10条生成记录详情",
)
class GenerationAIHistoryGroupedOut(BaseModel):
"""AI生成历史日期分组列表响应体。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"total_days": 2,
"page": 1,
"page_size": 10,
"groups": [
{
"generated_date": "2026-05-27",
"total": 18,
"items": [],
},
{
"generated_date": "2026-05-26",
"total": 6,
"items": [],
},
],
}
}
)
total_days: int = Field(..., description="当前生成类型下,用户一共有多少个生成日期分组")
page: int = Field(..., description="当前日期分组分页页码")
page_size: int = Field(..., description="当前每页返回的日期分组数量,最大10")
groups: list[GenerationAIHistoryDayGroupOut] = Field(
default_factory=list,
description="按生成日期倒序排列的历史记录分组列表",
)
class GenerationAIHistoryDayItemsOut(BaseModel):
"""指定日期下AI生成历史分页响应体。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"generated_date": "2026-05-27",
"total": 18,
"page": 2,
"page_size": 10,
"items": [],
}
}
)
generated_date: str = Field(..., description="当前查询的生成日期,格式:YYYY-MM-DD")
total: int = Field(..., description="当前日期下的生成成功记录总数")
page: int = Field(..., description="当前日期下的记录分页页码")
page_size: int = Field(..., description="当前日期下每页返回的生成记录数量")
items: list[GenerationAITaskOut] = Field(
default_factory=list,
description="当前日期下的生成记录详情列表,按 generated_at 倒序排列",
)