import sys sys.path.insert(0, '/home/hoffmann_admin/.openclaw/workspace') # Simple test of the updated tripwire scoring import re from dataclasses import dataclass from typing import List, Dict, Any # Tripwire patterns TRIPWIRE_PATTERNS = { "time_specific": r"\b(\d{1,2}:\d{2}\s*(?:AM|PM|am|pm)?|\d{1,2}\s*(?:AM|PM|am|pm))\b", "day_reference": r"\b(tomorrow|today|tonight|yesterday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\b", "date_reference": r"\b(\d{1,2}/\d{1,2}|\d{1,2}-\d{1,2})\b", "can_you": r"\b(can you|could you|would you|will you|are you able to)\b", "need_someone": r"\b(need someone|need a|looking for|anyone available|who can)\b", "pickup_dropoff": r"\b(pick up|drop off|grab|get)\b", "coverage": r"\b(cover|watch|stay with|babysit|fill in for)\b", "swap_change": r"\b(swap|switch|change|move)\b", "location": r"\b(at|to)\s+(?:the\s+)?([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*|school|practice|game|dentist|doctor)\b", "school_practice": r"\b(school|practice|game|match|dentist|doctor|appointment)\b", "coordination_question": r"\?.*\b(you|someone|anyone|who|what time|when)\b", } # Family members FAMILY_MEMBER_ALIASES = { "matt": ["matt", "matthew", "hoffmann"], "aundrea": ["aundrea", "mom"], "sullivan": ["sully", "sullivan"], "harper": ["harper"], "maggie": ["maggie", "dog"], } # Coordination keywords COORDINATION_KEYWORDS = [ "can you", "could you", "would you", "will you", "need someone", "need a", "looking for", "anyone available", "pick up", "drop off", "grab", "get", "cover", "watch", "stay with", "babysit", "fill in", "swap", "switch", "change", "move", "who can", "are you", "will you be", ] @dataclass class TripwireResult: fired: bool score: float matches: List[str] confidence: str def run_tripwire(text: str) -> TripwireResult: text_lower = text.lower() matches = [] score = 0.0 # Compile patterns compiled_patterns = { name: re.compile(pattern, re.IGNORECASE) for name, pattern in TRIPWIRE_PATTERNS.items() } # Pattern matching — base scores pattern_scores = { "time_specific": 0.15, "day_reference": 0.15, "date_reference": 0.15, "can_you": 0.25, "need_someone": 0.25, "pickup_dropoff": 0.25, "coverage": 0.25, "swap_change": 0.20, "location": 0.10, "school_practice": 0.10, "coordination_question": 0.10, } for name, pattern in compiled_patterns.items(): if pattern.search(text): matches.append(name) score += pattern_scores.get(name, 0.10) # === COMBO BONUSES === # Combo 1: Time + Day = Schedule mention (strong signal) if "time_specific" in matches and "day_reference" in matches: score += 0.20 matches.append("combo:time+day") # Combo 2: Day + Location = Appointment/Activity (strong signal) if "day_reference" in matches and "location" in matches: score += 0.15 matches.append("combo:day+location") # Combo 3: Action verb + Family member = Direct coordination has_action = any(m in matches for m in ["can_you", "need_someone", "pickup_dropoff", "coverage"]) has_family = any(name.lower() in text_lower for name in FAMILY_MEMBER_ALIASES.keys()) if has_action and has_family: score += 0.15 matches.append("combo:action+family") # Combo 4: Question + Coordination keyword = Request if "?" in text and any(kw in text_lower for kw in COORDINATION_KEYWORDS): score += 0.10 matches.append("combo:question+coord") # Coordination keyword bonus coord_matches = [kw for kw in COORDINATION_KEYWORDS if kw in text_lower] if coord_matches: score += min(len(coord_matches) * 0.10, 0.30) matches.extend([f"coord:{kw}" for kw in coord_matches[:3]]) score = min(score, 1.0) if score >= 0.7: confidence = "high" elif score >= 0.4: confidence = "medium" else: confidence = "low" fired = score >= 0.4 return TripwireResult( fired=fired, score=round(score, 2), matches=matches, confidence=confidence, ) # Test messages from our data test_messages = [ ("pick up Harper from dance tomorrow night", "Should fire (coordination)"), ("Sully's haircut tomorrow at 8", "Should fire (time+day combo)"), ("dinner at parents house on Sunday", "Should fire (day+location combo)"), ("I have to go to a work thing on June 7th", "Should fire (date+activity)"), ("Remember we have dinner at your parents house on Sunday", "Should fire (day+location)"), ("When is Harper's next haircut scheduled for?", "Maybe (question)"), ("Oh, and does Maggie take her heart guard every mon", "No (routine info)"), ("You can just say hi too!", "No (general chat)"), ("We need to talk with my siblings, my mom asked us to", "Maybe (action+family)"), ("Can you pick up Sully from practice at 3 PM?", "Should fire (action+time+day+family)"), ("I need someone to cover my shift on Saturday", "Should fire (need_someone+day)"), ("Aundrea, can you grab milk on the way home?", "Should fire (action+family)"), ] print("=" * 80) print("SHADOW MODE TRIPWIRE — SCORING VALIDATION") print("=" * 80) print(f"{'Message':<50} {'Score':>6} {'Fired':>6} {'Confidence':>10} {'Reason':<30}") print("-" * 80) for msg, expected in test_messages: result = run_tripwire(msg) status = "✅" if result.fired else "❌" print(f"{msg[:48]:<50} {result.score:>6.2f} {status:>6} {result.confidence:>10} {expected:<30}") if result.matches: print(f" → Matches: {', '.join(result.matches)}") print("\n" + "=" * 80) print("SUMMARY") print("=" * 80) fired_count = sum(1 for msg, _ in test_messages if run_tripwire(msg).fired) print(f"Messages tested: {len(test_messages)}") print(f"Should fire (expected): {sum(1 for _, e in test_messages if 'Should fire' in e)}") print(f"Actually fired: {fired_count}") print(f"Threshold: 0.4 (unchanged)") print(f"\nKey combos now catching:") print(f" • time+day → +0.20 (catches 'haircut tomorrow at 8')") print(f" • day+location → +0.15 (catches 'dinner at parents on Sunday')") print(f" • action+family → +0.15 (catches 'can you pick up Sully')") print(f" • question+coord → +0.10 (catches 'When is...?')")