MohitRajput45 commited on
Commit
8aa3867
·
1 Parent(s): af09308

project completed

Browse files
app/api.py CHANGED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # FILE: app/api.py
3
+ # PURPOSE: Acts as the central "loader" for all heavy AI objects.
4
+ # Streamlit reruns the entire script on every user action,
5
+ # so we use @st.cache_resource to load models ONCE and
6
+ # reuse the same object for the entire app session.
7
+ # ============================================================
8
+
9
+ import os # Standard library: used to build file paths safely
10
+ import sys # Standard library: used to modify Python's module search path
11
+ import streamlit as st # Streamlit: the web framework powering the entire UI
12
+
13
+ # ─────────────────────────────────────────────────────────────
14
+ # PATH SETUP BLOCK
15
+ # Problem: This file lives inside app/ but our source code lives
16
+ # in src/ (one level up). Python won't find src/ unless
17
+ # we manually tell it where to look.
18
+ # ─────────────────────────────────────────────────────────────
19
+
20
+ # Get the absolute path of THIS file (e.g., /home/user/project/app/api.py)
21
+ current_dir = os.path.dirname(os.path.abspath(__file__))
22
+
23
+ # Go one level UP from app/ to reach the project root (e.g., /home/user/project/)
24
+ project_root = os.path.abspath(os.path.join(current_dir, "../"))
25
+
26
+ # Only add the project root to sys.path if it isn't already there.
27
+ # sys.path is the list of directories Python searches when you do "import X".
28
+ if project_root not in sys.path:
29
+ sys.path.append(project_root)
30
+
31
+ # ─────────────────────────────────────────────────────────────
32
+ # IMPORTS — now safe because project_root is on sys.path
33
+ # ─────────────────────────────────────────────────────────────
34
+
35
+ # Import our main chatbot class from src/chatbot/groq_bot.py
36
+ from src.chatbot.groq_bot import MindGuardChatbot
37
+
38
+ # Import our SHAP explainability class from src/explainability/shap_explainer.py
39
+ from src.explainability.shap_explainer import MindGuardSHAPExplainer
40
+
41
+
42
+ # ─────────────────────────────────────────────────────────────
43
+ # CACHED LOADER: MindGuard Chatbot
44
+ # ─────────────────────────────────────────────────────────────
45
+
46
+ @st.cache_resource # <-- This decorator is the KEY. It tells Streamlit:
47
+ # "Run this function only ONCE. After that, return the
48
+ # same object every time instead of rebuilding it."
49
+ # Without this, the bot would reload on every keypress.
50
+ def get_mindguard_bot():
51
+ """
52
+ Instantiates the MindGuardChatbot and keeps it alive in memory.
53
+ This loads:
54
+ - The Groq LLM connection
55
+ - The Whisper audio transcription model
56
+ - The SQLite database connection
57
+ All of these are expensive to create, so we create them once.
58
+ """
59
+ return MindGuardChatbot() # Create and return a new bot instance
60
+
61
+
62
+ # ─────────────────────────────────────────────────────────────
63
+ # CACHED LOADER: SHAP Explainer
64
+ # ─────────────────────────────────────────────────────────────
65
+
66
+ @st.cache_resource # Same caching strategy as above — critical here because
67
+ # MindGuardSHAPExplainer loads a full XLM-RoBERTa model
68
+ # (~1.1GB weights) which takes ~10-15 seconds to load.
69
+ # Caching means that cost is paid only once at startup.
70
+ def get_shap_explainer():
71
+ """
72
+ Instantiates the MindGuardSHAPExplainer and keeps it alive in memory.
73
+ This loads:
74
+ - XLMRobertaTokenizer (converts text to token IDs)
75
+ - XLMRobertaForSequenceClassification (the 35-emotion neural network)
76
+ - shap.Explainer (the Game Theory math engine wrapped around the model)
77
+ All three are heavy objects — caching is non-negotiable for a smooth UX.
78
+ """
79
+ return MindGuardSHAPExplainer() # Create and return a new explainer instance
app/components/chat_ui.py CHANGED
@@ -0,0 +1,381 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # FILE: app/components/chat_ui.py
3
+ # PURPOSE: Renders the entire Chat Companion page.
4
+ # After every bot response it now:
5
+ # 1. Runs SHAP to explain which words drove the emotion
6
+ # 2. Reads the predicted emotion + risk level from the DB
7
+ # 3. Displays them as colored badges under the message
8
+ # 4. Embeds the SHAP HTML report in a collapsible expander
9
+ # ============================================================
10
+
11
+ import streamlit as st # Core UI framework
12
+ import os # For building file paths
13
+ import streamlit.components.v1 as components # Lets us embed raw HTML (the SHAP report)
14
+
15
+ # Import our two cached AI loaders from api.py
16
+ # get_mindguard_bot() → returns the chatbot (LLM + Whisper + DB)
17
+ # get_shap_explainer() → returns the SHAP XAI engine (XLM-R model)
18
+ from api import get_mindguard_bot, get_shap_explainer
19
+
20
+
21
+ # ─────────────────────────────────────────────────────────────
22
+ # COLOR CONFIGURATION
23
+ # These dictionaries map risk levels and emotion names to
24
+ # hex color codes so we can render styled HTML badges.
25
+ # ─────────────────────────────────────────────────────────────
26
+
27
+ # Maps risk level strings → (background_color, text_color) tuples
28
+ RISK_COLORS = {
29
+ "High": ("#ff4b4b", "white"), # Bright red — urgent / crisis
30
+ "Medium": ("#ffa500", "white"), # Orange — elevated concern
31
+ "Low": ("#21c354", "white"), # Green — safe / normal
32
+ }
33
+
34
+ # Maps individual emotion label strings → a single hex background color
35
+ # Grouped by emotional valence for clarity:
36
+ EMOTION_COLORS = {
37
+ # ── Clinical / high-severity emotions (red family) ──────────────
38
+ "Suicidal": "#ff4b4b", # Maximum urgency — bright red
39
+ "Depression": "#e05260", # Dark rose
40
+ "Anxiety": "#e07052", # Warm red-orange
41
+ "Bipolar": "#c0392b", # Deep crimson
42
+ "Stress": "#e67e22", # Amber-orange
43
+ "Personality disorder": "#9b59b6", # Purple — complex/clinical
44
+
45
+ # ── Positive emotions (green family) ────────────────────────────
46
+ "joy": "#21c354",
47
+ "love": "#2ecc71",
48
+ "gratitude": "#27ae60",
49
+ "admiration": "#1abc9c",
50
+ "optimism": "#16a085",
51
+ "relief": "#52be80",
52
+ "excitement": "#58d68d",
53
+ "pride": "#a9cce3", # Soft blue-green
54
+
55
+ # ── Neutral emotions (blue) ──────────────────────────────────────
56
+ "Normal": "#3498db",
57
+ "neutral": "#3498db",
58
+
59
+ # ── Negative / distressed emotions (amber-brown family) ─────────
60
+ "sadness": "#e08000",
61
+ "grief": "#ca6f1e",
62
+ "fear": "#e74c3c",
63
+ "anger": "#c0392b",
64
+ "annoyance": "#d35400",
65
+ "disappointment": "#ca6f1e",
66
+ "remorse": "#a04000",
67
+ "disgust": "#7d6608",
68
+ }
69
+
70
+
71
+ # ─────────────────────────────────────────────────────────────
72
+ # HELPER: Build an HTML emotion badge string
73
+ # Returns a colored pill-shaped <span> tag with the emotion name.
74
+ # ─────────────────────────────────────────────────────────────
75
+ def _emotion_badge(emotion: str) -> str:
76
+ # Look up the color; fall back to grey if emotion isn't in our map
77
+ color = EMOTION_COLORS.get(emotion, "#555555")
78
+
79
+ # Build and return a self-contained inline HTML span element
80
+ # unsafe_allow_html=True must be used in the st.markdown call to render this
81
+ return (
82
+ f'<span style="'
83
+ f'background:{color};' # Background color from our map
84
+ f'color:white;' # White text for contrast
85
+ f'padding:3px 10px;' # Pill-style inner spacing
86
+ f'border-radius:12px;' # Rounded corners
87
+ f'font-size:13px;'
88
+ f'font-weight:600;' # Semi-bold text
89
+ f'">🧠 {emotion}</span>' # Brain emoji + emotion label
90
+ )
91
+
92
+
93
+ # ─────────────────────────────────────────────────────────────
94
+ # HELPER: Build an HTML risk-level badge string
95
+ # Similar to emotion badge but with icons matching severity.
96
+ # ─────────────────────────────────────────────────────────────
97
+ def _risk_badge(risk: str) -> str:
98
+ # Unpack background and text color from our tuple map
99
+ bg, fg = RISK_COLORS.get(risk, ("#888888", "white"))
100
+
101
+ # Choose a severity icon to reinforce the color signal visually
102
+ icons = {"High": "🚨", "Medium": "⚠️", "Low": "✅"}
103
+ icon = icons.get(risk, "•") # Default bullet if risk level is unexpected
104
+
105
+ return (
106
+ f'<span style="'
107
+ f'background:{bg};'
108
+ f'color:{fg};'
109
+ f'padding:3px 10px;'
110
+ f'border-radius:12px;'
111
+ f'font-size:13px;'
112
+ f'font-weight:600;'
113
+ f'">{icon} Risk: {risk}</span>'
114
+ )
115
+
116
+
117
+ # ─────────────────────────────────────────────────────────────
118
+ # HELPER: Render the SHAP HTML report inside the chat message
119
+ # Uses streamlit.components.v1.html() to embed arbitrary HTML
120
+ # inside a collapsible expander so it doesn't clutter the chat.
121
+ # ─────────────────────────────────────────────────────────────
122
+ def _render_shap_inline(html_path: str):
123
+ # Only attempt to render if the file actually exists on disk.
124
+ # The file is written by shap_explainer.generate_visual_report().
125
+ if os.path.exists(html_path):
126
+ # Read the entire SHAP HTML file into a string
127
+ with open(html_path, "r", encoding="utf-8") as f:
128
+ shap_html = f.read()
129
+
130
+ # Wrap in a Streamlit expander so it's hidden by default.
131
+ # expanded=False means the user must click to open it.
132
+ with st.expander("🔬 View XAI Word-Level Explanation (SHAP)", expanded=False):
133
+ # components.html() injects raw HTML into an iframe inside Streamlit.
134
+ # height=300 sets the iframe height in pixels.
135
+ # scrolling=True enables vertical scroll inside the iframe.
136
+ components.html(shap_html, height=300, scrolling=True)
137
+ else:
138
+ # If no report exists yet, show a subtle placeholder message
139
+ st.caption("_SHAP report not yet generated._")
140
+
141
+
142
+ # ─────────────────────────────────────────────────────────────
143
+ # HELPER: Query DB for the most recent emotion + risk level
144
+ # Called immediately after bot.generate_response() because the
145
+ # bot writes the diagnosed_emotion and risk_level to SQLite during
146
+ # response generation. We read it back to display in the UI.
147
+ # ─────────────────────────────────────────────────────────────
148
+ def _get_last_emotion_risk(bot) -> tuple:
149
+ try:
150
+ # Import the database class from src (path was fixed in api.py's sys.path setup)
151
+ from src.database.db_operations import MindGuardDatabase
152
+
153
+ db = MindGuardDatabase() # Open a new DB connection
154
+
155
+ # Run a SQL query to get the single most recent row, ordered newest-first
156
+ db.cursor.execute(
157
+ "SELECT diagnosed_emotion, risk_level "
158
+ "FROM chat_history "
159
+ "ORDER BY timestamp DESC "
160
+ "LIMIT 1"
161
+ )
162
+
163
+ row = db.cursor.fetchone() # Fetch the one result row (or None if empty)
164
+ db.close() # Always close the DB connection to avoid leaks
165
+
166
+ if row:
167
+ # dict(row) converts the sqlite3.Row object to a regular Python dict
168
+ # so we can access columns by name like a dictionary
169
+ return dict(row)["diagnosed_emotion"], dict(row)["risk_level"]
170
+
171
+ except Exception:
172
+ # Silently swallow any DB errors (e.g., table doesn't exist yet)
173
+ # so a DB issue never crashes the entire chat UI
174
+ pass
175
+
176
+ # Fallback values if DB is empty or an error occurred
177
+ return "Unknown", "Unknown"
178
+
179
+
180
+ # ─────────────────────────────────────────────────────────────
181
+ # MAIN RENDER FUNCTION
182
+ # Called by main.py when the user selects "💬 Chat Companion"
183
+ # in the sidebar navigation radio buttons.
184
+ # ─────────────────────────────────────────────────────────────
185
+ def render_chat():
186
+ # Page title and subtitle at the top of the main content area
187
+ st.title("🧠 MindGuard Companion")
188
+ st.markdown("Your clinical-grade, empathetic AI. Type a message or upload a voice note.")
189
+
190
+ # ── Load the cached AI objects ────────────────────────────
191
+ # These calls are instant after the first load because of @st.cache_resource
192
+ bot = get_mindguard_bot() # The Groq chatbot
193
+ shap_ex = get_shap_explainer() # The SHAP XAI explainer
194
+
195
+ # ── Session State Initialization ──────────────────────────
196
+ # st.session_state persists values across Streamlit reruns
197
+ # within the same browser session. We use it as our "memory".
198
+
199
+ if "messages" not in st.session_state:
200
+ # messages: a list of dicts. Each dict has at minimum:
201
+ # { "role": "user"|"assistant", "content": "..." }
202
+ # Assistant messages may also carry:
203
+ # { "emotion": "...", "risk": "...", "shap_path": "..." }
204
+ st.session_state.messages = []
205
+
206
+ if "session_id" not in st.session_state:
207
+ # session_id is passed to the bot so it can group DB records
208
+ # per user/session. Hardcoded for demo; replace with auth later.
209
+ st.session_state.session_id = "demo_user_001"
210
+
211
+ # ── SHAP Report Path ──────────────────────────────────────
212
+ # shap_explainer.py always writes to artifacts/shap_report.html
213
+ # at the project root. We build that path here once so we don't
214
+ # repeat the logic in multiple places.
215
+ SHAP_HTML_PATH = os.path.join(
216
+ # Start from this file's location: app/components/
217
+ # Go up TWO levels (components/ → app/ → project_root/)
218
+ os.path.abspath(os.path.join(os.path.dirname(__file__), "../../")),
219
+ "artifacts", # The artifacts/ folder at project root
220
+ "shap_report.html" # The fixed filename shap_explainer.py writes to
221
+ )
222
+
223
+ # ─────────────────────────────────────────────────────────
224
+ # SIDEBAR SECTION
225
+ # st.sidebar renders everything inside it in the left panel.
226
+ # ─────────────────────────────────────────────────────────
227
+ with st.sidebar:
228
+ st.header("🎙️ Voice Input")
229
+ st.write("Feeling overwhelmed? Talk to MindGuard directly.")
230
+
231
+ # st.audio_input() renders a mic button in the sidebar.
232
+ # Returns a BytesIO-like object when a recording is complete,
233
+ # or None if no recording has been made yet.
234
+ audio_value = st.audio_input("Record a voice note")
235
+
236
+ if audio_value:
237
+ # ── Infinite Loop Prevention ──────────────────────
238
+ # Streamlit reruns on every state change. Without this guard,
239
+ # processing the audio would trigger a rerun, which would
240
+ # process the same audio again — infinitely.
241
+ # FIX: Track the BYTE SIZE of the audio as a unique fingerprint.
242
+ # If the size matches last time, we already processed this recording.
243
+ current_audio_size = len(audio_value.getvalue())
244
+
245
+ if ("last_audio_size" not in st.session_state or
246
+ st.session_state.last_audio_size != current_audio_size):
247
+
248
+ # Lock this audio's size into memory to prevent reprocessing
249
+ st.session_state.last_audio_size = current_audio_size
250
+
251
+ # Write the audio bytes to a temp .wav file on disk.
252
+ # Whisper needs a file path, not a BytesIO object.
253
+ temp_path = os.path.join("data", "raw", "temp_streamlit.wav")
254
+ with open(temp_path, "wb") as f:
255
+ f.write(audio_value.getbuffer()) # getbuffer() gives raw bytes
256
+
257
+ # Transcribe the audio file using Whisper, then get a response
258
+ with st.spinner("Transcribing and analyzing via Whisper…"):
259
+ # bot.audio_processor.transcribe() runs Whisper on the .wav file
260
+ transcribed_text = bot.audio_processor.transcribe(temp_path)
261
+ # bot.generate_response() sends the text to Groq LLM + writes to DB
262
+ response = bot.generate_response(
263
+ user_input=transcribed_text,
264
+ session_id=st.session_state.session_id
265
+ )
266
+
267
+ # Run SHAP on the transcribed text.
268
+ # This writes a new shap_report.html to artifacts/
269
+ with st.spinner("Generating XAI explanation…"):
270
+ shap_ex.generate_visual_report(transcribed_text)
271
+
272
+ # Pull the emotion + risk level that the bot just saved to the DB
273
+ emotion, risk = _get_last_emotion_risk(bot)
274
+
275
+ # Append the user's (transcribed) voice message to the chat history
276
+ st.session_state.messages.append({
277
+ "role": "user",
278
+ # Format it visually to indicate it came from voice, not typing
279
+ "content": f"🎤 **Voice Note:** *{transcribed_text}*"
280
+ })
281
+
282
+ # Append the assistant's response WITH the analysis metadata
283
+ st.session_state.messages.append({
284
+ "role": "assistant",
285
+ "content": response,
286
+ "emotion": emotion, # e.g. "Anxiety"
287
+ "risk": risk, # e.g. "High"
288
+ "shap_path": SHAP_HTML_PATH, # Path to render the SHAP report
289
+ })
290
+ # NOTE: No st.rerun() here. Streamlit will naturally continue
291
+ # down the script and render the new messages in the next section.
292
+
293
+ st.divider() # A horizontal line separator in the sidebar
294
+
295
+ # Clear button: wipes all chat history and resets the audio lock
296
+ if st.button("🗑️ Clear Chat History"):
297
+ st.session_state.messages = [] # Empty the message list
298
+ st.session_state.pop("last_audio_size", None) # Reset audio fingerprint
299
+ st.rerun() # Force a full page refresh to visually clear the chat window
300
+
301
+ # ─────────────────────────────────────────────────────────
302
+ # MAIN CHAT WINDOW — Render Historical Messages
303
+ # Loop through all messages stored in session_state and
304
+ # re-render them. This is how Streamlit "remembers" the chat.
305
+ # ─────────────────────────────────────────────────────────
306
+ for msg in st.session_state.messages:
307
+ # st.chat_message() renders a chat bubble with the correct
308
+ # avatar: human avatar for "user", robot avatar for "assistant"
309
+ with st.chat_message(msg["role"]):
310
+ # Render the message text (supports markdown formatting)
311
+ st.markdown(msg["content"])
312
+
313
+ # Only assistant messages carry the analysis metadata.
314
+ # We check for the "emotion" key to know if this is an
315
+ # analyzed message (not all assistant messages will have it
316
+ # if the DB was empty or an error occurred).
317
+ if msg["role"] == "assistant" and "emotion" in msg:
318
+ # Place the two badges side-by-side using columns
319
+ col1, col2 = st.columns([1, 1]) # Equal-width columns
320
+ with col1:
321
+ # unsafe_allow_html=True is required because our badge
322
+ # is a raw HTML string, not standard Markdown
323
+ st.markdown(_emotion_badge(msg["emotion"]), unsafe_allow_html=True)
324
+ with col2:
325
+ st.markdown(_risk_badge(msg["risk"]), unsafe_allow_html=True)
326
+
327
+ # Render the SHAP explanation report below the badges
328
+ _render_shap_inline(msg.get("shap_path", ""))
329
+
330
+ # ─────────────────────────────────────────────────────────
331
+ # MAIN CHAT WINDOW — Handle New Text Input
332
+ # st.chat_input() renders a fixed input bar at the bottom.
333
+ # The walrus operator (:=) assigns AND checks in one step:
334
+ # "if there is a new prompt, assign it to 'prompt' and enter the block"
335
+ # ─────────────────────────────────────────────────────────
336
+ if prompt := st.chat_input("How are you feeling right now?"):
337
+
338
+ # 1. Immediately show the user's message (before the bot responds)
339
+ st.session_state.messages.append({"role": "user", "content": prompt})
340
+ with st.chat_message("user"):
341
+ st.markdown(prompt)
342
+
343
+ # 2. Generate bot response + analysis inside the assistant bubble
344
+ with st.chat_message("assistant"):
345
+
346
+ # Step A: Get the LLM response (heavy — show spinner to user)
347
+ with st.spinner("Diagnosing emotion and retrieving clinical strategy…"):
348
+ response = bot.generate_response(prompt, st.session_state.session_id)
349
+
350
+ # Step B: Run SHAP AFTER the response so the chat feels fast.
351
+ # The user sees the reply first, then waits briefly for XAI.
352
+ with st.spinner("Generating XAI word-level explanation…"):
353
+ # Overwrites artifacts/shap_report.html with a fresh analysis
354
+ shap_ex.generate_visual_report(prompt)
355
+
356
+ # Step C: Read the emotion + risk that the bot stored in SQLite
357
+ # during generate_response() — this is a near-instant DB read
358
+ emotion, risk = _get_last_emotion_risk(bot)
359
+
360
+ # Step D: Render the response text in the chat bubble
361
+ st.markdown(response)
362
+
363
+ # Step E: Render the emotion and risk badges side by side
364
+ col1, col2 = st.columns([1, 1])
365
+ with col1:
366
+ st.markdown(_emotion_badge(emotion), unsafe_allow_html=True)
367
+ with col2:
368
+ st.markdown(_risk_badge(risk), unsafe_allow_html=True)
369
+
370
+ # Step F: Embed the SHAP HTML report in a collapsible expander
371
+ _render_shap_inline(SHAP_HTML_PATH)
372
+
373
+ # 3. Save the complete message (with metadata) to session_state
374
+ # so it re-renders correctly on the next Streamlit rerun
375
+ st.session_state.messages.append({
376
+ "role": "assistant",
377
+ "content": response,
378
+ "emotion": emotion, # Predicted emotion label
379
+ "risk": risk, # Risk level (High/Medium/Low)
380
+ "shap_path": SHAP_HTML_PATH, # Path to the saved SHAP HTML report
381
+ })
app/components/dashboard_ui.py CHANGED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # FILE: app/components/dashboard_ui.py
3
+ # PURPOSE: Renders the Clinical Overview dashboard page.
4
+ # Organized into two tabs:
5
+ # Tab 1 — "📈 Analytics": metrics, charts, history table
6
+ # Tab 2 — "🔬 XAI Report": renders the latest SHAP HTML
7
+ # report so clinicians can review explanations
8
+ # without going back to the chat.
9
+ # ============================================================
10
+
11
+ import streamlit as st # Core UI framework
12
+ import streamlit.components.v1 as components # For embedding raw HTML (SHAP report)
13
+ import pandas as pd # For converting SQL rows into a DataFrame
14
+ import os # For checking if the SHAP file exists
15
+ import sys # For fixing the import path
16
+
17
+ # ─────────────────────────────────────────────────────────────
18
+ # PATH SETUP BLOCK
19
+ # Same pattern as api.py: this file is inside app/components/
20
+ # so we need to walk up TWO levels to reach the project root
21
+ # where the src/ package lives.
22
+ # ─────────────────────────────────────────────────────────────
23
+
24
+ # Get the absolute path of this file (app/components/dashboard_ui.py)
25
+ current_dir = os.path.dirname(os.path.abspath(__file__))
26
+
27
+ # Walk UP two directories: components/ → app/ → project_root/
28
+ project_root = os.path.abspath(os.path.join(current_dir, "../../"))
29
+
30
+ # Add project_root to Python's search path so "from src.xxx import yyy" works
31
+ if project_root not in sys.path:
32
+ sys.path.append(project_root)
33
+
34
+ # Import our database helper class from src/database/db_operations.py
35
+ from src.database.db_operations import MindGuardDatabase
36
+
37
+
38
+ # ─────────────────────────────────────────────────────────────
39
+ # SHAP REPORT PATH
40
+ # shap_explainer.py ALWAYS writes its output to this exact path.
41
+ # We define it once here so any function in this file can use it.
42
+ # ─────────────────────────────────────────────────────────────
43
+ SHAP_HTML_PATH = os.path.join(
44
+ project_root, # e.g., /home/user/project/
45
+ "artifacts", # The artifacts/ folder at project root
46
+ "shap_report.html" # Fixed filename — shap_explainer.py never changes this
47
+ )
48
+
49
+
50
+ # ─────────────────────────────────────────────────────────────
51
+ # MAIN RENDER FUNCTION
52
+ # Called by main.py when the user selects "📊 Clinical Dashboard"
53
+ # in the sidebar navigation.
54
+ # ─────────────────────────────────────────────────────────────
55
+ def render_dashboard():
56
+ # Page header in the main content area
57
+ st.title("📊 Clinical Overview")
58
+ st.markdown("Real-time emotional tracking, risk assessment, and XAI reports.")
59
+
60
+ # ── Create Two Tabs ───────────────────────────────────────
61
+ # st.tabs() returns a list of tab context managers.
62
+ # We unpack them into tab1 and tab2 immediately.
63
+ tab1, tab2 = st.tabs(["📈 Analytics", "🔬 XAI Report"])
64
+
65
+
66
+ # =========================================================
67
+ # TAB 1: ANALYTICS
68
+ # Shows metrics, bar charts, and a history table built from
69
+ # the chat_history table in our SQLite database.
70
+ # =========================================================
71
+ with tab1:
72
+
73
+ # ── Database Read ─────────────────────────────────────
74
+ # Open a database connection to read all historical records
75
+ db = MindGuardDatabase()
76
+
77
+ # Execute a SQL SELECT to fetch the three columns we need,
78
+ # sorted newest-first so the table shows recent entries at the top
79
+ db.cursor.execute(
80
+ "SELECT timestamp, diagnosed_emotion, risk_level "
81
+ "FROM chat_history "
82
+ "ORDER BY timestamp DESC"
83
+ )
84
+
85
+ # fetchall() returns a list of sqlite3.Row objects (like dicts)
86
+ rows = db.cursor.fetchall()
87
+
88
+ # Close the DB connection immediately after reading.
89
+ # Leaving connections open can cause locking issues.
90
+ db.close()
91
+
92
+ # Guard clause: if there are no rows in the DB yet,
93
+ # show an info message and exit the function early.
94
+ if not rows:
95
+ st.info("No data yet. Start chatting to generate analytics.")
96
+ return # Exit render_dashboard() — nothing to display
97
+
98
+ # Convert the list of sqlite3.Row objects into a Pandas DataFrame.
99
+ # dict(r) turns each Row into a regular Python dict first.
100
+ # DataFrame() then stacks all those dicts into a table.
101
+ df = pd.DataFrame([dict(r) for r in rows])
102
+
103
+ # ── TOP METRICS ROW ───────────────────────────────────
104
+ # Compute the three summary statistics shown as metric cards
105
+
106
+ total = len(df) # Total number of chat interactions recorded
107
+ high_risk = len(df[df["risk_level"] == "High"]) # Count rows where risk_level equals "High"
108
+ # mode()[0] returns the most-frequently-occurring value in that column
109
+ top_emotion = df["diagnosed_emotion"].mode()[0] if not df.empty else "N/A"
110
+
111
+ # st.columns(3) creates three equal-width side-by-side columns
112
+ col1, col2, col3 = st.columns(3)
113
+ col1.metric("Total Interactions", total)
114
+ # delta_color="inverse" makes the delta arrow red on increase
115
+ # (since more high-risk flags is BAD, not good)
116
+ col2.metric("High Risk Flags", high_risk, delta_color="inverse")
117
+ col3.metric("Primary Emotion", top_emotion)
118
+
119
+ # ── RISK LEVEL SUMMARY ────────────────────────────────
120
+ st.divider() # Horizontal separator line
121
+ st.subheader("Risk Level Summary")
122
+
123
+ # Create three columns, one per risk level
124
+ risk_cols = st.columns(3)
125
+
126
+ # Loop through each risk level and display its count
127
+ for i, level in enumerate(["High", "Medium", "Low"]):
128
+ # Count rows where risk_level matches this level
129
+ count = len(df[df["risk_level"] == level])
130
+
131
+ # Pick an icon that visually reinforces the severity
132
+ icons = {"High": "🚨", "Medium": "⚠️", "Low": "✅"}
133
+
134
+ # Display as a Streamlit metric card in the correct column
135
+ risk_cols[i].metric(f"{icons[level]} {level}", count)
136
+
137
+ # ── BAR CHARTS ───────────────────────────────────────
138
+ st.divider()
139
+
140
+ # Two charts side by side using columns
141
+ chart_col1, chart_col2 = st.columns(2)
142
+
143
+ with chart_col1:
144
+ st.subheader("Emotion Frequency")
145
+ # value_counts() counts how many times each emotion appeared.
146
+ # st.bar_chart() renders it as an interactive bar chart automatically.
147
+ st.bar_chart(df["diagnosed_emotion"].value_counts())
148
+
149
+ with chart_col2:
150
+ st.subheader("Risk Level Distribution")
151
+ # Same pattern: count occurrences of each risk level
152
+ st.bar_chart(df["risk_level"].value_counts())
153
+
154
+ # ── RECENT HISTORY TABLE ─────────────────────────────
155
+ st.divider()
156
+ st.subheader("Recent Session History")
157
+
158
+ # Show only the 20 most recent rows to keep the table manageable.
159
+ # .rename() gives the columns friendly display names for the UI.
160
+ st.dataframe(
161
+ df.head(20).rename(columns={
162
+ "timestamp": "Time", # Raw DB column name → human label
163
+ "diagnosed_emotion": "Emotion",
164
+ "risk_level": "Risk"
165
+ }),
166
+ use_container_width=True # Stretch table to fill available width
167
+ )
168
+
169
+
170
+ # =========================================================
171
+ # TAB 2: XAI REPORT
172
+ # Loads and renders the SHAP HTML report that was saved to disk
173
+ # by shap_explainer.generate_visual_report() during the last chat.
174
+ # This gives clinicians a dedicated view of the explanation.
175
+ # =========================================================
176
+ with tab2:
177
+ st.subheader("🔬 Last SHAP Word-Level Explanation")
178
+
179
+ # Explanation of what the user is looking at
180
+ st.markdown(
181
+ "This report shows **which words** drove the model's emotion prediction. "
182
+ "**Red highlights** = words that pushed the model TOWARDS that emotion. "
183
+ "**Blue highlights** = words that pushed AGAINST it. "
184
+ "Generated using SHAP (SHapley Additive exPlanations) — Game Theory math."
185
+ )
186
+
187
+ # os.path.exists() checks whether the file has been created yet.
188
+ # It won't exist on the very first run before any chat messages.
189
+ if os.path.exists(SHAP_HTML_PATH):
190
+
191
+ # Read the entire SHAP HTML report file into a Python string
192
+ with open(SHAP_HTML_PATH, "r", encoding="utf-8") as f:
193
+ shap_html = f.read()
194
+
195
+ # components.html() injects the raw HTML string into an iframe
196
+ # inside the Streamlit page. This is how we display SHAP's
197
+ # interactive visualization without needing a Jupyter notebook.
198
+ # height=500 gives enough space to see word highlights clearly.
199
+ # scrolling=True allows vertical scroll if the report is tall.
200
+ components.html(shap_html, height=500, scrolling=True)
201
+
202
+ # Show the file path as a small caption below the report
203
+ # so developers can quickly find the file for debugging
204
+ st.caption(f"Report path: `{SHAP_HTML_PATH}`")
205
+
206
+ else:
207
+ # No report exists yet — guide the user to generate one
208
+ st.info(
209
+ "No SHAP report found yet. "
210
+ "Send a message in the **💬 Chat Companion** tab "
211
+ "and the XAI report will appear here automatically after your first message."
212
+ )
app/main.py CHANGED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from components.chat_ui import render_chat
3
+ from components.dashboard_ui import render_dashboard
4
+
5
+ # Set the wide layout for a more professional dashboard look
6
+ st.set_page_config(page_title="MindGuard AI", page_icon="🛡️", layout="wide")
7
+
8
+ # Sidebar Navigation
9
+ st.sidebar.title("🛡️ MindGuard AI")
10
+ st.sidebar.markdown("Welcome to the control panel.")
11
+
12
+ # Create radio buttons to act as tabs
13
+ page = st.sidebar.radio("Navigation", ["💬 Chat Companion", "📊 Clinical Dashboard"])
14
+
15
+ # Route the user to the correct component based on their selection
16
+ if page == "💬 Chat Companion":
17
+ render_chat()
18
+ elif page == "📊 Clinical Dashboard":
19
+ render_dashboard()
data/knowledge_base/cbt_techniques.json DELETED
File without changes
data/knowledge_base/coping_strategies.json ADDED
@@ -0,0 +1,1335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "cbt_panic_001",
4
+ "primary_emotion": "Panic",
5
+ "target_risk_level": "High",
6
+ "category": "Sensory Grounding",
7
+ "strategy_name": "The 5-4-3-2-1 Grounding Technique",
8
+ "tags": ["panic attack", "dissociation", "immediate relief", "breathing"],
9
+ "source": "Standard Cognitive Behavioral Therapy (CBT)",
10
+ "content": "When experiencing a panic attack, use the 5-4-3-2-1 method to ground yourself in the present. Identify 5 things you can see, 4 things you can touch, 3 things you can hear, 2 things you can smell, and 1 thing you can taste. This interrupts the brain's panic loop and forces the nervous system to process reality."
11
+ },
12
+ {
13
+ "id": "cbt_anxiety_001",
14
+ "primary_emotion": "Anxiety",
15
+ "target_risk_level": "Medium",
16
+ "category": "Breathwork",
17
+ "strategy_name": "Box Breathing",
18
+ "tags": ["stress", "nervousness", "heart rate", "calming"],
19
+ "source": "Clinical Breathwork Guidelines",
20
+ "content": "To lower heart rate and reduce physical anxiety symptoms, practice Box Breathing. Inhale deeply for 4 seconds, hold your breath for 4 seconds, exhale slowly for 4 seconds, and hold your lungs empty for 4 seconds. Repeat this cycle 4 times to signal safety to your vagus nerve."
21
+ },
22
+ {
23
+ "id": "cbt_depression_001",
24
+ "primary_emotion": "Depression",
25
+ "target_risk_level": "High",
26
+ "category": "Behavioral Activation",
27
+ "strategy_name": "Micro-Tasking",
28
+ "tags": ["burnout", "motivation", "fatigue", "executive dysfunction"],
29
+ "source": "Behavioral Activation Therapy (BAT)",
30
+ "content": "When feeling a depressive lack of motivation, focus entirely on micro-tasks. Do not try to clean the whole room; just take one single cup to the kitchen. Accomplishing a micro-task triggers a small dopamine release, making the very next task slightly easier to start."
31
+ },
32
+
33
+ {
34
+ "id": "fin_001",
35
+ "primary_emotion": "Stress",
36
+ "target_risk_level": "Medium",
37
+ "category": "Financial Stress",
38
+ "strategy_name": "Worry Postponement & Scheduled Containment",
39
+ "tags": ["debt", "rumination", "sleep disruption"],
40
+ "source": "Cognitive Behavioral Therapy (CBT)",
41
+ "content": "When intrusive thoughts about money disrupt your focus, write the specific financial fear on a piece of paper and physically place it in a 'worry jar'. Assign a strict 15-minute window (e.g., 4:00 PM) to review these fears. Outside that window, tell your brain: 'This is logged and will be handled during Worry Time.'"
42
+ },
43
+ {
44
+ "id": "fin_002",
45
+ "primary_emotion": "Anxiety",
46
+ "target_risk_level": "High",
47
+ "category": "Financial Stress",
48
+ "strategy_name": "The Downward Arrow Technique",
49
+ "tags": ["catastrophizing", "core beliefs", "bankruptcy fear"],
50
+ "source": "Cognitive Behavioral Therapy (CBT)",
51
+ "content": "Identify the surface automatic thought (e.g., 'I won't be able to pay rent'). Ask yourself: 'If that happens, what is the worst part about it?' Keep asking this for each subsequent answer until you hit the core belief (e.g., 'I will be abandoned by my family'). Address the core fear of abandonment directly, separating it from your bank balance."
52
+ },
53
+ {
54
+ "id": "fin_003",
55
+ "primary_emotion": "fear",
56
+ "target_risk_level": "Medium",
57
+ "category": "Financial Stress",
58
+ "strategy_name": "Cognitive Defusion: Labeling the Story",
59
+ "tags": ["panic", "loss of control", "overthinking"],
60
+ "source": "Acceptance and Commitment Therapy (ACT)",
61
+ "content": "When terrified of financial ruin, alter how you speak to yourself. Instead of saying 'I am going broke,' say out loud, 'I am having the thought that I am going broke.' This linguistic shift introduces psychological distance, breaking the illusion that a thought is an absolute, unavoidable reality."
62
+ },
63
+ {
64
+ "id": "fin_004",
65
+ "primary_emotion": "realization",
66
+ "target_risk_level": "Low",
67
+ "category": "Financial Stress",
68
+ "strategy_name": "Radical Acceptance of Current Baseline",
69
+ "tags": ["denial", "budgeting", "reality check"],
70
+ "source": "Dialectical Behavior Therapy (DBT)",
71
+ "content": "Acknowledge your exact debt or low balance without judgment or defense mechanisms. Say, 'My account balance is exactly $X, and fighting this reality only causes suffering.' Acceptance is not approval; it is simply acknowledging the starting line so you can accurately strategize your next move."
72
+ },
73
+ {
74
+ "id": "fin_005",
75
+ "primary_emotion": "nervousness",
76
+ "target_risk_level": "Medium",
77
+ "category": "Financial Stress",
78
+ "strategy_name": "Circle of Control Mapping",
79
+ "tags": ["inflation", "market drops", "agency"],
80
+ "source": "Stoic Philosophy & CBT",
81
+ "content": "Draw two concentric circles. In the outer circle, write financial factors you cannot control (e.g., interest rates, inflation, job market). In the inner circle, write what you can control (e.g., cancelling a subscription, updating your resume). Direct 100% of your current executive function strictly to the inner circle."
82
+ },
83
+ {
84
+ "id": "fin_006",
85
+ "primary_emotion": "disappointment",
86
+ "target_risk_level": "Medium",
87
+ "category": "Financial Stress",
88
+ "strategy_name": "Values-Based Auditing",
89
+ "tags": ["shame", "spending habits", "lifestyle creep"],
90
+ "source": "Acceptance and Commitment Therapy (ACT)",
91
+ "content": "When feeling shame over a lack of wealth or 'wasted' money, clarify your top three core life values (e.g., family, creativity, health). Review your recent expenses and label which ones served those values and which served ego or anxiety. Realign your limited budget to only fund your defined values, reducing the psychological sting of frugality."
92
+ },
93
+ {
94
+ "id": "fin_007",
95
+ "primary_emotion": "sadness",
96
+ "target_risk_level": "Medium",
97
+ "category": "Financial Stress",
98
+ "strategy_name": "Behavioral Activation: Zero-Cost Joy",
99
+ "tags": ["poverty fatigue", "isolation", "depression"],
100
+ "source": "Behavioral Activation Therapy (BAT)",
101
+ "content": "Financial depletion often causes withdrawal from pleasurable activities. Combat this by scheduling one specific, zero-cost activity today (e.g., walking in a specific park, reading a library book, calling a friend). This prevents the brain from conflating 'lack of money' with 'inability to experience joy'."
102
+ },
103
+ {
104
+ "id": "fin_008",
105
+ "primary_emotion": "anger",
106
+ "target_risk_level": "High",
107
+ "category": "Financial Stress",
108
+ "strategy_name": "Opposite Action: Opening the Mail",
109
+ "tags": ["avoidance", "late fees", "denial"],
110
+ "source": "Dialectical Behavior Therapy (DBT)",
111
+ "content": "When anger or fear makes you avoid looking at bills, your urge is to hide. Use 'Opposite Action'. Approach the bills, hold them in your hand, take a slow breath, and open them. Acting contrary to the avoidance urge proves to your nervous system that you can survive the discomfort."
112
+ },
113
+ {
114
+ "id": "fin_009",
115
+ "primary_emotion": "optimism",
116
+ "target_risk_level": "Low",
117
+ "category": "Financial Stress",
118
+ "strategy_name": "Implementation Intentions (If-Then Planning)",
119
+ "tags": ["planning", "saving", "habit building"],
120
+ "source": "Cognitive Psychology",
121
+ "content": "Harness moments of financial motivation by creating strict 'If-Then' algorithms for your behavior. Write down: 'IF I am tempted to buy an item over $50 online, THEN I will force a 24-hour waiting period before checking out.' This bypasses decision fatigue in the moment."
122
+ },
123
+ {
124
+ "id": "fin_010",
125
+ "primary_emotion": "embarrassment",
126
+ "target_risk_level": "Medium",
127
+ "category": "Financial Stress",
128
+ "strategy_name": "Externalizing the Shame Narrative",
129
+ "tags": ["status", "comparative suffering", "guilt"],
130
+ "source": "Narrative Therapy",
131
+ "content": "When feeling embarrassed about your job or income, separate your identity from the capitalist framework. Write a brief paragraph personifying 'The Economy' as an external entity with its own flaws. Reframe your narrative from 'I am a failure' to 'I am navigating a hostile external system with the resources I currently have.'"
132
+ },
133
+ {
134
+ "id": "body_001",
135
+ "primary_emotion": "disgust",
136
+ "target_risk_level": "High",
137
+ "category": "Body Image Issues",
138
+ "strategy_name": "Body Neutrality: Functional Focus",
139
+ "tags": ["self-hatred", "mirror checking", "dysmorphia"],
140
+ "source": "Acceptance and Commitment Therapy (ACT)",
141
+ "content": "When feeling intense physical disgust, stop trying to force yourself to feel 'beautiful.' Instead, state out loud three purely functional things your body is doing for you right now (e.g., 'My lungs are processing oxygen,' 'My legs are holding me upright'). Shift the metric from aesthetics to utility."
142
+ },
143
+ {
144
+ "id": "body_002",
145
+ "primary_emotion": "sadness",
146
+ "target_risk_level": "Medium",
147
+ "category": "Body Image Issues",
148
+ "strategy_name": "Self-Compassion Break",
149
+ "tags": ["aging", "weight gain", "grief"],
150
+ "source": "Mindful Self-Compassion (Neff)",
151
+ "content": "Place your hand over your heart to trigger oxytocin release. Say internally: 'This is a moment of suffering. Suffering is a normal part of the human experience. May I be kind to myself in this moment.' Acknowledge the pain of body changes without validating the self-criticism."
152
+ },
153
+ {
154
+ "id": "body_003",
155
+ "primary_emotion": "disapproval",
156
+ "target_risk_level": "Medium",
157
+ "category": "Body Image Issues",
158
+ "strategy_name": "Externalizing the Inner Critic",
159
+ "tags": ["negative self-talk", "eating disorders", "shame"],
160
+ "source": "Narrative Therapy",
161
+ "content": "Give your hyper-critical internal voice a ridiculous name or persona (e.g., 'The Snobby Fashion Critic'). When you hear thoughts like 'Your arms look terrible,' respond to the persona: 'Thanks for the input, Snob, but I'm trying to live my life right now.' This breaks over-identification with the cruel thought."
162
+ },
163
+ {
164
+ "id": "body_004",
165
+ "primary_emotion": "fear",
166
+ "target_risk_level": "High",
167
+ "category": "Body Image Issues",
168
+ "strategy_name": "Exposure and Response Prevention (ERP)",
169
+ "tags": ["body checking", "compulsions", "OCD"],
170
+ "source": "Cognitive Behavioral Therapy (CBT)",
171
+ "content": "If you compulsively check your reflection or pinch your skin, commit to a 30-minute exposure period. Wear an outfit that triggers mild discomfort, and strictly refuse to look in a mirror or touch the area. Sit with the spike in anxiety until your nervous system realizes you are not in actual danger."
172
+ },
173
+ {
174
+ "id": "body_005",
175
+ "primary_emotion": "anger",
176
+ "target_risk_level": "High",
177
+ "category": "Body Image Issues",
178
+ "strategy_name": "Opposite Action: Reclaiming Space",
179
+ "tags": ["isolation", "hiding", "shame"],
180
+ "source": "Dialectical Behavior Therapy (DBT)",
181
+ "content": "When body hatred makes you want to cancel plans, stay home, and wear baggy clothes, do the exact opposite. Put on clothes that fit your current body, stand up straight, and go out into public. Action precedes emotion; taking up space rewires the brain to stop acting like prey."
182
+ },
183
+ {
184
+ "id": "body_006",
185
+ "primary_emotion": "embarrassment",
186
+ "target_risk_level": "Medium",
187
+ "category": "Body Image Issues",
188
+ "strategy_name": "Mirror Retraining",
189
+ "tags": ["focusing on flaws", "dysmorphia", "perception"],
190
+ "source": "Cognitive Behavioral Therapy (CBT)",
191
+ "content": "Stand in front of a mirror and intentionally describe your body using only clinical, objective, non-judgmental terms. Instead of 'my stomach is gross,' say 'my torso has skin and fat to protect my organs.' Correct any subjective insults immediately with a neutral biological fact."
192
+ },
193
+ {
194
+ "id": "body_007",
195
+ "primary_emotion": "Stress",
196
+ "target_risk_level": "Medium",
197
+ "category": "Body Image Issues",
198
+ "strategy_name": "Cognitive Restructuring: All-or-Nothing Checking",
199
+ "tags": ["diet culture", "perfectionism", "control"],
200
+ "source": "Cognitive Behavioral Therapy (CBT)",
201
+ "content": "Identify black-and-white thinking regarding food or exercise (e.g., 'I ate a cookie, the day is ruined'). Dispute this actively: 'A cookie provides energy. My body processes it just like other foods. One choice does not undo my overall health.' Replace the extreme binary with a realistic spectrum."
202
+ },
203
+ {
204
+ "id": "body_008",
205
+ "primary_emotion": "grief",
206
+ "target_risk_level": "Medium",
207
+ "category": "Body Image Issues",
208
+ "strategy_name": "Mourning the 'Ideal'",
209
+ "tags": ["chronic illness", "aging", "postpartum"],
210
+ "source": "Grief Therapy",
211
+ "content": "Allow yourself 15 minutes to genuinely grieve the body you used to have, or the 'ideal' body you never achieved. Cry or write about the loss. Acknowledging this as a valid grief process stops you from masking sadness with misplaced self-hatred."
212
+ },
213
+ {
214
+ "id": "body_009",
215
+ "primary_emotion": "nervousness",
216
+ "target_risk_level": "Low",
217
+ "category": "Body Image Issues",
218
+ "strategy_name": "Somatic Tracking",
219
+ "tags": ["hyper-awareness", "discomfort", "interoception"],
220
+ "source": "Pain Reprocessing Therapy (PRT)",
221
+ "content": "When hyper-fixated on an uncomfortable physical sensation (like how your thighs touch), close your eyes. Observe the sensation purely as physical data (pressure, heat, tension) without assigning the label of 'bad' or 'ugly'. Remind your brain: 'This is a safe sensation, it is just pressure.'"
222
+ },
223
+ {
224
+ "id": "body_010",
225
+ "primary_emotion": "neutral",
226
+ "target_risk_level": "Low",
227
+ "category": "Body Image Issues",
228
+ "strategy_name": "Urge Surfing",
229
+ "tags": ["skin picking", "dietary restriction", "mindfulness"],
230
+ "source": "Dialectical Behavior Therapy (DBT)",
231
+ "content": "When the urge to restrict food or pick at your skin arises, visualize the urge as an ocean wave. Do not fight it, but do not act on it. Watch it peak in intensity and then naturally crash and recede. Know that you can experience an intense urge without obeying it."
232
+ },
233
+ {
234
+ "id": "exist_001",
235
+ "primary_emotion": "fear",
236
+ "target_risk_level": "High",
237
+ "category": "Existential Dread",
238
+ "strategy_name": "Dropping Anchor",
239
+ "tags": ["void", "meaninglessness", "panic"],
240
+ "source": "Acceptance and Commitment Therapy (ACT)",
241
+ "content": "When overwhelmed by the vastness of the universe or the finality of death, plant your feet firmly on the floor. Push down hard. Notice your back against the chair. Say aloud: 'There is a universe, but I am in this room, on this chair, right now.' Grounding in immediate physics cuts through abstract panic."
242
+ },
243
+ {
244
+ "id": "exist_002",
245
+ "primary_emotion": "confusion",
246
+ "target_risk_level": "Medium",
247
+ "category": "Existential Dread",
248
+ "strategy_name": "Values Clarification",
249
+ "tags": ["purpose", "directionless", "apathy"],
250
+ "source": "Acceptance and Commitment Therapy (ACT)",
251
+ "content": "If you feel life has no inherent meaning, accept that as a premise. Then, construct your own. Pick one area (e.g., being a good friend, creating art, learning). Commit to one small action today that serves that chosen value. Meaning is not found; it is actively built through localized behavior."
252
+ },
253
+ {
254
+ "id": "exist_003",
255
+ "primary_emotion": "realization",
256
+ "target_risk_level": "Medium",
257
+ "category": "Existential Dread",
258
+ "strategy_name": "Meaning-Making & Tragic Optimism",
259
+ "tags": ["suffering", "nihilism", "acceptance"],
260
+ "source": "Logotherapy (Frankl)",
261
+ "content": "When facing the inescapable suffering of life, ask yourself: 'How can I take a stance of dignity toward this suffering?' Find a way to extract a lesson or a piece of resilience from the pain. Transform the dread into a deep, defiant empathy for others experiencing the same human condition."
262
+ },
263
+ {
264
+ "id": "exist_004",
265
+ "primary_emotion": "sadness",
266
+ "target_risk_level": "High",
267
+ "category": "Existential Dread",
268
+ "strategy_name": "The Premeditation of Evils (Negative Visualization)",
269
+ "tags": ["loss", "death anxiety", "impermanence"],
270
+ "source": "Stoic Philosophy",
271
+ "content": "Spend 5 minutes meditating on the eventual loss of everything you love. Lean into the sadness of impermanence. Then, open your eyes and look at your life right now. The goal is to trigger a profound, urgent sense of gratitude for the temporary existence of the people and things around you."
272
+ },
273
+ {
274
+ "id": "exist_005",
275
+ "primary_emotion": "grief",
276
+ "target_risk_level": "High",
277
+ "category": "Existential Dread",
278
+ "strategy_name": "Radical Acceptance of Impermanence",
279
+ "tags": ["death", "aging", "loss of time"],
280
+ "source": "Dialectical Behavior Therapy (DBT)",
281
+ "content": "Stop fighting the reality of time passing. Say out loud: 'Everything is temporary, and that is the nature of reality. Fighting this causes my suffering.' Acceptance frees up the cognitive energy you are wasting on wishing the universe operated by different physical laws."
282
+ },
283
+ {
284
+ "id": "exist_006",
285
+ "primary_emotion": "Anxiety",
286
+ "target_risk_level": "Medium",
287
+ "category": "Existential Dread",
288
+ "strategy_name": "Zooming In (Gestalt Phenomenological Focus)",
289
+ "tags": ["big picture overwhelm", "insignificance", "focus"],
290
+ "source": "Gestalt Therapy",
291
+ "content": "Existential dread happens when you zoom out too far. Intentionally zoom in. Focus entirely on the sensory experience of a micro-task: making tea, washing a dish, or listening to a song. Confine your awareness strictly to a 3-foot radius around your body."
292
+ },
293
+ {
294
+ "id": "exist_007",
295
+ "primary_emotion": "disappointment",
296
+ "target_risk_level": "Medium",
297
+ "category": "Existential Dread",
298
+ "strategy_name": "Writing a Legacy Letter",
299
+ "tags": ["unfulfilled dreams", "regret", "purpose"],
300
+ "source": "Narrative Therapy",
301
+ "content": "If you feel your life hasn't mattered, write a letter to someone you care about (or a hypothetical future person) detailing the hardest lessons you've learned. Framing your struggles and disappointments as wisdom to be passed down transforms 'wasted time' into 'valuable data'."
302
+ },
303
+ {
304
+ "id": "exist_008",
305
+ "primary_emotion": "Stress",
306
+ "target_risk_level": "Medium",
307
+ "category": "Existential Dread",
308
+ "strategy_name": "Defusion: The 'Just a Story' Technique",
309
+ "tags": ["solipsism", "simulation theory", "obsessive thoughts"],
310
+ "source": "Acceptance and Commitment Therapy (ACT)",
311
+ "content": "When trapped in looping thoughts about reality or simulation theories, label the thought process. Say, 'My brain is currently telling me the 'Simulation Story' again.' Treat the existential thought like a boring movie playing in the background while you go about your actual, physical day."
312
+ },
313
+ {
314
+ "id": "exist_009",
315
+ "primary_emotion": "curiosity",
316
+ "target_risk_level": "Low",
317
+ "category": "Existential Dread",
318
+ "strategy_name": "Socratic Questioning of the Void",
319
+ "tags": ["nihilism", "philosophy", "intellectualizing"],
320
+ "source": "Cognitive Behavioral Therapy (CBT)",
321
+ "content": "If you conclude 'nothing matters,' challenge the premise. Ask: 'If nothing matters, why am I distressed that nothing matters? The distress itself proves that I value meaning.' Use logic to dismantle the absolute nature of your nihilism and uncover your hidden desire for connection."
322
+ },
323
+ {
324
+ "id": "exist_010",
325
+ "primary_emotion": "neutral",
326
+ "target_risk_level": "Low",
327
+ "category": "Existential Dread",
328
+ "strategy_name": "Somatic Grounding: Temperature Variance",
329
+ "tags": ["dissociation", "derealization", "numbness"],
330
+ "source": "Somatic Experiencing",
331
+ "content": "When feeling disconnected from reality (derealization), hold an ice cube in one hand and a mug of hot tea in the other. Focus rapidly shifting your attention between the extreme cold and the extreme heat. The intense, contrasting physical data pulls your brain out of abstract detachment."
332
+ },
333
+ {
334
+ "id": "care_001",
335
+ "primary_emotion": "caring",
336
+ "target_risk_level": "Medium",
337
+ "category": "Caregiver Burnout",
338
+ "strategy_name": "DEAR MAN for Boundary Setting",
339
+ "tags": ["enmeshment", "over-functioning", "asking for help"],
340
+ "source": "Dialectical Behavior Therapy (DBT)",
341
+ "content": "Use this script to ask a relative for help or set a limit: Describe the situation objectively, Express your feelings clearly ('I am exhausted'), Assert your need ('I need you to take the Thursday shift'), Reinforce the benefit ('This ensures Mom gets better care'). Stay Mindful, Appear confident, and Negotiate if needed."
342
+ },
343
+ {
344
+ "id": "care_002",
345
+ "primary_emotion": "annoyance",
346
+ "target_risk_level": "High",
347
+ "category": "Caregiver Burnout",
348
+ "strategy_name": "The STOP Skill",
349
+ "tags": ["frustration", "snapping", "reactivity"],
350
+ "source": "Dialectical Behavior Therapy (DBT)",
351
+ "content": "When a patient or loved one repeatedly asks the same question or refuses help, Stop immediately. Take a step back (literally step out of the room). Observe your spiking heart rate and angry thoughts. Proceed mindfully—choose an intentional response rather than snapping defensively."
352
+ },
353
+ {
354
+ "id": "care_003",
355
+ "primary_emotion": "anger",
356
+ "target_risk_level": "High",
357
+ "category": "Caregiver Burnout",
358
+ "strategy_name": "FAST Skill for Self-Respect",
359
+ "tags": ["resentment", "martyrdom", "exploitation"],
360
+ "source": "Dialectical Behavior Therapy (DBT)",
361
+ "content": "Maintain your self-respect when caregiving. Be Fair to yourself and the patient. Avoid over-apologizing for taking breaks. Stick to your values (you are allowed to rest). Be Truthful; do not pretend you are fine if you are burning out. Honesty prevents explosive resentment later."
362
+ },
363
+ {
364
+ "id": "care_004",
365
+ "primary_emotion": "remorse",
366
+ "target_risk_level": "Medium",
367
+ "category": "Caregiver Burnout",
368
+ "strategy_name": "Self-Compassion: 'Just Like Me'",
369
+ "tags": ["guilt", "not doing enough", "mistakes"],
370
+ "source": "Mindfulness-Based Stress Reduction (MBSR)",
371
+ "content": "When feeling guilty for losing your patience, remind yourself: 'There are thousands of caregivers in the world feeling this exact same frustration right now. I am human, I am fatigued, and my reaction makes sense given the load I carry.' Depersonalize the guilt."
372
+ },
373
+ {
374
+ "id": "care_005",
375
+ "primary_emotion": "Depression",
376
+ "target_risk_level": "High",
377
+ "category": "Caregiver Burnout",
378
+ "strategy_name": "Behavioral Activation: Micro-Respite",
379
+ "tags": ["hopelessness", "loss of self", "fatigue"],
380
+ "source": "Behavioral Activation Therapy (BAT)",
381
+ "content": "You cannot wait for a 'week off' to recover. Schedule 10-minute micro-respites daily where you engage in an identity-affirming activity unrelated to caregiving (e.g., playing a guitar, reading a thriller). You must actively maintain a sliver of your non-caregiver identity to survive."
382
+ },
383
+ {
384
+ "id": "care_006",
385
+ "primary_emotion": "Stress",
386
+ "target_risk_level": "High",
387
+ "category": "Caregiver Burnout",
388
+ "strategy_name": "TIPP: Paced Breathing",
389
+ "tags": ["crisis", "medical emergencies", "hyperventilation"],
390
+ "source": "Dialectical Behavior Therapy (DBT)",
391
+ "content": "During a sudden medical issue or caregiving crisis, override your fight-or-flight response. Breathe in for 5 seconds, and breathe out for 7 seconds. Making the exhale longer than the inhale forcibly slows your heart rate, allowing your prefrontal cortex to make rational medical decisions."
392
+ },
393
+ {
394
+ "id": "care_007",
395
+ "primary_emotion": "disappointment",
396
+ "target_risk_level": "Low",
397
+ "category": "Caregiver Burnout",
398
+ "strategy_name": "Radical Acceptance of Illness Progression",
399
+ "tags": ["dementia", "terminal illness", "denial"],
400
+ "source": "Dialectical Behavior Therapy (DBT)",
401
+ "content": "Stop fighting the fact that your loved one is declining despite your hard work. Repeat: 'I am doing everything right, and the disease is still progressing. I cannot cure this; I can only care.' Severing your sense of failure from their biological decline is crucial for survival."
402
+ },
403
+ {
404
+ "id": "care_008",
405
+ "primary_emotion": "grief",
406
+ "target_risk_level": "High",
407
+ "category": "Caregiver Burnout",
408
+ "strategy_name": "Processing Ambiguous Grief",
409
+ "tags": ["anticipatory grief", "personality changes", "loss"],
410
+ "source": "Cognitive Behavioral Therapy (CBT)",
411
+ "content": "Acknowledge that you are grieving someone who is still alive. Write down the specific things you miss about who they used to be before the illness or injury. Validating 'Ambiguous Loss' helps process the complex sorrow that occurs when a physical body is present but the mind/relationship has changed."
412
+ },
413
+ {
414
+ "id": "care_009",
415
+ "primary_emotion": "nervousness",
416
+ "target_risk_level": "Medium",
417
+ "category": "Caregiver Burnout",
418
+ "strategy_name": "Check the Facts on 'Enough'",
419
+ "tags": ["hyper-vigilance", "worry", "perfectionism"],
420
+ "source": "Dialectical Behavior Therapy (DBT)",
421
+ "content": "When panicked that you missed a medication or aren't doing enough, write down strictly the verifiable facts. (e.g., 'Mom is fed, meds were taken at 8 AM, she is resting'). Separate the factual reality of safety from your emotional feeling of impending doom."
422
+ },
423
+ {
424
+ "id": "care_010",
425
+ "primary_emotion": "Stress",
426
+ "target_risk_level": "High",
427
+ "category": "Caregiver Burnout",
428
+ "strategy_name": "PLEASE Skill for Vulnerability Reduction",
429
+ "tags": ["exhaustion", "self-neglect", "physical health"],
430
+ "source": "Dialectical Behavior Therapy (DBT)",
431
+ "content": "Treat physical illness, balance eating, avoid mood-altering drugs, get balanced sleep, and exercise. As a caregiver, you treat your own physical neglect as a badge of honor. Reframe self-care as tactical maintenance: 'If the machine (me) breaks, the patient suffers. I must eat to operate.'"
432
+ },
433
+ {
434
+ "id": "overwhelm_001",
435
+ "primary_emotion": "Stress",
436
+ "target_risk_level": "High",
437
+ "category": "General Overwhelm",
438
+ "strategy_name": "TIPP: Temperature (Mammalian Dive Reflex)",
439
+ "tags": ["meltdown", "hysteria", "crisis reset"],
440
+ "source": "Dialectical Behavior Therapy (DBT)",
441
+ "content": "When emotional arousal is at an absolute maximum and you cannot think, hold your breath and submerge your face in a bowl of ice water for 30 seconds (or place an ice pack on your eyes and cheeks). This triggers the mammalian dive reflex, rapidly dropping heart rate and forcing parasympathetic regulation."
442
+ },
443
+ {
444
+ "id": "overwhelm_002",
445
+ "primary_emotion": "Anxiety",
446
+ "target_risk_level": "Medium",
447
+ "category": "General Overwhelm",
448
+ "strategy_name": "Brain Dump & Eisenhower Matrix",
449
+ "tags": ["executive dysfunction", "task paralysis", "to-do list"],
450
+ "source": "Cognitive Behavioral Therapy (CBT)",
451
+ "content": "When paralyzed by too many tasks, write every single thought and to-do item on a piece of paper. Then, plot them on a 2x2 grid: Urgent/Important, Not Urgent/Important, Urgent/Not Important, Not Urgent/Not Important. Only execute the top-left quadrant today. Ignore the rest."
452
+ },
453
+ {
454
+ "id": "overwhelm_003",
455
+ "primary_emotion": "Anxiety",
456
+ "target_risk_level": "High",
457
+ "category": "General Overwhelm",
458
+ "strategy_name": "Somatic Pendulation",
459
+ "tags": ["panic attack", "chest tightness", "trauma"],
460
+ "source": "Somatic Experiencing",
461
+ "content": "Focus your attention on the area of your body experiencing the most overwhelming distress (e.g., a tight chest). Then, explicitly shift your attention to a completely neutral or calm part of your body (e.g., your right big toe). Swing your focus back and forth to teach your nervous system that not all of you is in danger."
462
+ },
463
+ {
464
+ "id": "overwhelm_004",
465
+ "primary_emotion": "Suicidal",
466
+ "target_risk_level": "High",
467
+ "category": "General Overwhelm",
468
+ "strategy_name": "Crisis Survival: ACCEPTS Skill",
469
+ "tags": ["crisis", "self-harm urges", "distress tolerance"],
470
+ "source": "Dialectical Behavior Therapy (DBT)",
471
+ "content": "When urges to self-harm peak, focus solely on surviving the next 10 minutes without making things worse. Use Activities (do a puzzle), Contributing (help someone else), Comparisons (watch a sad movie), Emotions (listen to upbeat music), Pushing away (visualize locking the thought in a box), Thoughts (count backward from 100 by 7s), and Sensations (hold ice)."
472
+ },
473
+ {
474
+ "id": "overwhelm_005",
475
+ "primary_emotion": "Bipolar",
476
+ "target_risk_level": "High",
477
+ "category": "General Overwhelm",
478
+ "strategy_name": "Social Rhythm Therapy Routing",
479
+ "tags": ["mania", "sleep disruption", "dysregulation"],
480
+ "source": "Interpersonal and Social Rhythm Therapy (IPSRT)",
481
+ "content": "When feeling the chaotic escalation of a manic or hypomanic swing, aggressively lock down your circadian rhythms. Go to bed, wake up, and eat meals at the exact same time down to the minute. Biological predictability acts as a scaffolding to contain neurochemical volatility."
482
+ },
483
+ {
484
+ "id": "overwhelm_006",
485
+ "primary_emotion": "fear",
486
+ "target_risk_level": "Medium",
487
+ "category": "General Overwhelm",
488
+ "strategy_name": "Probability vs. Possibility Check",
489
+ "tags": ["paranoia", "worst-case scenario", "future tripping"],
490
+ "source": "Cognitive Behavioral Therapy (CBT)",
491
+ "content": "When overwhelmed by a terrifying future scenario, ask: 'Is this merely possible, or is it statistically probable?' Write down the actual evidence that the disaster will happen, versus the evidence that you will survive and adapt. Stop treating a 1% possibility like a 99% certainty."
492
+ },
493
+ {
494
+ "id": "overwhelm_007",
495
+ "primary_emotion": "confusion",
496
+ "target_risk_level": "Low",
497
+ "category": "General Overwhelm",
498
+ "strategy_name": "The 'Next Right Step' Focus",
499
+ "tags": ["brain fog", "decision fatigue", "stuck"],
500
+ "source": "Acceptance and Commitment Therapy (ACT)",
501
+ "content": "When totally overwhelmed by the complexity of life, abandon the 5-year plan and the 1-month plan. Ask yourself only: 'What is the very next physical action I need to take to align with my values?' It might be 'drink a glass of water.' Do that, and then ask the question again."
502
+ },
503
+ {
504
+ "id": "overwhelm_008",
505
+ "primary_emotion": "nervousness",
506
+ "target_risk_level": "Medium",
507
+ "category": "General Overwhelm",
508
+ "strategy_name": "Box Breathing (Square Breathing)",
509
+ "tags": ["jitters", "stage fright", "vagus nerve"],
510
+ "source": "Clinical Breathwork Guidelines",
511
+ "content": "To quickly lower a spiking heart rate, visualize a square. Inhale for 4 seconds, hold full for 4 seconds, exhale for 4 seconds, hold empty for 4 seconds. Repeat 4 times. This balanced ratio forcefully engages the vagus nerve, signaling biological safety to the brain."
512
+ },
513
+ {
514
+ "id": "overwhelm_009",
515
+ "primary_emotion": "sadness",
516
+ "target_risk_level": "Medium",
517
+ "category": "General Overwhelm",
518
+ "strategy_name": "IMPROVE the Moment",
519
+ "tags": ["crying spells", "hopelessness", "coping"],
520
+ "source": "Dialectical Behavior Therapy (DBT)",
521
+ "content": "When overwhelmed by despair, make the current moment 10% more tolerable. Use Imagery (visualize a safe place), Meaning (find purpose in the pain), Prayer (or opening to the universe), Relaxation (muscle release), One thing in the moment (hyper-focus), Vacation (take a brief mental break), and Encouragement (cheerlead yourself)."
522
+ },
523
+ {
524
+ "id": "overwhelm_010",
525
+ "primary_emotion": "realization",
526
+ "target_risk_level": "Low",
527
+ "category": "General Overwhelm",
528
+ "strategy_name": "Self-Soothing via 5 Senses",
529
+ "tags": ["sensory overload", "autistic burnout", "grounding"],
530
+ "source": "Dialectical Behavior Therapy (DBT)",
531
+ "content": "When the environment or your mind is too loud, deliberately curate your sensory input to self-soothe. Look at something beautiful, listen to brown noise, touch a soft blanket, smell a strong essential oil, or taste a sour candy. Overwriting chaotic sensory input with chosen, pleasant input re-anchors the mind."
532
+ },
533
+
534
+ {
535
+ "id": "acad_001",
536
+ "primary_emotion": "nervousness",
537
+ "target_risk_level": "Medium",
538
+ "category": "Academic Pressure",
539
+ "strategy_name": "State-Dependent Recall Anchoring",
540
+ "tags": ["exam anxiety", "blanking out", "technical interviews", "memory"],
541
+ "source": "Cognitive Psychology",
542
+ "content": "When freezing during a high-stakes technical test or interview, do not try to force the answer. Instead, deliberately visualize the exact environment where you studied the material. Recall the lighting, the screen setup, and your posture. Reactivating the environmental memory network often acts as a backdoor to retrieve the blocked technical data."
543
+ },
544
+ {
545
+ "id": "acad_002",
546
+ "primary_emotion": "confusion",
547
+ "target_risk_level": "Medium",
548
+ "category": "Academic Pressure",
549
+ "strategy_name": "The 'Black Box' Deconstruction",
550
+ "tags": ["overwhelm", "complex systems", "learning paralysis"],
551
+ "source": "Constructivist Learning Theory",
552
+ "content": "When overwhelmed by a massive, interconnected topic (like a complex system architecture), stop trying to understand the whole. Draw a literal box. Label it with the system's name. Write exactly what goes IN the box (inputs) and what comes OUT (outputs). Ignore how the inside works until your brain accepts the baseline boundaries."
553
+ },
554
+ {
555
+ "id": "acad_003",
556
+ "primary_emotion": "annoyance",
557
+ "target_risk_level": "Low",
558
+ "category": "Academic Pressure",
559
+ "strategy_name": "Pattern Interrupt for Debugging Loops",
560
+ "tags": ["frustration", "problem solving", "fixation"],
561
+ "source": "Gestalt Psychology",
562
+ "content": "If you have spent more than 45 minutes fixing the same logical error or broken module with increasing anger, you are in a cognitive tunnel. Stand up and complete a mechanical task requiring spatial awareness (e.g., washing a single dish, organizing a drawer). This forces the brain to shift from focused-mode to diffuse-mode thinking, allowing background pattern recognition to solve the error."
563
+ },
564
+ {
565
+ "id": "acad_004",
566
+ "primary_emotion": "disappointment",
567
+ "target_risk_level": "Medium",
568
+ "category": "Academic Pressure",
569
+ "strategy_name": "Externalizing the Grade",
570
+ "tags": ["perfectionism", "failure", "self-worth"],
571
+ "source": "Rational Emotive Behavior Therapy (REBT)",
572
+ "content": "When crushed by a poor grade or failed assessment, physically write down the score on a piece of paper and place it across the room. Look at it and state: 'That number reflects my performance on one specific metric on one specific day. It is not an assessment of my global intelligence or future trajectory.' Break the enmeshment between data and identity."
573
+ },
574
+ {
575
+ "id": "acad_005",
576
+ "primary_emotion": "fear",
577
+ "target_risk_level": "High",
578
+ "category": "Academic Pressure",
579
+ "strategy_name": "Worst-Case Scenario Scripting",
580
+ "tags": ["catastrophizing", "dropout fears", "thesis"],
581
+ "source": "Stoic Negative Visualization",
582
+ "content": "When paralyzed by the fear of failing out of a program, write a step-by-step script of the absolute worst-case scenario. Then, write out your exact, logistical survival plan for that scenario (e.g., 'I will move back home, work this specific job, and pivot to this specific field'). Fear thrives in vagueness; concrete planning strips its power."
583
+ },
584
+ {
585
+ "id": "acad_006",
586
+ "primary_emotion": "Stress",
587
+ "target_risk_level": "Medium",
588
+ "category": "Academic Pressure",
589
+ "strategy_name": "Pomodoro with Somatic Reset",
590
+ "tags": ["focus", "burnout prevention", "posture"],
591
+ "source": "Somatic Experiencing",
592
+ "content": "During intense study blocks, the body stores stress as muscular tension, mimicking a threat response. Every 25 minutes, do not look at your phone. Instead, do a 60-second 'somatic reset': physically shake your hands, roll your neck, and push your feet hard into the floor to discharge accumulated cortisol."
593
+ },
594
+ {
595
+ "id": "acad_007",
596
+ "primary_emotion": "envy",
597
+ "target_risk_level": "Low",
598
+ "category": "Academic Pressure",
599
+ "strategy_name": "Compare-and-Despair Reframing",
600
+ "tags": ["imposter syndrome", "cohort comparison", "jealousy"],
601
+ "source": "Cognitive Behavioral Therapy (CBT)",
602
+ "content": "When feeling inferior to peers who seem to grasp concepts faster, identify the specific 'invisible variables.' Tell yourself: 'I am comparing my internal struggle to their curated external output. I do not know their background resources, prior exposure, or hidden failures.' Pivot your focus back to your own baseline metric."
603
+ },
604
+ {
605
+ "id": "acad_008",
606
+ "primary_emotion": "Depression",
607
+ "target_risk_level": "High",
608
+ "category": "Academic Pressure",
609
+ "strategy_name": "The 'Ugly First Draft' Mandate",
610
+ "tags": ["procrastination", "perfectionism", "apathy"],
611
+ "source": "Behavioral Activation",
612
+ "content": "When depression makes starting a project feel impossible, lower the bar to zero. Open a document and explicitly type: 'This is the ugly, terrible version that I am allowed to hate.' Give yourself permission to write objectively bad material. You cannot edit a blank page, but you can refine a terrible draft."
613
+ },
614
+ {
615
+ "id": "acad_009",
616
+ "primary_emotion": "surprise",
617
+ "target_risk_level": "Low",
618
+ "category": "Academic Pressure",
619
+ "strategy_name": "Cognitive Reappraisal of Arousal",
620
+ "tags": ["pop quizzes", "unexpected deadlines", "shock"],
621
+ "source": "Biopsychology",
622
+ "content": "When hit with a sudden deadline or pop quiz, your body floods with adrenaline, which feels like panic. Immediately relabel the physiological sensation out loud: 'I am not panicking, my body is mobilizing energy to help me focus.' Reappraising arousal as excitement or readiness prevents the brain from entering a freeze state."
623
+ },
624
+ {
625
+ "id": "acad_010",
626
+ "primary_emotion": "optimism",
627
+ "target_risk_level": "Low",
628
+ "category": "Academic Pressure",
629
+ "strategy_name": "Future-Self Delegation",
630
+ "tags": ["planning", "motivation", "momentum"],
631
+ "source": "Solution-Focused Brief Therapy (SFBT)",
632
+ "content": "When feeling highly motivated, do not just study harder—build infrastructure. Create templates, organize folders, or map out tomorrow's schedule. Treat your 'Optimistic Current Self' as an assistant setting up a frictionless environment for your 'Tired Future Self'."
633
+ },
634
+ {
635
+ "id": "work_001",
636
+ "primary_emotion": "Anxiety",
637
+ "target_risk_level": "Medium",
638
+ "category": "Workplace Burnout",
639
+ "strategy_name": "Boundary Anchoring via Ritual",
640
+ "tags": ["remote work", "work-life balance", "hyper-vigilance"],
641
+ "source": "Context-Dependent Conditioning",
642
+ "content": "If you work and relax in the same space, your brain cannot switch off. Create a strict, physical 'shutdown ritual.' At the end of the workday, close all tabs, say out loud 'The workday is complete,' and physically cover your laptop or workspace with a towel or cloth. This visual cue acts as a psychological off-switch."
643
+ },
644
+ {
645
+ "id": "work_002",
646
+ "primary_emotion": "anger",
647
+ "target_risk_level": "High",
648
+ "category": "Workplace Burnout",
649
+ "strategy_name": "The 'Not My Circus' De-escalation",
650
+ "tags": ["toxic culture", "micromanagement", "resentment"],
651
+ "source": "Acceptance and Commitment Therapy (ACT)",
652
+ "content": "When enraged by poor management or toxic colleagues, internally visualize a glass wall dropping between you and them. Repeat the mantra: 'Their dysfunction is their property, not my responsibility.' Refuse to absorb the emotional weight of systemic incompetence that you do not have the authority to fix."
653
+ },
654
+ {
655
+ "id": "work_003",
656
+ "primary_emotion": "sadness",
657
+ "target_risk_level": "Medium",
658
+ "category": "Workplace Burnout",
659
+ "strategy_name": "Value-Extraction Mapping",
660
+ "tags": ["dead-end job", "boredom", "loss of purpose"],
661
+ "source": "Logotherapy",
662
+ "content": "If your job feels meaningless, shift from 'What is this job doing to me?' to 'What am I extracting from this job?' Identify three highly specific, transferable skills you are getting paid to practice right now (e.g., conflict resolution, a specific software, patience). Frame the job merely as a funded training ground for your next step."
663
+ },
664
+ {
665
+ "id": "work_004",
666
+ "primary_emotion": "embarrassment",
667
+ "target_risk_level": "Medium",
668
+ "category": "Workplace Burnout",
669
+ "strategy_name": "The 10-10-10 Reality Check",
670
+ "tags": ["mistakes", "public speaking", "shame"],
671
+ "source": "Cognitive Behavioral Therapy (CBT)",
672
+ "content": "After making a public mistake in a meeting or sending a flawed email, halt the shame spiral by asking: 'Will this matter in 10 days? 10 months? 10 years?' The answer is almost always no. Acknowledge that the 'spotlight effect' makes you believe everyone is analyzing your failure, when in reality, they are focused on themselves."
673
+ },
674
+ {
675
+ "id": "work_005",
676
+ "primary_emotion": "Stress",
677
+ "target_risk_level": "High",
678
+ "category": "Workplace Burnout",
679
+ "strategy_name": "Ruthless Prioritization (The 1-3-5 Rule)",
680
+ "tags": ["overload", "task paralysis", "deadlines"],
681
+ "source": "Executive Functioning Coaching",
682
+ "content": "When overwhelmed by an infinite task list, constrain yourself mechanically. You are only allowed to choose: 1 Major Task, 3 Medium Tasks, and 5 Small Tasks for the entire day. Hide the rest of the list. By mathematically limiting your scope, you bypass decision fatigue and regain agency."
683
+ },
684
+ {
685
+ "id": "work_006",
686
+ "primary_emotion": "disapproval",
687
+ "target_risk_level": "Low",
688
+ "category": "Workplace Burnout",
689
+ "strategy_name": "Constructive Depersonalization of Feedback",
690
+ "tags": ["performance reviews", "criticism", "defensiveness"],
691
+ "source": "Dialectical Behavior Therapy (DBT)",
692
+ "content": "When receiving harsh feedback, separate your ego from the output. Imagine the reviewer is critiquing a piece of machinery you happen to operate, not your soul. Extract the actionable data point (e.g., 'the report needs more metrics') and discard the emotional phrasing (e.g., 'this is sloppy')."
693
+ },
694
+ {
695
+ "id": "work_007",
696
+ "primary_emotion": "nervousness",
697
+ "target_risk_level": "Medium",
698
+ "category": "Workplace Burnout",
699
+ "strategy_name": "Power-Posing & Vagal Toning",
700
+ "tags": ["interviews", "negotiation", "intimidation"],
701
+ "source": "Embodied Cognition",
702
+ "content": "Five minutes before a high-stakes meeting, go to a private space. Stand with your feet wide and hands on your hips (expansive posture) while humming loudly from your chest. The posture decreases cortisol, while the vocal cord vibration stimulates the vagus nerve, biologically overriding the subservient fear response."
703
+ },
704
+ {
705
+ "id": "work_008",
706
+ "primary_emotion": "pride",
707
+ "target_risk_level": "Low",
708
+ "category": "Workplace Burnout",
709
+ "strategy_name": "The 'Brag Document' Integration",
710
+ "tags": ["imposter syndrome", "promotion", "self-advocacy"],
711
+ "source": "Narrative Therapy",
712
+ "content": "Capitalize on moments of professional success by immediately logging them in a dedicated 'Brag Document'. Detail the exact problem, your specific action, and the result. Relying on memory for performance reviews invites imposter syndrome; relying on a concrete, self-authored dataset builds unshakeable professional confidence."
713
+ },
714
+ {
715
+ "id": "work_009",
716
+ "primary_emotion": "disgust",
717
+ "target_risk_level": "High",
718
+ "category": "Workplace Burnout",
719
+ "strategy_name": "Moral Injury Boundaries",
720
+ "tags": ["ethical conflicts", "corporate hypocrisy", "alienation"],
721
+ "source": "Trauma-Informed Care",
722
+ "content": "When forced to execute policies you find ethically repulsive, document your objections cleanly in writing, then detach. Tell yourself: 'I am operating within a compromised system. I am selling my labor, not my morality.' Plan an active exit strategy so the compromise feels temporary, not permanent."
723
+ },
724
+ {
725
+ "id": "work_010",
726
+ "primary_emotion": "neutral",
727
+ "target_risk_level": "Low",
728
+ "category": "Workplace Burnout",
729
+ "strategy_name": "Micro-Pacing (The 85% Rule)",
730
+ "tags": ["hustle culture", "endurance", "chronic fatigue"],
731
+ "source": "Sports Psychology",
732
+ "content": "Operating at 100% capacity daily guarantees burnout. Intentionally aim to operate at 85% effort. This leaves a 15% biological reserve for emergencies, unexpected tasks, or emotional regulation. Perfect compliance is less sustainable than consistent, adequate performance."
733
+ },
734
+ {
735
+ "id": "mh_001",
736
+ "primary_emotion": "Bipolar",
737
+ "target_risk_level": "High",
738
+ "category": "Advanced Mental Health",
739
+ "strategy_name": "The 'Dark Therapy' Protocol",
740
+ "tags": ["mania", "hypomania", "sensory reduction"],
741
+ "source": "Chronotherapy",
742
+ "content": "If you recognize the early signs of a manic escalation (racing thoughts, decreased need for sleep), forcefully enforce 'virtual darkness' for 14 hours a day. Block all blue light, use blackout curtains, and eliminate screens after 6 PM. Simulating extended nights chemically forces the brain to produce melatonin and suppress manic acceleration."
743
+ },
744
+ {
745
+ "id": "mh_002",
746
+ "primary_emotion": "Personality disorder",
747
+ "target_risk_level": "High",
748
+ "category": "Advanced Mental Health",
749
+ "strategy_name": "Fact-Checking 'Splitting' (Black/White Thinking)",
750
+ "tags": ["BPD", "idealization", "devaluation", "abandonment"],
751
+ "source": "Dialectical Behavior Therapy (DBT)",
752
+ "content": "When suddenly feeling that a loved one is entirely evil or hates you, pause the reaction. Draw a line down a paper. On the left, write the current trigger (e.g., 'They didn't text back'). On the right, write 3 times they showed you care in the last month. Force your brain to hold both positive and negative truths simultaneously."
753
+ },
754
+ {
755
+ "id": "mh_003",
756
+ "primary_emotion": "Suicidal",
757
+ "target_risk_level": "High",
758
+ "category": "Advanced Mental Health",
759
+ "strategy_name": "The 'Delay, Don't Decide' Contract",
760
+ "tags": ["crisis", "ideation", "safety planning"],
761
+ "source": "Collaborative Assessment and Management of Suicidality (CAMS)",
762
+ "content": "In an acute crisis, the brain craves the permanence of an exit. Do not try to convince yourself to live for the next 50 years; that is too overwhelming. Make a concrete, ironclad agreement to delay any action for exactly 48 hours. Focus only on surviving the delay window by sleeping or distracting. Ideation is a wave; it always peaks and crests."
763
+ },
764
+ {
765
+ "id": "mh_004",
766
+ "primary_emotion": "grief",
767
+ "target_risk_level": "High",
768
+ "category": "Advanced Mental Health",
769
+ "strategy_name": "The 'Growing Around the Grief' Visualization",
770
+ "tags": ["complex trauma", "loss", "PTSD"],
771
+ "source": "Tonkin’s Model of Grief",
772
+ "content": "Stop expecting the grief to 'shrink' over time. Visualize your grief as a large black sphere in a jar. The sphere never gets smaller. Instead, visualize the jar (your life, your experiences, your capacity) expanding around it. Focus on building the jar through new, safe experiences, rather than trying to erase the sphere."
773
+ },
774
+ {
775
+ "id": "mh_005",
776
+ "primary_emotion": "remorse",
777
+ "target_risk_level": "Medium",
778
+ "category": "Advanced Mental Health",
779
+ "strategy_name": "Reparation over Rumination",
780
+ "tags": ["moral OCD", "guilt", "toxic shame"],
781
+ "source": "Acceptance and Commitment Therapy (ACT)",
782
+ "content": "When tortured by guilt over a past mistake, recognize that rumination is a selfish way of punishing yourself without helping anyone. Shift from internal torture to external action. If you can safely apologize, do it once, cleanly. If you cannot, donate time or money to a cause related to your mistake. Make the guilt functional."
783
+ },
784
+ {
785
+ "id": "mh_006",
786
+ "primary_emotion": "fear",
787
+ "target_risk_level": "High",
788
+ "category": "Advanced Mental Health",
789
+ "strategy_name": "Grounding via Peripheral Vision",
790
+ "tags": ["hyper-vigilance", "PTSD", "flashbacks"],
791
+ "source": "Polyvagal Theory",
792
+ "content": "During a flashback or trauma trigger, your vision narrows to a hyper-focused tunnel (predator/prey mode). Intentionally soften your gaze and stretch your awareness to the extreme edges of your peripheral vision. Taking in a wide horizontal field of view biologically signals the brainstem that the environment is safe and clear of immediate threats."
793
+ },
794
+ {
795
+ "id": "mh_007",
796
+ "primary_emotion": "Anxiety",
797
+ "target_risk_level": "Medium",
798
+ "category": "Advanced Mental Health",
799
+ "strategy_name": "Worry Scripting (Exposure)",
800
+ "tags": ["GAD", "intrusive thoughts", "rumination"],
801
+ "source": "Cognitive Behavioral Therapy (CBT)",
802
+ "content": "If you are obsessed with a terrifying thought, do not push it away. Write out the fear in excruciating, vivid detail for 15 straight minutes. Do not stop typing or writing. By forcing the brain to confront the specific narrative rather than a vague shadow of dread, the amygdala eventually habituates and the anxiety burns itself out."
803
+ },
804
+ {
805
+ "id": "mh_008",
806
+ "primary_emotion": "Depression",
807
+ "target_risk_level": "High",
808
+ "category": "Advanced Mental Health",
809
+ "strategy_name": "Opposite Action to Lethargy",
810
+ "tags": ["MDD", "psychomotor retardation", "isolation"],
811
+ "source": "Dialectical Behavior Therapy (DBT)",
812
+ "content": "When severe depression dictates that you must stay in bed with the lights off, recognize this as a symptom, not a truth. Identify the exact opposite action: stand up, turn on a bright light, and put cold water on your face. You do not have to feel better to move; you move to manipulate the chemistry that will eventually make you feel better."
813
+ },
814
+ {
815
+ "id": "mh_009",
816
+ "primary_emotion": "Personality disorder",
817
+ "target_risk_level": "High",
818
+ "category": "Advanced Mental Health",
819
+ "strategy_name": "The 'Wise Mind' Intersect",
820
+ "tags": ["impulsivity", "emotional dysregulation", "BPD"],
821
+ "source": "Dialectical Behavior Therapy (DBT)",
822
+ "content": "Before making a highly destructive or impulsive choice (e.g., sending a vicious text, quitting a job in a rage), draw two overlapping circles. One is 'Emotion Mind' (how you feel), one is 'Reason Mind' (pure logic). Write the demands of both. You are only permitted to act on a decision that exists in the overlapping middle: the 'Wise Mind'."
823
+ },
824
+ {
825
+ "id": "mh_010",
826
+ "primary_emotion": "Bipolar",
827
+ "target_risk_level": "Medium",
828
+ "category": "Advanced Mental Health",
829
+ "strategy_name": "Energy Budgeting (Spoon Theory)",
830
+ "tags": ["depressive crash", "chronic illness", "pacing"],
831
+ "source": "Chronic Illness Management",
832
+ "content": "During the depressive phase following a manic episode, visualize having only 10 'energy tokens' for the day. A shower costs 3. Making food costs 4. Checking emails costs 3. Once the 10 tokens are gone, you are done. Refuse all other demands without guilt. Radical pacing prevents further neurochemical depletion."
833
+ },
834
+ {
835
+ "id": "gen_001",
836
+ "primary_emotion": "caring",
837
+ "target_risk_level": "Low",
838
+ "category": "Generalized Coping & Social",
839
+ "strategy_name": "The 'Drop the Rope' Strategy",
840
+ "tags": ["arguments", "enmeshment", "toxic family"],
841
+ "source": "Acceptance and Commitment Therapy (ACT)",
842
+ "content": "When trapped in a cyclical, unwinnable argument with a stubborn person, imagine you are playing tug-of-war over a pit. The only way to win is to realize you don't have to pull back. Simply open your hands and drop the rope. Say, 'I hear your perspective,' and disengage. You cannot be pulled into the pit if you aren't holding the rope."
843
+ },
844
+ {
845
+ "id": "gen_002",
846
+ "primary_emotion": "sadness",
847
+ "target_risk_level": "Medium",
848
+ "category": "Generalized Coping & Social",
849
+ "strategy_name": "Scheduling 'Pity Time'",
850
+ "tags": ["breakups", "loneliness", "wallowing"],
851
+ "source": "Strategic Therapy",
852
+ "content": "If you are consumed by sadness over a breakup or rejection, do not try to 'stay positive.' Set an alarm for 20 minutes. During this time, you must cry, listen to sad music, and wallow with maximum intensity. When the alarm sounds, the pity party is strictly over. Compartmentalizing the emotion prevents it from bleeding into your entire day."
853
+ },
854
+ {
855
+ "id": "gen_003",
856
+ "primary_emotion": "annoyance",
857
+ "target_risk_level": "Low",
858
+ "category": "Generalized Coping & Social",
859
+ "strategy_name": "The 5-Minute Favor Rule",
860
+ "tags": ["social fatigue", "obligations", "resentment"],
861
+ "source": "Cognitive Behavioral Therapy (CBT)",
862
+ "content": "When overwhelmed by people asking for your time or help, implement a hard filter: If the request takes 5 minutes or less, do it immediately. If it takes more, you must say, 'Let me check my schedule and get back to you.' This breaks the habit of automatic, people-pleasing 'yes' responses."
863
+ },
864
+ {
865
+ "id": "gen_004",
866
+ "primary_emotion": "love",
867
+ "target_risk_level": "Medium",
868
+ "category": "Generalized Coping & Social",
869
+ "strategy_name": "Differentiating Love from Codependency",
870
+ "tags": ["relationships", "anxious attachment", "boundaries"],
871
+ "source": "Attachment Theory",
872
+ "content": "When feeling intensely driven to 'fix' a partner's mood, pause. Ask yourself: 'Am I doing this to support them, or am I doing this to alleviate my own anxiety about their mood?' True love allows the other person to experience their own distress without you frantically orchestrating their rescue."
873
+ },
874
+ {
875
+ "id": "gen_005",
876
+ "primary_emotion": "gratitude",
877
+ "target_risk_level": "Low",
878
+ "category": "Generalized Coping & Social",
879
+ "strategy_name": "Specific, Micro-Gratitude",
880
+ "tags": ["depression", "cynicism", "reframing"],
881
+ "source": "Positive Psychology",
882
+ "content": "Generic gratitude ('I'm thankful for my health') quickly loses its psychological impact. Force your brain to scan for novel, micro-gratitudes. E.g., 'I am grateful the coffee was perfectly hot today,' or 'I appreciate that the bus was on time.' Training the brain to hunt for small positives overrides the default negative bias."
883
+ },
884
+ {
885
+ "id": "gen_006",
886
+ "primary_emotion": "excitement",
887
+ "target_risk_level": "Low",
888
+ "category": "Generalized Coping & Social",
889
+ "strategy_name": "Harnessing the Zeigarnik Effect",
890
+ "tags": ["ADHD", "hyper-focus", "starting projects"],
891
+ "source": "Cognitive Psychology",
892
+ "content": "If you are prone to getting overly excited about new hobbies but abandoning them, use the Zeigarnik effect (brains obsess over unfinished tasks). Always stop your work session right in the middle of a sentence, or leaving a piece half-assembled. This creates a psychological itch that makes it overwhelmingly easy to resume the next day."
893
+ },
894
+ {
895
+ "id": "gen_007",
896
+ "primary_emotion": "desire",
897
+ "target_risk_level": "Medium",
898
+ "category": "Generalized Coping & Social",
899
+ "strategy_name": "Urge Delay Protocol",
900
+ "tags": ["addiction", "shopping", "impulse control"],
901
+ "source": "Dialectical Behavior Therapy (DBT)",
902
+ "content": "When overwhelmed by a desire to consume (alcohol, online shopping, texting an ex), implement a mandatory 20-minute delay. Tell yourself: 'I am allowed to do this, but I must wait 20 minutes.' Use that time to drink water and change rooms. Often, the physiological peak of the craving passes within that window."
903
+ },
904
+ {
905
+ "id": "gen_008",
906
+ "primary_emotion": "realization",
907
+ "target_risk_level": "Low",
908
+ "category": "Generalized Coping & Social",
909
+ "strategy_name": "The 'Is It True?' Inquiry",
910
+ "tags": ["assumptions", "paranoia", "miscommunication"],
911
+ "source": "The Work (Byron Katie)",
912
+ "content": "When struck by a painful realization (e.g., 'My friends are hanging out without me, they hate me'), stop and ask four questions: 1. Is it true? 2. Can I absolutely know it's true? 3. How do I react when I believe this thought? 4. Who would I be without this thought? Dismantle the narrative before reacting to it."
913
+ },
914
+ {
915
+ "id": "gen_009",
916
+ "primary_emotion": "relief",
917
+ "target_risk_level": "Low",
918
+ "category": "Generalized Coping & Social",
919
+ "strategy_name": "Anchoring the Safety Cue",
920
+ "tags": ["post-crisis", "trauma recovery", "nervous system"],
921
+ "source": "Somatic Experiencing",
922
+ "content": "When a stressful event finally ends and you feel relief, do not just rush to the next task. Stop for 30 seconds. Put your hand on your stomach and explicitly tell your nervous system: 'The threat is gone. We are safe now.' Actively downloading the sensation of safety rewires the brain to heal from chronic stress."
923
+ },
924
+ {
925
+ "id": "gen_010",
926
+ "primary_emotion": "Normal",
927
+ "target_risk_level": "Low",
928
+ "category": "Generalized Coping & Social",
929
+ "strategy_name": "Preventative Maintenance (The HALT Method)",
930
+ "tags": ["daily routine", "stability", "self-care"],
931
+ "source": "Addiction Recovery Models",
932
+ "content": "Even on a totally normal day, prevent sudden mood crashes by checking the HALT acronym every few hours. Are you Hungry, Angry, Lonely, or Tired? Address the basic biological or social deficit immediately before it escalates into a larger psychological crisis."
933
+ },
934
+ {
935
+ "id": "gen_011",
936
+ "primary_emotion": "curiosity",
937
+ "target_risk_level": "Low",
938
+ "category": "Generalized Coping & Social",
939
+ "strategy_name": "The 'Beginner’s Mind' Approach",
940
+ "tags": ["boredom", "mindfulness", "stagnation"],
941
+ "source": "Zen Buddhism / Mindfulness",
942
+ "content": "When life feels dull or a relationship feels stagnant, approach a mundane routine as if you were an alien experiencing it for the first time. Pay extreme attention to the texture of the food, the tone of your partner's voice, or the route of your commute. Curiosity destroys apathy."
943
+ },
944
+ {
945
+ "id": "gen_012",
946
+ "primary_emotion": "amusement",
947
+ "target_risk_level": "Low",
948
+ "category": "Generalized Coping & Social",
949
+ "strategy_name": "Paradoxical Intention",
950
+ "tags": ["insomnia", "performance anxiety", "hyper-fixation"],
951
+ "source": "Logotherapy",
952
+ "content": "If you are suffering from insomnia, stop trying to force yourself to sleep. Instead, lie in the dark and try as hard as you can to stay awake. Keep your eyes open. By treating the problem with absurdity and attempting the exact opposite, you remove the anticipatory anxiety, allowing the body to relax."
953
+ },
954
+ {
955
+ "id": "gen_013",
956
+ "primary_emotion": "approval",
957
+ "target_risk_level": "Low",
958
+ "category": "Generalized Coping & Social",
959
+ "strategy_name": "Self-Validation Scripts",
960
+ "tags": ["people pleasing", "insecurity", "validation"],
961
+ "source": "Dialectical Behavior Therapy (DBT)",
962
+ "content": "When desperately seeking approval from others for a decision you made, cut out the middleman. Say to yourself out loud in a mirror: 'My decision makes sense given the information I had and the goals I hold. I approve of my own action.' Become the primary source of your own psychological endorsement."
963
+ },
964
+ {
965
+ "id": "acad_011",
966
+ "primary_emotion": "Stress",
967
+ "target_risk_level": "High",
968
+ "category": "Academic Pressure",
969
+ "strategy_name": "The 'Two-Minute Rule' for Executive Dysfunction",
970
+ "tags": ["ADHD", "starting tasks", "paralysis"],
971
+ "source": "Cognitive Behavioral Therapy (CBT)",
972
+ "content": "When totally paralyzed by an assignment, promise yourself you will only work on it for exactly 120 seconds. Set a timer. Once the timer goes off, you are fully allowed to quit with no guilt. Often, the hardest part is overcoming the friction of starting; once in motion, the brain is more likely to stay in motion."
973
+ },
974
+ {
975
+ "id": "acad_012",
976
+ "primary_emotion": "disgust",
977
+ "target_risk_level": "Medium",
978
+ "category": "Academic Pressure",
979
+ "strategy_name": "Re-framing Academic 'Busywork'",
980
+ "tags": ["cynicism", "boredom", "rebellion"],
981
+ "source": "Acceptance and Commitment Therapy (ACT)",
982
+ "content": "When feeling disgust toward rote memorization or pointless assignments, stop resisting the reality of the hoop you have to jump through. Reframe the task from 'learning' to 'compliance training.' Tell yourself: 'I am practicing my ability to execute boring tasks on command, which is a highly lucrative skill in the real world.'"
983
+ },
984
+ {
985
+ "id": "work_011",
986
+ "primary_emotion": "fear",
987
+ "target_risk_level": "Medium",
988
+ "category": "Workplace Burnout",
989
+ "strategy_name": "The 'Pre-Mortem' Analysis",
990
+ "tags": ["project launch", "anxiety", "leadership"],
991
+ "source": "Organizational Psychology",
992
+ "content": "Before starting a terrifying new project or role, assume it has already failed completely. Write down every reason why the failure happened. By facing the ultimate fear logically, you generate a precise list of preventative measures, transforming generalized terror into a concrete to-do list."
993
+ },
994
+ {
995
+ "id": "work_012",
996
+ "primary_emotion": "annoyance",
997
+ "target_risk_level": "Low",
998
+ "category": "Workplace Burnout",
999
+ "strategy_name": "Communication Batching",
1000
+ "tags": ["interruptions", "emails", "slack fatigue"],
1001
+ "source": "Productivity Psychology",
1002
+ "content": "To prevent chronic annoyance from constant notifications, turn off all alerts. Check emails and messages only at three scheduled intervals per day (e.g., 9 AM, 12 PM, 4 PM). Train your colleagues that you operate on asynchronous, deep-work schedules rather than immediate, reactive availability."
1003
+ },
1004
+ {
1005
+ "id": "work_013",
1006
+ "primary_emotion": "sadness",
1007
+ "target_risk_level": "High",
1008
+ "category": "Workplace Burnout",
1009
+ "strategy_name": "Grieving the 'Dream Job' Myth",
1010
+ "tags": ["disillusionment", "layoffs", "career crisis"],
1011
+ "source": "Grief Therapy",
1012
+ "content": "When crushed because your chosen career path is toxic or underpaying, allow yourself to formally grieve the 'dream job' you thought you were getting. Acknowledge the loss of that idealized future. Accept that work is an economic transaction, and begin sourcing your primary identity and joy from outside the capitalist structure."
1013
+ },
1014
+ {
1015
+ "id": "mh_011",
1016
+ "primary_emotion": "Personality disorder",
1017
+ "target_risk_level": "High",
1018
+ "category": "Advanced Mental Health",
1019
+ "strategy_name": "Identifying the 'Ghost Ship'",
1020
+ "tags": ["identity disturbance", "emptiness", "chameleon effect"],
1021
+ "source": "Dialectical Behavior Therapy (DBT)",
1022
+ "content": "If you suffer from identity instability and constantly mirror others, write down a 'Core Identity Anchor List'. List 5 absolute facts about your preferences that remain true regardless of who is in the room (e.g., 'I prefer tea over coffee', 'I hate loud music'). When you feel yourself dissolving into someone else's personality, review the list to find the floor."
1023
+ },
1024
+ {
1025
+ "id": "mh_012",
1026
+ "primary_emotion": "Anxiety",
1027
+ "target_risk_level": "High",
1028
+ "category": "Advanced Mental Health",
1029
+ "strategy_name": "The 'Float, Don't Fight' Technique",
1030
+ "tags": ["panic disorder", "agoraphobia", "hyperventilation"],
1031
+ "source": "The Claire Weekes Method",
1032
+ "content": "When a severe panic attack hits, struggling against it acts like adding fuel to a fire. Physically slump your shoulders, release your jaw, and imagine yourself floating on a wave. Say, 'I accept this feeling. Do your worst.' Demanding the panic to increase paradoxically short-circuits the adrenal feedback loop."
1033
+ },
1034
+
1035
+ {
1036
+ "id": "tech_001",
1037
+ "primary_emotion": "nervousness",
1038
+ "target_risk_level": "High",
1039
+ "category": "Career & Interview Anxiety",
1040
+ "strategy_name": "The 'Micro-Component' Grounding",
1041
+ "tags": ["technical interviews", "whiteboard freeze", "blanking out", "performance anxiety"],
1042
+ "source": "Cognitive Behavioral Therapy (CBT)",
1043
+ "content": "When your mind goes completely blank during a technical interview or high-stakes assessment, state the freeze aloud objectively: 'I need a moment to map the architecture.' Use that moment to draw or write the absolute smallest, simplest component of the system (like the data ingestion step). Grounding yourself in a micro-success reboots your working memory."
1044
+ },
1045
+ {
1046
+ "id": "tech_002",
1047
+ "primary_emotion": "confusion",
1048
+ "target_risk_level": "Medium",
1049
+ "category": "Project Overwhelm",
1050
+ "strategy_name": "Abstraction Layering",
1051
+ "tags": ["complex systems", "building from scratch", "cognitive overload"],
1052
+ "source": "Constructivist Learning Theory",
1053
+ "content": "When bypassing pre-built frameworks to build custom, modular systems from scratch, the cognitive load can cause paralysis. Apply 'Abstraction Layering.' Hide all sub-modules from your view and write the orchestrator pseudocode first. Define only what the inputs and outputs of each module must be before worrying about the internal mechanics."
1054
+ },
1055
+ {
1056
+ "id": "tech_003",
1057
+ "primary_emotion": "annoyance",
1058
+ "target_risk_level": "Medium",
1059
+ "category": "Workplace Burnout",
1060
+ "strategy_name": "The Hard Context Switch",
1061
+ "tags": ["debugging loops", "frustration", "hyper-fixation"],
1062
+ "source": "Gestalt Psychology",
1063
+ "content": "When stuck in a toxic, multi-hour loop of manually fixing the same pipeline or logic bug, enforce a 'Hard Context Switch.' Step away from the screen and engage in a tactile, non-digital task that requires spatial reasoning (e.g., assembling something, sketching on paper). This forces brain activity from the exhausted focused-mode to the diffuse-network, allowing background processing to find the solution."
1064
+ },
1065
+ {
1066
+ "id": "tech_004",
1067
+ "primary_emotion": "caring",
1068
+ "target_risk_level": "High",
1069
+ "category": "Caregiver Burnout",
1070
+ "strategy_name": "De-roling from High-Stakes Systems",
1071
+ "tags": ["vicarious trauma", "safeguard systems", "empathy fatigue"],
1072
+ "source": "Trauma-Informed Care",
1073
+ "content": "If you are developing tools designed to detect fraud, harm, or psychological distress, you are vulnerable to vicarious burnout. Implement 'De-roling.' At the end of your work session, explicitly state aloud, 'I am stepping out of the safeguard role. I cannot fix the world's risks right now.' Wash your hands or change your physical environment to signal the transition."
1074
+ },
1075
+ {
1076
+ "id": "imposter_001",
1077
+ "primary_emotion": "disappointment",
1078
+ "target_risk_level": "Medium",
1079
+ "category": "Imposter Syndrome",
1080
+ "strategy_name": "The 'Invisible Variables' Audit",
1081
+ "tags": ["comparing portfolios", "inadequacy", "self-doubt"],
1082
+ "source": "Cognitive Restructuring",
1083
+ "content": "When feeling inferior while looking at the polished GitHub repositories or achievements of peers, halt the comparison. Explicitly list the 'invisible variables' you cannot see: their uncommitted failed code, their background advantages, their copy-pasted templates, and their hidden burnout. Re-anchor your benchmark exclusively to your own progress from 6 months ago."
1084
+ },
1085
+ {
1086
+ "id": "dig_001",
1087
+ "primary_emotion": "Stress",
1088
+ "target_risk_level": "Medium",
1089
+ "category": "Digital Overload",
1090
+ "strategy_name": "Sensory Deprivation Micro-Breaks",
1091
+ "tags": ["screen fatigue", "notification overload", "overstimulation"],
1092
+ "source": "Dialectical Behavior Therapy (DBT)",
1093
+ "content": "When completely overwhelmed by digital noise, do not switch to a different app to 'relax.' Close your laptop, put in noise-canceling headphones (with no audio playing), and cover your eyes for 3 straight minutes. Removing all audiovisual inputs forces the central nervous system to drop its defensive hyper-vigilance."
1094
+ },
1095
+ {
1096
+ "id": "dig_002",
1097
+ "primary_emotion": "curiosity",
1098
+ "target_risk_level": "Low",
1099
+ "category": "Digital Overload",
1100
+ "strategy_name": "The 'Parking Lot' Technique",
1101
+ "tags": ["distraction", "rabbit holes", "ADHD tendencies"],
1102
+ "source": "Executive Functioning Coaching",
1103
+ "content": "When deep in focused work and suddenly struck by a desperate urge to research an unrelated topic or optimize a minor feature, do not open a new tab. Write the urge down in a physical notepad labeled 'The Parking Lot.' Tell your brain, 'We are not ignoring this, we are deferring it to 5:00 PM.' This satisfies the novelty-craving dopamine spike without derailing your workflow."
1104
+ },
1105
+ {
1106
+ "id": "dig_003",
1107
+ "primary_emotion": "disgust",
1108
+ "target_risk_level": "High",
1109
+ "category": "Digital Overload",
1110
+ "strategy_name": "Values-Based Consumption Audit",
1111
+ "tags": ["doomscrolling", "algorithmic fatigue", "cynicism"],
1112
+ "source": "Acceptance and Commitment Therapy (ACT)",
1113
+ "content": "When feeling deep disgust toward the state of the internet or your own doomscrolling habits, define two core values (e.g., 'Learning' and 'Connection'). Audit your last hour of browsing. If the content did not serve those values, log off and perform one physical action that does (e.g., read a physical book, text a friend). Reclaim agency from algorithmic manipulation."
1114
+ },
1115
+ {
1116
+ "id": "mh_013",
1117
+ "primary_emotion": "Bipolar",
1118
+ "target_risk_level": "High",
1119
+ "category": "Advanced Mental Health",
1120
+ "strategy_name": "Chronotherapeutic Lockdown",
1121
+ "tags": ["late-night productivity", "hypomania", "sleep architecture"],
1122
+ "source": "Interpersonal and Social Rhythm Therapy (IPSRT)",
1123
+ "content": "When late-night hyper-focus feels exhilarating but you recognize the signs of manic escalation, you must treat sleep as emergency medicine. At 10:00 PM, power down all devices regardless of your workflow state. Do not trust the hypomanic thought that 'I just need to finish this one module.' Lock down your circadian rhythm to prevent a neurochemical crash."
1124
+ },
1125
+ {
1126
+ "id": "mh_014",
1127
+ "primary_emotion": "Personality disorder",
1128
+ "target_risk_level": "High",
1129
+ "category": "Advanced Mental Health",
1130
+ "strategy_name": "Fact-Checking Rejection Sensitivity",
1131
+ "tags": ["code reviews", "criticism", "abandonment fears", "BPD"],
1132
+ "source": "Dialectical Behavior Therapy (DBT)",
1133
+ "content": "When receiving critical feedback (like a harsh PR review or a blunt email) triggers a catastrophic sense of worthlessness, separate the data from the narrative. Write down the exact words used in the feedback on one side of a page. On the other side, write your emotional interpretation. Force yourself to only address the literal data, actively ignoring the 'they hate me' narrative."
1134
+ },
1135
+ {
1136
+ "id": "mh_015",
1137
+ "primary_emotion": "Depression",
1138
+ "target_risk_level": "High",
1139
+ "category": "Advanced Mental Health",
1140
+ "strategy_name": "Somatic Re-engagement",
1141
+ "tags": ["lethargy", "anhedonia", "dissociation"],
1142
+ "source": "Somatic Experiencing",
1143
+ "content": "When depression makes you feel like a disembodied ghost staring at a screen, you must shock your proprioceptive system. Stand up and tightly squeeze your muscles, starting from your calves, up to your thighs, core, arms, and face. Hold for 10 seconds, then release. This forceful somatic activation pulls your consciousness back into your physical body."
1144
+ },
1145
+ {
1146
+ "id": "gen_014",
1147
+ "primary_emotion": "remorse",
1148
+ "target_risk_level": "Medium",
1149
+ "category": "Generalized Overwhelm",
1150
+ "strategy_name": "The 'Clean Slate' Forgiveness Protocol",
1151
+ "tags": ["procrastination guilt", "missed deadlines", "self-sabotage"],
1152
+ "source": "Mindful Self-Compassion",
1153
+ "content": "When paralyzed by guilt over wasting the entire day or procrastinating on crucial work, recognize that shame is an indulgence that prevents action. Speak out loud: 'I forgive myself for how I spent the last 8 hours. The past is inaccessible. The next 30 minutes are a clean slate.' Start with a 5-minute microscopic task to break the inertia."
1154
+ },
1155
+ {
1156
+ "id": "gen_015",
1157
+ "primary_emotion": "fear",
1158
+ "target_risk_level": "Medium",
1159
+ "category": "Career & Interview Anxiety",
1160
+ "strategy_name": "Decatastrophizing Rejection",
1161
+ "tags": ["job hunting", "ghosting", "fear of failure"],
1162
+ "source": "Rational Emotive Behavior Therapy (REBT)",
1163
+ "content": "If you are terrified of failing an upcoming interview, follow the fear to its logical conclusion. Ask yourself: 'If I fail, what happens next?' Keep answering until you hit the mundane reality (e.g., 'I will be sad for two days, keep practicing, and apply to a different company'). Stripping the fear of its vague, existential dread makes it manageable."
1164
+ },
1165
+ {
1166
+ "id": "gen_016",
1167
+ "primary_emotion": "optimism",
1168
+ "target_risk_level": "Medium",
1169
+ "category": "Project Overwhelm",
1170
+ "strategy_name": "Scope Creep Reality Testing",
1171
+ "tags": ["over-committing", "feature bloat", "burnout prevention"],
1172
+ "source": "Agile Methodology & ACT",
1173
+ "content": "When high motivation tricks you into expanding a project's scope beyond reason, employ reality testing. Force yourself to map out the exact hour-by-hour timeline required to build the new features. When you see the mathematical impossibility of the schedule, gracefully cut the scope by 50%. Protecting your baseline energy is more important than a flawless final product."
1174
+ },
1175
+ {
1176
+ "id": "gen_017",
1177
+ "primary_emotion": "pride",
1178
+ "target_risk_level": "Medium",
1179
+ "category": "Workplace Burnout",
1180
+ "strategy_name": "Decoupling Ego from Artifact",
1181
+ "tags": ["defensiveness", "perfectionism", "feedback"],
1182
+ "source": "Stoicism",
1183
+ "content": "When you feel fiercely defensive over a project you built, remind yourself: 'I am the architect, not the artifact.' Treat your work like an external tool you forged, not a piece of your soul. If someone finds a flaw in the tool, it does not mean the blacksmith is flawed; it just means the tool needs sharpening."
1184
+ },
1185
+ {
1186
+ "id": "gen_018",
1187
+ "primary_emotion": "embarrassment",
1188
+ "target_risk_level": "Medium",
1189
+ "category": "Career & Interview Anxiety",
1190
+ "strategy_name": "Shame Resilience via Transparency",
1191
+ "tags": ["asking for help", "hiding mistakes", "networking"],
1192
+ "source": "Shame Resilience Theory (Brown)",
1193
+ "content": "When embarrassed because you do not understand a core concept or need help, do not hide. Shame requires secrecy to survive. Reach out to a peer or mentor and explicitly name the feeling: 'I'm feeling a bit embarrassed to ask this, but I'm struggling with X.' Owning the vulnerability instantly neutralizes the power of the shame."
1194
+ },
1195
+ {
1196
+ "id": "gen_019",
1197
+ "primary_emotion": "anger",
1198
+ "target_risk_level": "Medium",
1199
+ "category": "Workplace Burnout",
1200
+ "strategy_name": "Constructive Channeling",
1201
+ "tags": ["system limitations", "red tape", "inefficiency"],
1202
+ "source": "Sublimation (Psychodynamic)",
1203
+ "content": "When infuriated by the limitations of a framework, a platform, or an organization's bureaucracy, do not vent endlessly. Sublimate the anger into a precise, targeted action. Write a highly logical proposal for an alternative, or build a proof-of-concept that bypasses the bottleneck. Turn the heat of the anger into a laser."
1204
+ },
1205
+ {
1206
+ "id": "mh_016",
1207
+ "primary_emotion": "Anxiety",
1208
+ "target_risk_level": "High",
1209
+ "category": "Advanced Mental Health",
1210
+ "strategy_name": "Environmental Friction Design",
1211
+ "tags": ["compulsions", "obsessive checking", "anxiety loops"],
1212
+ "source": "Behavioral Therapy",
1213
+ "content": "If you are caught in an anxious loop of repeatedly checking stats, emails, or messages, rely on physics, not willpower. Introduce extreme friction. Log out of all accounts, delete the apps, and leave your phone in another room. Make the physical act of acting on your anxiety so exhausting that your brain gives up on the compulsion."
1214
+ },
1215
+ {
1216
+ "id": "rel_001",
1217
+ "primary_emotion": "isolation",
1218
+ "target_risk_level": "High",
1219
+ "category": "General Overwhelm",
1220
+ "strategy_name": "Behavioral Activation for Connection",
1221
+ "tags": ["remote work", "loneliness", "withdrawing"],
1222
+ "source": "Behavioral Activation",
1223
+ "content": "When prolonged intense study or remote work makes you feel entirely disconnected from humanity, do not wait until you 'feel like' socializing. Schedule a low-stakes, 15-minute synchronous interaction (e.g., a coffee chat, a brief phone call). You must manually jumpstart your social circuitry through action, rather than waiting for motivation."
1224
+ },
1225
+ {
1226
+ "id": "rel_002",
1227
+ "primary_emotion": "love",
1228
+ "target_risk_level": "Medium",
1229
+ "category": "Caregiver Burnout",
1230
+ "strategy_name": "Scheduled Presence vs. Continuous Partial Attention",
1231
+ "tags": ["relationships", "distraction", "neglect"],
1232
+ "source": "Mindfulness-Based Stress Reduction",
1233
+ "content": "If the demands of your work are eroding your relationships, stop giving your partner 'continuous partial attention' while you think about code or problems. Dedicate 30 minutes of aggressive, device-free, undivided attention. Look at them. Listen actively. A concentrated block of true presence is far more nourishing than 5 hours of distracted physical proximity."
1234
+ },
1235
+ {
1236
+ "id": "gen_020",
1237
+ "primary_emotion": "realization",
1238
+ "target_risk_level": "Medium",
1239
+ "category": "Project Overwhelm",
1240
+ "strategy_name": "Sunk Cost Cognitive Reframing",
1241
+ "tags": ["pivoting", "abandoning work", "wasted time"],
1242
+ "source": "Behavioral Economics & CBT",
1243
+ "content": "When you realize the project architecture you spent weeks on is fundamentally flawed, combat the urge to stick with it out of stubbornness. Reframe the loss: 'The past three weeks were not wasted; they were the tuition I paid to learn exactly what does not work.' Cut your losses cleanly and pivot without self-punishment."
1244
+ },
1245
+ {
1246
+ "id": "gen_021",
1247
+ "primary_emotion": "surprise",
1248
+ "target_risk_level": "Medium",
1249
+ "category": "General Overwhelm",
1250
+ "strategy_name": "Radical Acceptance of the Pivot",
1251
+ "tags": ["sudden failures", "unexpected errors", "shock"],
1252
+ "source": "Dialectical Behavior Therapy (DBT)",
1253
+ "content": "When hit with a sudden, catastrophic failure (a server crash, a failed exam, a rejected application), stop fighting the reality of the moment. Say: 'This is the exact situation I am in right now. I do not like it, but it is happening.' Bypassing the 'why me?' phase saves critical cognitive energy needed to execute the recovery plan."
1254
+ },
1255
+ {
1256
+ "id": "mh_017",
1257
+ "primary_emotion": "neutral",
1258
+ "target_risk_level": "High",
1259
+ "category": "Advanced Mental Health",
1260
+ "strategy_name": "Tactical Sensory Seeking",
1261
+ "tags": ["anhedonia", "numbness", "flat affect"],
1262
+ "source": "Sensory Integration Therapy",
1263
+ "content": "When experiencing emotional flatlining or anhedonia, do not try to think your way into feeling joy. Bypass the mind and go to the body. Seek intense, safe sensory input: eat something extremely spicy, take a freezing cold shower, or listen to music at a high volume with deep bass. Shock the nervous system into registering an experience."
1264
+ },
1265
+ {
1266
+ "id": "gen_022",
1267
+ "primary_emotion": "approval",
1268
+ "target_risk_level": "Medium",
1269
+ "category": "Imposter Syndrome",
1270
+ "strategy_name": "Internalizing the Locus of Control",
1271
+ "tags": ["validation seeking", "people pleasing", "mentorship"],
1272
+ "source": "Cognitive Psychology",
1273
+ "content": "If you are constantly paralyzed until a mentor, boss, or senior peer validates your work, you have externalized your locus of control. Build a personalized rubric for success. Before submitting work, grade it against your own rubric. Say, 'This meets my internal standard of excellence.' Over time, rely more on your rubric than their applause."
1274
+ },
1275
+ {
1276
+ "id": "gen_023",
1277
+ "primary_emotion": "amusement",
1278
+ "target_risk_level": "Low",
1279
+ "category": "General Overwhelm",
1280
+ "strategy_name": "Auditing Dark Humor Avoidance",
1281
+ "tags": ["cynicism", "defensive mechanisms", "burnout"],
1282
+ "source": "Psychodynamic Therapy",
1283
+ "content": "Dark humor is a common coping mechanism in high-stress environments. However, notice when you are using irony to completely shield yourself from genuine pain or exhaustion. Pause and ask, 'What vulnerable truth is this joke hiding?' Allow yourself to drop the cynical armor for 5 minutes and admit that you are actually just tired and stressed."
1284
+ },
1285
+ {
1286
+ "id": "gen_024",
1287
+ "primary_emotion": "relief",
1288
+ "target_risk_level": "Low",
1289
+ "category": "Workplace Burnout",
1290
+ "strategy_name": "Parasympathetic Integration",
1291
+ "tags": ["post-deadline crash", "adrenaline withdrawal", "recovery"],
1292
+ "source": "Polyvagal Theory",
1293
+ "content": "After a massive project concludes or an interview ends, you will likely experience an adrenaline crash that feels like sudden depression or exhaustion. Do not force yourself to immediately start the next task. Give your parasympathetic nervous system 24 hours to metabolize the stress hormones. Rest is not a reward for the work; it is the final phase of the work."
1294
+ },
1295
+ {
1296
+ "id": "gen_025",
1297
+ "primary_emotion": "grief",
1298
+ "target_risk_level": "High",
1299
+ "category": "Career & Interview Anxiety",
1300
+ "strategy_name": "Mourning the Linear Path",
1301
+ "tags": ["career changes", "lost opportunities", "rejection"],
1302
+ "source": "Narrative Therapy",
1303
+ "content": "When an expected career path or major opportunity falls through, you must mourn the loss of the future you had mapped out in your head. Write out a brief narrative of the 'Ghost Ship'—the life you thought you were going to live. Acknowledge it is gone, grieve it briefly, and then purposefully turn your attention to the ship you are actually steering."
1304
+ },
1305
+ {
1306
+ "id": "mh_018",
1307
+ "primary_emotion": "Suicidal",
1308
+ "target_risk_level": "High",
1309
+ "category": "Advanced Mental Health",
1310
+ "strategy_name": "TIPP: Intense Aerobic Output",
1311
+ "tags": ["crisis intervention", "ideation", "agitation"],
1312
+ "source": "Dialectical Behavior Therapy (DBT)",
1313
+ "content": "When trapped in a state of high agitation and dangerous ideation, your body is flooded with unspent fight-or-flight energy. Engage in intense, exhausting aerobic exercise for 10-15 minutes (e.g., sprinting, burpees, rapid stair climbing) until you are physically spent. Expending the biochemical energy forcefully breaks the psychological panic loop."
1314
+ },
1315
+ {
1316
+ "id": "gen_026",
1317
+ "primary_emotion": "Stress",
1318
+ "target_risk_level": "Medium",
1319
+ "category": "Project Overwhelm",
1320
+ "strategy_name": "The 'Satisficing' Threshold",
1321
+ "tags": ["perfectionism", "hyper-parameter tuning", "endless tweaking"],
1322
+ "source": "Decision Theory",
1323
+ "content": "When you find yourself endlessly tweaking models, parameters, or designs for microscopic gains, apply 'Satisficing' (satisfy + suffice). Pre-define the exact metric of 'good enough' before you start (e.g., '85% accuracy is the stopping point'). Once you hit the threshold, you must deploy or move on. Diminishing returns destroy momentum."
1324
+ },
1325
+ {
1326
+ "id": "gen_027",
1327
+ "primary_emotion": "Anxiety",
1328
+ "target_risk_level": "Medium",
1329
+ "category": "Career & Interview Anxiety",
1330
+ "strategy_name": "The 'Reverse Interview' Shift",
1331
+ "tags": ["power dynamics", "intimidation", "self-worth"],
1332
+ "source": "Cognitive Reframing",
1333
+ "content": "If you feel small and intimidated entering a job interview, deliberately shift the power dynamic in your mind. Reframe the interaction: You are a consultant determining if this organization is worthy of your time and expertise. Prepare three probing questions about their culture or technical debt. Evaluating them naturally lowers your anxiety about being evaluated."
1334
+ }
1335
+ ]
src/audio/speech_to_text.py CHANGED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/audio/speech_to_text.py
2
+
3
+ import os
4
+ from dotenv import load_dotenv
5
+ from groq import Groq
6
+
7
+ class MindGuardAudioProcessor:
8
+ """
9
+ This class handles the Multi-Modal Audio component.
10
+ It takes an audio file (mp3, wav, m4a, etc.), sends it to Groq's
11
+ Whisper-large-v3 model, and returns the transcribed English text.
12
+ """
13
+ def __init__(self):
14
+ print("🎙️ Initializing MindGuard Audio Processing Unit...")
15
+
16
+ # --- STRICT ARCHITECTURE PATHING ---
17
+ self.script_dir = os.path.dirname(os.path.abspath(__file__))
18
+ self.project_root = os.path.abspath(os.path.join(self.script_dir, "../../"))
19
+
20
+ # 1. Load API Keys safely from the .env file
21
+ load_dotenv(os.path.join(self.project_root, ".env"))
22
+ api_key = os.environ.get("GROQ_API_KEY")
23
+ if not api_key:
24
+ raise ValueError("❌ GROQ_API_KEY not found in .env file!")
25
+
26
+ # 2. Initialize the Groq Client
27
+ self.client = Groq(api_key=api_key)
28
+ print("✅ Audio Processor is ready to listen!")
29
+
30
+ def transcribe(self, audio_file_path):
31
+ """
32
+ Reads a physical audio file and converts the speech to text.
33
+ """
34
+ print(f"\n🎧 Analyzing audio file from: {audio_file_path}...")
35
+
36
+ # 1. Safety Check: Does the file actually exist?
37
+ if not os.path.exists(audio_file_path):
38
+ raise FileNotFoundError(f"❌ Could not find audio file at: {audio_file_path}")
39
+
40
+ # 2. Open the file in 'rb' (Read Binary) mode
41
+ with open(audio_file_path, "rb") as file:
42
+ print("⚙️ Transcribing via Groq Whisper-large-v3...")
43
+
44
+ # 3. Call the Groq Audio API
45
+ transcription = self.client.audio.transcriptions.create(
46
+ file=(audio_file_path, file.read()),
47
+ model="whisper-large-v3",
48
+ prompt="The user is speaking about their mental health, emotions, or stress.", # Gives context to help the AI spell clinical words correctly
49
+ response_format="json",
50
+ language="en", # Forces the output to English
51
+ temperature=0.0 # 0.0 prevents hallucinating words that weren't spoken
52
+ )
53
+
54
+ # 4. Extract the raw text
55
+ transcribed_text = transcription.text
56
+
57
+ print("\n📝 --- Transcription Complete ---")
58
+ print(transcribed_text)
59
+ return transcribed_text
60
+
61
+ # --- EXECUTION BLOCK ---
62
+ if __name__ == "__main__":
63
+ processor = MindGuardAudioProcessor()
64
+
65
+ # --- THE FIX: Point exactly to your demo file ---
66
+ test_audio_path = os.path.join(processor.project_root, "data", "raw", "demo.mpeg")
67
+
68
+ # We use a try/except block so the script doesn't crash if you haven't recorded a file yet
69
+ try:
70
+ text_result = processor.transcribe(test_audio_path)
71
+ except FileNotFoundError as e:
72
+ print("\n⚠️ SETUP REQUIRED FOR TESTING:")
73
+ print("1. Record a short voice memo on your phone or computer (saying you feel stressed).")
74
+ print("2. Save it as 'demo.mpeg' (or .mp3 / .wav).")
75
+ print(f"3. Place it exactly here: {test_audio_path}")
76
+ print("4. Run this script again!")
src/chatbot/groq_bot.py CHANGED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from dotenv import load_dotenv
4
+ from groq import Groq
5
+
6
+ # --- BULLETPROOF IMPORT PATHING ---
7
+ # We add the main project folder to Python's system path so we can import our other scripts
8
+ script_dir = os.path.dirname(os.path.abspath(__file__))
9
+ project_root = os.path.abspath(os.path.join(script_dir, "../../"))
10
+ sys.path.append(project_root)
11
+
12
+ # Import our custom engines from Phase 1 and Phase 2!
13
+ from src.core_model.predict import MindGuardPredictor
14
+ from src.rag_engine.retriever import MindGuardRetriever
15
+ from src.audio.speech_to_text import MindGuardAudioProcessor
16
+ from src.database.db_operations import MindGuardDatabase
17
+
18
+ class MindGuardChatbot:
19
+ """
20
+ The central intelligence of MindGuard.
21
+ It orchestrates the emotion prediction, semantic search, and Groq LLM response.
22
+ """
23
+ def __init__(self):
24
+ print("🤖 Booting up MindGuard Conversational Agent...")
25
+
26
+ # 1. Load API Keys safely
27
+ load_dotenv(os.path.join(project_root, ".env"))
28
+ api_key = os.environ.get("GROQ_API_KEY")
29
+ if not api_key:
30
+ raise ValueError("❌ GROQ_API_KEY not found in .env file!")
31
+
32
+ # 2. Initialize the Groq LLM Client
33
+ self.client = Groq(api_key=api_key)
34
+
35
+ # 3. Wake up our internal tools
36
+ self.predictor = MindGuardPredictor()
37
+ self.retriever = MindGuardRetriever()
38
+ self.audio_processor = MindGuardAudioProcessor()
39
+ self.db = MindGuardDatabase()
40
+
41
+ # 4. Define the strict rules for the LLM
42
+ self.system_prompt = """
43
+ You are MindGuard, a highly empathetic, clinical-grade mental health AI.
44
+ Your goal is to de-escalate emotional distress and provide actionable coping strategies.
45
+
46
+ STRICT RULES:
47
+ 1. NEVER hallucinate medical advice. ONLY use the 'Clinical Strategy' provided in the prompt.
48
+ 2. Keep your response conversational, warm, and easy to read (use short paragraphs).
49
+ 3. Do not sound like a robot reading a textbook. Weave the clinical strategy naturally into your empathy.
50
+ 4. If the Risk Level is 'High', prioritize grounding the user immediately.
51
+ """
52
+ print("✅ MindGuard Agent is fully operational!")
53
+
54
+ def generate_response_from_audio(self, audio_file_path):
55
+ """Pipeline: Listen -> Transcribe -> Predict -> Retrieve -> Generate"""
56
+ print("\n" + "="*50)
57
+ print("🎤 RECEIVING VOICE NOTE...")
58
+
59
+ # 1. Convert the audio to text using our new module
60
+ transcribed_text = self.audio_processor.transcribe(audio_file_path)
61
+
62
+ # 2. Pass the transcribed text directly into our existing chatbot pipeline!
63
+ return self.generate_response(user_input=transcribed_text)
64
+
65
+ def generate_response(self, user_input, session_id="default_user"):
66
+ """The master pipeline: Predict -> Retrieve -> Remember -> Generate -> Save."""
67
+
68
+ print("\n" + "="*50)
69
+ print(f"👤 USER: {user_input}")
70
+
71
+ # STEP 1: The Psychologist (Core Model Prediction)
72
+ prediction = self.predictor.predict(user_input)
73
+ emotion = prediction['emotion']
74
+ risk = prediction['risk_level']
75
+ print(f"🧠 DIAGNOSIS: {emotion} (Risk: {risk})")
76
+
77
+ # STEP 2: The Librarian (RAG Retrieval)
78
+ strategy = self.retriever.get_coping_strategy(
79
+ user_query=user_input,
80
+ emotion_filter=emotion
81
+ )
82
+ print(f"🗄️ STRATEGY PULLED: {strategy[:50]}...")
83
+
84
+ # STEP 3: The Memory Bank (Pull recent history)
85
+ # We grab the last 3 messages so the bot remembers the flow of the conversation
86
+ history = self.db.get_recent_history(session_id=session_id, limit=3)
87
+ history_text = "No previous conversation."
88
+ if history:
89
+ history_text = "\n".join([f"User: {row['user_message']}\nMindGuard: {row['bot_response']}" for row in history])
90
+
91
+ # STEP 4: Prompt Engineering (Now with Memory!)
92
+ augmented_prompt = f"""
93
+ --- RECENT CONVERSATION HISTORY ---
94
+ {history_text}
95
+
96
+ --- CURRENT SITUATION ---
97
+ User's New Message: "{user_input}"
98
+ AI Core Diagnosis: {emotion}
99
+ Assessed Risk Level: {risk}
100
+
101
+ Required Clinical Strategy to Teach the User:
102
+ {strategy}
103
+
104
+ Draft your response to the user's new message now:
105
+ """
106
+
107
+ # STEP 5: The Mouth (Groq LLM Generation)
108
+ chat_completion = self.client.chat.completions.create(
109
+ messages=[
110
+ {"role": "system", "content": self.system_prompt},
111
+ {"role": "user", "content": augmented_prompt}
112
+ ],
113
+ model="llama-3.1-8b-instant",
114
+ temperature=0.3,
115
+ )
116
+
117
+ final_response = chat_completion.choices[0].message.content
118
+
119
+ print("\n🤖 MINDGUARD:")
120
+ print(final_response)
121
+ print("="*50)
122
+
123
+ # STEP 6: Save to Database
124
+ # We silently save this exact interaction so the bot remembers it next time
125
+ self.db.save_interaction(
126
+ session_id=session_id,
127
+ user_message=user_input,
128
+ bot_response=final_response,
129
+ emotion=emotion,
130
+ risk_level=risk
131
+ )
132
+
133
+ return final_response
134
+
135
+ # --- EXECUTION BLOCK ---
136
+ if __name__ == "__main__":
137
+ bot = MindGuardChatbot()
138
+
139
+ # Let's test the audio pipeline!
140
+ test_audio_path = os.path.join(project_root, "data", "raw", "demo.mpeg")
141
+
142
+ try:
143
+ bot.generate_response_from_audio(test_audio_path)
144
+ except Exception as e:
145
+ print(f"Error: {e}")
src/database/db_operations.py CHANGED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/database/db_operations.py
2
+
3
+ import os
4
+ import sqlite3
5
+
6
+ class MindGuardDatabase:
7
+ """
8
+ This class handles the Long-Term Memory of MindGuard.
9
+ It connects to a local SQLite database to save chat logs, track emotions,
10
+ and pull historical context for the LLM.
11
+ """
12
+ def __init__(self):
13
+ print("💾 Initializing MindGuard SQLite Memory Bank...")
14
+
15
+ # --- STRICT ARCHITECTURE PATHING ---
16
+ self.script_dir = os.path.dirname(os.path.abspath(__file__))
17
+ self.project_root = os.path.abspath(os.path.join(self.script_dir, "../../"))
18
+
19
+ # 1. Define the exact path for the local SQLite file
20
+ # We will save it right next to our ChromaDB in the artifacts folder
21
+ self.db_dir = os.path.join(self.project_root, "artifacts", "database")
22
+ os.makedirs(self.db_dir, exist_ok=True)
23
+ self.db_path = os.path.join(self.db_dir, "mindguard_memory.sqlite3")
24
+
25
+ try:
26
+ # 2. Establish connection to the local SQLite file
27
+ # check_same_thread=False allows Streamlit/FastAPI to talk to it later without crashing
28
+ self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
29
+
30
+ # This forces SQLite to return rows as dictionaries, exactly like PostgreSQL did!
31
+ self.conn.row_factory = sqlite3.Row
32
+ self.cursor = self.conn.cursor()
33
+ print(f"✅ Successfully connected to SQLite at: {self.db_path}")
34
+
35
+ # 3. Ensure our tables exist
36
+ self._create_tables()
37
+
38
+ except sqlite3.Error as e:
39
+ print(f"❌ DATABASE ERROR: Could not connect to SQLite.")
40
+ print(f"Details: {e}")
41
+
42
+ def _create_tables(self):
43
+ """Creates the database schema if it doesn't already exist."""
44
+ # --- THE FIX: SQLite Syntax ---
45
+ # PostgreSQL uses 'SERIAL' for auto-counting IDs. SQLite uses 'INTEGER PRIMARY KEY AUTOINCREMENT'
46
+ create_table_query = """
47
+ CREATE TABLE IF NOT EXISTS chat_history (
48
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49
+ session_id TEXT NOT NULL,
50
+ user_message TEXT NOT NULL,
51
+ bot_response TEXT NOT NULL,
52
+ diagnosed_emotion TEXT,
53
+ risk_level TEXT,
54
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
55
+ );
56
+ """
57
+ self.cursor.execute(create_table_query)
58
+ self.conn.commit() # SQLite requires us to manually commit structural changes
59
+ print("✅ Database schema validated.")
60
+
61
+ def save_interaction(self, session_id, user_message, bot_response, emotion, risk_level):
62
+ """Saves a single conversation turn into the database."""
63
+ # --- THE FIX: SQLite Parameters ---
64
+ # PostgreSQL uses %s for security. SQLite uses ?
65
+ insert_query = """
66
+ INSERT INTO chat_history (session_id, user_message, bot_response, diagnosed_emotion, risk_level)
67
+ VALUES (?, ?, ?, ?, ?);
68
+ """
69
+ self.cursor.execute(insert_query, (session_id, user_message, bot_response, emotion, risk_level))
70
+ self.conn.commit() # SQLite requires us to manually commit inserted data
71
+ print("💾 Interaction saved to Long-Term Memory.")
72
+
73
+ def get_recent_history(self, session_id, limit=3):
74
+ """
75
+ Pulls the last few messages from a specific user session.
76
+ We will feed this to the Groq LLM so it remembers what it just said.
77
+ """
78
+ select_query = """
79
+ SELECT user_message, bot_response
80
+ FROM chat_history
81
+ WHERE session_id = ?
82
+ ORDER BY timestamp DESC
83
+ LIMIT ?;
84
+ """
85
+ self.cursor.execute(select_query, (session_id, limit))
86
+ results = self.cursor.fetchall()
87
+
88
+ # SQLite returns Row objects. We convert them to standard dicts and reverse them.
89
+ return list(reversed([dict(row) for row in results]))
90
+
91
+ def close(self):
92
+ """Safely shuts down the database connection."""
93
+ self.cursor.close()
94
+ self.conn.close()
95
+ print("🔌 Database connection closed.")
96
+
97
+ # --- EXECUTION BLOCK ---
98
+ if __name__ == "__main__":
99
+ db = MindGuardDatabase()
100
+
101
+ # Simulate a user session
102
+ test_session = "user_mohit_001"
103
+
104
+ print("\n--- Testing Database Save ---")
105
+ db.save_interaction(
106
+ session_id=test_session,
107
+ user_message="I have a massive presentation tomorrow and my chest is tight.",
108
+ bot_response="I can sense the tension... start humming a low note.",
109
+ emotion="Nervousness",
110
+ risk_level="Medium"
111
+ )
112
+
113
+ print("\n--- Testing Database Retrieval ---")
114
+ history = db.get_recent_history(session_id=test_session)
115
+ for row in history:
116
+ print(f"User said: {row['user_message']}")
117
+ print(f"Bot said: {row['bot_response'][:50]}...") # Truncated for readability
118
+
119
+ db.close()
src/rag_engine/build_vector_db.py CHANGED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import chromadb
4
+ from chromadb.utils import embedding_functions
5
+
6
+ class MindGuardVectorDB:
7
+ """
8
+ This class handles the ingestion of clinical guidelines (text)
9
+ and converts them into mathematical embeddings stored in ChromaDB.
10
+ """
11
+ def __init__(self):
12
+ print("🗄️ Initializing MindGuard Vector Database Builder...")
13
+
14
+ # --- STRICT ARCHITECTURE PATHING ---
15
+ self.script_dir = os.path.dirname(os.path.abspath(__file__))
16
+ self.project_root = os.path.abspath(os.path.join(self.script_dir, "../../"))
17
+
18
+ # Paths aligned perfectly with the folder directory
19
+ self.knowledge_base_path = os.path.join(self.project_root, "data", "knowledge_base", "coping_strategies.json")
20
+ self.chroma_db_dir = os.path.join(self.project_root, "artifacts", "chroma_db")
21
+
22
+ # Ensure the Chroma DB output folder exists
23
+ os.makedirs(self.chroma_db_dir, exist_ok=True)
24
+
25
+ # --- INITIALIZE CHROMADB ---
26
+ # PersistentClient saves the database directly to your hard drive so you don't lose it when the script stops
27
+ self.chroma_client = chromadb.PersistentClient(path=self.chroma_db_dir)
28
+
29
+ # Initialize the Embedding Engine (all-MiniLM-L6-v2)
30
+ # This is a small, lightning-fast Hugging Face model that turns sentences into math vectors
31
+ self.embedding_fn = embedding_functions.DefaultEmbeddingFunction()
32
+
33
+ # Create or load the 'clinical_guidelines' collection
34
+ self.collection = self.chroma_client.get_or_create_collection(
35
+ name="clinical_guidelines",
36
+ embedding_function=self.embedding_fn
37
+ )
38
+ print(f"✅ Connected to ChromaDB at: {self.chroma_db_dir}")
39
+
40
+ def build_database(self):
41
+ """Reads the JSON file and mathematically embeds it into the database."""
42
+ print(f"📖 Reading clinical data from: {self.knowledge_base_path}...")
43
+
44
+ # 1. Read the JSON file
45
+ with open(self.knowledge_base_path, 'r', encoding='utf-8') as file:
46
+ cbt_data = json.load(file)
47
+
48
+ # 2. Prepare lists for ChromaDB insertion
49
+ documents = []
50
+ metadatas = []
51
+ ids = []
52
+
53
+ # 3. Parse the generalized data
54
+ # --- THE FIX: Removed 'enumerate' because we are using our own IDs now ---
55
+ for strategy in cbt_data:
56
+ # The actual text the LLM will read
57
+ documents.append(strategy["content"])
58
+
59
+ # --- THE FIX: Rich metadata for advanced filtering later ---
60
+ # We map to the exact keys in our new upgraded JSON schema
61
+ metadatas.append({
62
+ "emotion": strategy["primary_emotion"],
63
+ "risk_level": strategy["target_risk_level"],
64
+ "category": strategy["category"],
65
+ "strategy": strategy["strategy_name"],
66
+ # --- THE FIX: Convert the list of tags into a single comma-separated string for ChromaDB ---
67
+ "tags": ", ".join(strategy["tags"])
68
+ })
69
+
70
+ # --- THE FIX: Use our custom generalized ID from the JSON instead of generating a random one ---
71
+ ids.append(strategy["id"])
72
+
73
+ print("⚙️ Embedding text into mathematical vectors... (This may take a moment to download the model on the first run)")
74
+
75
+ # 4. Inject into the Vector Database
76
+ # Upsert means "Update or Insert" - it prevents duplicates if you run this script twice
77
+ self.collection.upsert(
78
+ documents=documents,
79
+ metadatas=metadatas,
80
+ ids=ids
81
+ )
82
+
83
+ print(f"✅ Successfully embedded {len(documents)} clinical coping strategies into ChromaDB!")
84
+ print("The RAG Knowledge Base is now primed and ready for the Retriever.")
85
+
86
+ # --- EXECUTION BLOCK ---
87
+ if __name__ == "__main__":
88
+ db_builder = MindGuardVectorDB()
89
+ db_builder.build_database()
src/rag_engine/retriever.py CHANGED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import chromadb
3
+ from chromadb.utils import embedding_functions
4
+
5
+ class MindGuardRetriever:
6
+ """
7
+ This class acts as the search engine for the RAG pipeline.
8
+ It takes a user's raw text, converts it to math, and pulls the
9
+ most clinically relevant coping strategy from our Chroma Vector Database.
10
+ """
11
+ def __init__(self):
12
+ print("🔎 Initializing MindGuard Semantic Retriever...")
13
+
14
+ # --- STRICT ARCHITECTURE PATHING ---
15
+ self.script_dir = os.path.dirname(os.path.abspath(__file__))
16
+ self.project_root = os.path.abspath(os.path.join(self.script_dir, "../../"))
17
+ self.chroma_db_dir = os.path.join(self.project_root, "artifacts", "chroma_db")
18
+
19
+ # --- CONNECT TO THE DATABASE ---
20
+ # We connect to the exact same persistent database we built in the previous step
21
+ self.chroma_client = chromadb.PersistentClient(path=self.chroma_db_dir)
22
+
23
+ # We MUST use the exact same embedding model used during database creation
24
+ # Otherwise, the search query and the database documents will be on different mathematical maps
25
+ self.embedding_fn = embedding_functions.DefaultEmbeddingFunction()
26
+
27
+ # Access the specific collection of clinical guidelines
28
+ self.collection = self.chroma_client.get_collection(
29
+ name="clinical_guidelines",
30
+ embedding_function=self.embedding_fn
31
+ )
32
+ print("✅ Connected to RAG Knowledge Base!")
33
+
34
+ def get_coping_strategy(self, user_query, emotion_filter=None, n_results=1):
35
+ """
36
+ Searches the database for the most relevant strategy.
37
+ Optionally filters by the specific emotion predicted by our XLM-RoBERTa model.
38
+ """
39
+ print(f"\n🧠 Searching Knowledge Base for: '{user_query}'")
40
+
41
+ # 1. Prepare the Search Parameters
42
+ search_kwargs = {
43
+ "query_texts": [user_query],
44
+ "n_results": n_results # How many strategies to return
45
+ }
46
+
47
+ # 2. Apply Metadata Filtering (Optional but highly recommended)
48
+ # If our AI Core already diagnosed 'Panic', we can force ChromaDB to ONLY look at Panic strategies
49
+ if emotion_filter:
50
+ print(f"🔒 Filtering RAG results strictly for: {emotion_filter}")
51
+ search_kwargs["where"] = {"emotion": emotion_filter}
52
+
53
+ # 3. Execute the Vector Search
54
+ results = self.collection.query(**search_kwargs)
55
+
56
+ # 4. Extract and return the actual text document
57
+ # ChromaDB returns a complex dictionary; we just want the raw clinical text
58
+ if results and results['documents'] and len(results['documents'][0]) > 0:
59
+ best_strategy = results['documents'][0][0]
60
+ print("✅ Found relevant clinical strategy!")
61
+ return best_strategy
62
+ else:
63
+ # Fallback in case the database is empty or the filter is too strict
64
+ print("⚠️ No specific strategy found in database.")
65
+ return "I am here to listen. Could you tell me a little more about how you are feeling?"
66
+
67
+ # --- EXECUTION BLOCK ---
68
+ if __name__ == "__main__":
69
+ # Instantiate the search engine
70
+ retriever = MindGuardRetriever()
71
+
72
+ # Simulate a user having a panic attack
73
+ test_query = "I can't breathe, my chest is so tight and the room is spinning."
74
+
75
+ # We pretend our XLM-RoBERTa model just predicted 'Panic'
76
+ diagnosed_emotion = "Panic"
77
+
78
+ # Run the retrieval!
79
+ strategy = retriever.get_coping_strategy(
80
+ user_query=test_query,
81
+ emotion_filter=diagnosed_emotion
82
+ )
83
+
84
+ print("\n--- RAG Retrieval Result ---")
85
+ print(strategy)