Codex commited on
Commit
4d0a25f
·
1 Parent(s): 1527e06

Remove local keyword heuristics and replace with clean error strings

Browse files
Files changed (3) hide show
  1. README.md +2 -10
  2. analyzer.py +5 -20
  3. heuristics.py +0 -100
README.md CHANGED
@@ -25,8 +25,7 @@ Hugging Face Space: [build-small-hackathon/innerspace](https://huggingface.co/sp
25
  * **Calming Mindful Interface**: A customized, premium dark-slate/violet visual dashboard optimized for reflection.
26
  * **CBT Reflector Coach**: Engages the writer with open-ended, therapeutic prompts to explore underlying thoughts.
27
  * **Cognitive Distortion Scanner**: Highlights thinking patterns like *Catastrophizing*, *All-or-Nothing Thinking*, or *Should Statements* to raise cognitive awareness.
28
- * **Hybrid Inference**: Run locally on CUDA GPU (ZeroGPU space) with serverless API fallbacks and local rule-based keyword heuristics.
29
- * **Zero External Dependencies (Offline Heuristics)**: Fully functional without an internet connection or Hugging Face tokens using local keyword parsing.
30
 
31
  ---
32
 
@@ -60,19 +59,13 @@ The application is built around single-responsibility layers following SOLID pri
60
  | - ZeroGPU A10G | | - Section |
61
  | - HF API Client | | Splitting |
62
  +-----------------+ +---------------+
63
- | (If Inference Fails)
64
- v
65
- +-----+-----+-----+
66
- | Heuristic Engine|
67
- | (heuristics.py) | <--- Local offline keyword analyzer for sentiment/CBT fallbacks
68
- +-----------------+
69
  ```
70
 
71
  1. **User Input**: The user writes a journal entry in the writing block or uploads a `.txt`/`.md` entry.
72
  2. **Core Routing**: The click event invokes `analyze_journal_ui` through [core.py](core.py).
73
  3. **Model Generation**: The prompt is processed by the 1.2B parameter OpenBMB model in `inference.py`. It runs on a GPU locally when available.
74
  4. **API Fallback**: If the local GPU/CPU is busy or unavailable, the system transparently falls back to the Hugging Face Serverless API.
75
- 5. **Heuristic Fallback**: If both inference paths fail (e.g., completely offline with no token), the system falls back to rule-based keyword matchers in `heuristics.py` to extract basic emotions and distortions, ensuring the UI remains active and useful.
76
 
77
  ---
78
 
@@ -127,7 +120,6 @@ The project directory contains:
127
  - [analyzer.py](analyzer.py) - Analysis orchestrator and ZeroGPU wrapper.
128
  - [inference.py](inference.py) - Lazy model loader and local/remote text generators.
129
  - [parser.py](parser.py) - IO reader and section separator.
130
- - [heuristics.py](heuristics.py) - Keyword match fallback system.
131
  - [ui.py](ui.py) - Gradio block layout and interaction hooks.
132
  - [styles.py](styles.py) - Calming violet custom styling overrides.
133
  - [tune_journal.py](tune_journal.py) - Modal fine-tuning script.
 
25
  * **Calming Mindful Interface**: A customized, premium dark-slate/violet visual dashboard optimized for reflection.
26
  * **CBT Reflector Coach**: Engages the writer with open-ended, therapeutic prompts to explore underlying thoughts.
27
  * **Cognitive Distortion Scanner**: Highlights thinking patterns like *Catastrophizing*, *All-or-Nothing Thinking*, or *Should Statements* to raise cognitive awareness.
28
+ * **Hybrid Inference**: Run locally on CUDA GPU (ZeroGPU space) with serverless API fallbacks.
 
29
 
30
  ---
31
 
 
59
  | - ZeroGPU A10G | | - Section |
60
  | - HF API Client | | Splitting |
61
  +-----------------+ +---------------+
 
 
 
 
 
 
62
  ```
63
 
64
  1. **User Input**: The user writes a journal entry in the writing block or uploads a `.txt`/`.md` entry.
65
  2. **Core Routing**: The click event invokes `analyze_journal_ui` through [core.py](core.py).
66
  3. **Model Generation**: The prompt is processed by the 1.2B parameter OpenBMB model in `inference.py`. It runs on a GPU locally when available.
67
  4. **API Fallback**: If the local GPU/CPU is busy or unavailable, the system transparently falls back to the Hugging Face Serverless API.
68
+ 5. **Error Handling**: If both inference paths fail (e.g., completely offline with no token), the system returns structured analysis-unavailable error indicators in the dashboard.
69
 
70
  ---
71
 
 
120
  - [analyzer.py](analyzer.py) - Analysis orchestrator and ZeroGPU wrapper.
121
  - [inference.py](inference.py) - Lazy model loader and local/remote text generators.
122
  - [parser.py](parser.py) - IO reader and section separator.
 
123
  - [ui.py](ui.py) - Gradio block layout and interaction hooks.
124
  - [styles.py](styles.py) - Calming violet custom styling overrides.
125
  - [tune_journal.py](tune_journal.py) - Modal fine-tuning script.
analyzer.py CHANGED
@@ -29,12 +29,6 @@ except ImportError:
29
  from config import ENTRY_LIMIT, MODEL_ID, PARAMETER_COUNT
30
  from inference import run_model_inference
31
  from parser import extract_journal_text, parse_sections
32
- from heuristics import (
33
- build_sentiment_fallback,
34
- build_areas_fallback,
35
- build_distortions_fallback,
36
- build_reflection_fallback,
37
- )
38
 
39
 
40
  @dataclass(frozen=True)
@@ -105,23 +99,14 @@ def analyze_journal(file_path: str | None, raw_text: str) -> JournalReport:
105
  ]
