Spaces:
Sleeping
Sleeping
Add structured debrief sequence with gentle, embodied reflection prompts
Browse files- src/debrief_sequence.py +71 -0
- 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 |
-
|
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 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
267 |
|
268 |
-
#
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
st.
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
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 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
|
305 |
-
|
306 |
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
|
320 |
-
|
321 |
-
|
|
|
|
|
|
|
|
|
322 |
|
323 |
-
|
324 |
-
|
325 |
-
|
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 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
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 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
|
|
361 |
else:
|
362 |
message = c.messages.create(
|
363 |
model="claude-3-opus-20240229",
|
364 |
-
max_tokens=
|
365 |
-
system=
|
|
|
|
|
|
|
|
|
366 |
messages=[
|
367 |
-
{"role":
|
368 |
-
for msg in st.session_state.messages
|
369 |
]
|
370 |
)
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
|
|
|
|
378 |
)
|
379 |
-
|
380 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
419 |
-
|
420 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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("---")
|