fix: 统一AI生成失败提示
This commit is contained in:
@@ -13,6 +13,7 @@ from apps.assets.serializers import AssetSerializer
|
||||
from apps.common.api import TeamScopedViewSetMixin, get_current_team
|
||||
from apps.common.celery_health import require_worker
|
||||
|
||||
from .generation_errors import classify_generation_error, public_error_for_task
|
||||
from .models import AITask, ImageConversation, ModelConfig
|
||||
from .serializers import AITaskSerializer, ImageConversationSerializer, ModelConfigSerializer
|
||||
from .services import enqueue_standalone_images
|
||||
@@ -69,7 +70,14 @@ class GenerateImageView(APIView):
|
||||
try:
|
||||
tasks = enqueue_standalone_images(team=team, user=request.user, prompt=prompt, mode=mode, count=count, product_id=product_id, reference_product=reference_product, model_id=model_id, model_entity_id=model_entity_id, ratio=ratio, image_model=image_model, conversation=conversation, reference_image_ids=reference_image_ids, platform_id=platform_id, batch_id=batch_id)
|
||||
except ValueError as exc: # 无可用模型 / 余额不足等,立即反馈
|
||||
return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
internal_kind = "user_credit_insufficient" if str(exc).strip().lower() == "insufficient credit" else ""
|
||||
public_error = classify_generation_error(
|
||||
exc, operation="image_generate", internal_kind=internal_kind
|
||||
)
|
||||
return Response(
|
||||
{"detail": public_error.fallback_message, "error": public_error.as_dict()},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
# 本次提交即刷新对话活跃时间,左栏「最近」据此置顶
|
||||
ImageConversation.objects.filter(id=conversation.id).update(last_active_at=timezone.now())
|
||||
# batch_id 回传给前端存进批次卡:后续「重跑这张 / 重跑整批」带它回来即可归回原批次
|
||||
@@ -90,17 +98,18 @@ class GenerateImageView(APIView):
|
||||
tasks = AITask.objects.filter(team=team, id__in=ids).prefetch_related(
|
||||
"generated_assets", "generated_assets__files"
|
||||
)
|
||||
data = [
|
||||
{
|
||||
"id": str(t.id),
|
||||
"status": t.status,
|
||||
"error_message": t.error_message,
|
||||
data = []
|
||||
for task in tasks:
|
||||
public_error = public_error_for_task(task)
|
||||
data.append({
|
||||
"id": str(task.id),
|
||||
"status": task.status,
|
||||
"error": public_error.as_dict() if public_error else None,
|
||||
"error_message": public_error.fallback_message if public_error else "",
|
||||
"assets": AssetSerializer(
|
||||
[a for a in t.generated_assets.all() if not a.is_deleted and a.purged_at is None], many=True
|
||||
[a for a in task.generated_assets.all() if not a.is_deleted and a.purged_at is None], many=True
|
||||
).data,
|
||||
}
|
||||
for t in tasks
|
||||
]
|
||||
})
|
||||
return Response({"tasks": data})
|
||||
|
||||
|
||||
@@ -199,11 +208,14 @@ class AITaskViewSet(TeamScopedViewSetMixin, ReadOnlyModelViewSet):
|
||||
qs = qs.filter(request_payload__product_id=product_id)
|
||||
tasks = list(qs.order_by("-created_at").prefetch_related("generated_assets", "generated_assets__files")[:limit])
|
||||
tasks.reverse() # 旧 → 新,与对话流/工作台批次流的时间序一致
|
||||
data = [
|
||||
{
|
||||
data = []
|
||||
for t in tasks:
|
||||
public_error = public_error_for_task(t)
|
||||
data.append({
|
||||
"id": str(t.id),
|
||||
"status": t.status,
|
||||
"error_message": t.error_message,
|
||||
"error": public_error.as_dict() if public_error else None,
|
||||
"error_message": public_error.fallback_message if public_error else "",
|
||||
"prompt": t.rp_prompt or "",
|
||||
"batch_id": t.rp_batch_id or "",
|
||||
"ratio": t.rp_ratio or "",
|
||||
@@ -217,9 +229,7 @@ class AITaskViewSet(TeamScopedViewSetMixin, ReadOnlyModelViewSet):
|
||||
"assets": AssetSerializer(
|
||||
[a for a in t.generated_assets.all() if not a.is_deleted and a.purged_at is None], many=True
|
||||
).data,
|
||||
}
|
||||
for t in tasks
|
||||
]
|
||||
})
|
||||
return Response({"tasks": data})
|
||||
|
||||
@action(detail=False, methods=["post"], url_path="mark-read")
|
||||
@@ -335,11 +345,14 @@ class ImageConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
|
||||
out.append(ref_cache[rid])
|
||||
return out
|
||||
|
||||
data = [
|
||||
{
|
||||
data = []
|
||||
for t in tasks:
|
||||
public_error = public_error_for_task(t)
|
||||
data.append({
|
||||
"id": str(t.id),
|
||||
"status": t.status,
|
||||
"error_message": t.error_message,
|
||||
"error": public_error.as_dict() if public_error else None,
|
||||
"error_message": public_error.fallback_message if public_error else "",
|
||||
"prompt": (t.request_payload or {}).get("prompt", ""),
|
||||
"batch_id": (t.request_payload or {}).get("batch_id", ""),
|
||||
"ratio": (t.request_payload or {}).get("ratio") or "",
|
||||
@@ -350,9 +363,7 @@ class ImageConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
|
||||
"assets": AssetSerializer(
|
||||
[a for a in t.generated_assets.all() if not a.is_deleted and a.purged_at is None], many=True
|
||||
).data,
|
||||
}
|
||||
for t in tasks
|
||||
]
|
||||
})
|
||||
return Response({"conversation_id": str(conversation.id), "tasks": data})
|
||||
|
||||
|
||||
@@ -409,7 +420,20 @@ class FreeVideoView(APIView):
|
||||
try:
|
||||
task = submit_free_video(team=team, user=request.user, params=request.data or {})
|
||||
except ValueError as exc:
|
||||
return Response({"detail": str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
message = str(exc)
|
||||
internal_kind = (
|
||||
"user_credit_insufficient" if "余额不足" in message
|
||||
else "model_unavailable" if "模型未配置" in message
|
||||
else "provider_rate_limited" if "任务进行中" in message
|
||||
else "invalid_input"
|
||||
)
|
||||
public_error = classify_generation_error(
|
||||
exc, operation="video_generate", internal_kind=internal_kind
|
||||
)
|
||||
return Response(
|
||||
{"detail": public_error.fallback_message, "error": public_error.as_dict()},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
# 重取带 prefetch 的实例,序列化统一走同一条路
|
||||
task = _free_video_task_queryset(team).get(id=task.id)
|
||||
return Response({"task": serialize_free_video_task(task)}, status=status.HTTP_202_ACCEPTED)
|
||||
|
||||
Reference in New Issue
Block a user