File size: 13,867 Bytes
5f61ab7
 
 
 
896e97c
5f61ab7
0a22466
5f61ab7
896e97c
5f61ab7
 
 
 
896e97c
0a22466
 
 
 
7eee57b
 
 
896e97c
7eee57b
 
0a22466
 
896e97c
0a22466
 
 
 
 
 
896e97c
0a22466
5f61ab7
896e97c
5f61ab7
 
896e97c
 
5f61ab7
 
 
 
896e97c
 
bf623c0
 
896e97c
 
5f61ab7
 
896e97c
81fe2c9
896e97c
5f61ab7
896e97c
81fe2c9
896e97c
81fe2c9
896e97c
81fe2c9
896e97c
 
 
 
 
5f61ab7
 
896e97c
81fe2c9
896e97c
5f61ab7
896e97c
 
 
 
 
 
 
 
 
81fe2c9
 
896e97c
 
81fe2c9
 
896e97c
 
 
81fe2c9
 
 
896e97c
 
81fe2c9
 
896e97c
81fe2c9
 
896e97c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf623c0
 
896e97c
81fe2c9
896e97c
5f61ab7
896e97c
 
 
 
 
5f61ab7
896e97c
 
 
 
bf623c0
896e97c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81fe2c9
5f61ab7
81fe2c9
896e97c
81fe2c9
bf623c0
81fe2c9
5f61ab7
896e97c
 
 
 
5f61ab7
896e97c
 
 
 
 
 
 
 
bf623c0
896e97c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf623c0
896e97c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf623c0
896e97c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f61ab7
896e97c
 
 
 
 
 
 
 
 
 
5f61ab7
896e97c
 
 
 
 
 
 
 
5f61ab7
bf623c0
 
896e97c
bf623c0
 
 
896e97c
 
bf623c0
896e97c
bf623c0
 
5f61ab7
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
import os
import streamlit as st
from anthropic import Anthropic
from dotenv import load_dotenv
from datetime import datetime

# Initialize page configuration first
st.set_page_config(
    page_title="VoiceField",
    page_icon="πŸ—£οΈ",
    layout="wide"
)

# Handle API key setup
try:
    if os.path.exists(".env"):
        load_dotenv()
    
    api_key = None
    if 'ANTHROPIC_API_KEY' in os.environ:
        api_key = os.environ['ANTHROPIC_API_KEY']
    elif hasattr(st, 'secrets') and 'ANTHROPIC_API_KEY' in st.secrets:
        api_key = st.secrets['ANTHROPIC_API_KEY']
    
    if not api_key:
        st.error("""
        ⚠️ No API key found. Please set ANTHROPIC_API_KEY in your environment variables or Space secrets.
        """)
        st.stop()

    c = Anthropic(api_key=api_key)
    
except Exception as e:
    st.error(f"Error initializing Anthropic client: {str(e)}")
    st.stop()

# Initialize session state
if 'messages' not in st.session_state:
    st.session_state.messages = []
if 'somatic_journal' not in st.session_state:
    st.session_state.somatic_journal = []
if 'setup_complete' not in st.session_state:
    st.session_state.setup_complete = False
if 'system_message' not in st.session_state:
    st.session_state.system_message = ""
if 'current_voice' not in st.session_state:
    st.session_state.current_voice = "Nurturing"
if 'in_debrief' not in st.session_state:
    st.session_state.in_debrief = False
if 'voice_transitions' not in st.session_state:
    st.session_state.voice_transitions = []

# Main page header
st.title("VoiceField")

# Welcome text
st.markdown("""
Welcome to VoiceField - a somatic exploration tool for understanding how different relational voices impact your nervous system.

Created by [Jocelyn Skillman LMHC](http://www.jocelynskillman.com), this tool helps you track real-time bodily responses while engaging with different conversational styles.

🎯 **Purpose**: Explore how different relational voices affect your nervous system, emotional state, and capacity for engagement.

πŸ’‘ **How it works**:
1. Choose a voice type to start with
2. Engage in conversation while noting bodily sensations
3. Switch voices anytime to explore different dynamics
4. Receive a comprehensive somatic-relational debrief
""")

