feat: behavioral pattern detection — frequency, time, withdrawal (closes #133) #146

Closed
Rockachopa wants to merge 2 commits from feat/133-behavioral-detection into main
Owner

Implementation

Detects crisis risk from behavioral patterns, not just message content.

Behavioral Signals

Signal Detection Risk Weight
Frequency spike >2x baseline msgs/hr 0.15
Frequency drop <30% baseline 0.20
Late night 2-5 AM local time 0.10
Session length increasing Span growing over time 0.10
Withdrawal Sustained frequency drop 0.20
Return after absence >48h gap 0.05
Abrupt termination Emotional + short exit 0.20

API

from behavioral_tracker import BehavioralTracker, get_risk_signals

tracker = BehavioralTracker()
tracker.record("session-1", message_length=100, emotional_content=True)
signals = get_risk_signals("session-1")
print(signals.behavioral_score)  # 0-1

Files

  • behavioral_tracker.py — BehavioralTracker class, SessionEvent, BehavioralSignals
  • tests/test_behavioral_tracker.py — 10 tests covering all signal types

Integration

Part of Epic #102 (Multimodal Crisis Detection). Complements content-based crisis_detector.py.

Closes #133

## Implementation Detects crisis risk from behavioral patterns, not just message content. ### Behavioral Signals | Signal | Detection | Risk Weight | |--------|-----------|-------------| | Frequency spike | >2x baseline msgs/hr | 0.15 | | Frequency drop | <30% baseline | 0.20 | | Late night | 2-5 AM local time | 0.10 | | Session length increasing | Span growing over time | 0.10 | | Withdrawal | Sustained frequency drop | 0.20 | | Return after absence | >48h gap | 0.05 | | Abrupt termination | Emotional + short exit | 0.20 | ### API ```python from behavioral_tracker import BehavioralTracker, get_risk_signals tracker = BehavioralTracker() tracker.record("session-1", message_length=100, emotional_content=True) signals = get_risk_signals("session-1") print(signals.behavioral_score) # 0-1 ``` ### Files - `behavioral_tracker.py` — BehavioralTracker class, SessionEvent, BehavioralSignals - `tests/test_behavioral_tracker.py` — 10 tests covering all signal types ### Integration Part of Epic #102 (Multimodal Crisis Detection). Complements content-based `crisis_detector.py`. Closes #133
Rockachopa added 2 commits 2026-04-15 16:33:34 +00:00
test: behavioral pattern detection (#133)
All checks were successful
Sanity Checks / sanity-test (pull_request) Successful in 3s
Smoke Test / smoke (pull_request) Successful in 5s
ba02b63a13
Timmy approved these changes 2026-04-15 23:05:48 +00:00
Timmy left a comment
Owner

Well-structured behavioral pattern detection. The signal types (frequency spike/drop, late-night, withdrawal, abrupt termination) are clinically relevant. Tests are thorough.

Concerns:

  1. CRITICAL — False negative risk in withdrawal detection: The DROP_THRESHOLD of 0.3 (30% of baseline) may miss gradual withdrawal. Someone going from 10 messages/day to 4 messages/day (60% drop) would not trigger at 0.3. Consider a more sensitive threshold like 0.5 for safety, or make it configurable.

  2. Late-night detection uses server time, not user local time: The LATE_NIGHT_START/END (2-5 AM) check uses the timestamp directly but there is no timezone handling. A user in a different timezone would get wrong results. This needs timezone awareness.

  3. In-memory only is correct for privacy but means behavioral baselines reset on restart. Document this limitation.

  4. Global baseline hardcoded: avg_messages_per_hour=5.0 etc. These should be learned from actual user data or be configurable.

  5. Good test coverage including edge cases for abrupt termination and compounding signals.

Approving with the note that the timezone issue (#2) should be addressed before merging to avoid false negatives for users in different timezones.

Well-structured behavioral pattern detection. The signal types (frequency spike/drop, late-night, withdrawal, abrupt termination) are clinically relevant. Tests are thorough. Concerns: 1. **CRITICAL — False negative risk in withdrawal detection**: The DROP_THRESHOLD of 0.3 (30% of baseline) may miss gradual withdrawal. Someone going from 10 messages/day to 4 messages/day (60% drop) would not trigger at 0.3. Consider a more sensitive threshold like 0.5 for safety, or make it configurable. 2. **Late-night detection uses server time, not user local time**: The LATE_NIGHT_START/END (2-5 AM) check uses the timestamp directly but there is no timezone handling. A user in a different timezone would get wrong results. This needs timezone awareness. 3. **In-memory only is correct for privacy** but means behavioral baselines reset on restart. Document this limitation. 4. **Global baseline hardcoded**: avg_messages_per_hour=5.0 etc. These should be learned from actual user data or be configurable. 5. **Good test coverage** including edge cases for abrupt termination and compounding signals. Approving with the note that the timezone issue (#2) should be addressed before merging to avoid false negatives for users in different timezones.
Author
Owner

Closing: Superseded by #149.

Closing: Superseded by #149.
Rockachopa closed this pull request 2026-04-16 01:47:37 +00:00
All checks were successful
Sanity Checks / sanity-test (pull_request) Successful in 3s
Smoke Test / smoke (pull_request) Successful in 5s

Pull request closed

Sign in to join this conversation.