import { HttpException } from '@nestjs/common'; // e.g. "400 Your request was rejected by the safety system, safety_violations=[sexual]" const SAFETY_VIOLATIONS_REGEX = /safety_violations=\[([^\]]*)\]/i; // Match genuine content-safety rejections by message, NOT by a bare 400 status: // a 400 can just as easily be an invalid-parameter error, which must not be // reported to the user as a safety violation. const SAFETY_MESSAGE_REGEX = /safety system|safety_violations|content[ _]policy|rejected as a result of our safety|moderation/i; /** * Normalizes errors thrown by AI generation providers (OpenAI image/chat, * LangChain DALL-E, Fal, Veo3, HeyGen, ElevenLabs, ...) into a clean * HttpException so a provider rejection (most notably an OpenAI safety * violation) returns a proper response instead of crashing the backend. * * When the provider reports a content-safety rejection, the flagged * category/categories are surfaced back to the user. */ export function generationError(err: any): HttpException { // Preserve errors we already raised intentionally (e.g. SubscriptionException). if (err instanceof HttpException) { return err; } const message: string = err?.error?.message || err?.message || String(err || ''); if (SAFETY_MESSAGE_REGEX.test(message)) { const categories = message.match(SAFETY_VIOLATIONS_REGEX)?.[1]?.trim(); const detail = categories ? ` Flagged categories: ${categories}.` : ''; return new HttpException( `Your request was rejected by the AI safety system.${detail} Please adjust your prompt and try again.`, 422 ); } // Not a recognized safety rejection (e.g. an invalid-parameter 400) — return // a generic message rather than mislabeling it as a content-safety issue. return new HttpException('AI generation failed, please try again later.', 500); }