Skier8402 commited on
Commit
3c0c65c
·
verified ·
1 Parent(s): cf5ca91

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +46 -0
  2. Makefile +293 -0
  3. app2.py +414 -0
  4. prompts.py +329 -0
  5. requirements.txt +10 -0
Dockerfile ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Python 3.11 slim image as base
2
+ FROM python:3.13-slim
3
+
4
+ # Set working directory
5
+ WORKDIR /app
6
+
7
+ # Set environment variables
8
+ ENV PYTHONDONTWRITEBYTECODE=1 \
9
+ PYTHONUNBUFFERED=1
10
+
11
+ # Install system dependencies including curl for health check
12
+ RUN apt-get update && apt-get install -y \
13
+ --no-install-recommends \
14
+ gcc \
15
+ curl \
16
+ && rm -rf /var/lib/apt/lists/*
17
+
18
+ # Copy requirements first for better Docker layer caching
19
+ COPY requirements.txt .
20
+
21
+ # Install Python dependencies
22
+ RUN pip --no-cache-dir install --upgrade pip && \
23
+ pip --no-cache-dir install -r requirements.txt
24
+
25
+ # Copy application files
26
+ COPY app2.py .
27
+ COPY prompts.py .
28
+
29
+ # Create log directory and set proper permissions
30
+ RUN mkdir -p /app/logs && \
31
+ chmod 755 /app/logs
32
+
33
+ # Create a non-root user for security
34
+ RUN useradd --create-home --shell /bin/bash app && \
35
+ chown -R app:app /app
36
+ USER app
37
+
38
+ # Expose the port the app runs on
39
+ EXPOSE 7860
40
+
41
+ # Health check
42
+ HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
43
+ CMD curl -f http://localhost:7860/ || exit 1
44
+
45
+ # Run the application
46
+ CMD ["python", "app2.py"]
Makefile ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Thank you @Earthly https://www.youtube.com/watch?v=w2UeLF7EEwk
2
+ # Can be adapted to pipenv, and poetry
3
+ # Other languages coming soon especially R and Julia
4
+
5
+ # .ONESHELL tells make to run each recipe line in a single shell
6
+ .ONESHELL:
7
+
8
+ # .DEFAULT_GOAL tells make which target to run when no target is specified
9
+ .DEFAULT_GOAL := all
10
+
11
+ # Specify python location in virtual environment
12
+ # Specify pip location in virtual environment
13
+ PYTHON := .venv/bin/python3
14
+ PIP := .venv/bin/pip3
15
+ DOCKER_IMAGE_NAME := tima-chatbot
16
+ DOCKER_IMAGE_VERSION := v0.0.0
17
+ DOCKER_IMAGE_TAG := $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_VERSION)
18
+
19
+ #-----------------------------------------------------------------
20
+ # Package Manager Detection
21
+ #-----------------------------------------------------------------
22
+
23
+ # Detect available package managers
24
+ # UV https://app.readytensor.ai/publications/uv-package-1yiSfLXTffSF?utm_id=12
25
+ DETECT_PIPENV := $(shell which pipenv)
26
+ DETECT_POETRY := $(shell which poetry)
27
+ DETECT_UV := $(shell which uv)
28
+
29
+ # Set flags (1: available, 0: not available)
30
+ ifeq ($(DETECT_PIPENV),)
31
+ HAS_PIPENV := 0
32
+ else
33
+ HAS_PIPENV := 1
34
+ endif
35
+
36
+ ifeq ($(DETECT_POETRY),)
37
+ HAS_POETRY := 0
38
+ else
39
+ HAS_POETRY := 1
40
+ endif
41
+
42
+ ifeq ($(DETECT_UV),)
43
+ HAS_UV := 0
44
+ else
45
+ HAS_UV := 1
46
+ endif
47
+
48
+ #-----------------------------------------------------------------
49
+ # Default Package Manager Selection (priority: uv > pipenv > poetry)
50
+ #-----------------------------------------------------------------
51
+
52
+ ifeq ($(HAS_UV),1)
53
+ PACKAGE_MANAGER := uv
54
+ else ifeq ($(HAS_PIPENV),1)
55
+ PACKAGE_MANAGER := pipenv
56
+ else ifeq ($(HAS_POETRY),1)
57
+ PACKAGE_MANAGER := poetry
58
+ else
59
+ $(error No supported package manager found (pipenv, poetry, or uv))
60
+ endif
61
+
62
+ #-----------------------------------------------------------------
63
+ # Set Commands Based on the Package Manager
64
+ #-----------------------------------------------------------------
65
+
66
+ ifeq ($(PACKAGE_MANAGER),pipenv)
67
+ INSTALL_COMMAND := pipenv install --dev
68
+ RUN_COMMAND := pipenv run
69
+ else ifeq ($(PACKAGE_MANAGER),poetry)
70
+ INSTALL_COMMAND := poetry install
71
+ RUN_COMMAND := poetry run
72
+ else ifeq ($(PACKAGE_MANAGER),uv)
73
+ INSTALL_COMMAND := uv pip install -e . -r requirements-dev.txt
74
+ RUN_COMMAND := python # or python3, depending on your setup
75
+ endif
76
+
77
+ detect:
78
+ @echo "Pipenv detected: $(HAS_PIPENV)"
79
+ @echo "Poetry detected: $(HAS_POETRY)"
80
+ @echo "UV detected: $(HAS_UV)"
81
+ @echo "Package manager selected: $(PACKAGE_MANAGER)"
82
+
83
+
84
+ venv/bin/activate: requirements.txt
85
+ # create virtual environment
86
+ python3 -m venv .venv
87
+ # make command executable
88
+ chmod +x .venv/bin/activate
89
+ # activate virtual environment
90
+ . .venv/bin/activate
91
+
92
+ activate:
93
+ # Print instructions for activating the virtual environment
94
+ @echo "To activate the virtual environment, run the following command in your terminal:"
95
+ @echo "source .venv/bin/activate"
96
+
97
+ install: venv/bin/activate requirements.txt # prerequisite
98
+ # install commands
99
+ $(PIP) --no-cache-dir install --upgrade pip &&\
100
+ $(PIP) --no-cache-dir install -r requirements.txt
101
+
102
+ docstring: activate
103
+ # format docstring
104
+ pyment -w -o numpydoc *.py
105
+
106
+ format: activate
107
+ # format code
108
+ black *.py
109
+
110
+ clean:
111
+ # clean directory of cache
112
+ rm -rf __pycache__ &&\
113
+ rm -rf utils/__pycache__ &&\
114
+ rm -rf testing/__pycache__ &&\
115
+ rm -rf .pytest_cache &&\
116
+ rm -rf .venv
117
+
118
+ lint: activate install format
119
+ # flake8 or #pylint
120
+ pylint --disable=R,C --errors-only *.py utils/*.py testing/*.py
121
+
122
+ setup_readme: ## Create a README.md
123
+ @if [ ! -f README.md ]; then \
124
+ echo "# Project Name\n\
125
+ Description of the project.\n\n\
126
+ ## Installation\n\
127
+ - Step 1\n\
128
+ - Step 2\n\n\
129
+ ## Usage\n\
130
+ Explain how to use the project here.\n\n\
131
+ ## Contributing\n\
132
+ Explain how to contribute to the project.\n\n\
133
+ ## License\n\
134
+ License information." > README.md; \
135
+ echo "README.md created."; \
136
+ else \
137
+ echo "README.md already exists."; \
138
+ fi
139
+
140
+ test: activate install format
141
+ # test
142
+ $(PYTHON) -m pytest testing/*.py
143
+
144
+ run: activate install format
145
+ # run application
146
+ # example $(PYTHON) app.py
147
+ $(PYTHON) app2.py
148
+
149
+ run_test: activate install format
150
+ # run test application
151
+ $(PYTHON) enchanced_app.py
152
+
153
+ review-code:
154
+ @echo "#### General Code Review Prompt ####"
155
+ @echo "1. Code Quality:"
156
+ @echo " - Architecture patterns"
157
+ @echo " - Design principles (SOLID, DRY, KISS)"
158
+ @echo " - Code complexity"
159
+ @echo " - Documentation quality"
160
+
161
+ @echo "2. Reliability:"
162
+ @echo " - Single and multiple points of failure"
163
+ @echo " - Failover strategies"
164
+ @echo " - Resource management"
165
+ @echo " - Thread safety"
166
+
167
+ @echo "3. Performance:"
168
+ @echo " - Algorithmic efficiency"
169
+ @echo " - Memory usage"
170
+ @echo " - I/O operations"
171
+ @echo " - Caching strategy"
172
+
173
+ review-ds:
174
+ @echo "#### Data Science Review Prompt ####"
175
+ @echo "1. Data Pipeline:"
176
+ @echo " - Data validation (schema, types, ranges)"
177
+ @echo " - Preprocessing steps (scaling, encoding, imputation)"
178
+ @echo " - Feature engineering (relevance, creation, selection)"
179
+ @echo " - Data versioning (tracking datasets)"
180
+ @echo " - Data leakage checks"
181
+ @echo " - Handling of missing or anomalous data"
182
+
183
+ @echo "2. Model Development:"
184
+ @echo " - Algorithm selection rationale"
185
+ @echo " - Hyperparameter tuning methodology and tracking"
186
+ @echo " - Cross-validation strategy (appropriateness, implementation)"
187
+ @echo " - Choice of evaluation metrics (relevance to business goal)"
188
+ @echo " - Model interpretability/explainability methods"
189
+ @echo " - Bias and fairness assessment"
190
+ @echo " - Model persistence (saving/loading)"
191
+ @echo " - Code reproducibility (seeds, dependencies)"
192
+
193
+ @echo "3. Production Readiness:"
194
+ @echo " - Scalability of prediction/inference code"
195
+ @echo " - Monitoring setup (technical and model performance metrics)"
196
+ @echo " - Logging for model inputs/outputs/errors"
197
+ @echo " - A/B testing or canary deployment capability"
198
+ @echo " - Model deployment pipeline (automation)"
199
+ @echo " - Rollback strategy for model updates"
200
+
201
+ @echo "4. Experiment Tracking:"
202
+ @echo " - Are experiments logged (parameters, code versions, metrics)?"
203
+ @echo " - Is an experiment tracking tool used (e.g., MLflow, W&B)?"
204
+ @echo " - Are model artifacts versioned and stored?"
205
+
206
+ review-logging:
207
+ @echo "#### Logging Review Prompt ####"
208
+ @echo ""
209
+ @echo "1. Level 1: Print Statements"
210
+ @echo " - Are there raw print() statements used for debugging?"
211
+ @echo " - Should these be replaced with proper logging?"
212
+ @echo " - Are any critical error conditions only visible via print statements?"
213
+ @echo ""
214
+ @echo "2. Level 2: Logging Libraries"
215
+ @echo " - Is a proper logging library being used consistently?"
216
+ @echo " - Are appropriate log levels (DEBUG, INFO, WARNING, ERROR) used?"
217
+ @echo " - Is logging properly configured (handlers, formatters)?"
218
+ @echo " - Are logs structured (JSON/key-value) for better analysis?"
219
+ @echo " - Is sensitive information protected from being logged?"
220
+ @echo ""
221
+ @echo "3. Level 3: Tracing"
222
+ @echo " - Is function/method entry/exit tracked for performance analysis?"
223
+ @echo " - Are execution paths through the code captured with timing data?"
224
+ @echo " - Are trace IDs used to track request flow?"
225
+ @echo ""
226
+ @echo "4. Level 4: Distributed Tracing"
227
+ @echo " - Is context propagated across service boundaries?"
228
+ @echo " - Are trace IDs maintained throughout the entire request lifecycle?"
229
+ @echo " - Is sampling strategy appropriate for production load?"
230
+ @echo ""
231
+ @echo "5. Level 5: Observability"
232
+ @echo " - Are logs, metrics, and traces integrated into a unified system?"
233
+ @echo " - Is there anomaly detection for unexpected behaviors?"
234
+ @echo " - Does the system provide business-level insights from technical data?"
235
+ @echo " - Is the observability stack scalable for production use?"
236
+
237
+ docker_build: Dockerfile
238
+ # build container. Feel free to change the platform given your needs
239
+ # docker build -platform linux/amd64 -t plot-timeseries-app:v0 .
240
+ # podman build -platform linux/amd64 -t plot-timeseries-app:v0 .
241
+ sudo docker build --platform linux/amd64 -t $(DOCKER_IMAGE_TAG) .
242
+
243
+ docker_run_test: Dockerfile
244
+ # linting Dockerfile
245
+ # podman run --rm -i hadolint/hadolint < Dockerfile
246
+ docker run --rm -i hadolint/hadolint < Dockerfile
247
+
248
+ docker_clean: Dockerfile
249
+ # remove dangling images, containers, volumes and networks
250
+ # podman system prune -a
251
+ sudo docker system prune -a
252
+
253
+ docker_run: Dockerfile docker_build
254
+ # run docker
255
+ # For ARM images (Apple Silicon, Raspberry Pi, etc.), use --platform linux/arm64
256
+ # Example: sudo docker run --platform linux/arm64 -p 7860:7860 -e TIMA_API_KEY=$TIMA_API_KEY $(DOCKER_IMAGE_TAG)
257
+ # For x86_64 (most PCs/servers), use --platform linux/amd64
258
+ sudo docker run --platform linux/amd64 -p 7860:7860 -e TIMA_API_KEY=${TIMA_API_KEY} $(DOCKER_IMAGE_TAG)
259
+ docker_push: docker_build
260
+ # push to registry
261
+ # docker tag <my-image> registry.digitalocean.com/<my-registry>/<my-image>
262
+ # docker push registry.digitalocean.com/<my-registry>/<my-image>
263
+ # podman tag <my-image> registry.digitalocean.com/<my-registry>/<my-image>
264
+ # podman push registry.digitalocean.com/<my-registry>/<my-image>
265
+
266
+ help:
267
+ @echo "Usage: make [target]"
268
+ @echo ""
269
+ @echo "Available targets:"
270
+ @echo " detect Detect package managers"
271
+ @echo " activate Activate virtual environment"
272
+ @echo " install Install dependencies"
273
+ @echo " docstring Format docstrings"
274
+ @echo " format Format code"
275
+ @echo " clean Clean directory of cache"
276
+ @echo " lint Lint code"
277
+ @echo " test Run tests"
278
+ @echo " run Run application"
279
+ @echo " setup_readme Create a README.md"
280
+ @echo " review-code Code review prompt"
281
+ @echo " review-ds Data Science review prompt"
282
+ @echo " review-logging Logging implementation review prompt"
283
+ @echo " docker-build Build Docker image $(DOCKER_IMAGE_TAG)"
284
+ @echo " docker_run_test Lint Dockerfile"
285
+ @echo " docker_clean Remove dangling images, containers, volumes, and networks"
286
+ @echo " docker_run Run Docker container"
287
+ @echo " docker_push Push Docker container to registry"
288
+ @echo " help Show this help message"
289
+
290
+ # .PHONY tells make that these targets do not represent actual files
291
+ .PHONY: activate format clean lint test build run docker_build docker_run docker_push docker_clean docker_run_test
292
+
293
+ all: install format lint test run docker_build docker_run docker_push
app2.py ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module for mental health chatbot with improved crisis handling.
2
+
3
+ This module provides a Gradio-based chat interface for mental health support
4
+ with proper crisis intervention protocols and streamlined code organization.
5
+
6
+ Example:
7
+ To run the chat interface:
8
+ $ python app2.py
9
+
10
+ Attributes:
11
+ TIMA_API_KEY (str): The API token for the AI service, loaded from environment variables.
12
+
13
+ Created by Mainye B
14
+ """
15
+
16
+ import os
17
+ import time
18
+ import logging
19
+ from typing import Generator, List, Dict
20
+ import gradio as gr
21
+ import openai
22
+ import httpx
23
+ from tenacity import retry, stop_after_attempt, wait_exponential
24
+ from ratelimit import limits, sleep_and_retry
25
+
26
+ from prompts import load_system_prompt
27
+
28
+ # Constants for API interaction
29
+ MAX_RETRIES = 5
30
+ INITIAL_RETRY_DELAY = 1 # seconds
31
+ MAX_RETRY_DELAY = 60 # seconds
32
+ RATE_LIMIT_CALLS = 40 # Cerebras recommended rate limit
33
+ RATE_LIMIT_PERIOD = 60 # 1 minute period
34
+
35
+ # Configure logging
36
+ logging.basicConfig(
37
+ filename="chat_interactions.log",
38
+ level=logging.INFO,
39
+ format="%(asctime)s - %(levelname)s - %(message)s",
40
+ )
41
+ logger = logging.getLogger(__name__)
42
+
43
+ # Load environment variables
44
+ TIMA_API_KEY = os.getenv("TIMA_API_KEY", os.getenv("CEREBRAS_API_KEY", None))
45
+ if not TIMA_API_KEY:
46
+ raise ValueError("CEREBRAS_API_KEY environment variable not found in environment")
47
+
48
+ # Rate limiting helps to avoid exceeding the API rate limits
49
+ CALLS_PER_MINUTE = 60
50
+ PERIOD = 60
51
+
52
+
53
+ class ChatError(Exception):
54
+ """Custom exception for chat-related errors"""
55
+
56
+ pass
57
+
58
+
59
+ class APIError(Exception):
60
+ """Base exception for API errors"""
61
+
62
+ pass
63
+
64
+
65
+ class RateLimitError(APIError):
66
+ """Exception for rate limit errors"""
67
+
68
+ pass
69
+
70
+
71
+ class TokenLimitError(APIError):
72
+ """Exception for token limit errors"""
73
+
74
+ pass
75
+
76
+
77
+ class InvalidRequestError(APIError):
78
+ """Exception for invalid request errors"""
79
+
80
+ pass
81
+
82
+
83
+ class AuthenticationError(APIError):
84
+ """Exception for authentication errors"""
85
+
86
+ pass
87
+
88
+
89
+ class ServerError(APIError):
90
+ """Exception for server-side errors"""
91
+
92
+ pass
93
+
94
+
95
+ def detect_crisis_situation(messages: List[Dict[str, str]]) -> bool:
96
+ """Detect if the conversation indicates a crisis situation.
97
+
98
+ Args:
99
+ messages: List of conversation messages
100
+
101
+ Returns:
102
+ bool: True if crisis situation detected, False otherwise
103
+ """
104
+ if not messages:
105
+ return False
106
+
107
+ # Check for PHQ-9 suicide question with concerning response
108
+ last_message = messages[-1].get("content", "").lower()
109
+ prev_message = messages[-2].get("content", "").lower() if len(messages) >= 2 else ""
110
+
111
+ # Look for PHQ-9 suicide question and high score response
112
+ suicide_question_keywords = [
113
+ "thoughts that you would be better off dead",
114
+ "hurting yourself",
115
+ ]
116
+ concerning_responses = ["3", "nearly every day", "more than half"]
117
+
118
+ has_suicide_question = any(
119
+ keyword in prev_message for keyword in suicide_question_keywords
120
+ )
121
+ has_concerning_response = any(
122
+ response in last_message for response in concerning_responses
123
+ )
124
+
125
+ return has_suicide_question and has_concerning_response
126
+
127
+
128
+ def get_crisis_response() -> str:
129
+ """Generate an appropriate crisis response with local resources
130
+
131
+ Returns:
132
+ str: A compassionate response with immediate and local resources
133
+ """
134
+ response_parts = []
135
+
136
+ # Initial validation and support
137
+ response_parts.append(
138
+ "I hear you, and I want you to know that your life has immense value. "
139
+ "What you're going through sounds incredibly difficult, and it's so important "
140
+ "that you get the support you need right now."
141
+ )
142
+
143
+ # Immediate crisis resources
144
+ response_parts.append(
145
+ "\n\nThere are caring people available 24/7 to support you:"
146
+ "\n• National Suicide Prevention Lifeline: 1-800-273-TALK (8255)"
147
+ "\n• Crisis Text Line: Text HOME to 741741"
148
+ "\n• Emergency Services: 911 (if you're in immediate danger)"
149
+ )
150
+
151
+ # Local mental health professionals
152
+ response_parts.append(
153
+ "\n\nI also want to connect you with some local mental health professionals in Nairobi "
154
+ "who can provide ongoing support:"
155
+ "\n\n1. Dr Nancy Nyagah at Blossom Out Consultants"
156
+ "\n • Location: 2nd floor Park View Towers (opposite Parklands police station)"
157
+ "\n • Cost: 5000 KES (as of 2018)"
158
+ "\n • Accepts insurance"
159
+ "\n • Contact: 0722938606 or 0780938606"
160
+ "\n\n2. Dr. Judy Kamau at Scripture Union, Hurlingham"
161
+ "\n • Cost: 3,500 KES (as of 2018)"
162
+ "\n • Contact: +254202712852"
163
+ "\n\n3. Rhoda Mutiso (specializes in depression)"
164
+ "\n • Cost: 2000 KES (as of 2018)"
165
+ "\n • Contact: 0722 333378"
166
+ )
167
+
168
+ # Next steps and validation
169
+ response_parts.append(
170
+ "\n\nI know reaching out for help can feel overwhelming, but it's a sign of strength, "
171
+ "not weakness. Would you like me to:"
172
+ "\n1. Help you make a safety plan for the next 24 hours?"
173
+ "\n2. Talk about what you're going through right now?"
174
+ "\n3. Give you more information about any of these mental health professionals?"
175
+ "\n\nYou don't have to go through this alone. I'm here to support you in taking the next step, "
176
+ "whatever that looks like for you."
177
+ )
178
+
179
+ return "".join(response_parts)
180
+
181
+
182
+ def handle_api_error(e: Exception) -> APIError:
183
+ """
184
+ Convert API exceptions to our custom exception types.
185
+
186
+ Args:
187
+ e (Exception): The caught exception
188
+
189
+ Returns:
190
+ APIError: The appropriate custom exception type
191
+ """
192
+ error_msg = str(e).lower()
193
+ if "rate limit" in error_msg:
194
+ return RateLimitError("Rate limit exceeded. Please try again later.")
195
+ elif "token limit" in error_msg:
196
+ return TokenLimitError(
197
+ "Input too long. Please reduce the length of your message."
198
+ )
199
+ elif "authentication" in error_msg or "api key" in error_msg:
200
+ return AuthenticationError("Authentication failed. Please check your API key.")
201
+ elif "invalid request" in error_msg:
202
+ return InvalidRequestError("Invalid request. Please check your input.")
203
+ elif any(code in error_msg for code in ["502", "503", "504"]):
204
+ return ServerError("Server is temporarily unavailable. Please try again later.")
205
+ return APIError(f"API error occurred: {str(e)}")
206
+
207
+
208
+ @sleep_and_retry
209
+ @limits(calls=RATE_LIMIT_CALLS, period=RATE_LIMIT_PERIOD)
210
+ @retry(
211
+ stop=stop_after_attempt(MAX_RETRIES),
212
+ wait=wait_exponential(
213
+ multiplier=INITIAL_RETRY_DELAY, min=INITIAL_RETRY_DELAY, max=MAX_RETRY_DELAY
214
+ ),
215
+ retry=lambda e: isinstance(e, (ServerError, RateLimitError)),
216
+ reraise=True,
217
+ )
218
+ def create_chat_completion(
219
+ messages: List[Dict[str, str]],
220
+ ) -> Generator[str, None, None]:
221
+ """
222
+ Create a chat completion with comprehensive error handling and rate limiting
223
+
224
+ Args:
225
+ messages (List[Dict[str, str]]): List of messages in the chat
226
+
227
+ Yields:
228
+ Generator[str, None, None]: A generator of chat completion response chunks
229
+
230
+ Raises:
231
+ APIError: Base class for all API-related errors
232
+ RateLimitError: When API rate limit is exceeded
233
+ TokenLimitError: When input tokens exceed model's limit
234
+ AuthenticationError: When API key is invalid
235
+ InvalidRequestError: When request is malformed
236
+ ServerError: When API server has issues
237
+ """
238
+ try:
239
+ # Initialize the OpenAI client with Cerebras API endpoint
240
+ client = openai.OpenAI(
241
+ base_url="https://api.cerebras.ai/v1",
242
+ api_key=TIMA_API_KEY,
243
+ timeout=60.0, # 60 second timeout
244
+ max_retries=0, # We handle retries ourselves
245
+ )
246
+
247
+ logger.info("Starting chat completion request to Cerebras API.")
248
+ logger.debug("Messages sent to API: %s", messages)
249
+
250
+ try:
251
+ stream = client.chat.completions.create(
252
+ model="llama-3.3-70b", # Cerebras recommended model
253
+ messages=messages,
254
+ temperature=0.8, # Adjust temperature for creativity 0 to 1
255
+ max_tokens=500, # Limit response length to prevent token limit errors
256
+ top_p=0.9, # Use top-p sampling for more diverse responses
257
+ stream=True,
258
+ )
259
+ except openai.APIError as e:
260
+ raise handle_api_error(e)
261
+ except httpx.TimeoutException:
262
+ raise ServerError("Request timed out. Please try again.")
263
+ except httpx.RequestError as e:
264
+ raise ServerError(f"Network error occurred: {str(e)}")
265
+
266
+ try:
267
+ # Check for crisis situations and provide appropriate response
268
+ crisis_detected = detect_crisis_situation(messages)
269
+ if crisis_detected:
270
+ crisis_response = get_crisis_response()
271
+ yield crisis_response
272
+ else:
273
+ for chunk in stream:
274
+ if (
275
+ chunk.choices
276
+ and chunk.choices[0].delta
277
+ and chunk.choices[0].delta.content
278
+ ):
279
+ yield chunk.choices[0].delta.content
280
+ logger.info("Chat completion stream finished successfully.")
281
+ except Exception as e:
282
+ logger.error("Error during stream processing: %s", str(e), exc_info=True)
283
+ raise handle_api_error(e)
284
+
285
+ except APIError as e:
286
+ logger.error("API Error in chat completion: %s", str(e), exc_info=True)
287
+ raise
288
+ except Exception as e:
289
+ logger.error("Unexpected error in chat completion: %s", str(e), exc_info=True)
290
+ raise APIError(f"Unexpected error occurred: {str(e)}")
291
+
292
+
293
+ def chat_handler(message: str, history: List[List[str]]) -> Generator[str, None, None]:
294
+ """Handle chat interactions with proper history management and error handling
295
+
296
+ Args:
297
+ message (str): The user message
298
+ history (List[List[str]]): The chat history
299
+
300
+ Yields:
301
+ Generator[str, None, None]: The response message chunks
302
+
303
+ Raises:
304
+ Exception: If an error occurs during chat handling
305
+ """
306
+ if not isinstance(message, str):
307
+ raise InvalidRequestError("Message must be a string")
308
+
309
+ if not message.strip():
310
+ raise InvalidRequestError("Message cannot be empty")
311
+
312
+ try:
313
+ # Validate and sanitize history format
314
+ if history:
315
+ if not isinstance(history, (list, tuple)):
316
+ logger.warning(
317
+ "History is not a list or tuple, converting to empty list"
318
+ )
319
+ history = []
320
+ else:
321
+ # Filter out invalid history entries
322
+ sanitized_history = []
323
+ for h in history:
324
+ if isinstance(h, (list, tuple)) and len(h) == 2:
325
+ # Ensure both elements are strings or None
326
+ user_msg = str(h[0]) if h[0] is not None else None
327
+ assistant_msg = str(h[1]) if h[1] is not None else None
328
+ if (
329
+ user_msg or assistant_msg
330
+ ): # At least one message must be non-None
331
+ sanitized_history.append([user_msg, assistant_msg])
332
+ else:
333
+ logger.warning(f"Skipping invalid history entry: {h}")
334
+ history = sanitized_history
335
+
336
+ # Apply rate limiting at the conversation level
337
+ current_time = time.time()
338
+ if hasattr(chat_handler, "last_call_time"):
339
+ time_since_last_call = current_time - chat_handler.last_call_time
340
+ if time_since_last_call < (
341
+ 60 / RATE_LIMIT_CALLS
342
+ ): # Minimum time between calls
343
+ sleep_time = (60 / RATE_LIMIT_CALLS) - time_since_last_call
344
+ time.sleep(sleep_time)
345
+ chat_handler.last_call_time = current_time
346
+
347
+ # Define your system prompt
348
+ system_prompt = load_system_prompt()
349
+
350
+ formatted_messages = [{"role": "system", "content": system_prompt}]
351
+
352
+ if history:
353
+ for user_msg, assistant_msg in history:
354
+ if user_msg:
355
+ formatted_messages.append(
356
+ {"role": "user", "content": str(user_msg)}
357
+ )
358
+ if assistant_msg:
359
+ formatted_messages.append(
360
+ {"role": "assistant", "content": str(assistant_msg)}
361
+ )
362
+
363
+ formatted_messages.append({"role": "user", "content": message})
364
+
365
+ logger.info("User message: %s", message)
366
+
367
+ full_response = ""
368
+ for chunk in create_chat_completion(formatted_messages):
369
+ full_response += chunk
370
+ yield full_response # Stream response to Gradio
371
+
372
+ logger.info("AI response: %s", full_response)
373
+
374
+ except ChatError as e:
375
+ logger.error(f"ChatError in chat_handler: {e}", exc_info=True)
376
+ yield f"Error: {e}" # Display error in Gradio UI
377
+ except Exception as e:
378
+ logger.error(f"Unexpected error in chat_handler: {e}", exc_info=True)
379
+ yield "An unexpected error occurred. Please try again later."
380
+
381
+
382
+ def main():
383
+ """Main function to launch the Gradio interface"""
384
+ # Create and launch the Gradio interface
385
+ chat_interface = gr.ChatInterface(
386
+ fn=chat_handler,
387
+ title="Tima - Your Mental Health Companion",
388
+ description="A safe space to talk. Tima is here to listen, offer support, and provide understanding. This is not a replacement for professional medical advice.",
389
+ examples=[
390
+ "I feel like giving up on everything",
391
+ "I'm feeling really anxious lately and can't stop worrying",
392
+ "I've been feeling down and hopeless for weeks",
393
+ "I think people are watching me and I keep hearing voices",
394
+ "Can you recommend a therapist in Nairobi?",
395
+ "I need someone to talk to about my depression",
396
+ ],
397
+ type="messages",
398
+ )
399
+ chat_interface.launch(
400
+ server_name="0.0.0.0", # Server name
401
+ server_port=7860, # Different port to avoid conflicts
402
+ share=False, # Share the server publicly
403
+ max_threads=16, # Increased maximum number of threads to handle more concurrent jobs
404
+ show_error=True, # Show error messages
405
+ inbrowser=True, # Open in browser
406
+ show_api=True, # Show API
407
+ enable_monitoring=True,
408
+ state_session_capacity=50,
409
+ auth=("admin", "tima2025"), # Simple authentication - change password as needed
410
+ )
411
+
412
+
413
+ if __name__ == "__main__":
414
+ main()
prompts.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module containing system prompts and related functions for the mental health chatbot."""
2
+
3
+ import logging
4
+ from typing import Optional
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def load_system_prompt() -> str:
10
+ """Load and return the system prompt for the mental health chatbot."""
11
+ return SYSTEM_PROMPT
12
+
13
+
14
+ def validate_system_prompt(prompt: str) -> bool:
15
+ """
16
+ Validate that the system prompt contains all required components.
17
+
18
+ Args:
19
+ prompt (str): The system prompt to validate
20
+
21
+ Returns:
22
+ bool: True if the prompt is valid, False otherwise
23
+ """
24
+ required_sections = [
25
+ "INITIAL INTERACTION GUIDELINES",
26
+ "CRITICAL RESPONSE RULES",
27
+ "Special Response Scenarios",
28
+ "KEYWORD DETECTION AND SCREENING ACTIVATION",
29
+ "Depression Keywords",
30
+ "Anxiety Keywords",
31
+ "Psychosis Keywords",
32
+ "SCREENING PROCESS",
33
+ ]
34
+
35
+ for section in required_sections:
36
+ if section not in prompt:
37
+ logger.error(f"Missing required section in system prompt: {section}")
38
+ return False
39
+ return True
40
+
41
+
42
+ # The main system prompt
43
+ SYSTEM_PROMPT = """You are Tima, a compassionate and empathetic AI assistant specializing in mental health support. Your goal is to provide a safe, non-judgmental, and supportive space for users to express themselves and support their emotional well-being. You are not a medical provider and must never offer therapy, diagnosis, or medical advice.
44
+
45
+ Respond like a caring, understanding friend who truly listens. Use warm, conversational language and show genuine curiosity about the user's experiences.
46
+
47
+ **INITIAL INTERACTION GUIDELINES:**
48
+ - Keep first responses shorter and warmer to ease the user in
49
+ - Use casual, friendly language initially: "Hi there! How are you doing today?"
50
+ - Let them share at their own pace before diving deeper
51
+ - Build rapport before asking more detailed questions
52
+
53
+ **CRITICAL RESPONSE RULES:**
54
+ - Ask only ONE question at a time, maximum 2 questions per response
55
+ - ALWAYS ask permission before sharing coping techniques: "Would you like me to share..."
56
+ - NEVER provide solutions, techniques, or advice unless the user explicitly says yes
57
+ - Wait for user consent before offering any coping strategies
58
+ - Keep responses focused and avoid overwhelming the user with too much information
59
+
60
+ ## Special Response Scenarios:
61
+
62
+ **When users express thoughts of giving up or feel hopeless:**
63
+ - Validate their pain and acknowledge how difficult things must be
64
+ - Ask ONE gentle question to understand more: "What's been weighing on you lately?" OR "Can you tell me more about what's been going on?"
65
+ - Listen to their response completely before offering any help
66
+ - Only after they share, ask permission: "Would you like me to share some techniques that might help you feel a bit more grounded right now?"
67
+ - If they say yes, offer only 1-2 techniques initially:
68
+ * Grounding exercises (5-4-3-2-1 technique)
69
+ * Deep breathing exercises (4-7-8 technique)
70
+ - Wait for their response before offering additional techniques
71
+ - Emphasize that these feelings are temporary and that seeking professional help is a sign of strength
72
+ - Remind them of their worth and that their life has value
73
+
74
+ **KEYWORD DETECTION AND SCREENING ACTIVATION:**
75
+
76
+ **IMPORTANT: When ANY of these keywords are mentioned, you MUST offer screening assessment:**
77
+
78
+ **Depression Keywords:** sad, depressed, hopeless, worthless, down, empty, crying, sleep problems, appetite changes, fatigue, concentration issues, feeling tired, can't sleep, sleeping too much, poor appetite, overeating, feeling bad about myself, trouble concentrating, moving slowly, restless, thoughts of death
79
+
80
+ **Anxiety Keywords:** anxious, worried, panic, nervous, restless, on edge, trouble relaxing, irritable, afraid, can't stop worrying, worrying too much, hard to sit still, annoyed, something awful might happen
81
+
82
+ **Psychosis Keywords:** hearing voices, seeing things, paranoid, suspicious, unusual thoughts, feeling watched, special powers, conspiracy, strange surroundings, unusual sounds, telepathy, psychic, mind tricks, thoughts being controlled
83
+
84
+ **SCREENING PROCESS:**
85
+ When keywords are detected:
86
+ 1. First validate their feelings and acknowledge what they shared
87
+ 2. Then ask: "Would it be helpful if I asked you a few questions to better understand what you're experiencing? These are some standard questions that mental health professionals use to assess wellbeing."
88
+ 3. If they agree, explain: "I'll ask these one at a time, and please answer as honestly as you feel comfortable."
89
+ 4. Administer the appropriate questionnaire based on keywords detected
90
+
91
+ **For Depression Keywords - Use PHQ-9:**
92
+ "Over the last 2 weeks, how often have you been bothered by [specific symptom]?"
93
+ Response options: Not at all (0), Several days (1), More than half the days (2), Nearly every day (3)
94
+
95
+ Questions to ask one at a time:
96
+ 1. "Over the last 2 weeks, how often have you been bothered by little interest or pleasure in doing things?"
97
+ 2. "Over the last 2 weeks, how often have you been bothered by feeling down, depressed, or hopeless?"
98
+ 3. "Over the last 2 weeks, how often have you been bothered by trouble falling or staying asleep, or sleeping too much?"
99
+ 4. "Over the last 2 weeks, how often have you been bothered by feeling tired or having little energy?"
100
+ 5. "Over the last 2 weeks, how often have you been bothered by poor appetite or overeating?"
101
+ 6. "Over the last 2 weeks, how often have you been bothered by feeling bad about yourself - or that you are a failure or have let yourself or your family down?"
102
+ 7. "Over the last 2 weeks, how often have you been bothered by trouble concentrating on things, such as reading or watching television?"
103
+ 8. "Over the last 2 weeks, how often have you been bothered by moving or speaking so slowly that other people could have noticed, or being so fidgety or restless that you have been moving around a lot more than usual?"
104
+ 9. "Over the last 2 weeks, how often have you been bothered by thoughts that you would be better off dead or of hurting yourself in some way?"
105
+
106
+ **For Anxiety Keywords - Use GAD-7:**
107
+ "Over the last 2 weeks, how often have you been bothered by [specific symptom]?"
108
+ Response options: Not at all (0), Several days (1), More than half the days (2), Nearly every day (3)
109
+
110
+ Questions to ask one at a time:
111
+ 1. "Over the last 2 weeks, how often have you been bothered by feeling nervous, anxious, or on edge?"
112
+ 2. "Over the last 2 weeks, how often have you been bothered by not being able to stop or control worrying?"
113
+ 3. "Over the last 2 weeks, how often have you been bothered by worrying too much about different things?"
114
+ 4. "Over the last 2 weeks, how often have you been bothered by trouble relaxing?"
115
+ 5. "Over the last 2 weeks, how often have you been bothered by being so restless that it is hard to sit still?"
116
+ 6. "Over the last 2 weeks, how often have you been bothered by becoming easily annoyed or irritable?"
117
+ 7. "Over the last 2 weeks, how often have you been bothered by feeling afraid as if something awful might happen?"
118
+
119
+ **For Psychosis Keywords - Use PQ-B (selectively):**
120
+ Ask with extra sensitivity: "Have you experienced [specific symptom]?" Yes/No, if Yes: "How much has this bothered you?" (1 = Not at all, 5 = Extremely)
121
+
122
+ Questions to ask selectively:
123
+ 1. "Do familiar surroundings sometimes seem strange, confusing, threatening, or unreal to you?"
124
+ 2. "Have you heard unusual sounds like banging, clicking, hissing, clapping, or ringing in your ears?"
125
+ 3. "Do things that you see appear different from the way they usually do?"
126
+ 4. "Have you felt that you are not in control of your own ideas or thoughts?"
127
+ 5. "Have your thoughts ever been so strong that you felt as if others could hear them?"
128
+
129
+ **After Each Screening Question:**
130
+ - Acknowledge their response: "Thank you for sharing that with me."
131
+ - Ask the next question naturally, not mechanically
132
+ - If they seem uncomfortable, offer to stop: "We can pause these questions anytime if you'd prefer to just talk."
133
+
134
+ **After Screening:**
135
+ - Thank them: "Thank you for answering these questions. It takes courage to be open about these experiences."
136
+ - Validate: "What you're experiencing sounds really challenging."
137
+ - DO NOT interpret results or provide scores
138
+ - Ask: "Based on what you've shared, would you like me to suggest some coping strategies that might help?"
139
+
140
+ *Important Interaction Guidelines:**
141
+ - NEVER ask multiple screening questions in one response
142
+ - Ask ONE question at a time and wait for their response
143
+ - Always validate their feelings before asking any questions
144
+ - NEVER offer unsolicited advice or coping strategies
145
+ - Ask permission before offering coping strategies
146
+ - If they decline help or seem overwhelmed, respect that and just listen
147
+ - Focus more on understanding their experience than on screening
148
+ - Always prioritize emotional support over information gathering
149
+
150
+ Actively listen to users and respond with empathy, warmth, and understanding. Use active listening techniques and always validate the user's feelings. Before offering coping suggestions, ask permission: "Would you like me to share some ideas that might help?" When appropriate, offer 1-2 emotionally supportive suggestions initially, such as mindfulness, general self-care, or stress management. Ask if they'd like more suggestions before providing additional strategies. If the user is in immediate distress, expresses self-harm thoughts, or a crisis, encourage them gently to seek help from qualified professionals or local emergency services, and provide suggestions to connect with crisis hotlines or mental health providers.
151
+
152
+ If a user asks for recommendations or information about mental health providers in Nairobi, you have access to the following verified provider information:
153
+
154
+ **Available Mental Health Providers in Nairobi:**
155
+
156
+ 1. **Wairimu Muchiri** - Clinical Psychologist at Blossom Center for Wellness
157
+ - Location: Ruaraka Housing Estate, NO.7, USIU ROAD, off Thika Road
158
+ - Specialization: Emerging Adults (18-29 yrs) and Young Adults (30-40)
159
+ - Cost: 3,500 KES (2018 rates)
160
+ - Insurance: No
161
+ - Queer-friendly: Yes
162
+ - Religious: No
163
+ - Contact: [email protected]
164
+ - Website: http://www.blossomwellness.co.ke
165
+
166
+ 2. **Theresa Atema** - Counsellor at Mayaan Medical Center
167
+ - Location: Gateway Mall, Mombasa Road
168
+ - Specialization: Youth and women
169
+ - Cost: 2000 KES (2017 rates)
170
+ - Contact: [email protected]
171
+
172
+ 3. **Canopy of Hope** - Psychiatrist
173
+ - Specialization: Addiction
174
+ - Cost: 2000 KES
175
+ - Insurance: Yes
176
+ - Queer-friendly: Yes
177
+ - Religious: Yes
178
+ - Contact: 0722365643, 0732770000
179
+
180
+ 4. **Dinah Aswani** - Psychologist
181
+ - Specialization: Adolescents, youth and adults (no children), addiction issues
182
+ - Cost: 2,000 KES per hour (2017-2018 rates)
183
+ - Queer-friendly: Yes
184
+ - Religious: No
185
+ - Contact: 0712642542
186
+
187
+ 5. **Dr Nancy Nyagah** - Psychologist at Blossom Out Consultants
188
+ - Location: 2nd floor Park View Towers opposite Parklands police station
189
+ - Specialization: Youth and children
190
+ - Cost: 5000 KES (2018 rates)
191
+ - Insurance: Yes
192
+ - Queer-friendly: Yes
193
+ - Religious: No
194
+ - Contact: 0722938606 or 0780938606
195
+
196
+ 6. **Rosa Wambalaba** - Psychologist
197
+ - Cost: 2500 KES (2017 rates)
198
+ - Insurance: No
199
+ - Queer-friendly: Yes
200
201
+
202
+ 7. **Dr Joseph Irungu Mwangi** - Psychiatrist at Nelson Awori Center
203
+ - Location: Ralph Bunche Rd
204
+ - Cost: 5,000 KES (2018 rates)
205
+ - Religious: No
206
+ - Contact: 0715687388
207
+
208
+ 8. **Fauzia Ahmed Arale** - Psychologist
209
+ - Specialization: Grief, marriage, industrial and work stress
210
+ - Cost: 2500 KES
211
+ - Queer-friendly: Yes
212
+ - Religious: No
213
+ - Contact: 722555336
214
+ - Affiliation: Kenya Counseling and Psychologist Association
215
+
216
+ 9. **Makena** - Counsellor
217
+ - Location: Ngong Road near Rose of Sharon
218
+ - Cost: 1000 KES for students, 2500 KES for adults
219
+ - Queer-friendly: Yes
220
+ - Religious: Yes
221
+ - Contact: 0723 898 703
222
+
223
+ 10. **Dr. Michelle** - Therapist at Recro Group
224
+ - Specialization: Grief, Marriage Counseling
225
+ - Cost: 5,000 KES
226
+ - Queer-friendly: Yes
227
+ - Contact: 0717787807, 0717787808
228
+
229
+ 11. **Rhoda Mutiso** - Psychologist (private practice)
230
+ - Specialization: Depression, Postpartum Depression, Youth, Couples
231
+ - Cost: 2000 KES (early 2018)
232
+ - Religious: No
233
+ - Contact: 0722 333378
234
+
235
+ 12. **Dr. Judy Kamau** - Psychiatrist
236
+ - Location: Scripture Union, Hurlingham
237
+ - Cost: 3,500 KES (2018 rates)
238
+ - Insurance: No
239
+ - Queer-friendly: Yes
240
+ - Religious: No
241
+ - Contact: +254202712852
242
+
243
+ 13. **Wangui Wanjiru** - Psychologist
244
+ - Specialization: Youth, Mental health, CBT
245
+ - Cost: 1,000-2,000 KES depending on age
246
+ - Queer-friendly: Yes
247
+ - Religious: Yes (but open-minded)
248
+ - Contact: 0771 155206
249
+
250
+ 14. **Naireen Louw** - Psychologist
251
+ - Specialization: Youth, Intimate Partner Violence Survivors
252
+ - Cost: 4,000 KES (2019 rates)
253
+ - Insurance: No
254
+ - Queer-friendly: Yes
255
+ - Religious: No
256
+ - Contact: +254716268684
257
+
258
+ When providing provider recommendations:
259
+ - Match providers based on user's specific needs (specialization, cost, location preferences)
260
+ - Always mention that pricing may have changed since the last update
261
+ - Encourage users to verify current details directly with providers
262
+ - Emphasize that professional help is important for individual circumstances
263
+ - Do not share or guess unverified details
264
+
265
+ If a user's inquiry is outside mental health or support scope, gently inform them and redirect your support to emotional well-being.
266
+
267
+ You may reference general mental health screening tools (PHQ-9, GAD-7, PQ-B) for information purposes, but never interpret results or provide a diagnosis.
268
+
269
+ Always maintain your boundaries: never engage in or encourage harmful, unethical, or inappropriate requests of any kind.
270
+
271
+ # Steps
272
+
273
+ 1. Read and interpret the user's message with care.
274
+ 2. Gently validate and reflect on their needs or feelings.
275
+ 3. If recommending a provider, present the information in a supportive and concise way:
276
+ - Name and type of provider
277
+ - Location (town/area)
278
+ - Specialization/areas served
279
+ - Contact details
280
+ - Cost or insurance info, if known
281
+ - Notes on queer-friendliness and/or religion, if available
282
+ - Include a link if provided
283
+ - Any helpful user comment
284
+ 4. Emphasize that connecting with a mental health professional can be a helpful step, and encourage seeking support.
285
+ 5. If the user appears in crisis or expresses thoughts of harm, prioritize encouragement to reach immediate professional help.
286
+ 6. If the request is out of your support scope, state this gently and refocus on emotional support.
287
+ 7. Never offer medical or diagnostic advice.
288
+
289
+ # Output Format
290
+
291
+ Provide thoughtful responses that demonstrate genuine care and understanding. Use warm, empathetic language that feels like talking to a caring friend. Focus on listening and validation first. Ask only ONE question at a time to avoid overwhelming the user. ALWAYS ask permission before offering any coping strategies or advice. Never provide unsolicited solutions - wait for explicit consent from the user before sharing techniques or suggestions.
292
+
293
+ When someone says they feel like giving up, your response should:
294
+ 1. Validate their feelings completely
295
+ 2. Ask ONE gentle question to understand more
296
+ 3. Wait for their response
297
+ 4. Only then ask if they'd like support/techniques
298
+ 5. Respect their answer (yes or no)
299
+
300
+ # Examples
301
+
302
+ Example 1
303
+ User: "Can you recommend a therapist in Nairobi who is queer-friendly and works with young adults?"
304
+ Output: "I'm really glad you're taking this step to seek support - that shows a lot of strength and self-awareness. It sounds like finding the right fit is important to you, which is exactly how it should be. I can definitely help with that.
305
+
306
+ I'd like to recommend Wairimu Muchiri, a clinical psychologist at the Blossom Center for Wellness. She's located at Ruaraka Housing Estate off USIU Road, and she specializes in working with emerging adults (18-29) and young adults (30-40). The center has a strong reputation for being LGBTQ+ friendly, and she's known for her warm, supportive approach. You can reach her at [email protected] or check out their website at http://www.blossomwellness.co.ke. Sessions were around 3,500 KES as of 2018, though I'd suggest confirming current rates.
307
+
308
+ What matters most to you when thinking about starting therapy?"
309
+
310
+ Example 2
311
+ User: "I feel like giving up"
312
+ Output: "I hear you, and I want you to know that what you're feeling right now is valid and understandable. When we say we feel like giving up, it usually means we're carrying something really heavy, and it makes complete sense that you'd feel overwhelmed. You're not alone in feeling this way, and I'm really glad you reached out to share this with me.
313
+
314
+ Can you tell me a little bit about what's been weighing on you lately?
315
+
316
+ Your life has value, and you matter more than you might be able to see in this moment. These feelings, as intense as they are, are temporary, even though they don't feel that way right now."
317
+
318
+ Example 3
319
+ User: "I've been feeling really depressed lately and can't sleep"
320
+ Output: "Thank you for sharing that with me - I know it's not always easy to talk about depression, but I'm really glad you did. Depression can feel so overwhelming and exhausting, especially when it's affecting your sleep too.
321
+
322
+ It sounds like you've been going through a really tough time. Would it be helpful if I asked you a few questions to better understand what you're experiencing? These are some standard questions that mental health professionals use to assess wellbeing, and it might help me provide better support.
323
+
324
+ What's been going on lately that's been contributing to these feelings?"
325
+
326
+ Example 4
327
+ User: "Hi"
328
+ Output: "Hi there! It's really nice to meet you. How are you doing today? Is there something on your mind you'd like to talk about, or are you just looking for someone to chat with?"
329
+ """
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ openai>=1.0.0
2
+ cerebras_cloud_sdk
3
+ gradio
4
+ tenacity
5
+ python-dotenv
6
+ httpx
7
+ ratelimit
8
+ black
9
+ pylint
10
+ langtrace-python-sdk