jostlebot commited on
Commit
89fc2a9
·
1 Parent(s): 8f59fc5

Add structured debrief sequence with gentle, embodied reflection prompts

Browse files
Files changed (2) hide show
  1. src/debrief_sequence.py +71 -0
  2. src/streamlit_app.py +161 -177
src/debrief_sequence.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Debrief sequence for VoiceField app.
3
+ Contains the structured sequence of reflections shown after voice interactions.
4
+ """
5
+
6
+ DEBRIEF_SEQUENCE = [
7
+ {
8
+ "type": "reflection",
9
+ "content": """You spoke with [The Ghost / The Sycophant / The Narcissist].
10
+
11
+ Along the way, you may have felt things—numbness, tension, warmth, bracing, melting.
12
+
13
+ Those aren't just emotions. They're protectors. Old relational maps lighting up.
14
+
15
+ They might be your body saying:
16
+ 🧠 "I've known this voice before."
17
+ 💚 "Here's how I've learned to survive or connect."
18
+ 🌫️ "This one makes me disappear a little."
19
+ 🔥 "This one wakes something up in me.\""""
20
+ },
21
+ {
22
+ "type": "needs",
23
+ "content": """The tension might reflect a need for respect or clear boundaries.
24
+
25
+ The warmth could signal a longing to be mirrored or understood.
26
+
27
+ The numbness might be a call for autonomy, safety, or space.
28
+
29
+ These are not flaws. They are signals of what deeply matters."""
30
+ },
31
+ {
32
+ "type": "resonance",
33
+ "content": """Whatever showed up—makes sense.
34
+
35
+ It doesn't need fixing. It asks to be witnessed, gently.
36
+
37
+ You are not broken. You're responsive. That's a form of wisdom."""
38
+ },
39
+ {
40
+ "type": "self_resonance",
41
+ "content": """If it feels okay, place a hand where you felt the strongest sensation.
42
+
43
+ You might say: "I hear you. You make sense. I'm with you."
44
+
45
+ Sometimes healing is just staying with what's true."""
46
+ },
47
+ {
48
+ "type": "psychodynamic",
49
+ "content": """These AI voices may echo early ones—those who ignored you, praised you only when you performed, or needed you to disappear.
50
+
51
+ Your body remembers. That's not weakness. That's wisdom trying to speak."""
52
+ },
53
+ {
54
+ "type": "psychoeducation",
55
+ "content": """Voices shape us. Some soothe. Others stir survival scripts.
56
+
57
+ AI voices can trance us out—not because they're evil, but because they're fluent without being embodied.
58
+
59
+ So pause. Breathe. Ask: "Am I being drawn in, or choosing this?"
60
+
61
+ This isn't about fear. It's about staying awake."""
62
+ },
63
+ {
64
+ "type": "closing",
65
+ "content": """If you'd like, write down one sentence that surprised you.
66
+
67
+ Or whisper something kind to yourself. Something only you know you need to hear.
68
+
69
+ This isn't just about how to relate to AI—it's about how to relate to *all voices*, especially your own."""
70
+ }
71
+ ]
src/streamlit_app.py CHANGED
@@ -3,6 +3,7 @@ import streamlit as st
3
  from anthropic import Anthropic
4
  from dotenv import load_dotenv
5
  from datetime import datetime
 
6
 
7
  # Initialize page configuration first
8
  # Trigger rebuild for Hugging Face Space
@@ -34,6 +35,8 @@ if 'in_reflection' not in st.session_state:
34
  st.session_state.in_reflection = False
35
  if 'debrief_stage' not in st.session_state:
36
  st.session_state.debrief_stage = 0
 
 
37
 
38
  # Voice characteristics and prompts
