import { extractBloodPressurePair } from './health-bp-parse.mjs'; import { looksLikeHealthReportPageRequest, looksLikeHealthQuickAssess } from './health-channel-aliases.mjs'; export { looksLikeHealthReportPageRequest, looksLikeHealthQuickAssess } from './health-channel-aliases.mjs'; export const HEALTH_RULE_INTENTS = Object.freeze({ BLOOD_PRESSURE: 'blood_pressure', WEIGHT: 'weight', HEART_RATE: 'heart_rate', SPO2: 'spo2', TEMPERATURE: 'temperature', SLEEP: 'sleep', SYMPTOM: 'symptom', ASSESS: 'assess', ARCHIVE: 'archive', }); const BP_PAIR = /(\d{2,3})\s*[//]\s*(\d{2,3})/; export function matchHealthIdleRules(text) { const raw = String(text ?? '').trim(); if (!raw) return { matched: false, confidence: 0 }; const bpPair = extractBloodPressurePair(raw); if (bpPair?.systolic != null && bpPair?.diastolic != null) { return { matched: true, intent: HEALTH_RULE_INTENTS.BLOOD_PRESSURE, confidence: bpPair.source === 'chinese' ? 0.88 : 0.95, extracted: { systolic: bpPair.systolic, diastolic: bpPair.diastolic }, }; } const bp = raw.match(BP_PAIR); if (bp) { return { matched: true, intent: HEALTH_RULE_INTENTS.BLOOD_PRESSURE, confidence: 0.95, extracted: { systolic: Number(bp[1]), diastolic: Number(bp[2]) }, }; } if (/血压/.test(raw) && /\d{2,3}/.test(raw)) { const nums = [...raw.matchAll(/\d{2,3}/g)].map((m) => Number(m[0])); if (nums.length >= 2) { return { matched: true, intent: HEALTH_RULE_INTENTS.BLOOD_PRESSURE, confidence: 0.8, extracted: { systolic: nums[0], diastolic: nums[1] }, }; } } const weight = raw.match(/体重\s*(\d{2,3}(?:\.\d)?)/); if (weight) { return { matched: true, intent: HEALTH_RULE_INTENTS.WEIGHT, confidence: 0.9, extracted: { weight: Number(weight[1]) }, }; } const hr = raw.match(/(?:心率|脉搏)\s*(\d{2,3})/); if (hr) { return { matched: true, intent: HEALTH_RULE_INTENTS.HEART_RATE, confidence: 0.9, extracted: { hr: Number(hr[1]) }, }; } const spo2 = raw.match(/(?:血氧|SpO2|spo2)\s*(\d{2,3})/i); if (spo2) { return { matched: true, intent: HEALTH_RULE_INTENTS.SPO2, confidence: 0.9, extracted: { spo2: Number(spo2[1]) }, }; } const temp = raw.match(/(?:体温|发烧)\s*(3\d(?:\.\d)?)/); if (temp) { return { matched: true, intent: HEALTH_RULE_INTENTS.TEMPERATURE, confidence: 0.9, extracted: { temperature: Number(temp[1]) }, }; } if (/睡了|睡眠/.test(raw) && /\d/.test(raw)) { return { matched: true, intent: HEALTH_RULE_INTENTS.SLEEP, confidence: 0.75, extracted: {} }; } if (/头晕|胸闷|乏力|心慌|头痛|气短/.test(raw)) { return { matched: true, intent: HEALTH_RULE_INTENTS.SYMPTOM, confidence: 0.8, extracted: {} }; } if (looksLikeHealthReportPageRequest(raw) || looksLikeHealthQuickAssess(raw)) { return { matched: true, intent: HEALTH_RULE_INTENTS.ASSESS, confidence: 0.85, extracted: {} }; } if (/我的档案|历史记录/.test(raw) || (/报告/.test(raw) && !looksLikeHealthReportPageRequest(raw))) { return { matched: true, intent: HEALTH_RULE_INTENTS.ARCHIVE, confidence: 0.8, extracted: {} }; } return { matched: false, confidence: 0 }; } export function shouldForceActionChoice(ruleResult, llm = null) { if (ruleResult?.matched && ruleResult.confidence >= 0.75) return false; const confidence = Number(llm?.confidence ?? 0); const gap = Number(llm?.top1 ?? 0) - Number(llm?.top2 ?? 0); if (confidence < 0.75 || gap < 0.15) return true; return false; } export function shouldRedirectOrdinaryChatToHealth(text) { const hit = matchHealthIdleRules(text); if (!hit.matched) return false; return [ HEALTH_RULE_INTENTS.BLOOD_PRESSURE, HEALTH_RULE_INTENTS.WEIGHT, HEALTH_RULE_INTENTS.HEART_RATE, HEALTH_RULE_INTENTS.SPO2, HEALTH_RULE_INTENTS.TEMPERATURE, HEALTH_RULE_INTENTS.SLEEP, HEALTH_RULE_INTENTS.SYMPTOM, HEALTH_RULE_INTENTS.ASSESS, ].includes(hit.intent); } export const HEALTH_REDIRECT_COPY = '你想记录健康数据吗?健康数据需要在健康助手通道里录入,这样才能保证记录准确并纳入你的健康基线分析。'; const HEALTH_ENTER_PATTERNS = [ /健康助手/, /健康小助手/, /进入健康/, /健康通道/, /录血压/, ]; export function isHealthEnterText(text) { const raw = String(text ?? '').trim(); if (!raw) return false; return HEALTH_ENTER_PATTERNS.some((pattern) => pattern.test(raw)); }