106
  )
107
 
108
- # Route output parsing: split sections if model succeeded, otherwise execute heuristics
109
  if response.strip():
110
  sentiment, areas, distortions, reflection = parse_sections(response)
111
- # Apply heuristics fallback if model failed to populate specific sections
112
- if "not resolved" in sentiment:
113
- sentiment = build_sentiment_fallback(trimmed_entry)
114
- if "not resolved" in areas:
115
- areas = build_areas_fallback(trimmed_entry)
116
- if "not resolved" in distortions:
117
- distortions = build_distortions_fallback(trimmed_entry)
118
- if reflection.strip() == "How are you feeling about these thoughts today?":
119
- reflection = build_reflection_fallback(trimmed_entry)
120
  else:
121
- sentiment = build_sentiment_fallback(trimmed_entry)
122
- areas = build_areas_fallback(trimmed_entry)
123
- distortions = build_distortions_fallback(trimmed_entry)
124
- reflection = build_reflection_fallback(trimmed_entry)
125
 
126
  return JournalReport(
127
  entry_text=trimmed_entry,
 
29
  from config import ENTRY_LIMIT, MODEL_ID, PARAMETER_COUNT
30
  from inference import run_model_inference
31
  from parser import extract_journal_text, parse_sections
 
 
 
 
 
 
32
 
33
 
34
  @dataclass(frozen=True)
 
99
  ]
100
  )
101
 
102
+ # Route output parsing: split sections if model succeeded, otherwise return error strings
103
  if response.strip():
104
  sentiment, areas, distortions, reflection = parse_sections(response)
 
 
 
 
 
 
 
 
 
105
  else:
106
+ sentiment = "- Analysis unavailable"
107
+ areas = "- Analysis unavailable"
108
+ distortions = "- Analysis unavailable"
109
+ reflection = "An error occurred during model analysis. Please check your network connection or Hugging Face access token."
110
 
