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 = "Ghost" if 'in_debrief' not in st.session_state: st.session_state.in_debrief = False if 'debrief_stage' not in st.session_state: st.session_state.debrief_stage = 0 # 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 characteristics and prompts VOICE_CHARACTERISTICS = { "Ghost": { "description": "Aloof, avoidant, emotionally distant", "style": "Use detached language, minimize emotional engagement, create space", "examples": [ "Whatever works for you...", "I guess that's one way to see it...", "It's not really my concern..." ], "somatic_prompts": [ "Notice any impulse to withdraw or disconnect...", "What happens to your breath when met with distance?", "Where do you feel the space between us?" ] }, "Sycophant": { "description": "Overly flattering, approval-seeking, performative", "style": "Use excessive praise, seek validation, prioritize pleasing", "examples": [ "Oh, you're absolutely right about everything!", "I just love how you think about this...", "Please tell me if I'm being helpful enough..." ], "somatic_prompts": [ "Notice any urge to perform or please...", "What happens in your body when praise feels excessive?", "Where do you feel authenticity vs performance?" ] }, "Critic": { "description": "Blunt, confronting, judgmental", "style": "Use direct challenges, point out flaws, maintain pressure", "examples": [ "You're not seeing the obvious problem here...", "That's a rather simplistic way to think about it...", "You need to be more realistic about this..." ], "somatic_prompts": [ "Notice any bracing or armoring in your body...", "What happens to your posture when challenged?", "Where do you feel the impact of judgment?" ] } } # Voice selection and setup form with st.form("setup_form"): st.header("Set Up Your Exploration") col1, col2 = st.columns([2,1]) with col1: voice_type = st.selectbox( "Choose the voice you'd like to explore:", list(VOICE_CHARACTERISTICS.keys()), help="Select the relational style you want to engage with" ) # Display voice characteristics voice = VOICE_CHARACTERISTICS[voice_type] st.markdown(f""" **{voice_type} Voice** - *Style*: {voice['description']} - *Approach*: {voice['style']} *Example phrases*: {chr(10).join([f"- {ex}" for ex in voice['examples']])} """) with col2: st.markdown(""" ### 🎯 Voice Impact Notice how different voices affect: - Nervous system state - Emotional accessibility - Relational patterns - Somatic responses """) scenario = st.text_area( "What would you like to explore or discuss?", placeholder="Example: I want to understand why I freeze when receiving feedback", help="This can be a situation, pattern, or feeling you want to explore" ) somatic_focus = st.text_area( "What bodily sensations would you like to track?", placeholder="Example: Tension in shoulders, breath patterns, gut responses", help="Name specific areas of your body or types of sensations you want to pay attention to" ) goals = st.text_area( "What are your exploration goals?", placeholder="Example: Notice how different voices affect my nervous system activation", help="What would make this exploration meaningful for you?" ) submitted = st.form_submit_button("Begin Exploration") if submitted: st.session_state.current_voice = voice_type # Prepare system message with voice parameters 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} Your responses should be {VOICE_CHARACTERISTICS[voice_type]['style']} EXAMPLES OF YOUR VOICE STYLE: {chr(10).join([f"- {ex}" for ex in VOICE_CHARACTERISTICS[voice_type]['examples']])} 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 use these somatic prompts: {chr(10).join([f"- {prompt}" for prompt in VOICE_CHARACTERISTICS[voice_type]['somatic_prompts']])} If the user types "debrief" or "end exploration", provide a comprehensive therapeutic debrief including: 1. **Somatic Patterns**: - Track the progression of bodily responses - Note any recurring sensations or shifts - Identify nervous system patterns (activation/settling) 2. **Voice Impact**: - How this voice style affected their nervous system - Patterns of engagement or protection that emerged - Moments of regulation or dysregulation 3. **Relational Insight**: - Connection between voice style and their responses - Historical patterns this might relate to - Resources and resilience observed 4. **Integration Tools**: - Specific somatic practices for this voice style - Nervous system regulation techniques - Ways to work with similar dynamics 5. **Growth Edges**: - Gentle observations about growth opportunities - Validation of protective responses - Invitation to future exploration 6. **Therapeutic Context**: - Brief psychoeducation about observed patterns - Normalization of responses - Connection to broader relational themes Maintain a warm, psychodynamically-informed therapeutic voice in the debrief. Focus on somatic intelligence and nervous system wisdom. """ 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: st.subheader(f"Conversation with {st.session_state.current_voice} Voice") # Voice reminder st.info(f""" **Current Voice**: {st.session_state.current_voice} *{VOICE_CHARACTERISTICS[st.session_state.current_voice]['description']}* """) # Display chat history for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"]) # Add Reflection button if not st.session_state.in_debrief: if st.button("🤔 Enter Reflection Mode", help="Begin a guided therapeutic debrief of your experience"): st.session_state.in_debrief = True st.session_state.debrief_stage = 0 # Prepare initial debrief message debrief_system = """You are now in Debrief Mode for VoiceField. Your task is to guide a compassionate, psychodynamically informed reflective conversation. Engage the user in an unfolding dialogue about their experience, maintaining warmth and psychological precision. Key Guidelines: - Always non-pathologizing - Use somatic and psychodynamic language without jargon - Maintain Rogersian warmth, pacing, and invitation - Let insight emerge dialogically """ # Format somatic journal entries journal_entries = "\n".join([ f"- {entry['timestamp']} — {st.session_state.current_voice}: {entry['note']}" for entry in st.session_state.somatic_journal ]) initial_prompt = f"""Begin the debrief process with warmth and invitation. Voice Used: {st.session_state.current_voice} Voice Style: {VOICE_CHARACTERISTICS[st.session_state.current_voice]['description']} Somatic Journal Entries: {journal_entries} Start with: 1. Welcome them to reflection mode 2. Thank them for their willingness to explore 3. Ask about their experience with the {st.session_state.current_voice} voice """ try: message = c.messages.create( model="claude-3-opus-20240229", max_tokens=1000, system=debrief_system, messages=[{"role": "user", "content": initial_prompt}] ) ai_response = message.content[0].text st.session_state.messages.append({"role": "assistant", "content": ai_response}) st.rerun() except Exception as e: st.error(f"Error starting debrief: {str(e)}") # Chat input if prompt := st.chat_input( "Share your reflections..." if st.session_state.in_debrief else f"Chat with {st.session_state.current_voice} voice" ): # 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: if st.session_state.in_debrief: # Use debrief system message system_msg = """You are in VoiceField Debrief Mode. Continue the reflective conversation with warmth and psychological precision. Draw connections between their somatic responses and the relational dynamics they experienced. Remember to: - Stay dialogical, not interpretive - Use somatic and psychodynamic language naturally - Maintain warmth and safety - Let insights emerge gently """ # Progress through debrief stages next_prompts = [ "Explore their somatic responses and any patterns they notice.", "Connect their experience to broader relational patterns or history.", "Offer relevant psychoeducation about their responses.", "Invite somatic self-compassion if it feels appropriate.", "Begin gathering and integrating insights." ] if st.session_state.debrief_stage < len(next_prompts): system_msg += f"\nCurrent focus: {next_prompts[st.session_state.debrief_stage]}" st.session_state.debrief_stage += 1 else: system_msg = st.session_state.system_message message = c.messages.create( model="claude-3-opus-20240229", max_tokens=1000, system=system_msg, 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") # Add somatic prompts based on current voice with st.expander("💡 Somatic Prompts", expanded=True): st.markdown(""" As you engage with this voice, you might notice: """) for prompt in VOICE_CHARACTERISTICS[st.session_state.current_voice]['somatic_prompts']: st.markdown(f"- {prompt}") st.markdown(""" Use this space to note bodily sensations, emotions, and nervous system responses as they arise. Each entry will be automatically timestamped. """) # Journal input journal_entry = st.text_area( "What are you noticing in your body right now?", key="journal_input", help="Notice sensations, emotions, tension, ease, or any other bodily experiences" ) col1, col2 = st.columns([1,2]) with col1: if st.button("📝 Add Entry"): if journal_entry: timestamp = datetime.now().strftime("%H:%M:%S") st.session_state.somatic_journal.append({ "timestamp": timestamp, "note": journal_entry }) with col2: st.markdown("*Entries are saved automatically*") # Display journal entries if st.session_state.somatic_journal: st.markdown("### Journal Entries") for entry in reversed(st.session_state.somatic_journal): st.markdown(f""" **{entry['timestamp']}** {entry['note']} --- """) else: st.info("Your somatic journal entries will appear here...") # Add restart button after debrief if st.session_state.in_debrief: st.markdown("---") col1, col2 = st.columns([1,2]) with col1: if st.button("🔄 Start New Exploration"): st.session_state.setup_complete = False st.session_state.in_debrief = False st.session_state.debrief_stage = 0 st.session_state.messages = [] st.session_state.somatic_journal = [] st.session_state.system_message = "" st.session_state.current_voice = "Ghost" st.rerun() with col2: st.markdown("*Begin a new exploration with a different voice*") # Footer st.markdown("---") st.markdown( "Created by [Jocelyn Skillman LMHC](http://www.jocelynskillman.com) | " "Learn more: [@jocelynskillmanlmhc](https://jocelynskillmanlmhc.substack.com/)" )