Treat AI create input as a research brief and localize FA prompts.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
d37b920c01
commit
57800634ca
@@ -105,7 +105,7 @@ Rules:
|
|||||||
- Use 2-6 top-level categories when appropriate.
|
- Use 2-6 top-level categories when appropriate.
|
||||||
- Add subcategories only where they help shoppers browse (max depth ${MAX_DEPTH}).
|
- Add subcategories only where they help shoppers browse (max depth ${MAX_DEPTH}).
|
||||||
- Every node needs nameEn and nameFa (Farsi in Persian script).
|
- Every node needs nameEn and nameFa (Farsi in Persian script).
|
||||||
- Descriptions are optional, max 120 characters, English only.
|
- Descriptions are optional, max 120 characters. Match the user prompt language (Persian if the prompt is Persian, otherwise English).
|
||||||
- Do not duplicate category names at the same level.
|
- Do not duplicate category names at the same level.
|
||||||
- Total nodes must not exceed ${MAX_TOTAL}.`;
|
- Total nodes must not exceed ${MAX_TOTAL}.`;
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Return ONLY valid JSON with this shape:
|
|||||||
{
|
{
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"label": "Field label in English",
|
"label": "Field label",
|
||||||
"type": "text | textarea | select | multi_select",
|
"type": "text | textarea | select | multi_select",
|
||||||
"isRequired": true,
|
"isRequired": true,
|
||||||
"options": ["Option A", "Option B"]
|
"options": ["Option A", "Option B"]
|
||||||
@@ -108,6 +108,7 @@ Rules:
|
|||||||
- textarea fields are for longer specs (features, care instructions).
|
- textarea fields are for longer specs (features, care instructions).
|
||||||
- Mark important buyer-facing specs as required.
|
- Mark important buyer-facing specs as required.
|
||||||
- Labels must be unique and human-readable.
|
- Labels must be unique and human-readable.
|
||||||
|
- Write labels and option text in the same language as the user hint. If the hint is Persian (Farsi), use Persian labels and options; otherwise use English.
|
||||||
- Do not include price, stock, SKU, or warranty period fields.
|
- Do not include price, stock, SKU, or warranty period fields.
|
||||||
- Prefer metric units common in Iran when relevant.`;
|
- Prefer metric units common in Iran when relevant.`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IsEnum, IsOptional, IsString, MinLength, ValidateIf } from 'class-validator';
|
import { IsEnum, IsOptional, IsString, MaxLength, MinLength, ValidateIf } from 'class-validator';
|
||||||
|
|
||||||
export enum ProductAiLanguage {
|
export enum ProductAiLanguage {
|
||||||
en = 'en',
|
en = 'en',
|
||||||
@@ -16,8 +16,10 @@ export class CreateProductByAiDto {
|
|||||||
@MinLength(1)
|
@MinLength(1)
|
||||||
categoryId!: string;
|
categoryId!: string;
|
||||||
|
|
||||||
|
/** Product brief for AI: model name, optional URL, and notes. */
|
||||||
@IsString()
|
@IsString()
|
||||||
@MinLength(2)
|
@MinLength(2)
|
||||||
|
@MaxLength(4000)
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|
||||||
@IsEnum(ProductAiLanguage)
|
@IsEnum(ProductAiLanguage)
|
||||||
|
|||||||
@@ -285,18 +285,21 @@ Return ONLY valid JSON with this shape:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
- Be factual and realistic for the named product in the given category.
|
- The user input is a research brief in ${inputLanguage}. It may include a model/product name, a product page URL, and extra notes — not a final storefront title.
|
||||||
|
- Infer the real product from the brief (and any URL/model clues). Do not copy URLs, markdown, or raw notes into title/nameFa.
|
||||||
|
- Write clean storefront titles: "title" in English, "nameFa" in Persian. Keep brand/model accurate.
|
||||||
|
- Be factual and realistic for the identified product in the given category.
|
||||||
- If technical form fields are provided, fill technicalValues using exact field keys.
|
- If technical form fields are provided, fill technicalValues using exact field keys.
|
||||||
- For select fields use one allowed option value exactly.
|
- For select fields use one allowed option value exactly.
|
||||||
- For multi_select fields use an array of allowed option values.
|
- For multi_select fields use an array of allowed option values.
|
||||||
- For text/textarea fields use plain strings.
|
- For text/textarea fields use plain strings.
|
||||||
- The user input name is in ${inputLanguage}; keep that spelling in the matching field and translate/generate the other language field.
|
- Prefer the input language for summary/description tone, but always return both title and nameFa.
|
||||||
- descriptionHtml should be 2-4 paragraphs with key specs.
|
- descriptionHtml should be 2-4 paragraphs with key specs.
|
||||||
- tags: 3-8 relevant lowercase tags.`;
|
- tags: 3-8 relevant lowercase tags.`;
|
||||||
|
|
||||||
const userPrompt = JSON.stringify({
|
const userPrompt = JSON.stringify({
|
||||||
category: input.categoryName,
|
category: input.categoryName,
|
||||||
inputName: input.productName,
|
researchBrief: input.productName,
|
||||||
inputLanguage: input.language,
|
inputLanguage: input.language,
|
||||||
technicalFields: technicalSchema,
|
technicalFields: technicalSchema,
|
||||||
});
|
});
|
||||||
@@ -327,15 +330,9 @@ Rules:
|
|||||||
fields: TechnicalFormField[];
|
fields: TechnicalFormField[];
|
||||||
},
|
},
|
||||||
): AiProductDraft {
|
): AiProductDraft {
|
||||||
const title =
|
const fallbackName = this.shortProductLabel(input.productName);
|
||||||
input.language === ProductAiLanguage.en
|
const title = String(draft.title ?? '').trim() || fallbackName;
|
||||||
? input.productName
|
const nameFa = String(draft.nameFa ?? '').trim() || fallbackName;
|
||||||
: String(draft.title ?? input.productName).trim();
|
|
||||||
|
|
||||||
const nameFa =
|
|
||||||
input.language === ProductAiLanguage.fa
|
|
||||||
? input.productName
|
|
||||||
: String(draft.nameFa ?? '').trim();
|
|
||||||
|
|
||||||
const summary = String(draft.summary ?? '').trim();
|
const summary = String(draft.summary ?? '').trim();
|
||||||
const descriptionHtml = normalizeAiDescriptionHtml(
|
const descriptionHtml = normalizeAiDescriptionHtml(
|
||||||
@@ -395,4 +392,14 @@ Rules:
|
|||||||
technicalValues,
|
technicalValues,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** First non-URL line of the research brief, truncated for fallback titles. */
|
||||||
|
private shortProductLabel(brief: string): string {
|
||||||
|
const line =
|
||||||
|
brief
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.map((part) => part.trim())
|
||||||
|
.find((part) => part && !/^https?:\/\//i.test(part)) ?? brief.trim();
|
||||||
|
return line.slice(0, 120).trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user