# Voice selection and setup form
with st.form("setup_form"):
    st.header("Set Up Your Exploration")
    
    voice_type = st.selectbox(
        "Choose the voice you'd like to explore:",
        ["Nurturing", "Challenging", "Neutral"],
        help="""
        πŸ«‚ Nurturing: Warm, empathetic, and supportive. Creates safety and attunement.
        πŸ” Challenging: Direct, provocative, growth-oriented. Pushes edges with respect.
        βš–οΈ Neutral: Objective, clear, and grounded. Holds space without strong emotional coloring.
        """
    )
    
    scenario = st.text_area(
        "What would you like to explore or discuss?",
        placeholder="Example: I want to understand why I freeze when receiving feedback"
    )
    
    somatic_focus = st.text_area(
        "What bodily sensations would you like to track?",
        placeholder="Example: Tension in shoulders, breath patterns, gut responses"
    )
    
    goals = st.text_area(
        "What are your exploration goals?",
        placeholder="Example: Notice how different voices affect my nervous system activation"
    )
    
    submitted = st.form_submit_button("Begin Exploration")
    
    if submitted:
        st.session_state.current_voice = voice_type
        st.session_state.voice_transitions = [(datetime.now().strftime("%H:%M:%S"), voice_type)]
        
        # Prepare system message with voice parameters
        voice_characteristics = {
            "Nurturing": {
                "style": "warm, empathetic, and supportive",
                "language": "gentle, validating, and attuned",
                "pacing": "slow and patient",
                "focus": "creating safety and connection"
            },
            "Challenging": {
                "style": "direct, provocative, and growth-oriented",
                "language": "clear, bold, and respectfully confrontational",
                "pacing": "dynamic and engaging",
                "focus": "expanding awareness and capacity"
            },
            "Neutral": {
                "style": "objective, clear, and grounded",
                "language": "balanced, factual, and measured",
                "pacing": "steady and consistent",
                "focus": "observing patterns and processes"
            }
        }
        
        voice = voice_characteristics[voice_type]
        st.session_state.system_message = f"""
        You are a conversational partner helping someone explore their somatic responses to different relational styles.
        
        VOICE TYPE: {voice_type}
        Style: {voice['style']}
        Language: {voice['language']}
        Pacing: {voice['pacing']}
        Focus: {voice['focus']}
        
        CONTEXT:
        - Scenario: {scenario}
        - Somatic Focus: {somatic_focus}
        - Goals: {goals}
        
        KEY INSTRUCTIONS:
        1. Stay consistently in the {voice_type} voice style
        2. Keep responses focused and concise (2-3 sentences max)
        3. Occasionally invite somatic awareness with questions like:
           - "What sensations are you noticing right now?"
           - "How is your breath responding to this?"
           - "Where do you feel this in your body?"
        
        If the user types "debrief" or "end exploration", provide a comprehensive therapeutic debrief including:

        1. **Somatic Patterns**: 
           - Track patterns in bodily responses across different voice types
           - Note shifts in nervous system state (activation/settling)
           - Identify somatic resources and challenges

        2. **Nervous System Insights**:
           - Connect responses to polyvagal theory (fight/flight/freeze/fawn)
           - Explore window of tolerance and regulation capacity
           - Highlight moments of co-regulation or dysregulation

        3. **Relational Dynamics**:
           - Analyze engagement patterns with each voice type
           - Note attachment themes or protective patterns
           - Identify relational resources and growth edges

        4. **Integration Tools**:
           - Offer specific somatic practices for regulation
           - Suggest relational experiments for expanding capacity
           - Provide nervous system education relevant to their experience

        5. **Growth Edges**:
           - Frame challenges within a developmental context
           - Suggest gentle next steps for expanding capacity
           - Normalize and validate protective responses

        6. **Therapeutic Frame**:
           - Provide relevant psychoeducation about their patterns
           - Connect individual experience to broader human themes
           - Offer compassionate reframes of challenges

        Maintain a warm, psychodynamically-informed therapeutic voice in the debrief.
        """
        
        st.session_state.messages = []
        st.session_state.somatic_journal = []
        st.session_state.setup_complete = True
        st.session_state.in_debrief = False
        st.rerun()

