fix(schedule): parse ASR times with spaces around colons for ITL routing
Memind CI / Test, build, and release guards (push) Successful in 4m15s
Memind CI / Test, build, and release guards (push) Successful in 4m15s
Voice/wechat ASR often emits forms like "17 :08", which bypassed timed_reminder classification and fell through to agent_schedule + schedule-guard false failures. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+15
-3
@@ -12,6 +12,16 @@ function normalizeText(text) {
|
||||
.trim();
|
||||
}
|
||||
|
||||
/** ASR/wechat often inserts spaces around colons, e.g. "17 :08". */
|
||||
function normalizeLooseTimeText(text) {
|
||||
return String(text ?? '')
|
||||
.replace(
|
||||
/([0-9]{1,2}|[零一二两三四五六七八九十]{1,3})\s*([::])\s*/gu,
|
||||
'$1$2',
|
||||
)
|
||||
.replace(/([::])\s*([0-9]{1,2})/gu, '$1$2');
|
||||
}
|
||||
|
||||
function pad2(value) {
|
||||
return String(value).padStart(2, '0');
|
||||
}
|
||||
@@ -44,7 +54,8 @@ function chineseHourToNumber(value) {
|
||||
}
|
||||
|
||||
export function parseHourMinute(text) {
|
||||
const match = text.match(
|
||||
const normalized = normalizeLooseTimeText(text);
|
||||
const match = normalized.match(
|
||||
/(?:今天|明天|后天|今晚|明晚)?(?:早上|上午|清晨|每天(?:早上|上午)?)?(早上|上午|清晨|中午|下午|傍晚|晚上|凌晨)?([0-9]{1,2}|[零一二两三四五六七八九十]{1,3})(?:点|:|:)(半|[0-9]{1,2}分?)?/,
|
||||
);
|
||||
if (!match) return null;
|
||||
@@ -66,7 +77,8 @@ export function parseHourMinute(text) {
|
||||
}
|
||||
|
||||
function countTimeExpressions(text) {
|
||||
const matches = String(text ?? '').match(
|
||||
const normalized = normalizeLooseTimeText(text);
|
||||
const matches = normalized.match(
|
||||
/[0-9零一二两三四五六七八九十]{1,3}(?:点|:|:)(?:半|[0-9]{1,2}(?:分(?!开)|(?![0-9]))?)?/gu,
|
||||
);
|
||||
return matches?.length ?? 0;
|
||||
@@ -93,7 +105,7 @@ function extractTimedReminderTitle(text) {
|
||||
}
|
||||
|
||||
function cleanupTimedReminderTitleFragment(text) {
|
||||
let title = String(text ?? '').trim();
|
||||
let title = normalizeLooseTimeText(String(text ?? '').trim());
|
||||
title = title
|
||||
.replace(/^(?:帮我|请|麻烦)?(?:设置|设|创建|添加)(?:一个|个)?提醒[,,、::\s]*/u, '')
|
||||
.replace(/^(?:帮我|请)?(?:设置|设)(?:一个|个)?(?:待办|代办|带办|代拜)[,,、::\s]*/u, '');
|
||||
|
||||
@@ -125,6 +125,25 @@ test('specific dated reminder still routes to schedule assistant', () => {
|
||||
assert.equal(shouldUseScheduleAssistant('明天下午三点提醒我开会'), true);
|
||||
});
|
||||
|
||||
test('parses ASR reminder text with spaced fullwidth colon before minutes', () => {
|
||||
const now = Date.UTC(2026, 7, 24, 9, 7, 0); // 2026-08-24 17:07 Asia/Shanghai
|
||||
const intent = parseScheduleIntent('设置一个提醒,今天下午 17 :08要开会', {
|
||||
timezone: 'Asia/Shanghai',
|
||||
now,
|
||||
});
|
||||
assert.equal(intent.action, 'create_timed_reminder');
|
||||
assert.equal(intent.title, '要开会');
|
||||
assert.equal(intent.hour, 17);
|
||||
assert.equal(intent.minute, 8);
|
||||
assert.equal(intent.remindLocal, '2026-08-24 17:08');
|
||||
});
|
||||
|
||||
test('ASR spaced colon routes to ITL timed reminder not agent', () => {
|
||||
const result = classifyUserIntent('设置一个提醒,今天下午 17 :08要开会');
|
||||
assert.equal(result.kind, 'timed_reminder');
|
||||
assert.equal(result.layer, 'L1');
|
||||
});
|
||||
|
||||
test('parses direct reminder setup phrase from wechat', () => {
|
||||
const now = Date.UTC(2026, 7, 24, 4, 0, 0); // 2026-08-24 12:00 Asia/Shanghai
|
||||
const intent = parseScheduleIntent('帮我设置提醒,下午 14:30 分开会,项目计划例会', {
|
||||
|
||||
Reference in New Issue
Block a user