feat: separate prior records behind an owner switch and count last-period questions

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-15 10:48:30 +03:30
co-authored by Cursor
parent 3a8a684aa4
commit 6bc5e1bec7
29 changed files with 775 additions and 77 deletions
+15 -4
View File
@@ -19,7 +19,7 @@ export type CountEntity =
| 'routine'
| 'project';
export type CountPeriod = 'today' | 'yesterday' | 'week' | 'month';
export type CountPeriod = 'today' | 'yesterday' | 'week' | 'last_week' | 'month' | 'last_month';
export type LeadBucket = 'summary' | 'open' | 'archived' | 'converted';
export type DealBucket = 'all' | 'open' | 'won' | 'lost' | 'stale';
@@ -51,6 +51,7 @@ export function normalizeAskText(raw: string) {
.replace(/ي/g, 'ی')
.replace(/ك/g, 'ک')
.replace(/‌/g, ' ')
.replace(/[؟?!.,،؛:]+/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
@@ -64,7 +65,7 @@ function detectEntity(q: string): CountEntity | null {
if (/لید|سرنخ/.test(q)) return 'lead';
if (/پیش\s*فاکتور/.test(q)) return 'quote';
if (/فاکتور/.test(q)) return 'invoice';
if (/معامله/.test(q)) return 'deal';
if (/معاملات|معامله/.test(q)) return 'deal';
if (/چک/.test(q)) return 'cheque';
if (/رزرو|نوبت/.test(q)) return 'reservation';
if (/روتین/.test(q)) return 'routine';
@@ -81,7 +82,9 @@ function detectEntity(q: string): CountEntity | null {
function detectPeriod(q: string): CountPeriod | undefined {
if (/دیروز/.test(q)) return 'yesterday';
if (/امروز/.test(q)) return 'today';
if (/هفته\s*(?:ی\s+)?(گذشته|قبل|پیش)/.test(q)) return 'last_week';
if (/این\s*هفته|هفته\s*جاری/.test(q)) return 'week';
if (/ماه\s*(?:ی\s+)?(گذشته|قبل|پیش)/.test(q)) return 'last_month';
if (/این\s*ماه|ماه\s*جاری/.test(q)) return 'month';
return undefined;
}
@@ -152,6 +155,9 @@ const HINT_STOP = [
'مرحله',
'وضعیت',
'هفته',
'گذشته',
'پیش',
'قبل',
'لطفا',
'الان',
'شده',
@@ -219,6 +225,11 @@ export function classifyCountQuestion(raw: string): CountIntent | null {
const entity = detectEntity(q);
if (!entity) return null;
const personSource = SOURCE_PATTERNS.find((item) => item.re.test(q))?.source;
const period = detectPeriod(q);
let hint = leftoverHint(q, entity);
if (period && entity === 'lead' && !/مرحله|وضعیت|ستون/.test(q)) {
hint = hint.replace(/(^|\s)جدید(?=\s|$)/g, ' ').replace(/\s+/g, ' ').trim();
}
return {
entity,
personSource:
@@ -226,8 +237,8 @@ export function classifyCountQuestion(raw: string): CountIntent | null {
mood: entity === 'person' ? detectMood(q) : undefined,
leadBucket: detectLeadBucket(q),
dealBucket: detectDealBucket(q),
period: detectPeriod(q),
hint: leftoverHint(q, entity),
period,
hint,
};
}