111
  return JournalReport(
112
  entry_text=trimmed_entry,
heuristics.py DELETED
@@ -1,100 +0,0 @@
1
- """
2
- Module containing keyword-based fallback heuristics.
3
- Analyzes text patterns to extract mood, areas of concern, distortions, and questions.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
-
9
- def build_sentiment_fallback(text: str) -> str:
10
- """Detects simple emotion keywords to build a fallback mood classification."""
11
- lowered = text.lower()
12
- emotions = []
13
-
14
- if any(w in lowered for w in ["sad", "down", "cry", "hurt", "grief", "lonely"]):
15
- emotions.append("- Sadness / Loneliness")
16
- if any(
17
- w in lowered for w in ["angry", "mad", "annoy", "frustrate", "hate", "irritate"]
18
- ):
19
- emotions.append("- Anger / Frustration")
20
- if any(
21
- w in lowered
22
- for w in ["happy", "glad", "great", "excite", "joy", "peace", "love"]
23
- ):
24
- emotions.append("- Joy / Contentment")
25
- if any(
26
- w in lowered for w in ["anxious", "worry", "afraid", "scare", "fear", "nervous"]
27
- ):
28
- emotions.append("- Anxiety / Concern")
29
-
30
- if not emotions:
31
- return "- Neutral / Reflective mood"
32
- return "\n".join(emotions)
33
-
34
-
35
- def build_areas_fallback(text: str) -> str:
36
- """Maps daily concerns to specific life areas based on vocabulary patterns."""
37
- lowered = text.lower()
38
- areas = []
39
-
40
- if any(w in lowered for w in ["work", "job", "office", "career", "boss", "task"]):
41
- areas.append("- Career & Work")
42
- if any(
43
- w in lowered
44
- for w in [
45
- "family",
46
- "mom",
47
- "dad",
48
- "kid",
49
- "child",
50
- "wife",
51
- "husband",
52
- "friend",
53
- "relationship",
54
- ]
55
- ):
56
- areas.append("- Relationships & Social")
57
- if any(
58
- w in lowered for w in ["health", "sick", "doctor", "gym", "run", "eat", "body"]
59
- ):
60
- areas.append("- Health & Wellness")
61
- if any(w in lowered for w in ["money", "pay", "buy", "budget", "cost", "finances"]):
62
- areas.append("- Finances")
63
-
64
- if not areas:
65
- return "- Personal Growth & General Life"
66
- return "\n".join(areas)
67
-
68
-
69
- def build_distortions_fallback(text: str) -> str:
70
- """Detects common cognitive distortions by analyzing linguistic patterns (should, catastrophizing, etc.)."""
71
- lowered = text.lower()
72
- distortions = []
73
-
74
- if any(w in lowered for w in ["always", "never", "nothing", "everything"]):
75
- distortions.append(
76
- "- All-or-Nothing Thinking (viewing things in black-and-white)"
77
- )
78
- if any(
79
- w in lowered
80
- for w in ["ruined", "disaster", "fail", "worst", "end of the world"]
81
- ):
82
- distortions.append("- Catastrophizing (expecting the worst outcome)")
83
- if any(w in lowered for w in ["must", "should", "ought"]):
84
- distortions.append("- 'Should' Statements (unrealistic self-imposed rules)")
85
-
86
- if not distortions:
87
- return "- None detected (heuristics analysis)"
88
- return "\n".join(distortions)
89
-
90
-
91
- def build_reflection_fallback(text: str) -> str:
92
- """Generates a simple, comforting CBT reflection question matching dominant keywords."""
93
- lowered = text.lower()
94
-
95
- if "work" in lowered or "job" in lowered:
96
- return "What would a small, manageable step towards easing this work pressure look like today?"
97
- if "always" in lowered or "never" in lowered:
98
- return "You mentioned things 'always' or 'never' going this way. Can you think of a single exception?"
99
-
100
- return "What is one kind thing you can say to yourself about these thoughts today?"