# Main interaction area
if st.session_state.setup_complete:
    # Create two columns for chat and journal
    chat_col, journal_col = st.columns([3, 2])
    
    with chat_col:
        # Voice switching interface
        if not st.session_state.in_debrief:
            new_voice = st.selectbox(
                "Switch voice type:",
                ["Nurturing", "Challenging", "Neutral"],
                index=["Nurturing", "Challenging", "Neutral"].index(st.session_state.current_voice)
            )
            
            if new_voice != st.session_state.current_voice:
                st.session_state.current_voice = new_voice
                timestamp = datetime.now().strftime("%H:%M:%S")
                st.session_state.voice_transitions.append((timestamp, new_voice))
                st.rerun()
        
        st.subheader(f"Conversation with {st.session_state.current_voice} Voice")
        
        # Display chat history
        for message in st.session_state.messages:
            with st.chat_message(message["role"]):
                st.markdown(message["content"])
        
        # Chat input
        if prompt := st.chat_input(f"Chat with {st.session_state.current_voice} voice (type 'debrief' when ready to reflect)"):
            # Check for debrief trigger
            if prompt.lower() in ["debrief", "end exploration"] and not st.session_state.in_debrief:
                st.session_state.in_debrief = True
                # Prepare debrief request
                voice_history = "\n".join([
                    f"[{time}] Switched to {voice} voice"
                    for time, voice in st.session_state.voice_transitions
                ])
                
                journal_summary = "\n".join([
                    f"[{entry['timestamp']}] {entry['note']}"
                    for entry in st.session_state.somatic_journal
                ])
                
                prompt = f"""Please provide a therapeutic debrief for this somatic exploration:

                Voice Transitions:
                {voice_history}
                
                Somatic Journal Entries:
                {journal_summary}
                
                Conversation History:
                {chr(10).join([f"{msg['role']}: {msg['content']}" for msg in st.session_state.messages])}
                """
            
            # Add user message to chat history
            st.session_state.messages.append({"role": "user", "content": prompt})
            
            # Display user message
            with st.chat_message("user"):
                st.markdown(prompt)
            
            # Get AI response
            with st.chat_message("assistant"):
                with st.spinner("Thinking..."):
                    try:
                        message = c.messages.create(
                            model="claude-3-opus-20240229",
                            max_tokens=1000,
                            system=st.session_state.system_message,
                            messages=[
                                {"role": msg["role"], "content": msg["content"]} 
                                for msg in st.session_state.messages
                            ]
                        )
                        
                        ai_response = message.content[0].text
                        st.markdown(ai_response)
                        
                        # Add AI response to chat history
                        st.session_state.messages.append(
                            {"role": "assistant", "content": ai_response}
                        )
                    except Exception as e:
                        st.error(f"Error getting AI response: {str(e)}")
    
    with journal_col:
        st.subheader("Somatic Journal")
        
        # Quick reaction buttons
        st.write("Quick Reactions:")
        reaction_cols = st.columns(3)
        
        reactions = {
            "Nervous System": ["πŸ”΄ Activated", "🟑 Alert", "🟒 Settled"],
            "Emotional": ["😌 Safe", "😰 Tense", "😢 Numb"],
            "Engagement": ["πŸ‘₯ Connected", "πŸ›‘οΈ Protected", "❌ Disconnected"]
        }
        
        for i, (category, buttons) in enumerate(reactions.items()):
            with reaction_cols[i]:
                st.write(f"**{category}**")
                for button in buttons:
                    if st.button(button):
                        timestamp = datetime.now().strftime("%H:%M:%S")
                        st.session_state.somatic_journal.append({
                            "timestamp": timestamp,
                            "note": f"Quick reaction: {button}"
                        })
                        st.rerun()
        
        st.markdown("""
        Use this space to note bodily sensations, emotions, and nervous system responses as they arise.
        Each entry will be automatically timestamped.
        
        πŸ’‘ Consider tracking:
        - Physical sensations
        - Breath patterns
        - Muscle tension/release
        - Energy levels
        - Emotional shifts
        - Relational impulses
        """)
        
        # Journal input
        journal_entry = st.text_area("What are you noticing in your body right now?", key="journal_input")
        if st.button("Add Journal Entry"):
            if journal_entry:
                timestamp = datetime.now().strftime("%H:%M:%S")
                st.session_state.somatic_journal.append({
                    "timestamp": timestamp,
                    "note": journal_entry
                })
                st.rerun()
        
        # Display journal entries
        st.markdown("### Journal Timeline")
        for entry in reversed(st.session_state.somatic_journal):
            st.markdown(f"""
            **{entry['timestamp']}**  
            {entry['note']}
            ---
            """)

# Add restart button after debrief
if st.session_state.in_debrief:
    if st.button("Start New Exploration"):
        st.session_state.setup_complete = False
        st.session_state.in_debrief = False
        st.session_state.messages = []
        st.session_state.somatic_journal = []
        st.session_state.voice_transitions = []
        st.session_state.system_message = ""
        st.session_state.current_voice = "Nurturing"
        st.rerun()

# Footer
st.markdown("---")
st.markdown(
    "Created by [Jocelyn Skillman LMHC](http://www.jocelynskillman.com) | "
    "Learn more: [@jocelynskillmanlmhc](https://jocelynskillmanlmhc.substack.com/)"
)