fix: keep birthdays and show Shamsi dates

Imported customers keep their birthday, and assistant text plus remaining calendars use the Jalali date.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-13 18:04:53 +03:30
co-authored by Cursor
parent 5f4cc47c41
commit cf326d1ed9
34 changed files with 637 additions and 105 deletions
+10 -7
View File
@@ -4,6 +4,7 @@ import {
ServiceUnavailableException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { rewriteGregorianDates } from '../common/jalali';
export type NoteAiExtract = {
nextAction: {
@@ -44,6 +45,7 @@ Rules:
- dueAt: ISO 8601 date-time if a deadline is mentioned; otherwise null.
- memoryItems: durable facts about the customer (preferences, budget, decisions, family, objections) — NOT tasks.
- Keep text in Persian when the note is Persian.
- If summary mentions a date, write it Shamsi/Jalali like ۱۴۰۴/۰۷/۲۱, never Gregorian month names such as سپتامبر.
- Do not invent facts not supported by the note.
- Maximum 5 memory items.`;
@@ -76,7 +78,8 @@ Rules:
- summary: 3–8 concise Persian sentences covering purpose, customer needs, outcomes, and commitments.
- nextAction: one short Persian follow-up if needed; otherwise null.
- Do not invent facts not in the transcript.
- Keep names, numbers, and amounts accurate.`;
- Keep names, numbers, and amounts accurate.
- Dates in summary and nextAction must be Shamsi/Jalali like ۱۴۰۴/۰۷/۲۱, never Gregorian month names such as سپتامبر.`;
const userParts = [
input.personName ? `Customer: ${input.personName}` : null,
@@ -89,12 +92,12 @@ Rules:
nextAction?: string | null;
}>(system, userParts.join('\n'));
const summary = String(raw.summary ?? '').trim().slice(0, 4000);
const summary = rewriteGregorianDates(String(raw.summary ?? '').trim()).slice(0, 4000);
if (!summary) {
throw new BadRequestException('خلاصه تماس خالی بود');
}
const nextAction = raw.nextAction
? String(raw.nextAction).trim().slice(0, 500) || null
? rewriteGregorianDates(String(raw.nextAction).trim()).slice(0, 500) || null
: null;
return { summary, nextAction };
}
@@ -169,9 +172,9 @@ Rules:
const nextAction =
raw.nextAction && typeof raw.nextAction === 'object'
? {
title: String(raw.nextAction.title ?? '').trim().slice(0, 200),
title: rewriteGregorianDates(String(raw.nextAction.title ?? '').trim()).slice(0, 200),
description: raw.nextAction.description
? String(raw.nextAction.description).trim().slice(0, 2000)
? rewriteGregorianDates(String(raw.nextAction.description).trim()).slice(0, 2000)
: undefined,
dueAt: raw.nextAction.dueAt
? String(raw.nextAction.dueAt).trim()
@@ -182,7 +185,7 @@ Rules:
const memoryItems = Array.isArray(raw.memoryItems)
? raw.memoryItems
.map((item) => ({
content: String(item?.content ?? '').trim().slice(0, 500),
content: rewriteGregorianDates(String(item?.content ?? '').trim()).slice(0, 500),
category: item?.category
? String(item.category).trim().slice(0, 40)
: undefined,
@@ -197,7 +200,7 @@ Rules:
return {
nextAction: cleanedNext,
memoryItems,
summary: raw.summary ? String(raw.summary).trim().slice(0, 500) : undefined,
summary: raw.summary ? rewriteGregorianDates(String(raw.summary).trim()).slice(0, 500) : undefined,
};
}
}