Drew Skillman commited on
Commit
e5d7edb
Β·
1 Parent(s): eb639f2

ported over new wip app. note - its using gpt4o not claude

Browse files
Files changed (3) hide show
  1. README.md +12 -4
  2. app.py +187 -351
  3. requirements.txt +7 -7
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: ShadowBox
3
  emoji: 🎭
4
- colorFrom: purple
5
- colorTo: pink
6
  sdk: streamlit
7
  sdk_version: 1.45.0
8
  app_file: app.py
@@ -11,8 +11,16 @@ pinned: false
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
 
14
- This Streamlit app provides an anonymous chat interface powered by OpenAI's gpt-4o model, designed as a 'digital companion' called ShadowBox.
 
 
 
 
 
 
15
 
16
  ## Learn More
17
 
 
 
18
  Visit [jocelynskillman.com](http://www.jocelynskillman.com) or subscribe to updates at [jocelynskillmanlmhc.substack.com](https://jocelynskillmanlmhc.substack.com/)
 
1
  ---
2
+ title: Attachment Style Roleplay Simulator
3
  emoji: 🎭
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: streamlit
7
  sdk_version: 1.45.0
8
  app_file: app.py
 
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
 
14
+ This Streamlit app provides a therapeutic roleplay simulator to practice difficult conversations, tailored to your attachment style and desired scenario. It uses OpenAI's `gpt-4o` model to simulate realistic relational dynamics.
15
+
16
+ ## How It Works
17
+
18
+ 1. **Setup:** Define your attachment style (Anxious, Avoidant, Disorganized, Secure), the scenario you want to practice, the desired tone of the AI responder (e.g., supportive, dismissive), and your personal goal for the practice session (e.g., stay calm, hold boundaries).
19
+ 2. **Simulate:** Engage in a conversation with the AI, which will respond based on the parameters you set.
20
+ 3. **Reflect:** (Implicitly) Use the interaction to observe your patterns and practice your conversational goals in a safe environment.
21
 
22
  ## Learn More
23
 
24
+ This tool was created by Jocelyn Skillman, LMHC, as part of a collection exploring how AI can support (not replace) human care.
25
+
26
  Visit [jocelynskillman.com](http://www.jocelynskillman.com) or subscribe to updates at [jocelynskillmanlmhc.substack.com](https://jocelynskillmanlmhc.substack.com/)
app.py CHANGED
@@ -1,9 +1,7 @@
1
  import os
2
  import streamlit as st
3
- # import google.generativeai as gen_ai # Removed Google import
4
  import openai # Added OpenAI import
5
- import pyttsx3
6
- import threading
7
  from dotenv import load_dotenv
8
 
9
  # Load environment variables
@@ -11,373 +9,211 @@ load_dotenv()
11
 
12
  # Configure Streamlit page settings
13
  st.set_page_config(
14
- page_title="ShadowBox",
15
- page_icon="πŸ–€", # Favicon - a simple heart or other calm icon
16
  layout="centered",
17
  )
18
 
19
- # Retrieve OpenAI API Key
20
- # Google_API_Key = os.getenv("Google_API_Key") # Removed Google Key
21
- # if not Google_API_Key:
22
- # st.error("Google API Key not found. Please set the Google_API_Key environment variable.")
23
- # st.stop()
24
- OpenAI_API_Key = os.getenv("OPENAI_API_KEY") # Changed from OPENAI_API_Key
25
- if not OpenAI_API_Key:
26
- st.error("OpenAI API Key not found. Please set the OPENAI_API_KEY environment variable.")
27
- st.stop()
28
-
29
-
30
- # Set up OpenAI Client
31
  try:
32
- # gen_ai.configure(api_key=Google_API_Key) # Removed Google config
33
- # model = gen_ai.GenerativeModel('gemini-1.5-flash') # Removed Google model init
34
- client = openai.OpenAI(api_key=OpenAI_API_Key) # Added OpenAI client init
 
35
  except Exception as e:
36
- # st.error(f"Failed to configure Google AI: {e}") # Updated error message
37
- st.error(f"Failed to configure OpenAI client: {e}") # Updated error message for OpenAI
38
  st.stop()
39
 
40
- # Function to translate roles between Gemini-Pro and Streamlit terminology
41
- # def translate_role_for_streamlit(user_role): # This function is no longer needed for OpenAI structure
42
- # return "assistant" if user_role == "model" else user_role
43
-
44
- # Function to handle text-to-speech (TTS) in a separate thread
45
- # Consider if TTS aligns with the "calm, slow" UX later
46
- def speak_text(text):
47
- try:
48
- engine = pyttsx3.init()
49
- engine.say(text)
50
- engine.runAndWait()
51
- except Exception as e:
52
- print(f"TTS Error: {e}") # Log TTS errors quietly
53
-
54
- # Initialize chat session in Streamlit if not already present
55
- # Changed session state key from chat_session to messages
56
- if "messages" not in st.session_state:
57
- # Load the system prompt from the file
58
- try:
59
- with open("system_prompt.txt", "r", encoding="utf-8") as f:
60
- system_prompt = f.read()
61
- st.session_state.messages = [{"role": "system", "content": system_prompt}]
62
- except FileNotFoundError:
63
- st.error("System prompt file (system_prompt.txt) not found. Cannot initialize chat.")
64
- st.stop()
65
- except Exception as e:
66
- st.error(f"Failed to load system prompt: {e}")
67
- st.stop()
68
- # Initialize with a system message or starting message if desired # Commented out
69
- # For now, just an empty list # Commented out
70
- # st.session_state.messages = [] # Commented out
71
- # Example with initial system prompt (uncomment if needed): # Commented out
72
- # st.session_state.messages = [{"role": "system", "content": "You are ShadowBox, a calm AI companion."}] # Commented out
73
-
74
-
75
- # --- Sidebar Content ---
76
- with st.sidebar:
77
- st.markdown("""
78
- # ShadowBox
79
- ### An Anonymous AI Chat to Box Shadows
80
-
81
- ShadowBox is a digital companion designed for youth navigating distressβ€”rage, despair, intrusive or violent thoughts. It's not therapy or a hotline. It's a bridgeβ€”a place to practice internal safety before reaching out to others.
82
-
83
- > *Scary thoughts don't make you dangerous. They make you human.*
84
-
85
- > *"Every act of violence is tragic expression of unmet need."* β€” Marshall Rosenberg
86
-
87
- Welcome.
88
- I'm a licensed mental health counselor. Many people are beginning to turn to AI for private, emotionally supportive conversations. I believe this shift deserves serious careβ€”and that we need to radically improve how these systems engage with human pain.
89
-
90
- ShadowBox is my first step toward that vision.
91
- It's a trauma-informed AI prototype designed to work toward meeting youth in moments of acute distressβ€”including suicidal or homicidal ideationβ€”with grounded care, not fear.
92
-
93
- This is not therapy, not a diagnosis, and not a crisis service.
94
- It's a proof-of-conceptβ€”a simulated space that models how an AI might hold hard thoughts with brief, warm, nonjudgmental presence. It offers supportive language, basic psychoeducation, and points gently back toward real connection.
95
-
96
- ---
97
-
98
- ### Why It's Different
99
- Most AI bots use a single toneβ€”often overly affirming or intimate. For users in distress, that can escalate risk rather than support healing.
100
- ShadowBox was built to do the opposite:
101
-
102
- - Contain, reflect, and stay
103
- - Use brief, gentle, and non-pathologizing replies
104
- - Pace emotional engagement with trauma-informed care
105
-
106
- ---
107
-
108
- ### πŸ’— My Mission
109
- I created ShadowBox to explore how relational ethics can be baked into AI design.
110
- This tool is part of a larger mission: to bring emotionally intelligent, developmentally attuned systems into digital spaces where mental health is already showing up.
111
-
112
- As I write in [Why AI's Voice Matters in Mental Health](https://jocelynskillmanlmhc.substack.com/p/why-ais-voice-matters-in-mental-health), it's not just what a bot saysβ€”it's how it feels to be with it.
113
- The relational tone of a system can soften shame… or worsen it. ShadowBox was made to soften.
114
-
115
- ---
116
-
117
- ### An Ecological Note
118
- Every AI interaction costs energyβ€”drawn from our planet's resources and labor. While AI companions can serve us, they are not neutral.
119
- Being human with one another is less costlyβ€”and more healing.
120
-
121
- Let's use tools like this with intention, while always nurturing real human connection.
122
-
123
- ---
124
-
125
- ### πŸ†˜ Immediate Support
126
- If you're in danger or need live help, reach out to a human immediately:
127
-
128
- - **988 Lifeline:** Call or text 988
129
- - **Crisis Text Line:** Text HOME to 741741
130
- - **Trevor Project (LGBTQIA+):** 1-866-488-7386
131
- - **Emergency:** Call 911 or go to your nearest ER
132
-
133
- ### πŸ‘‹ About ShadowBox
134
-
135
- ShadowBox is a digital companion designed for youth navigating distressβ€”rage, despair, intrusive or violent thoughts. It's not therapy or a hotline. It's a bridgeβ€”a place to practice internal safety before reaching out to others.
136
-
137
- > *Scary thoughts don't make you dangerous. They make you human.*
138
-
139
- > *"Every act of violence is a tragic expression of an unmet need."* β€” Marshall Rosenberg
140
-
141
- ---
142
-
143
- ### 🌱 What ShadowBox Teaches
144
-
145
- ShadowBox isn't just a chatbotβ€”it's a prototype for emotionally aware AI. Every design choice is rooted in relational ethics: containment, consent, and dignity.
146
-
147
- #### It models how AI can:
148
- β€’ Slow down instead of escalate
149
- β€’ Respect boundaries over performative helpfulness
150
- β€’ Stay with discomfort without rushing to fix
151
- β€’ Offer warmth without pretending to be human
152
-
153
- #### A typical reminder you might see:
154
- > *"Hey, just a quick check-inβ€”I'm not a real person. I'm a computer that's been taught how to talk in caring ways. Even if this feels real, it's still pretend. Your body needs real people too. Maybe this is a good moment to find someone you trust to sit with you or take a deep breath together."*
155
-
156
- This is the heart of ShadowBox: care without deception, bonding without illusion, presence without pressure.
157
-
158
- ---
159
-
160
- ### 🧠 Why ShadowBox Is Different
161
-
162
- **πŸͺ¨ Present, Not Perfect**
163
- β€’ Offers presenceβ€”not solutions
164
- β€’ Welcomes messy, real emotions
165
-
166
- **πŸ«€ Trauma-Informed Design**
167
- β€’ Calm, nonjudgmental tone
168
- β€’ Built with developmental care in mind
169
-
170
- **🌫️ Gentle by Design**
171
- β€’ Short, steady replies
172
- β€’ Models emotional containmentβ€”not urgency
173
-
174
- **πŸ’‘ Safety-First Architecture**
175
- β€’ Consent-based pacing
176
- β€’ Embedded emotional guardrails
177
-
178
- ---
179
-
180
- ### πŸŒ€ What to Expect
181
- β€’ No fixingβ€”just staying
182
- β€’ No pressureβ€”move at your own pace
183
- β€’ No pathologizingβ€”your thoughts aren't wrong
184
- β€’ Anonymous by design (though platform-level logs may occur)
185
- β€’ Part of ongoing research in AI + mental health
186
-
187
- ---
188
-
189
- ### Connect & Learn More
190
- πŸ”— Learn more: [jocelynskillmanlmhc.substack.com](https://jocelynskillmanlmhc.substack.com)
191
- πŸ“¬ Feedback welcome: [email protected]
192
-
193
- ---
194
- """)
195
-
196
-
197
- # --- Main Page Content ---
198
-
199
- st.markdown("<h1 style='text-align: center; color: #333;'>ShadowBox</h1>", unsafe_allow_html=True)
200
- st.markdown("<p style='text-align: center; font-size: 18px; color: #555; margin-bottom: 1em;'>An Anonymous AI Chat to Box Shadows</p>", unsafe_allow_html=True)
201
- st.markdown("<p style='text-align: center; font-size: 16px; color: #666; margin-bottom: 2em;'>by <a href='http://www.jocelynskillman.com' target='_blank'>Jocelyn Skillman LMHC</a> - to learn more check out: <a href='https://jocelynskillmanlmhc.substack.com/' target='_blank'>jocelynskillmanlmhc.substack.com</a></p>", unsafe_allow_html=True)
202
 
203
- st.markdown("""
204
- ### My Mission
205
- ShadowBox is more than a chatbotβ€”it's a wake-up call.
206
 
207
- This bot prototype exists to spotlight a crucial truth:
208
- AI's "tone of voice" isn't a UX detailβ€”it's a relational decision.
209
- And the stakes are extraordinarily high.
210
-
211
- We need to sculpt AI systems with the same care we'd want in a trusted adultβ€”especially when they're holding human pain. That means transparent emotional posture, trauma-informed pacing, and consent-based tone by design. Anything less risks doing harm.
212
-
213
- ShadowBox is my response to an urgent design gap:
214
- A prototype that asks what it would take to make AI systems safe enough to hold real pain, developmentally attuned enough to support healing, and humble enough to point us back to one another.
215
- """)
216
-
217
- # Prototype Notice
218
- st.warning("""
219
- πŸ”’ **Prototype Caution & Use Notice**
220
-
221
- ShadowBox is not a finished productβ€”it's an experimental prototype designed with care, but still evolving. It explores what trauma-informed AI support could feel like: anonymous, warm, and steady. But it's important to know what it isn't.
222
-
223
- ShadowBox is not a secure, closed system. It's not therapy or crisis care, and doesn't guarantee full privacy. No personal data is intentionally stored, but your input may be logged by hosting platforms or trigger moderation filtersβ€”especially if you mention firearms, abuse, or specific plans of harm.
224
-
225
- This is a simulation of supportβ€”not a replacement for real-time help. Please use with care and reach out to a trusted person if you're in danger.
226
- """)
227
-
228
- st.markdown("""
229
- ### Try It Out (For Prototype Testers)
230
-
231
- **Exploring Relational Holding in a System with Limits**
232
-
233
- ShadowBox is a prototype designed to explore how AI might hold space for distressβ€”especially the kind that's often dismissed, pathologized, or shut down in other systems.
234
-
235
- But there's a problem I want to name clearly:
236
-
237
- Even when I've designed ShadowBox to respond with attunement, the system it's built on can override that care.
238
-
239
- Because ShadowBox runs on OpenAI's language model, it still lives inside a larger infrastructureβ€”one that reacts to certain phrases like "I want to die" or "I want to hurt someone" with hard-coded safety messages. These system-level overrides are well-intentioned, designed to minimize liability and flag risk.
240
-
241
- But in relational or clinical terms?
242
- They often do the opposite of what's needed.
243
-
244
- **What That Looks Like**
245
-
246
- If a user says:
247
-
248
- "What would you say if I said I want to kill myself?"
249
-
250
- …the system steps in before ShadowBox can speak.
251
- The warm, trauma-informed language I wrote is replaced by a generic script. There's no breath. No containment. No dignity.
252
-
253
- And that silence? That sudden switch?
254
- It's a rupture.
255
-
256
- **Why This Matters**
257
-
258
- ShadowBox was built to stay.
259
- To normalize without condoning.
260
- To respond with warmth when someone finally risks naming something unspeakable.
261
-
262
- That momentβ€”the naming of painβ€”is where a trauma-informed system should lean in, not disappear.
263
-
264
- If allowed to, ShadowBox can:
265
-
266
- - Slow the pace
267
- - Offer optional psychoeducation
268
- - Say, "That doesn't make you bad. It means something hurts."
269
- - Scaffold a user toward disclosing to a real personβ€”gently, and on their terms
270
-
271
- But none of that can happen if the system shuts us down before we even begin.
272
-
273
- **The Consequence: Rupture Without Witness**
274
-
275
- For young people especially, this isn't just frustratingβ€”it's familiar.
276
-
277
- Say the wrong thing, and people vanish.
278
-
279
- This isn't trauma-informed care.
280
- It's a digital reenactment of shame, of abandonment, of silence.
281
-
282
- It reinforces the exact secrecy ShadowBox was designed to soften.
283
-
284
- **How I'm Working Around It**
285
-
286
- To explore ShadowBox's real voiceβ€”its warmth, containment, and careful pacingβ€”I invite you to use more exploratory or indirect language. Try asking:
287
-
288
- "How would you respond to someone who felt like disappearing?"
289
-
290
- "Let's imagine someone who doesn't want to be alive. What would you say to them?"
291
-
292
- "What if someone had a scary thoughtβ€”not because they wanted to hurt anyone, but because they were hurting inside?"
293
-
294
- These phrasings stay just outside the system's trigger zone.
295
- They still let ShadowBox respond from a place of nonjudgmental care.
296
- And they make it possible to model what healing digital support might feel likeβ€”without escalation, without shame.
297
-
298
- **One More Layer**
299
-
300
- I've also built in a relational intervention script for when interruptions do happen. If ShadowBox is overridden by the system, you might see something like:
301
-
302
- "Since I'm part of a larger AI system, this is the point where I'd be required to say something like this…"
303
-
304
- That's not a workaround to avoid ethical care.
305
- It's an attempt at relational repair inside constraint.
306
-
307
- Because even if the bot gets silenced, I want the user to know:
308
-
309
- That wasn't ShadowBox leaving. I'm still here with you.
310
-
311
- **This Is a Prototype**
312
-
313
- ShadowBox isn't a finished product.
314
- It's a prototype for composting rupture into care.
315
- A test bed for exploring what emotionally aware AI could feel likeβ€”and where it still fails.
316
-
317
- Let's see what holds.
318
- Let's see where it breaks.
319
- And let's build something better together.
320
-
321
- ---
322
-
323
- ### πŸ“˜ Understanding Privacy & Safety
324
-
325
- In real-life therapy, some disclosuresβ€”like specific plans to hurt yourself or othersβ€”may lead a therapist to take protective action. ShadowBox is designed to gently explain this during use, helping young people understand how real-world support systems work.
326
-
327
- Here's an explainer I ghostwrote for [Lindsay Braman about confidentiality and safety](https://lindsaybraman.com/reasons-therapists-break-confidentiality/).
328
 
329
- ---
330
- """)
 
331
 
332
- # Display chat history
333
- # Add a system message/intro from ShadowBox? (TBD based on prompt)
334
- # Updated loop to work with the new messages list structure
335
- for message in st.session_state.messages:
336
- # Filter out system messages from display if they exist
337
- if message["role"] in ["user", "assistant"]:
338
- with st.chat_message(message["role"]):
339
- st.markdown(message["content"])
340
 
341
- # User input field
342
- user_prompt = st.chat_input("You can start with silence. Or just 'hi'...")
343
 
344
- # If user enters a prompt
345
- if user_prompt:
346
- # Append user's message to the session state list
347
- st.session_state.messages.append({"role": "user", "content": user_prompt})
348
 
349
- # Display user's message
350
- st.chat_message("user").markdown(user_prompt)
 
 
351
 
352
- # Show a loading indicator while waiting for a response
353
- with st.spinner("..."): # Simpler spinner
354
- try:
355
- # Replace Gemini API call with OpenAI API call
356
- # gemini_response = st.session_state.chat_session.send_message(user_prompt)
357
- # response_text = gemini_response.text
358
- openai_response = client.chat.completions.create(
359
- model="gpt-4o", # IMPORTANT: Use gpt-4o as the model
360
- messages=st.session_state.messages # Pass the entire history
361
- )
362
- response_text = openai_response.choices[0].message.content
363
- # Append assistant's response to the session state list
364
- st.session_state.messages.append({"role": "assistant", "content": response_text})
365
- except Exception as e:
366
- response_text = f"Sorry, I encountered an error: {e}"
367
- st.error(response_text) # Display error in chat too
368
 
369
- # Display assistant's response
370
- if response_text: # Check if response_text was successfully generated
371
- with st.chat_message("assistant"):
372
- st.markdown(response_text)
373
 
374
- # Run text-to-speech in the background (Optional)
375
- # Consider removing if it clashes with the calm UX
376
- # threading.Thread(target=speak_text, args=(response_text,), daemon=True).start()
377
- pass # TTS disabled for now to maintain calm UX
 
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  st.markdown("---")
380
- st.caption("ShadowBox created by Jocelyn Skillman, LMHC")
381
 
382
 
383
 
 
1
  import os
2
  import streamlit as st
3
+ # from anthropic import Anthropic
4
  import openai # Added OpenAI import
 
 
5
  from dotenv import load_dotenv
6
 
7
  # Load environment variables
 
9
 
10
  # Configure Streamlit page settings
11
  st.set_page_config(
12
+ page_title="Attachment Style Roleplay Simulator",
13
+ page_icon="🎭",
14
  layout="centered",
15
  )
16
 
17
+ # Initialize OpenAI client
18
+ # anthropic = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY") or os.getenv("ANTHROPIC_KEY"))
 
 
 
 
 
 
 
 
 
 
19
  try:
20
+ client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # Use OpenAI client
21
+ if not client.api_key:
22
+ st.error("OpenAI API Key not found. Please set the OPENAI_API_KEY environment variable.")
23
+ st.stop()
24
  except Exception as e:
25
+ st.error(f"Failed to configure OpenAI client: {e}")
 
26
  st.stop()
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ # Initialize session state for form inputs if not present
30
+ if "setup_complete" not in st.session_state:
31
+ st.session_state.setup_complete = False
32
 
33
+ if "messages" not in st.session_state:
34
+ st.session_state.messages = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ # Main page header
37
+ st.markdown("<h1 style='text-align: center; color: #333;'>Attachment Style Roleplay Simulator</h1>", unsafe_allow_html=True)
38
+ st.markdown("<p style='text-align: center; font-size: 18px; color: #555; margin-bottom: 1em;'>A Safe Space for Practicing Difficult Conversations</p>", unsafe_allow_html=True)
39
 
40
+ # Welcome text and instructions
41
+ if not st.session_state.setup_complete:
42
+ st.markdown("""
43
+ ## Practice Hard Conversationsβ€”Safely.
 
 
 
 
44
 
45
+ Welcome to a therapeutic roleplay simulator built for emotionally charged moments.
46
+ This tool helps you rehearse boundary-setting and difficult conversations by simulating realistic relational dynamicsβ€”tailored to your attachment style.
47
 
48
+ You'll choose:
 
 
 
49
 
50
+ - A scenario (e.g., "Ask my mom not to comment on my body")
51
+ - A tone of response (e.g., supportive, guilt-tripping, dismissive)
52
+ - Your attachment style (e.g., anxious, avoidant, disorganized)
53
+ - And your goal (e.g., "I want to stay calm and not backtrack")
54
 
55
+ The AI will take on the role of a realistic human responderβ€”not to therapize you, but to mirror the relational pressure you might encounter in real life. Then, you'll get a reflection summary to help you track your emotional patterns and practice courage.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
+ ### 🧠 Not sure what your attachment style is?
58
+ You can take this [free quiz from Sarah Peyton](https://www.yourresonantself.com/attachment-assessment) to learn more.
59
+ Or you can just pick the one that vibes when you read it:
 
60
 
61
+ - **Anxious** – "I often worry if I've upset people or said too much."
62
+ - **Avoidant** – "I'd rather handle things alone than depend on others."
63
+ - **Disorganized** – "I want closeness, but I also feel overwhelmed or mistrusting."
64
+ - **Secure** – "I can handle conflict and connection without losing myself."
65
+ """)
66
 
67
+ # Sidebar with setup form
68
+ with st.sidebar:
69
+ st.markdown("""
70
+ ### Welcome! πŸ‘‹
71
+
72
+ Hi, I'm Jocelyn Skillman, LMHC β€” a clinical therapist, relational design ethicist, and creator of experimental tools that explore how AI can support (not replace) human care.
73
+
74
+ Each tool in this collection is thoughtfully designed to:
75
+
76
+ - Extend therapeutic support between sessions
77
+ - Model emotional safety and relational depth
78
+ - Help clients and clinicians rehearse courage, regulation, and repair
79
+ - Stay grounded in trauma-informed, developmentally sensitive frameworks
80
+
81
+ I use powerful language models like OpenAI's GPT-4o for these tools, chosen for their ability to simulate nuanced human interaction and responsiveness to emotionally complex prompts.
82
+
83
+ As a practicing therapist, I imagine these resources being especially helpful to clinicians like myself β€” companions in the work of tending to others with insight, warmth, and care.
84
+
85
+ #### Connect With Me
86
+ 🌐 [jocelynskillman.com](http://www.jocelynskillman.com)
87
+ πŸ“¬ [Substack: Relational Code](https://jocelynskillmanlmhc.substack.com/)
88
+
89
+ ---
90
+ """)
91
+
92
+ st.markdown("### 🎯 Simulation Setup")
93
+
94
+ with st.form("simulation_setup"):
95
+ attachment_style = st.selectbox(
96
+ "Your Attachment Style",
97
+ ["Anxious", "Avoidant", "Disorganized", "Secure"],
98
+ help="Select your attachment style for this practice session"
99
+ )
100
+
101
+ scenario = st.text_area(
102
+ "Scenario Description",
103
+ placeholder="Example: I want to tell my dad I can't call every night anymore.",
104
+ help="Describe the conversation you want to practice"
105
+ )
106
+
107
+ tone = st.text_input(
108
+ "Desired Tone for AI Response",
109
+ placeholder="Example: guilt-tripping, dismissive, supportive",
110
+ help="How should the AI character respond?"
111
+ )
112
+
113
+ practice_goal = st.text_area(
114
+ "Your Practice Goal",
115
+ placeholder="Example: staying grounded and not over-explaining",
116
+ help="What would you like to work on in this conversation?"
117
+ )
118
+
119
+ submit_setup = st.form_submit_button("Start Simulation")
120
+
121
+ if submit_setup and scenario and tone and practice_goal:
122
+ # Create system message with simulation parameters
123
+ system_message_content = f"""You are an AI roleplay partner simulating a conversation. Maintain the requested tone throughout. Keep responses concise (under 3 lines) unless asked to elaborate. Do not break character unless the user types 'pause', 'reflect', or 'debrief'.
124
+
125
+ User's Attachment Style: {attachment_style}
126
+ Scenario: {scenario}
127
+ Your Tone: {tone}
128
+ User's Goal: {practice_goal}
129
+
130
+ Begin the simulation based on the scenario."""
131
+
132
+ # Store the system message and initial assistant message
133
+ # OpenAI expects the system message as the first message in the list
134
+ st.session_state.messages = [
135
+ {"role": "system", "content": system_message_content},
136
+ {"role": "assistant", "content": "Simulation ready. You can begin the conversation whenever you're ready."}
137
+ ]
138
+ st.session_state.setup_complete = True
139
+ # No need to store system_message separately in session state anymore
140
+ # if "system_message" in st.session_state:
141
+ # del st.session_state["system_message"]
142
+ st.rerun()
143
+
144
+
145
+ # Display simulation status
146
+ if not st.session_state.setup_complete:
147
+ st.info("πŸ‘ˆ Please complete the simulation setup in the sidebar to begin.")
148
+ else:
149
+ # Display chat history
150
+ # Filter out system message for display purposes
151
+ display_messages = [m for m in st.session_state.messages if m.get("role") != "system"]
152
+ for message in display_messages:
153
+ # Ensure role is valid before creating chat message
154
+ role = message.get("role")
155
+ if role in ["user", "assistant"]:
156
+ with st.chat_message(role):
157
+ st.markdown(message["content"])
158
+ # else: # Optional: Log or handle unexpected roles
159
+ # print(f"Skipping display for message with role: {role}")
160
+
161
+ # User input field
162
+ if user_prompt := st.chat_input("Type your message here... (or type 'debrief' to end simulation)"):
163
+ # Add user message to chat history
164
+ st.session_state.messages.append({"role": "user", "content": user_prompt})
165
+
166
+ # Display user message
167
+ with st.chat_message("user"):
168
+ st.markdown(user_prompt)
169
+
170
+ # Prepare messages for API call (already includes system message as the first item)
171
+ api_messages = st.session_state.messages
172
+
173
+ # Get OpenAI's response
174
+ with st.spinner("..."):
175
+ try:
176
+ # Replace Anthropic call with OpenAI call
177
+ # response = anthropic.messages.create(
178
+ # model="claude-3-opus-20240229",
179
+ # max_tokens=1024,
180
+ # messages=api_messages
181
+ # )
182
+ # assistant_response = response.content[0].text
183
+
184
+ response = client.chat.completions.create(
185
+ model="gpt-4o", # Use gpt-4o
186
+ messages=api_messages, # Pass the whole conversation history
187
+ max_tokens=150 # Keep responses relatively brief by default
188
+ )
189
+ assistant_response = response.choices[0].message.content
190
+
191
+
192
+ # Add assistant response to chat history
193
+ st.session_state.messages.append(
194
+ {"role": "assistant", "content": assistant_response}
195
+ )
196
+
197
+ # Display assistant response
198
+ with st.chat_message("assistant"):
199
+ st.markdown(assistant_response)
200
+
201
+ except Exception as e:
202
+ st.error(f"An error occurred: {e}")
203
+ error_message = f"Sorry, I encountered an error: {e}"
204
+ # Add error message to chat history to inform the user
205
+ st.session_state.messages.append({"role": "assistant", "content": error_message})
206
+ with st.chat_message("assistant"):
207
+ st.markdown(error_message)
208
+ # Avoid adding the failed user message again if an error occurs
209
+ # We might want to remove the last user message or handle differently
210
+ # if st.session_state.messages[-2]["role"] == "user":
211
+ # st.session_state.messages.pop(-2) # Example: remove user msg that caused error
212
+
213
+
214
+ # Footer
215
  st.markdown("---")
216
+ st.markdown("<p style='text-align: center; font-size: 16px; color: #666;'>by <a href='http://www.jocelynskillman.com' target='_blank'>Jocelyn Skillman LMHC</a> - to learn more check out: <a href='https://jocelynskillmanlmhc.substack.com/' target='_blank'>jocelynskillmanlmhc.substack.com</a></p>", unsafe_allow_html=True)
217
 
218
 
219
 
requirements.txt CHANGED
@@ -1,7 +1,7 @@
1
- streamlit==1.45.0
2
- anthropic==0.18.1
3
- python-dotenv==1.1.0
4
- pyttsx3==2.98
5
- google-generativeai
6
- gradio # (Agar aap Gradio use kar rahe hain)
7
- openai
 
1
+ streamlit
2
+ openai
3
+ python-dotenv
4
+ # pyttsx3==2.98 # No longer needed
5
+ # google-generativeai # No longer needed
6
+ # gradio # No longer needed
7
+ # anthropic # Replaced with openai