@@ -389,6 +389,7 @@ class SlotExtractor {
389389 private val distanceRegex = Regex (" \\ b(?:far|distance|km|kilometers|kilometre|kilometres|mile|miles|meter|meters|metre|metres|feet|ft|yard|yards|yd|long|length|covered|travelled|traveled|route|path|journey|span|range|how far|mileage)\\ b" , RegexOption .IGNORE_CASE )
390390 private val sleepRegex = Regex (" \\ b(?:sleep|slept|sleeping|asleep|nap|napped|napping|rest|rested|resting|snooze|snoozed|snoozing|doze|dozed|dozing|slumber|bedtime|night|overnight|bed|zzz)\\ b" , RegexOption .IGNORE_CASE )
391391 private val sleepQualityRegex = Regex (" \\ b(?:quality|score|rating|rate|well|badly|good|bad|poor|deep|light|efficiency|grade|rank|analysis|performance|how well)\\ b" , RegexOption .IGNORE_CASE )
392+ private val deepSleepRegex = Regex (" \\ b(?:deep\\ s+sleep|deep\\ s+sleeping|deep\\ s+slumber|rem\\ s+sleep|rem|slow\\ s+wave\\ s+sleep|sws|restorative\\ s+sleep|profound\\ s+sleep|heavy\\ s+sleep|sound\\ s+sleep|stage\\ s+(?:3|4|three|four))\\ b" , RegexOption .IGNORE_CASE )
392393 private val heartRegex = Regex (" \\ b(?:heart|cardiac|cardio|cardiovascular|pulse|beat|beats|beating|bpm|rhythm|ticker)\\ b" , RegexOption .IGNORE_CASE )
393394 private val caloriesRegex = Regex (" \\ b(?:calorie|calories|kcal|energy|burn|burned|burnt|burning|expend|expended|consume|consumed|intake|kilojoule|kilojoules|kj|food energy|metabolic|metabolism|fat)\\ b" , RegexOption .IGNORE_CASE )
394395 private val oxygenRegex = Regex (" \\ b(?:oxygen|o2|spo2|saturation|sat|blood oxygen|pulse ox|oximeter|oximetry|breathing|respiratory|respiration|air|breathe)\\ b" , RegexOption .IGNORE_CASE )
@@ -539,22 +540,31 @@ class SlotExtractor {
539540 }
540541
541542 private fun extractMetric (processedText : String , originalText : String ): String? {
543+ // Sort metrics by specificity (multi-word metrics first) to avoid false matches
544+ // e.g., "deep sleep" should be checked before "sleep"
545+ val sortedMetrics = synonymPatterns.entries.sortedByDescending { it.key.split(" " ).size }
546+
542547 // Direct synonym matching on processed text first
543- for ((metric, pattern) in synonymPatterns ) {
548+ for ((metric, pattern) in sortedMetrics ) {
544549 if (pattern.containsMatchIn(processedText)) {
545550 return metric
546551 }
547552 }
548553
549554 // Fallback to original text
550- for ((metric, pattern) in synonymPatterns ) {
555+ for ((metric, pattern) in sortedMetrics ) {
551556 if (pattern.containsMatchIn(originalText)) {
552557 return metric
553558 }
554559 }
555560
556561 // Context-based inference with expanded patterns
557562
563+ // Deep Sleep context - check BEFORE general sleep
564+ if (deepSleepRegex.containsMatchIn(originalText)) {
565+ return " deep sleep"
566+ }
567+
558568 // Walking/Movement context
559569 if (walkingMovementRegex.containsMatchIn(originalText)) {
560570 return if (distanceRegex.containsMatchIn(originalText)) {
@@ -2438,17 +2448,30 @@ class SlotExtractor {
24382448 Regex (" \\ benergy\\ s+(?:consumption|used|expended)\\ b" , RegexOption .IGNORE_CASE ),
24392449 Regex (" \\ bhow\\ s+much\\ s+(?:did\\ s+I\\ s+)?burn\\ b" , RegexOption .IGNORE_CASE )
24402450 ),
2451+ " deep sleep" to listOf (
2452+ Regex (" \\ bdeep\\ s+sleep\\ b|\\ bdeep\\ s+sleeping\\ b|\\ bdeep\\ s+slumber\\ b" , RegexOption .IGNORE_CASE ),
2453+ Regex (" \\ brem\\ s+sleep\\ b|\\ brem\\ b" , RegexOption .IGNORE_CASE ),
2454+ Regex (" \\ bdeep\\ s+(?:rest|phase|stage)\\ b" , RegexOption .IGNORE_CASE ),
2455+ Regex (" \\ b(?:stage\\ s+)?(?:3|4|three|four)(?:\\ s+sleep)?\\ b" , RegexOption .IGNORE_CASE ),
2456+ Regex (" \\ bslow\\ s+wave\\ s+sleep\\ b|\\ bsws\\ b" , RegexOption .IGNORE_CASE ),
2457+ Regex (" \\ brestorative\\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2458+ Regex (" \\ bhow\\ s+(?:much|long|well).*deep\\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2459+ Regex (" \\ bdeep\\ s+sleep\\ s+(?:time|duration|hours|quality|data)\\ b" , RegexOption .IGNORE_CASE ),
2460+ Regex (" \\ b(?:total|nightly)\\ s+deep\\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2461+ Regex (" \\ bhours?\\ s+(?:of\\ s+)?deep\\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2462+ Regex (" \\ bprofound\\ s+sleep\\ b|\\ bheavy\\ s+sleep\\ b|\\ bsound\\ s+sleep\\ b" , RegexOption .IGNORE_CASE )
2463+ ),
24412464 " sleep" to listOf (
24422465 Regex (" \\ bsleep\\ b|\\ bslept\\ b|\\ bsleeping\\ b|\\ basleep\\ b" , RegexOption .IGNORE_CASE ),
24432466 Regex (" \\ brest\\ b|\\ brested\\ b|\\ bresting\\ b" , RegexOption .IGNORE_CASE ),
2444- Regex (" \\ bhow\\ s+(?:much|long|well).*sleep\\ b" , RegexOption .IGNORE_CASE ),
2467+ Regex (" \\ bhow\\ s+(?:much|long|well).*sleep\\ b(?!.*deep) " , RegexOption .IGNORE_CASE ),
24452468 Regex (" \\ b(?:last\\ s+)?night'?s\\ s+sleep\\ b|\\ btonight'?s\\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2446- Regex (" \\ bsleep\\ s+(?:time|duration|hours|quality|data|tracking|pattern)\\ b" , RegexOption .IGNORE_CASE ),
2447- Regex (" \\ b(?:total|nightly)\\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2448- Regex (" \\ bhours?\\ s+(?:of\\ s+)?sleep\\ b|\\ bslept\\ s+(?:for\\ s+)?\\ d+\\ s+hours?\\ b" , RegexOption .IGNORE_CASE ),
2449- Regex (" \\ b(?:deep|light|rem) \\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
2469+ Regex (" \\ bsleep\\ s+(?:time|duration|hours|quality|data|tracking|pattern)\\ b(?!.*deep) " , RegexOption .IGNORE_CASE ),
2470+ Regex (" \\ b(?:total|nightly)\\ s+sleep\\ b(?!.*deep) " , RegexOption .IGNORE_CASE ),
2471+ Regex (" \\ bhours?\\ s+(?:of\\ s+)?sleep\\ b(?!.*deep) |\\ bslept\\ s+(?:for\\ s+)?\\ d+\\ s+hours?\\ b" , RegexOption .IGNORE_CASE ),
2472+ Regex (" \\ blight \\ s+sleep\\ b" , RegexOption .IGNORE_CASE ),
24502473 Regex (" \\ bnap\\ b|\\ bnapped\\ b|\\ bnapping\\ b|\\ bsnooze\\ b" , RegexOption .IGNORE_CASE ),
2451- Regex (" \\ bsleep\\ s+(?:score|rating|efficiency|quality)\\ b" , RegexOption .IGNORE_CASE )
2474+ Regex (" \\ bsleep\\ s+(?:score|rating|efficiency|quality)\\ b(?!.*deep) " , RegexOption .IGNORE_CASE )
24522475 ),
24532476 " weight" to listOf (
24542477 Regex (" \\ bweight\\ b|\\ bweigh\\ b|\\ bweighing\\ b|\\ bweighed\\ b" , RegexOption .IGNORE_CASE ),
0 commit comments