Jocelyn Skillman commited on
Commit
4c59e23
·
0 Parent(s):

Initial commit

Browse files
Files changed (5) hide show
  1. .gitattributes +35 -0
  2. .gitignore +135 -0
  3. README.md +14 -0
  4. app.py +197 -0
  5. requirements.txt +6 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ .python-version
87
+
88
+ # PEP 582; used by poetry and pdm
89
+ __pypackages__/
90
+
91
+ # Celery stuff
92
+ celerybeat-schedule
93
+ celerybeat.pid
94
+
95
+ # SageMath parsed files
96
+ *.sage.py
97
+
98
+ # Environments
99
+ .env
100
+ .venv
101
+ env/
102
+ venv/
103
+ ENV/
104
+ env.bak/
105
+ venv.bak/
106
+
107
+ # Spyder project settings
108
+ .spyderproject
109
+ .spyproject
110
+
111
+ # Rope project settings
112
+ .ropeproject
113
+
114
+ # mkdocs documentation
115
+ /site
116
+
117
+ # mypy
118
+ .mypy_cache/
119
+ .dmypy.json
120
+ dmypy.json
121
+
122
+ # Pyre type checker
123
+ .pyre/
124
+
125
+ # pytype static type analyzer
126
+ .pytype/
127
+
128
+ # Cython debug symbols
129
+ cython_debug/
130
+
131
+ # VSCode settings
132
+ .vscode/
133
+
134
+ # JetBrains IDE settings
135
+ .idea/
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: ShadowBox
3
+ emoji: 🖤
4
+ colorFrom: indigo
5
+ colorTo: gray
6
+ sdk: streamlit
7
+ sdk_version: 1.42.2
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
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.
app.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
10
+ 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") # Added OpenAI Key check
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}")
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
+ # Initialize with a system message or starting message if desired
58
+ # For now, just an empty list
59
+ st.session_state.messages = []
60
+ # Example with initial system prompt (uncomment if needed):
61
+ # st.session_state.messages = [{"role": "system", "content": "You are ShadowBox, a calm AI companion."}]
62
+
63
+
64
+ # --- Sidebar Content ---
65
+ with st.sidebar:
66
+ st.header("👁‍🗨 Box your Shadows")
67
+ st.markdown("Start a chat in the main window.")
68
+ st.divider()
69
+
70
+ st.header("🤝 About ShadowBox")
71
+ st.markdown("""
72
+ **What Is ShadowBox?**
73
+ Not a therapist, hotline, or fixer. It's a slow, anonymous, digital companion for youth carrying thoughts they don't feel safe saying out loud—especially about harming others or themselves.
74
+ It offers a place to say the unspeakable without fear, met with dignity, not danger.
75
+ *(More details in the full 'About' section - TBD)*
76
+ """)
77
+ st.divider()
78
+
79
+ st.header("📚 Resources + Ethics")
80
+ st.markdown("""
81
+ **How ShadowBox Works:** Runs on a carefully trained generative AI prompt structure, shaped by clinical insight for accountability, distress tolerance, and respect for voice.
82
+ **Privacy:** Does NOT collect identity, IP, or store conversations. No tracking. No surveillance. You are witnessed, not watched.
83
+ *(More details in the full 'Resources & Ethics' section - TBD)*
84
+ """)
85
+ st.divider()
86
+
87
+ st.header("🧠 Why This Matters")
88
+ st.markdown("""
89
+ Addresses the gap in how systems respond to youth with intrusive thoughts. Grounded in clinical wisdom, developmental attunement, and radical respect. Offers a practice field for emotional honesty.
90
+ *(More details in the full 'Why This Matters' section - TBD)*
91
+ """)
92
+ st.divider()
93
+
94
+ st.header("📖 A Short History")
95
+ st.markdown("""
96
+ Born from hope and fear about AI's role in mental health. Youth already turn to AI; ShadowBox aims to provide a *designed relationship* grounded in ethics, safety, and clinical insight.
97
+ *(More details in the full 'A Short History' section - TBD)*
98
+ """)
99
+ st.divider()
100
+
101
+ st.header("🆘 Need Help Right Now?")
102
+ st.warning("ShadowBox is not a crisis line. If you need immediate support from real people:")
103
+ st.markdown("""
104
+ * **Call or Text 988:** (Suicide & Crisis Lifeline) - 24/7, free, confidential.
105
+ * **The Trevor Project:** 1-866-488-7386 or Text 'START' to 678-678 (for LGBTQIA+ youth)
106
+ * **Crisis Text Line:** Text 'HOME' to 741741
107
+ * **YouthLine:** Text 'teen2teen' to 839863 or Call 1-877-968-8491 (teens helping teens)
108
+ * **In an Emergency:** Call 911 or go to the nearest ER. State: "I need mental health support. This is not a crime."
109
+ """)
110
+
111
+
112
+ # --- Main Page Content ---
113
+
114
+ st.markdown("<h1 style='text-align: center; color: #333;'>ShadowBox</h1>", unsafe_allow_html=True)
115
+ st.markdown("<p style='text-align: center; font-size: 18px; color: #555;'>An Anonymous AI Chat to Box Shadows</p>", unsafe_allow_html=True)
116
+
117
+ st.markdown("""
118
+ Welcome. We've found our way to a special space—a place made to hold what's hard to say out loud.
119
+
120
+ ShadowBox is a digital companion, powered by generative AI and grounded in clinical mental health care, designed for youth navigating the stormy terrain of thoughts like rage, despair, and even violent ideas or urges.
121
+
122
+ ShadowBox stays present and warm with the hardest parts of ourselves so we can learn to, too.
123
+
124
+ It isn't a hotline. It's not therapy. And it's definitely not surveillance. The thoughts that alarm us don't set off alarms here.
125
+
126
+ Lots of us—more than we think—have experienced scary or unwanted thoughts. Thoughts of harm. Thoughts about hurting ourselves or others. These thoughts don't make us dangerous. They make us human.
127
+
128
+ Intrusive thoughts are often how the brain reacts to stress. When our nervous system feels overwhelmed, it can kick into fight, flight, or freeze. Sometimes the urge to harm is our brain's way of trying to protect us, push pain away, or signal an unmet need.
129
+
130
+ Sharing these thoughts can feel scary, fearing judgment or consequences. ShadowBox is a bridge—a place to build internal safety and practice sharing shadow parts before connecting with others.
131
+
132
+ *"Every act of violence is a tragic expression of an unmet need."* — Marshall Rosenberg
133
+
134
+ ---
135
+ **Important Notes:**
136
+ * **Safety & Privacy:** ShadowBox is designed to care for dark thoughts but won't generate harmful content. **It does not store your identity or conversations.** No accounts, no tracking. Your privacy is paramount. Using *this* platform is designed for anonymity.
137
+ * **Not Therapy:** You're talking to AI shaped by a therapist, not a real person. It's intended as a bridge to human support. It can't feel, breathe with you, or hug you—things we all need.
138
+ * **Warmth & Education:** Responses aim for warmth and friendship. It might offer mental health education, encourage connection, or suggest grounding practices. It remembers the *current* conversation but saves nothing long-term.
139
+ * **Mandatory Reporting:** This bot is **NOT** a mandatory reporter. It can help explore sharing difficult feelings safely. *If you express a serious, immediate plan to harm yourself or someone else, standard mental health practice involves a 'Duty to Warn' for safety. Learn more [here](link_to_confidentiality_info - TBD).*
140
+
141
+ ---
142
+ This is a prototype. Feedback is valuable (link to feedback form - TBD).
143
+ """)
144
+
145
+ # Display chat history
146
+ # Add a system message/intro from ShadowBox? (TBD based on prompt)
147
+ # Updated loop to work with the new messages list structure
148
+ for message in st.session_state.messages:
149
+ # Filter out system messages from display if they exist
150
+ if message["role"] in ["user", "assistant"]:
151
+ with st.chat_message(message["role"]):
152
+ st.markdown(message["content"])
153
+
154
+ # User input field
155
+ user_prompt = st.chat_input("You can start with silence. Or just 'hi'...")
156
+
157
+ # If user enters a prompt
158
+ if user_prompt:
159
+ # Append user's message to the session state list
160
+ st.session_state.messages.append({"role": "user", "content": user_prompt})
161
+
162
+ # Display user's message
163
+ st.chat_message("user").markdown(user_prompt)
164
+
165
+ # Show a loading indicator while waiting for a response
166
+ with st.spinner("..."): # Simpler spinner
167
+ try:
168
+ # Replace Gemini API call with OpenAI API call
169
+ # gemini_response = st.session_state.chat_session.send_message(user_prompt)
170
+ # response_text = gemini_response.text
171
+ openai_response = client.chat.completions.create(
172
+ model="gpt-4o",
173
+ messages=st.session_state.messages # Pass the entire history
174
+ )
175
+ response_text = openai_response.choices[0].message.content
176
+ # Append assistant's response to the session state list
177
+ st.session_state.messages.append({"role": "assistant", "content": response_text})
178
+ except Exception as e:
179
+ response_text = f"Sorry, I encountered an error: {e}"
180
+ st.error(response_text) # Display error in chat too
181
+
182
+ # Display assistant's response
183
+ if response_text: # Check if response_text was successfully generated
184
+ with st.chat_message("assistant"):
185
+ st.markdown(response_text)
186
+
187
+ # Run text-to-speech in the background (Optional)
188
+ # Consider removing if it clashes with the calm UX
189
+ # threading.Thread(target=speak_text, args=(response_text,), daemon=True).start()
190
+ pass # TTS disabled for now to maintain calm UX
191
+
192
+ st.markdown("---")
193
+ st.caption("ShadowBox created by Jocelyn Skillman, LMHC. [Learn More](link_to_substack - TBD)")
194
+
195
+
196
+
197
+
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ python-dotenv
2
+ google-generativeai
3
+ streamlit # (Agar aap Streamlit use kar rahe hain)
4
+ gradio # (Agar aap Gradio use kar rahe hain)
5
+ pyttsx3
6
+ openai