Normalize AI product HTML for the rich text description editor.
Sanitize model output to an allowlisted fragment and convert plain/markdown fallbacks so description text stays inside the advanced editor. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
23ba6344f6
commit
2c4d02bd0e
@@ -14,6 +14,7 @@ import {
|
||||
requestAiJsonCompletion,
|
||||
resolveAiProvider,
|
||||
} from '../common/ai-provider.util';
|
||||
import { normalizeAiDescriptionHtml } from '../common/ai-html.util';
|
||||
|
||||
type TechnicalFormField = {
|
||||
key: string;
|
||||
@@ -150,13 +151,15 @@ Follow the user's instructions for language and tone.
|
||||
Keep the summary to 1–3 sentences. No HTML. No markdown.
|
||||
Prefer a technical, informative style: what the product is and why its traits matter.
|
||||
Never write sales CTAs, discounts, "buy now", or vague praise like "excellent choice" / "amazing experience".`
|
||||
: `You write e-commerce product descriptions as HTML for Iranian stores.
|
||||
: `You write e-commerce product descriptions as HTML for an advanced rich-text editor in Iranian stores.
|
||||
Return ONLY valid JSON: { "html": "..." }
|
||||
The "html" value must be a raw HTML fragment (real tags, not escaped entities, not markdown, not code fences).
|
||||
Allowed tags only: p, ul, ol, li, strong, em, br.
|
||||
Do not wrap in html/body/div. Do not include scripts, styles, classes, or attributes.
|
||||
Follow the user's instructions for language and tone.
|
||||
Prefer a technical, informative structure: what it is, key traits/composition, typical use.
|
||||
Never write sales CTAs, discounts, or vague marketing slogans.
|
||||
Aim for 2–4 short paragraphs and optionally a bullet list. No scripts, styles, or markdown.`;
|
||||
Aim for 2–4 short paragraphs and optionally a bullet list.`;
|
||||
|
||||
const userPrompt = JSON.stringify({
|
||||
instructions: dto.prompt.trim(),
|
||||
@@ -190,7 +193,7 @@ Aim for 2–4 short paragraphs and optionally a bullet list. No scripts, styles,
|
||||
return { field: ProductAiFillField.summary, text };
|
||||
}
|
||||
|
||||
const html = String(parsed.html ?? '').trim();
|
||||
const html = normalizeAiDescriptionHtml(String(parsed.html ?? ''));
|
||||
if (!html) {
|
||||
throw new BadRequestException('AI returned an empty description');
|
||||
}
|
||||
@@ -284,7 +287,9 @@ Rules:
|
||||
: String(draft.nameFa ?? '').trim();
|
||||
|
||||
const summary = String(draft.summary ?? '').trim();
|
||||
const descriptionHtml = String(draft.descriptionHtml ?? '').trim();
|
||||
const descriptionHtml = normalizeAiDescriptionHtml(
|
||||
String(draft.descriptionHtml ?? ''),
|
||||
);
|
||||
const tags = Array.isArray(draft.tags)
|
||||
? draft.tags.map((tag) => String(tag).trim()).filter(Boolean).slice(0, 12)
|
||||
: [];
|
||||
|
||||
Reference in New Issue
Block a user