fix: 收口三视图错误提示
This commit is contained in:
@@ -1591,6 +1591,28 @@ class ModelLibraryTriviewTaskTests(TestCase):
|
||||
self.assertEqual(status_res.json()["status"], AITask.Status.RESERVED)
|
||||
self.assertIsNone(status_res.json()["model"])
|
||||
|
||||
task = AITask.objects.get(id=task_id)
|
||||
raw_error = (
|
||||
"400 moderation_blocked Your request was rejected by the safety system. "
|
||||
"request ID 47ffb351-2307-4c91-bd7e-76749729eef7"
|
||||
)
|
||||
task.status = AITask.Status.FAILED
|
||||
task.error_message = raw_error
|
||||
task.save(update_fields=["status", "error_message"])
|
||||
|
||||
failed_status = self.client.get(f"/api/models/{self.model.id}/triview-status/?task_id={task_id}")
|
||||
self.assertEqual(failed_status.status_code, 200)
|
||||
payload = failed_status.json()
|
||||
self.assertEqual(payload["error"]["code"], "content_rejected")
|
||||
self.assertEqual(payload["error"]["operation"], "triview_generate")
|
||||
self.assertEqual(
|
||||
payload["error_message"],
|
||||
"三视图内容需要调整:内容未通过生成审核,请调整描述或素材后重试。",
|
||||
)
|
||||
self.assertNotIn("moderation_blocked", payload["error_message"])
|
||||
self.assertNotIn("Azure", str(payload))
|
||||
self.assertNotIn("47ffb351-2307-4c91-bd7e-76749729eef7", str(payload))
|
||||
|
||||
|
||||
class _FakeStreamResp:
|
||||
"""模拟 requests 流式响应:支持 with、raise_for_status、可写 encoding、iter_lines。"""
|
||||
|
||||
@@ -556,13 +556,17 @@ class ModelLibraryViewSet(ModelViewSet):
|
||||
if task is None:
|
||||
raise ValidationError({"task_id": "任务不存在或不属于当前模特"})
|
||||
model.refresh_from_db()
|
||||
from apps.ai.generation_errors import public_error_for_task
|
||||
|
||||
public_error = public_error_for_task(task, operation="triview_generate")
|
||||
return Response(
|
||||
{
|
||||
"task_id": str(task.id),
|
||||
"status": task.status,
|
||||
"estimated_cost": str(task.estimated_cost),
|
||||
"actual_cost": str(task.actual_cost),
|
||||
"error_message": task.error_message,
|
||||
"error": public_error.as_dict() if public_error else None,
|
||||
"error_message": public_error.fallback_message if public_error else "",
|
||||
"model": ModelLibrarySerializer(model).data if task.status == AITask.Status.SUCCEEDED else None,
|
||||
},
|
||||
status=status.HTTP_200_OK,
|
||||
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
UserPreference
|
||||
} from "./types";
|
||||
import { publicModelDisplayName } from "./model-display";
|
||||
import { isPublicGenerationError, presentGenerationError } from "./generation-error";
|
||||
import { generationErrorText } from "./generation-error";
|
||||
import { CornerMarks, Decorations, Sidebar, ToastLike } from "./components/app-shell";
|
||||
import {
|
||||
AccountPage,
|
||||
@@ -75,11 +75,6 @@ const crumbLabels: Partial<Record<Page, string>> = {
|
||||
const imgwbKey = (mode?: string) => `airshelf:imgwb:${mode || "image"}`;
|
||||
type ImgwbSaved = { pending?: string[]; results?: Asset[]; count?: number; ts?: number; productId?: string; productTitle?: string };
|
||||
|
||||
function generationTaskErrorText(error: unknown, fallback: string): string {
|
||||
if (!isPublicGenerationError(error)) return fallback;
|
||||
const presentation = presentGenerationError(error);
|
||||
return `${presentation.title}:${presentation.description}`;
|
||||
}
|
||||
function loadImgwb(mode?: string): ImgwbSaved | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(imgwbKey(mode));
|
||||
@@ -634,7 +629,7 @@ export function App() {
|
||||
if (!TERMINAL.has(t.status)) continue;
|
||||
pending.delete(t.id);
|
||||
if (t.status === "succeeded") assets.push(...(t.assets || []));
|
||||
else if (t.error_message) lastErr = generationTaskErrorText(t.error, t.error_message);
|
||||
else if (t.error_message) lastErr = generationErrorText(t.error, t.error_message);
|
||||
}
|
||||
// 进度回写:保留还在跑的 id + 已出结果,供刷新后恢复
|
||||
saveImgwb(mode, { pending: [...pending], results: assets });
|
||||
@@ -670,7 +665,7 @@ export function App() {
|
||||
if (!TERMINAL.has(t.status)) continue;
|
||||
pending.delete(t.id);
|
||||
if (t.status === "succeeded") assets.push(...(t.assets || []));
|
||||
else if (t.error_message) error = generationTaskErrorText(t.error, t.error_message);
|
||||
else if (t.error_message) error = generationErrorText(t.error, t.error_message);
|
||||
}
|
||||
}
|
||||
return { assets, error };
|
||||
|
||||
@@ -648,6 +648,7 @@ export const api = {
|
||||
status: string;
|
||||
estimated_cost: string;
|
||||
actual_cost: string;
|
||||
error?: unknown;
|
||||
error_message: string;
|
||||
model: ModelEntity | null;
|
||||
}>(`/api/models/${id}/triview-status/?${query.toString()}`);
|
||||
|
||||
@@ -95,3 +95,9 @@ export function presentGenerationError(error: PublicGenerationError): Generation
|
||||
referenceId: error.reference_id
|
||||
};
|
||||
}
|
||||
|
||||
export function generationErrorText(error: unknown, fallback: string): string {
|
||||
if (!isPublicGenerationError(error)) return fallback;
|
||||
const presentation = presentGenerationError(error);
|
||||
return `${presentation.title}:${presentation.description}`;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ChangeEvent, CSSProperties, KeyboardEvent } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { RefreshCw, Upload, User, X } from "lucide-react";
|
||||
import { api } from "../api";
|
||||
import { generationErrorText } from "../generation-error";
|
||||
import type { ModelEntity } from "../types";
|
||||
import { MediaLightbox, useBodyScrollLock, useOverlayTransition } from "../components/overlays";
|
||||
import "../models-page.css";
|
||||
@@ -154,7 +155,10 @@ function ModelDetailModal({ model, close, onZoom, onSaved, onNotify, onBillingCh
|
||||
if (["failed", "cancelled"].includes(result.status)) {
|
||||
onBillingChanged?.();
|
||||
setTriviewStatus("");
|
||||
onNotify?.("error", result.error_message || "三视图生成失败,预留积分已释放");
|
||||
onNotify?.("error", generationErrorText(
|
||||
result.error,
|
||||
result.error_message || "三视图生成失败,预留积分已释放",
|
||||
));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user