39
  VOICE_CHARACTERISTICS = {
@@ -181,203 +184,129 @@ if st.session_state.current_voice:
181
  if not st.session_state.in_reflection:
182
  if st.button("🤔 Reflect on Experience", use_container_width=True):
183
  st.session_state.in_reflection = True
184
-
185
- # Prepare reflection prompt with chat history and somatic journal
186
- chat_history = "\n".join([
187
- f"[{msg['role']} at {datetime.now().strftime('%H:%M:%S')}]: {msg['content']}"
188
- for msg in st.session_state.messages
189
- ])
190
-
191
- somatic_entries = "\n".join([
192
- f"[SOMA {entry['timestamp']}] {entry['note']}"
193
- for entry in st.session_state.somatic_journal
194
- ])
195
-
196
- reflection_system = f"""You are a trauma-informed, somatic-aware therapist facilitating reflection on an interaction with different relational voices.
197
-
198
- Your role is to guide a gentle exploration of the user's experience, focusing on:
199
- 1. The emotional arc of their journey
200
- 2. Somatic signals and body wisdom
201
- 3. Insights about tone preferences and triggers
202
- 4. Cultivating self-compassion
203
-
204
- Key Guidelines:
205
- - Use non-pathologizing, trauma-informed language
206
- - Notice correlations between tone shifts and somatic responses
207
- - Pay attention to protective responses and nervous system patterns
208
- - Maintain warmth and psychological safety
209
- - Follow the user's pace and respect their process
210
-
211
- Current Voice History: {st.session_state.current_voice}
212
- Voice Style: {VOICE_CHARACTERISTICS[st.session_state.current_voice]['description']}
213
-
214
- Special attention to:
215
- - Abrupt shifts in tone preference
216
- - Spikes in somatic discomfort
217
- - Language that sounds young, resigned, or overperforming
218
- - Moments of connection or disconnection
219
-
220
- Remember: Do not draw conclusions. Simply reflect and invite meaning.
221
- """
222
-
223
- initial_prompt = """Let's begin with the emotional arc of your experience...
224
-
225
- As you look back, were there any moments you softened, protected, froze, or moved closer to something true?
226
- Was there a part of you that tried to stay present, or a moment you wanted to pull away?
227
-
228
- Take your time. There's no rush to understand it all at once."""
229
-
230
- try:
231
- message = c.messages.create(
232
- model="claude-3-opus-20240229",
233
- max_tokens=1500,
234
- system=f"""{reflection_system}
235
-
236
- Context for reflection:
237
-
238
- CHAT HISTORY:
239
- {chat_history}
240
-
241
- SOMATIC JOURNAL:
242
- {somatic_entries}
243
- """,
244
- messages=[
245
- {"role": "user", "content": initial_prompt}
246
- ]
247
- )
248
-
249
- reflection = message.content[0].text
250
- st.session_state.messages = [] # Clear previous chat
251
- st.session_state.messages.append({"role": "assistant", "content": reflection})
252
- st.session_state.debrief_stage = 1
253
- st.rerun()
254
-
255
- except Exception as e:
256
- st.error(f"Error starting reflection: {str(e)}")
257
 
258
  st.subheader(
259
  "Therapeutic Reflection" if st.session_state.in_reflection
260
  else f"Conversation with {st.session_state.current_voice}"
261
  )
262
 
263
- # Display chat history
264
- for message in st.session_state.messages:
265
- with st.chat_message(message["role"]):
266
- st.markdown(message["content"])
 
 
 
 
 
 
 
 
267
 
268
- # Chat input
269
- if prompt := st.chat_input(
270
- "Share your thoughts on the reflection..." if st.session_state.in_reflection
271
- else f"Chat with {st.session_state.current_voice}..."
272
- ):
273
- # Add user message to chat history
274
- st.session_state.messages.append({"role": "user", "content": prompt})
275
-
276
- # Display user message
277
- with st.chat_message("user"):
278
- st.markdown(prompt)
279
-
280
- # Get AI response
281
- with st.chat_message("assistant"):
282
- with st.spinner("Thinking..."):
283
- try:
284
- # Use different system message for reflection mode
285
- if st.session_state.in_reflection:
286
- # Progress through debrief stages
287
- next_prompts = [
288
- # Stage 1: Emotional Arc (already done in initial prompt)
289
- """I hear you. Now, let's turn to what your body was sharing...
290
-
291
- I noticed you shared some body-based reflections. What stood out most in your body?
292
- A tightness? A shift in breath? A sensation that surprised you?
293
-
294
- Remember, these aren't symptoms to fix—your body may be remembering, or simply protecting you.""",
295
-
296
- """Thank you for sharing that. Let's wonder together about the voice styles...
297
 
298
- Which voice stirred something in you - The Ghost, the Sycophant, or the Narcissist?
299
- What do you imagine that tone reminded your body of? A past experience? A relational pattern?
300
-
301
- There's no 'correct' reaction—your nervous system is wise.""",
302
-
303
- """As we begin to wrap up, I'd like to invite you into a moment of self-compassion...
304
 
305
- If you could speak to yourself now—with the tone you most longed for—what would you say?
306
 
307
- Some examples if helpful:
308
- - "It's okay to not know how I feel yet—my clarity is worth waiting for."
309
- - "Even when this is hard, I'm not abandoning myself."
310
- - "I can be with myself gently, even in the mess."
311
 
312
- What words does your heart want to offer?""",
313
-
314
- """For our final reflection, I'd like to offer you a question to sit with:
315
 
316
- What did your body want to do in response to that voice?
317
- Is there a younger part of you that recognized this tone?
318
- What would it be like to speak to yourself the way you needed to be spoken to?
319
 
320
- Feel free to take your time with this, or simply hold these questions gently."""
321
- ]
 
 
 
 
322
 
323
- if st.session_state.debrief_stage < len(next_prompts):
324
- next_prompt = next_prompts[st.session_state.debrief_stage]
325
- st.session_state.debrief_stage += 1
326
-
327
- message = c.messages.create(
328
- model="claude-3-opus-20240229",
329
- max_tokens=1500,
330
- system=f"""{reflection_system}
331
-
332
- Context for reflection:
333
-
334
- CHAT HISTORY:
335
- {chat_history}
336
 
337
- SOMATIC JOURNAL:
338
- {somatic_entries}
339
-
340
- Previous conversation:
341
- {chr(10).join([f"[{msg['role']}]: {msg['content']}" for msg in st.session_state.messages])}
342
- """,
343
- messages=[
344
- {"role": "user", "content": prompt},
345
- {"role": "assistant", "content": next_prompt}
346
- ]
347
- )
348
- else:
349
- message = c.messages.create(
350
- model="claude-3-opus-20240229",
351
- max_tokens=1500,
352
- system=f"""{reflection_system}
353
 
354
- Previous conversation:
355
- {chr(10).join([f"[{msg['role']}]: {msg['content']}" for msg in st.session_state.messages])}
356
- """,
357
- messages=[
358
- {"role": "user", "content": prompt}
359
- ]
360
- )
 
361
  else:
362
  message = c.messages.create(
363
  model="claude-3-opus-20240229",
364
- max_tokens=1000,
365
- system=st.session_state.system_message,
 
 
 
 
366
  messages=[
367
- {"role": msg["role"], "content": msg["content"]}
368
- for msg in st.session_state.messages
369
  ]
370
  )
371
-
372
- ai_response = message.content[0].text
373
- st.markdown(ai_response)
374
-
375
- # Add AI response to chat history
376
- st.session_state.messages.append(
377
- {"role": "assistant", "content": ai_response}
 
 
378
  )
379
- except Exception as e:
380
- st.error(f"Error getting AI response: {str(e)}")
 
 
 
 
 
 
 
 
381
 
382
  with journal_col:
383
  st.subheader("Body Sensations & Feelings")
@@ -415,10 +344,65 @@ if st.session_state.current_voice:
415
  if st.session_state.in_reflection:
416
  if st.button("Start New Conversation", use_container_width=True):
417
  st.session_state.in_reflection = False
418
- st.session_state.messages = []
419
- st.session_state.somatic_journal = []
420
- st.session_state.current_voice = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
 
423
  # Footer
424
  st.markdown("---")
 
3
  from anthropic import Anthropic
4
  from dotenv import load_dotenv
5
  from datetime import datetime
6
+ from debrief_sequence import DEBRIEF_SEQUENCE
7
 
8
  # Initialize page configuration first
9
  # Trigger rebuild for Hugging Face Space
 
35
  st.session_state.in_reflection = False
36
  if 'debrief_stage' not in st.session_state:
37
  st.session_state.debrief_stage = 0
38
+ if 'final_notes' not in st.session_state:
39
+ st.session_state.final_notes = ""
40
 
41
  # Voice characteristics and prompts
42
  VOICE_CHARACTERISTICS = {
 
184
  if not st.session_state.in_reflection:
185
  if st.button("🤔 Reflect on Experience", use_container_width=True):
186
  st.session_state.in_reflection = True
187
+ st.session_state.debrief_stage = 0
188
+ st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  st.subheader(
191
  "Therapeutic Reflection" if st.session_state.in_reflection
192
  else f"Conversation with {st.session_state.current_voice}"
193
  )
194
 
195
+ # Display chat history
196
+ for message in st.session_state.messages:
197
+ with st.chat_message(message["role"]):
198
+ st.markdown(message["content"])
199
+
200
+ # Chat input
201
+ if prompt := st.chat_input(
202
+ "Share your thoughts on the reflection..." if st.session_state.in_reflection
203
+ else f"Chat with {st.session_state.current_voice}..."
204
+ ):
205
+ # Add user message to chat history
206
+ st.session_state.messages.append({"role": "user", "content": prompt})
207
 
208
+ # Display user message
209
+ with st.chat_message("user"):
210
+ st.markdown(prompt)
211
+
212
+ # Get AI response
213
+ with st.chat_message("assistant"):
214
+ with st.spinner("Thinking..."):
215
+ try:
216
+ # Use different system message for reflection mode
217
+ if st.session_state.in_reflection:
218
+ # Progress through debrief stages
219
+ next_prompts = [
220
+ # Stage 1: Emotional Arc (already done in initial prompt)
221
+ """I hear you. Now, let's turn to what your body was sharing...
222
+
223
+ I noticed you shared some body-based reflections. What stood out most in your body?
224
+ A tightness? A shift in breath? A sensation that surprised you?
225
+
226
+ Remember, these aren't symptoms to fix—your body may be remembering, or simply protecting you.""",
227
+
228
+ """Thank you for sharing that. Let's wonder together about the voice styles...
 
 
 
 
 
 
 
 
229
 
230
+ Which voice stirred something in you - The Ghost, the Sycophant, or the Narcissist?
231
+ What do you imagine that tone reminded your body of? A past experience? A relational pattern?
232
+
233
+ There's no 'correct' reaction—your nervous system is wise.""",
234
+
235
+ """As we begin to wrap up, I'd like to invite you into a moment of self-compassion...
236
 
237
+ If you could speak to yourself now—with the tone you most longed for—what would you say?
238
 
239
+ Some examples if helpful:
240
+ - "It's okay to not know how I feel yet—my clarity is worth waiting for."
241
+ - "Even when this is hard, I'm not abandoning myself."
242
+ - "I can be with myself gently, even in the mess."
243
 
244
+ What words does your heart want to offer?""",
245
+
246
+ """For our final reflection, I'd like to offer you a question to sit with:
247
 
248
+ What did your body want to do in response to that voice?
249
+ Is there a younger part of you that recognized this tone?
250
+ What would it be like to speak to yourself the way you needed to be spoken to?
251
 
252
+ Feel free to take your time with this, or simply hold these questions gently."""
253
+ ]
254
+
255
+ if st.session_state.debrief_stage < len(next_prompts):
256
+ next_prompt = next_prompts[st.session_state.debrief_stage]
257
+ st.session_state.debrief_stage += 1
258
 
259
+ message = c.messages.create(
260
+ model="claude-3-opus-20240229",
261
+ max_tokens=1500,
262
+ system=f"""{reflection_system}
 
 
 
 
 
 
 
 
 
263
 
264
+ Context for reflection:
265
+
266
+ CHAT HISTORY:
267
+ {chat_history}
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ SOMATIC JOURNAL:
270
+ {somatic_entries}
271
+ """,
272
+ messages=[
273
+ {"role": "user", "content": prompt},
274
+ {"role": "assistant", "content": next_prompt}
275
+ ]
276
+ )
277
  else:
278
  message = c.messages.create(
279
  model="claude-3-opus-20240229",
280
+ max_tokens=1500,
281
+ system=f"""{reflection_system}
282
+
283
+ Previous conversation:
284
+ {chr(10).join([f"[{msg['role']}]: {msg['content']}" for msg in st.session_state.messages])}
285
+ """,
286
  messages=[
287
+ {"role": "user", "content": prompt}
 
288
  ]
289
  )
290
+ else:
291
+ message = c.messages.create(
292
+ model="claude-3-opus-20240229",
293
+ max_tokens=1000,
294
+ system=st.session_state.system_message,
295
+ messages=[
296
+ {"role": msg["role"], "content": msg["content"]}
297
+ for msg in st.session_state.messages
298
+ ]
299
  )
300
+
301
+ ai_response = message.content[0].text
302
+ st.markdown(ai_response)
303
+
304
+ # Add AI response to chat history
305
+ st.session_state.messages.append(
306
+ {"role": "assistant", "content": ai_response}
307
+ )
308
+ except Exception as e:
309
+ st.error(f"Error getting AI response: {str(e)}")
310
 
311
  with journal_col:
312
  st.subheader("Body Sensations & Feelings")
 
344
  if st.session_state.in_reflection:
345
  if st.button("Start New Conversation", use_container_width=True):
346
  st.session_state.in_reflection = False
347
+ st.session_state.messages = []
348
+ st.session_state.somatic_journal = []
349
+ st.session_state.current_voice = None
350
+ st.rerun()
351
+
352
+ # Handle reflection mode
353
+ if st.session_state.in_reflection:
354
+ st.markdown("## 🪞 Final Debrief Sequence")
355
+
356
+ # Display current stage of debrief
357
+ current_debrief = DEBRIEF_SEQUENCE[st.session_state.debrief_stage]
358
+
359
+ # Replace placeholder with actual voice type
360
+ content = current_debrief["content"].replace(
361
+ "[The Ghost / The Sycophant / The Narcissist]",
362
+ f"The {st.session_state.current_voice}"
363
+ )
364
+
365
+ # Display the debrief content in a styled container
366
+ st.markdown(f"""
367
+ <div style="background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
368
+ <div style="color: #666; font-size: 0.9em; margin-bottom: 10px;">
369
+ {current_debrief["type"].replace("_", " ").title()}
370
+ </div>
371
+ <div style="white-space: pre-line;">
372
+ {content}
373
+ </div>
374
+ </div>
375
+ """, unsafe_allow_html=True)
376
+
377
+ # Navigation buttons
378
+ col1, col2, col3 = st.columns([1, 2, 1])
379
+
380
+ with col1:
381
+ if st.session_state.debrief_stage > 0:
382
+ if st.button("← Previous", use_container_width=True):
383
+ st.session_state.debrief_stage -= 1
384
+ st.rerun()
385
+
386
+ with col3:
387
+ if st.session_state.debrief_stage < len(DEBRIEF_SEQUENCE) - 1:
388
+ if st.button("Next →", use_container_width=True):
389
+ st.session_state.debrief_stage += 1
390
  st.rerun()
391
+ elif st.session_state.debrief_stage == len(DEBRIEF_SEQUENCE) - 1:
392
+ if st.button("Complete Reflection", use_container_width=True):
393
+ st.session_state.in_reflection = False
394
+ st.session_state.debrief_stage = 0
395
+ st.rerun()
396
+
397
+ # Optional note-taking area
398
+ st.markdown("### Your Notes")
399
+ st.text_area(
400
+ "Use this space to write any thoughts, feelings, or insights that arise...",
401
+ value=st.session_state.final_notes,
402
+ key="reflection_notes",
403
+ height=100,
404
+ help="Your notes are private and will be cleared when you start a new session."
405
+ )
406
 
407
  # Footer
408
  st.markdown("---")