feat(*): 创作体验 - 支持资源技能引用并统一生成反馈与画布列表展示

This commit is contained in:
ddCat
2026-08-02 22:29:44 +08:00
parent e404d4f716
commit 0253072232
20 changed files with 589 additions and 130 deletions

View File

@@ -2,6 +2,9 @@ export const CONTENT_MODERATION_ERROR_CODE = "sensitive_words_detected";
export const CONTENT_MODERATION_MESSAGE = "内容审核未通过,本次平台积分未扣除或已退还。请修改提示词后重新生成。";
const DEFAULT_GENERATION_ERROR_MESSAGE = "生成失败,请稍后重试。";
const NETWORK_ERROR_MESSAGE = "网络异常。";
export type GenerationFailureMetadata = {
errorDetails: string;
generationErrorCode?: string;
@@ -9,8 +12,8 @@ export type GenerationFailureMetadata = {
};
export function generationFailureMetadata(error: unknown, prompt: string): GenerationFailureMetadata {
const raw = error instanceof Error ? error.message : String(error || "生成失败");
if (!isContentModerationError(raw)) return { errorDetails: raw || "生成失败" };
const raw = rawGenerationError(error);
if (!isContentModerationError(raw)) return { errorDetails: generationErrorMessage(error) };
return {
errorDetails: CONTENT_MODERATION_MESSAGE,
generationErrorCode: CONTENT_MODERATION_ERROR_CODE,
@@ -19,8 +22,20 @@ export function generationFailureMetadata(error: unknown, prompt: string): Gener
}
export function generationErrorMessage(error: unknown) {
const raw = error instanceof Error ? error.message : String(error || "生成失败");
return isContentModerationError(raw) ? CONTENT_MODERATION_MESSAGE : raw || "生成失败";
const raw = rawGenerationError(error);
if (isContentModerationError(raw)) return CONTENT_MODERATION_MESSAGE;
const providerMessage = extractStructuredProviderMessage(raw) || extractWrappedProviderMessage(raw);
const displayMessage = providerMessage || raw;
if (isContentModerationError(displayMessage)) return CONTENT_MODERATION_MESSAGE;
if (isNetworkFailure(displayMessage)) return NETWORK_ERROR_MESSAGE;
if (!providerMessage) {
if (hasHttpStatus(raw, 429)) return "服务当前繁忙,请稍后重试。";
if (hasHttpStatus(raw, 401, 403)) return "生成服务鉴权失败,请检查渠道配置。";
if (hasHttpStatus(raw, 404)) return "生成服务地址不可用,请检查渠道配置。";
if (hasHttpStatus(raw, 500, 502, 503, 504) || containsInfrastructureDetails(raw)) return NETWORK_ERROR_MESSAGE;
}
return displayMessage || DEFAULT_GENERATION_ERROR_MESSAGE;
}
export function isContentModerationError(value: unknown) {
@@ -45,3 +60,59 @@ export function generationPromptFingerprint(value: string) {
}
return `${normalized.length}:${(hash >>> 0).toString(36)}`;
}
function rawGenerationError(error: unknown) {
if (error instanceof Error) return error.message.trim();
if (typeof error === "string") return error.trim();
return providerPayloadMessage(error);
}
function extractStructuredProviderMessage(raw: string) {
for (let index = raw.indexOf("{"); index >= 0; index = raw.indexOf("{", index + 1)) {
try {
const message = providerPayloadMessage(JSON.parse(raw.slice(index).trim()));
if (message) return message;
} catch {
// 上游常把 JSON 拼在 HTTP 状态后;不是完整 JSON 时继续尝试下一个对象起点。
}
}
return "";
}
function extractWrappedProviderMessage(raw: string) {
const interfaceFailure = raw.match(/^接口请求失败[::]\s*(.*)$/s);
const requestFailure = raw.match(/^Request failed with status code \d{3}\s*[::-]?\s*(.+)$/is);
const wrapped = interfaceFailure?.[1] ?? requestFailure?.[1];
if (!wrapped) return "";
const message = wrapped
.replace(/^\d{3}(?:\s+(?:Bad Gateway|Service Unavailable|Gateway Timeout|Internal Server Error|Not Found|Unauthorized|Forbidden|Too Many Requests))?\s*[::-]?\s*/i, "")
.trim();
return message && !containsInfrastructureDetails(message) ? message : "";
}
function providerPayloadMessage(payload: unknown): string {
if (typeof payload === "string") return payload.trim();
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return "";
const record = payload as Record<string, unknown>;
if (record.error && typeof record.error === "object") {
const nested = providerPayloadMessage(record.error);
if (nested) return nested;
}
for (const key of ["message", "msg", "detail"] as const) {
const value = record[key];
if (typeof value === "string" && value.trim()) return value.trim();
}
return typeof record.error === "string" ? record.error.trim() : "";
}
function isNetworkFailure(value: string) {
return /\b(?:dial tcp|connection refused|connection reset|no such host|i\/o timeout|context deadline exceeded|network error|failed to fetch|fetch failed|socket hang up|econnrefused|econnreset|etimedout)\b/i.test(value);
}
function hasHttpStatus(value: string, ...statuses: number[]) {
return statuses.some((status) => new RegExp(`\\b${status}\\b`).test(value));
}
function containsInfrastructureDetails(value: string) {
return /(?:接口请求失败|Request failed with status code|https?:\/\/|\b(?:GET|POST|PUT|PATCH|DELETE)\s+["']?|Bad Gateway|Service Unavailable|Gateway Timeout|upstream_error)/i.test(value);
}