Spaces:
Sleeping
Sleeping
Said Lfagrouche
commited on
Commit
·
8e66145
0
Parent(s):
Initial commit with Git LFS enabled
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .dockerignore +36 -0
- .env.docker +6 -0
- .env.example +4 -0
- .gitattributes +42 -0
- .gitignore +56 -0
- Backend/Dockerfile +27 -0
- Dockerfile +48 -0
- README.md +12 -0
- api_mental_health.py +1370 -0
- create_vector_db.py +188 -0
- data/conversations/2ae6de8a-bf2d-4d01-8269-e13d5cf3d927/9b2ff07a-def6-43a7-9efd-e8ed6e8cdfd6.json +11 -0
- data/conversations/33f954fb-f60d-4ea0-bd59-1396253c8cdc/71bea3ca-82ef-4857-93cf-84955c3eddb3.json +11 -0
- data/conversations/5fc04208-6f76-4774-9c40-bda7e0a28caf/2c256fde-2660-453e-b404-37bcc8a8f93a.json +11 -0
- data/conversations/5fc04208-6f76-4774-9c40-bda7e0a28caf/72ebbd21-33dc-4148-8905-1d944451c3ba.json +11 -0
- data/conversations/67e763f8-5a97-491d-977b-2071af8a8abe/ef88b6ba-c13b-49ff-9dfd-33c6e58d1487.json +11 -0
- data/conversations/72f347f1-ba29-4050-bb6c-02e0851e088a/75c4bb13-daa6-415b-8913-6cc584324940.json +11 -0
- data/conversations/b132a94e-553b-47cd-a855-fcf853dc8320/6465a103-6163-40e7-b7a5-acb2e475181c.json +11 -0
- data/conversations/d5072833-ef63-4ed2-92ab-d1476f81e17d/34e2ac93-7fef-467f-a672-6a5e0638440f.json +11 -0
- data/sessions/08f51081-9d82-4a8c-a3f4-c159a584c3a4.json +12 -0
- data/sessions/139f262d-769d-4c3f-8d04-0cefc2cdd33e.json +12 -0
- data/sessions/261e6110-82a3-4f26-bc1e-bbebabea059d.json +12 -0
- data/sessions/2ae6de8a-bf2d-4d01-8269-e13d5cf3d927.json +12 -0
- data/sessions/33f954fb-f60d-4ea0-bd59-1396253c8cdc.json +12 -0
- data/sessions/37f38f66-78af-4544-88b9-3c31734c429d.json +12 -0
- data/sessions/5fc04208-6f76-4774-9c40-bda7e0a28caf.json +12 -0
- data/sessions/67e763f8-5a97-491d-977b-2071af8a8abe.json +12 -0
- data/sessions/72f347f1-ba29-4050-bb6c-02e0851e088a.json +12 -0
- data/sessions/75289adb-550c-4a4d-9035-ec54f1c954c6.json +12 -0
- data/sessions/b0408071-2a67-4cf4-bdf8-593b71dba326.json +12 -0
- data/sessions/b132a94e-553b-47cd-a855-fcf853dc8320.json +13 -0
- data/sessions/d5072833-ef63-4ed2-92ab-d1476f81e17d.json +12 -0
- data/users/13c3dcfc-d8ff-4305-88fe-8e610bd8457b.json +16 -0
- data/users/1dc2a9f3-0e67-4086-9119-be87d1fcf147.json +16 -0
- data/users/3989efcc-7bd0-49a6-8f26-53b0f9169fd5.json +16 -0
- data/users/4ba9d533-2d2a-4f1b-b00e-bd4f58f38fa1.json +16 -0
- data/users/4d352ce8-fd66-4756-9ee1-617406668f93.json +16 -0
- data/users/70c8bae7-d171-4c2f-a27a-0ba4f3b1778b.json +18 -0
- data/users/79a850ac-bee6-45e9-8aef-f47fa20dc669.json +16 -0
- data/users/c22207e8-0863-4592-be45-0c68d0ce8df3.json +18 -0
- data/users/d31a3577-b6b8-4e2a-a292-5a1a3c373dc5.json +16 -0
- data/users/ddb6322a-4f73-435b-8734-ff8d32a23bcd.json +16 -0
- data/users/e2d0468b-f229-4b8f-9fa1-95bd9be68eba.json +18 -0
- mental_health_model_artifacts/chroma_db/chroma.sqlite3 +3 -0
- mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/data_level0.bin +3 -0
- mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/header.bin +3 -0
- mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/index_metadata.pickle +3 -0
- mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/length.bin +3 -0
- mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/link_lists.bin +3 -0
- mental_health_model_artifacts/crisis_classifier.pkl +3 -0
- mental_health_model_artifacts/feature_selector.pkl +3 -0
.dockerignore
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Virtual environments
|
2 |
+
venv/
|
3 |
+
.env
|
4 |
+
|
5 |
+
# System files
|
6 |
+
.DS_Store
|
7 |
+
__pycache__/
|
8 |
+
*.py[cod]
|
9 |
+
*$py.class
|
10 |
+
*.so
|
11 |
+
.Python
|
12 |
+
build/
|
13 |
+
develop-eggs/
|
14 |
+
dist/
|
15 |
+
downloads/
|
16 |
+
eggs/
|
17 |
+
.eggs/
|
18 |
+
lib/
|
19 |
+
lib64/
|
20 |
+
parts/
|
21 |
+
sdist/
|
22 |
+
var/
|
23 |
+
wheels/
|
24 |
+
*.egg-info/
|
25 |
+
.installed.cfg
|
26 |
+
*.egg
|
27 |
+
|
28 |
+
# Git
|
29 |
+
.git
|
30 |
+
.gitignore
|
31 |
+
|
32 |
+
# Local data directories (will be created in container)
|
33 |
+
data/
|
34 |
+
|
35 |
+
# Test directories
|
36 |
+
test_chroma_db/
|
.env.docker
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Template for Docker deployment
|
2 |
+
# Replace with your actual API keys before deployment or use Hugging Face Spaces Secrets
|
3 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
4 |
+
LANGCHAIN_API_KEY=your_langchain_api_key_here
|
5 |
+
LANGCHAIN_TRACING_V2=true
|
6 |
+
LANGCHAIN_PROJECT=MentalHealthCounselorPOC
|
.env.example
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
OPENAI_API_KEY=
|
2 |
+
LANGCHAIN_API_KEY=
|
3 |
+
LANGCHAIN_TRACING_V2='true'
|
4 |
+
LANGCHAIN_PROJECT=
|
.gitattributes
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
36 |
+
# Set up Git LFS for large files
|
37 |
+
*.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
38 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
39 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
40 |
+
mental_health_model_artifacts/chroma_db/chroma.sqlite3 filter=lfs diff=lfs merge=lfs -text
|
41 |
+
mental_health_model_artifacts/chroma_db/**/*.bin filter=lfs diff=lfs merge=lfs -text
|
42 |
+
mental_health_model_artifacts/**/*.pkl filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Virtual environments
|
2 |
+
venv/
|
3 |
+
env/
|
4 |
+
ENV/
|
5 |
+
.env
|
6 |
+
|
7 |
+
# Python cache
|
8 |
+
__pycache__/
|
9 |
+
*.py[cod]
|
10 |
+
*$py.class
|
11 |
+
*.so
|
12 |
+
.Python
|
13 |
+
build/
|
14 |
+
develop-eggs/
|
15 |
+
dist/
|
16 |
+
downloads/
|
17 |
+
eggs/
|
18 |
+
.eggs/
|
19 |
+
lib/
|
20 |
+
lib64/
|
21 |
+
parts/
|
22 |
+
sdist/
|
23 |
+
var/
|
24 |
+
wheels/
|
25 |
+
*.egg-info/
|
26 |
+
.installed.cfg
|
27 |
+
*.egg
|
28 |
+
|
29 |
+
# Jupyter Notebook
|
30 |
+
.ipynb_checkpoints
|
31 |
+
|
32 |
+
# OS specific
|
33 |
+
.DS_Store
|
34 |
+
.DS_Store?
|
35 |
+
._*
|
36 |
+
.Spotlight-V100
|
37 |
+
.Trashes
|
38 |
+
ehthumbs.db
|
39 |
+
Thumbs.db
|
40 |
+
|
41 |
+
# IDE specific
|
42 |
+
.idea/
|
43 |
+
.vscode/
|
44 |
+
*.swp
|
45 |
+
*.swo
|
46 |
+
|
47 |
+
# Local data directories (optional - you might want to keep these)
|
48 |
+
# Uncomment if you don't want to track these
|
49 |
+
# data/
|
50 |
+
|
51 |
+
# Test directories
|
52 |
+
test_chroma_db/
|
53 |
+
|
54 |
+
# Logs
|
55 |
+
logs/
|
56 |
+
*.log
|
Backend/Dockerfile
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.13-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
# Copy requirements file
|
6 |
+
COPY requirements.txt .
|
7 |
+
|
8 |
+
# Install dependencies
|
9 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
10 |
+
|
11 |
+
# Download NLTK data
|
12 |
+
RUN python -c "import nltk; nltk.download('punkt'); nltk.download('wordnet'); nltk.download('stopwords')"
|
13 |
+
|
14 |
+
# Create necessary directories
|
15 |
+
RUN mkdir -p data/users data/sessions data/conversations data/feedback
|
16 |
+
|
17 |
+
# Copy only the necessary files
|
18 |
+
COPY api_mental_health.py .
|
19 |
+
COPY .env.example .env
|
20 |
+
COPY create_vector_db.py .
|
21 |
+
COPY mental_health_model_artifacts/ mental_health_model_artifacts/
|
22 |
+
|
23 |
+
# Expose the port Hugging Face Spaces expects
|
24 |
+
EXPOSE 7860
|
25 |
+
|
26 |
+
# Command to run the application
|
27 |
+
CMD ["uvicorn", "api_mental_health:app", "--host", "0.0.0.0", "--port", "7860"]
|
Dockerfile
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.13-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
# Install git and git-lfs for downloading large files (if needed)
|
6 |
+
RUN apt-get update && \
|
7 |
+
apt-get install -y git git-lfs && \
|
8 |
+
apt-get clean && \
|
9 |
+
rm -rf /var/lib/apt/lists/*
|
10 |
+
|
11 |
+
# Copy requirements file
|
12 |
+
COPY requirements.txt .
|
13 |
+
|
14 |
+
# Install dependencies
|
15 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
16 |
+
|
17 |
+
# Download NLTK data
|
18 |
+
RUN python -c "import nltk; nltk.download('punkt'); nltk.download('wordnet'); nltk.download('stopwords')"
|
19 |
+
|
20 |
+
# Create necessary directories
|
21 |
+
RUN mkdir -p data/users data/sessions data/conversations data/feedback
|
22 |
+
RUN mkdir -p mental_health_model_artifacts/chroma_db
|
23 |
+
|
24 |
+
# Copy application files
|
25 |
+
COPY api_mental_health.py .
|
26 |
+
COPY .env.example .env
|
27 |
+
COPY create_vector_db.py .
|
28 |
+
|
29 |
+
# Copy model artifacts if they exist, or they should be mounted/downloaded at runtime
|
30 |
+
# Note: For Hugging Face Spaces deployment, you'll need to use Git LFS or
|
31 |
+
# provide a way to download these models at container startup
|
32 |
+
COPY mental_health_model_artifacts/ mental_health_model_artifacts/
|
33 |
+
|
34 |
+
# Create a startup script to handle potential model downloading
|
35 |
+
RUN echo '#!/bin/bash\n\
|
36 |
+
# Check if models exist\n\
|
37 |
+
if [ ! -f "mental_health_model_artifacts/crisis_classifier.pkl" ]; then\n\
|
38 |
+
echo "Warning: Model artifacts not found. Please mount them or implement a download method."\n\
|
39 |
+
fi\n\
|
40 |
+
# Start the API server\n\
|
41 |
+
exec uvicorn api_mental_health:app --host 0.0.0.0 --port 7860\n\
|
42 |
+
' > /app/start.sh && chmod +x /app/start.sh
|
43 |
+
|
44 |
+
# Expose the port Hugging Face Spaces expects
|
45 |
+
EXPOSE 7860
|
46 |
+
|
47 |
+
# Use the startup script
|
48 |
+
CMD ["/app/start.sh"]
|
README.md
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Thera Guide Ai
|
3 |
+
emoji: 🚀
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: purple
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
license: mit
|
9 |
+
short_description: crisis-detection
|
10 |
+
---
|
11 |
+
|
12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
api_mental_health.py
ADDED
@@ -0,0 +1,1370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# api_mental_health.py
|
2 |
+
from fastapi import FastAPI, HTTPException, UploadFile, File, Form
|
3 |
+
from pydantic import BaseModel
|
4 |
+
import pandas as pd
|
5 |
+
import numpy as np
|
6 |
+
import joblib
|
7 |
+
import re
|
8 |
+
import nltk
|
9 |
+
from nltk.tokenize import word_tokenize
|
10 |
+
from nltk.stem import WordNetLemmatizer
|
11 |
+
from nltk.corpus import stopwords
|
12 |
+
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
|
13 |
+
import chromadb
|
14 |
+
from chromadb.config import Settings
|
15 |
+
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
|
16 |
+
from langchain_chroma import Chroma
|
17 |
+
from openai import OpenAI
|
18 |
+
import os
|
19 |
+
from dotenv import load_dotenv
|
20 |
+
from langsmith import Client, traceable
|
21 |
+
from langchain_core.runnables import RunnablePassthrough
|
22 |
+
from langchain_core.prompts import ChatPromptTemplate
|
23 |
+
import logging
|
24 |
+
from typing import List, Dict, Optional, Any, Union, Annotated
|
25 |
+
from datetime import datetime
|
26 |
+
from uuid import uuid4, UUID
|
27 |
+
import json
|
28 |
+
import requests
|
29 |
+
from fastapi.responses import StreamingResponse
|
30 |
+
from io import BytesIO
|
31 |
+
import base64
|
32 |
+
|
33 |
+
# Set up logging
|
34 |
+
logging.basicConfig(level=logging.INFO)
|
35 |
+
logger = logging.getLogger(__name__)
|
36 |
+
|
37 |
+
# Load environment variables
|
38 |
+
load_dotenv()
|
39 |
+
|
40 |
+
# Download required NLTK data
|
41 |
+
nltk.download('punkt')
|
42 |
+
nltk.download('wordnet')
|
43 |
+
nltk.download('stopwords')
|
44 |
+
|
45 |
+
# Initialize FastAPI app
|
46 |
+
app = FastAPI(title="Mental Health Counselor API")
|
47 |
+
|
48 |
+
# Initialize global storage (to be replaced with proper database)
|
49 |
+
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
|
50 |
+
os.makedirs(DATA_DIR, exist_ok=True)
|
51 |
+
os.makedirs(os.path.join(DATA_DIR, "users"), exist_ok=True)
|
52 |
+
os.makedirs(os.path.join(DATA_DIR, "sessions"), exist_ok=True)
|
53 |
+
os.makedirs(os.path.join(DATA_DIR, "conversations"), exist_ok=True)
|
54 |
+
os.makedirs(os.path.join(DATA_DIR, "feedback"), exist_ok=True)
|
55 |
+
|
56 |
+
# Initialize components
|
57 |
+
STOPWORDS = set(stopwords.words("english"))
|
58 |
+
lemmatizer = WordNetLemmatizer()
|
59 |
+
analyzer = SentimentIntensityAnalyzer()
|
60 |
+
output_dir = "mental_health_model_artifacts"
|
61 |
+
|
62 |
+
# Global variables for models and vector store
|
63 |
+
response_clf = None
|
64 |
+
crisis_clf = None
|
65 |
+
vectorizer = None
|
66 |
+
le = None
|
67 |
+
selector = None
|
68 |
+
lda = None
|
69 |
+
vector_store = None
|
70 |
+
llm = None
|
71 |
+
openai_client = None
|
72 |
+
langsmith_client = None
|
73 |
+
|
74 |
+
# Load models and initialize ChromaDB at startup
|
75 |
+
@app.on_event("startup")
|
76 |
+
async def startup_event():
|
77 |
+
global response_clf, crisis_clf, vectorizer, le, selector, lda, vector_store, llm, openai_client, langsmith_client
|
78 |
+
|
79 |
+
# Check environment variables
|
80 |
+
if not os.environ.get("OPENAI_API_KEY"):
|
81 |
+
logger.error("OPENAI_API_KEY not set in .env file")
|
82 |
+
raise HTTPException(status_code=500, detail="OPENAI_API_KEY not set in .env file")
|
83 |
+
if not os.environ.get("LANGCHAIN_API_KEY"):
|
84 |
+
logger.error("LANGCHAIN_API_KEY not set in .env file")
|
85 |
+
raise HTTPException(status_code=500, detail="LANGCHAIN_API_KEY not set in .env file")
|
86 |
+
os.environ["LANGCHAIN_TRACING_V2"] = "true"
|
87 |
+
os.environ["LANGCHAIN_PROJECT"] = "MentalHealthCounselorPOC"
|
88 |
+
|
89 |
+
# Initialize LangSmith client
|
90 |
+
logger.info("Initializing LangSmith client")
|
91 |
+
langsmith_client = Client()
|
92 |
+
|
93 |
+
# Load saved components
|
94 |
+
logger.info("Loading model artifacts")
|
95 |
+
try:
|
96 |
+
response_clf = joblib.load(f"{output_dir}/response_type_classifier.pkl")
|
97 |
+
crisis_clf = joblib.load(f"{output_dir}/crisis_classifier.pkl")
|
98 |
+
vectorizer = joblib.load(f"{output_dir}/tfidf_vectorizer.pkl")
|
99 |
+
le = joblib.load(f"{output_dir}/label_encoder.pkl")
|
100 |
+
selector = joblib.load(f"{output_dir}/feature_selector.pkl")
|
101 |
+
|
102 |
+
try:
|
103 |
+
lda = joblib.load(f"{output_dir}/lda_model.pkl")
|
104 |
+
except Exception as lda_error:
|
105 |
+
logger.warning(f"Failed to load LDA model: {lda_error}. Creating placeholder model.")
|
106 |
+
from sklearn.decomposition import LatentDirichletAllocation
|
107 |
+
lda = LatentDirichletAllocation(n_components=10, random_state=42)
|
108 |
+
# Note: Placeholder is untrained; retrain for accurate results
|
109 |
+
|
110 |
+
except FileNotFoundError as e:
|
111 |
+
logger.error(f"Missing model artifact: {e}")
|
112 |
+
raise HTTPException(status_code=500, detail=f"Missing model artifact: {e}")
|
113 |
+
|
114 |
+
# Initialize ChromaDB
|
115 |
+
chroma_db_path = f"{output_dir}/chroma_db"
|
116 |
+
if not os.path.exists(chroma_db_path):
|
117 |
+
logger.error(f"ChromaDB not found at {chroma_db_path}. Run create_vector_db.py first.")
|
118 |
+
raise HTTPException(status_code=500, detail=f"ChromaDB not found at {chroma_db_path}. Run create_vector_db.py first.")
|
119 |
+
|
120 |
+
try:
|
121 |
+
logger.info("Initializing ChromaDB")
|
122 |
+
chroma_client = chromadb.PersistentClient(
|
123 |
+
path=chroma_db_path,
|
124 |
+
settings=Settings(anonymized_telemetry=False)
|
125 |
+
)
|
126 |
+
|
127 |
+
embeddings = OpenAIEmbeddings(
|
128 |
+
model="text-embedding-ada-002",
|
129 |
+
api_key=os.environ["OPENAI_API_KEY"],
|
130 |
+
disallowed_special=(),
|
131 |
+
chunk_size=1000
|
132 |
+
)
|
133 |
+
global vector_store
|
134 |
+
vector_store = Chroma(
|
135 |
+
client=chroma_client,
|
136 |
+
collection_name="mental_health_conversations",
|
137 |
+
embedding_function=embeddings
|
138 |
+
)
|
139 |
+
except Exception as e:
|
140 |
+
logger.error(f"Error initializing ChromaDB: {e}")
|
141 |
+
raise HTTPException(status_code=500, detail=f"Error initializing ChromaDB: {e}")
|
142 |
+
|
143 |
+
# Initialize OpenAI client and LLM
|
144 |
+
logger.info("Initializing OpenAI client and LLM")
|
145 |
+
global openai_client, llm
|
146 |
+
openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
147 |
+
llm = ChatOpenAI(
|
148 |
+
model="gpt-4o-mini",
|
149 |
+
temperature=0.7,
|
150 |
+
api_key=os.environ["OPENAI_API_KEY"]
|
151 |
+
)
|
152 |
+
|
153 |
+
# Pydantic model for request
|
154 |
+
class PatientContext(BaseModel):
|
155 |
+
context: str
|
156 |
+
|
157 |
+
# New Pydantic models for expanded API functionality
|
158 |
+
class UserProfile(BaseModel):
|
159 |
+
user_id: Optional[str] = None
|
160 |
+
username: str
|
161 |
+
name: str
|
162 |
+
role: str = "counselor"
|
163 |
+
specializations: List[str] = []
|
164 |
+
years_experience: Optional[int] = None
|
165 |
+
custom_crisis_keywords: List[str] = []
|
166 |
+
preferences: Dict[str, Any] = {}
|
167 |
+
created_at: Optional[datetime] = None
|
168 |
+
updated_at: Optional[datetime] = None
|
169 |
+
|
170 |
+
class SessionData(BaseModel):
|
171 |
+
session_id: Optional[str] = None
|
172 |
+
counselor_id: str
|
173 |
+
patient_identifier: str # Anonymized ID
|
174 |
+
session_notes: str = ""
|
175 |
+
session_preferences: Dict[str, Any] = {}
|
176 |
+
crisis_keywords: List[str] = []
|
177 |
+
created_at: Optional[datetime] = None
|
178 |
+
updated_at: Optional[datetime] = None
|
179 |
+
|
180 |
+
class ConversationEntry(BaseModel):
|
181 |
+
session_id: str
|
182 |
+
message: str
|
183 |
+
sender: str # 'patient' or 'counselor'
|
184 |
+
timestamp: Optional[datetime] = None
|
185 |
+
suggested_response: Optional[str] = None
|
186 |
+
response_type: Optional[str] = None
|
187 |
+
crisis_flag: bool = False
|
188 |
+
risk_level: Optional[str] = None
|
189 |
+
|
190 |
+
class FeedbackData(BaseModel):
|
191 |
+
suggestion_id: str
|
192 |
+
counselor_id: str
|
193 |
+
rating: int # 1-5 scale
|
194 |
+
was_effective: bool
|
195 |
+
comments: Optional[str] = None
|
196 |
+
|
197 |
+
class AnalysisRequest(BaseModel):
|
198 |
+
text: str
|
199 |
+
patient_background: Optional[Dict[str, Any]] = None
|
200 |
+
patient_age: Optional[int] = None
|
201 |
+
cultural_context: Optional[str] = None
|
202 |
+
|
203 |
+
class MultiModalInput(BaseModel):
|
204 |
+
session_id: str
|
205 |
+
counselor_id: str
|
206 |
+
input_type: str # 'text', 'audio', 'video'
|
207 |
+
content: str # Text content or file path/url
|
208 |
+
metadata: Dict[str, Any] = {}
|
209 |
+
|
210 |
+
class InterventionRequest(BaseModel):
|
211 |
+
patient_issue: str
|
212 |
+
patient_background: Optional[Dict[str, Any]] = None
|
213 |
+
intervention_type: Optional[str] = None # e.g., 'CBT', 'DBT', 'mindfulness'
|
214 |
+
|
215 |
+
# Text preprocessing function
|
216 |
+
@traceable(run_type="tool", name="Clean Text")
|
217 |
+
def clean_text(text):
|
218 |
+
if pd.isna(text):
|
219 |
+
return ""
|
220 |
+
text = str(text).lower()
|
221 |
+
text = re.sub(r"[^a-zA-Z']", " ", text)
|
222 |
+
tokens = word_tokenize(text)
|
223 |
+
tokens = [lemmatizer.lemmatize(tok) for tok in tokens if tok not in STOPWORDS and len(tok) > 2]
|
224 |
+
return " ".join(tokens)
|
225 |
+
|
226 |
+
# Feature engineering function
|
227 |
+
@traceable(run_type="tool", name="Engineer Features")
|
228 |
+
def engineer_features(context, response=""):
|
229 |
+
context_clean = clean_text(context)
|
230 |
+
context_len = len(context_clean.split())
|
231 |
+
context_vader = analyzer.polarity_scores(context)['compound']
|
232 |
+
context_questions = context.count('?')
|
233 |
+
crisis_keywords = ['suicide', 'hopeless', 'worthless', 'kill', 'harm', 'desperate', 'overwhelmed', 'alone']
|
234 |
+
context_crisis_score = sum(1 for word in crisis_keywords if word in context.lower())
|
235 |
+
|
236 |
+
context_tfidf = vectorizer.transform([context_clean]).toarray()
|
237 |
+
tfidf_cols = [f"tfidf_context_{i}" for i in range(context_tfidf.shape[1])]
|
238 |
+
response_tfidf = np.zeros_like(context_tfidf)
|
239 |
+
|
240 |
+
lda_topics = lda.transform(context_tfidf)
|
241 |
+
|
242 |
+
feature_cols = ["context_len", "context_vader", "context_questions", "crisis_flag"] + \
|
243 |
+
[f"topic_{i}" for i in range(10)] + tfidf_cols + \
|
244 |
+
[f"tfidf_response_{i}" for i in range(response_tfidf.shape[1])]
|
245 |
+
|
246 |
+
features = pd.DataFrame({
|
247 |
+
"context_len": [context_len],
|
248 |
+
"context_vader": [context_vader],
|
249 |
+
"context_questions": [context_questions],
|
250 |
+
**{f"topic_{i}": [lda_topics[0][i]] for i in range(10)},
|
251 |
+
**{f"tfidf_context_{i}": [context_tfidf[0][i]] for i in range(context_tfidf.shape[1])},
|
252 |
+
**{f"tfidf_response_{i}": [response_tfidf[0][i]] for i in range(response_tfidf.shape[1])},
|
253 |
+
})
|
254 |
+
|
255 |
+
crisis_features = features[["context_len", "context_vader", "context_questions"] + [f"topic_{i}" for i in range(10)]]
|
256 |
+
crisis_flag = crisis_clf.predict(crisis_features)[0]
|
257 |
+
if context_crisis_score > 0:
|
258 |
+
crisis_flag = 1
|
259 |
+
features["crisis_flag"] = crisis_flag
|
260 |
+
|
261 |
+
return features, feature_cols
|
262 |
+
|
263 |
+
# Prediction function
|
264 |
+
@traceable(run_type="chain", name="Predict Response Type")
|
265 |
+
def predict_response_type(context):
|
266 |
+
features, feature_cols = engineer_features(context)
|
267 |
+
selected_features = selector.transform(features[feature_cols])
|
268 |
+
pred_encoded = response_clf.predict(selected_features)[0]
|
269 |
+
pred_label = le.inverse_transform([pred_encoded])[0]
|
270 |
+
confidence = response_clf.predict_proba(selected_features)[0].max()
|
271 |
+
|
272 |
+
if "?" in context and context.count("?") > 0:
|
273 |
+
pred_label = "Question"
|
274 |
+
if "trying" in context.lower() and "hard" in context.lower() and not any(kw in context.lower() for kw in ["how", "what", "help"]):
|
275 |
+
pred_label = "Validation"
|
276 |
+
if "trying" in context.lower() and "positive" in context.lower() and not any(kw in context.lower() for kw in ["how", "what", "help"]):
|
277 |
+
pred_label = "Question"
|
278 |
+
|
279 |
+
crisis_flag = bool(features["crisis_flag"].iloc[0])
|
280 |
+
|
281 |
+
return {
|
282 |
+
"response_type": pred_label,
|
283 |
+
"crisis_flag": crisis_flag,
|
284 |
+
"confidence": confidence,
|
285 |
+
"features": features.to_dict()
|
286 |
+
}
|
287 |
+
|
288 |
+
# RAG suggestion function
|
289 |
+
@traceable(run_type="chain", name="RAG Suggestion")
|
290 |
+
def generate_suggestion_rag(context, response_type, crisis_flag):
|
291 |
+
results = vector_store.similarity_search_with_score(context, k=3)
|
292 |
+
retrieved_contexts = [
|
293 |
+
f"Patient: {res[0].page_content}\nCounselor: {res[0].metadata['response']} (Type: {res[0].metadata['response_type']}, Crisis: {res[0].metadata['crisis_flag']}, Score: {res[1]:.2f})"
|
294 |
+
for res in results
|
295 |
+
]
|
296 |
+
|
297 |
+
prompt_template = ChatPromptTemplate.from_template(
|
298 |
+
"""
|
299 |
+
You are an expert mental health counseling assistant. A counselor has provided the following patient situation:
|
300 |
+
|
301 |
+
Patient Situation: {context}
|
302 |
+
|
303 |
+
Predicted Response Type: {response_type}
|
304 |
+
Crisis Flag: {crisis_flag}
|
305 |
+
|
306 |
+
Based on the predicted response type and crisis flag, provide a suggested response for the counselor to use with the patient. The response should align with the response type ({response_type}) and be sensitive to the crisis level.
|
307 |
+
|
308 |
+
For reference, here are similar cases from past conversations:
|
309 |
+
{retrieved_contexts}
|
310 |
+
|
311 |
+
Guidelines:
|
312 |
+
- If Crisis Flag is True, prioritize safety, empathy, and suggest immediate resources (e.g., National Suicide Prevention Lifeline at 988).
|
313 |
+
- For 'Empathetic Listening', focus on validating feelings without giving direct advice or questions.
|
314 |
+
- For 'Advice', provide practical, actionable suggestions.
|
315 |
+
- For 'Question', pose an open-ended question to encourage further discussion.
|
316 |
+
- For 'Validation', affirm the patient's efforts or feelings.
|
317 |
+
|
318 |
+
Output in the following format:
|
319 |
+
```json
|
320 |
+
{{
|
321 |
+
"suggested_response": "Your suggested response here",
|
322 |
+
"risk_level": "Low/Moderate/High"
|
323 |
+
}}
|
324 |
+
```
|
325 |
+
"""
|
326 |
+
)
|
327 |
+
|
328 |
+
rag_chain = (
|
329 |
+
{
|
330 |
+
"context": RunnablePassthrough(),
|
331 |
+
"response_type": lambda x: response_type,
|
332 |
+
"crisis_flag": lambda x: "Crisis" if crisis_flag else "No Crisis",
|
333 |
+
"retrieved_contexts": lambda x: "\n".join(retrieved_contexts)
|
334 |
+
}
|
335 |
+
| prompt_template
|
336 |
+
| llm
|
337 |
+
)
|
338 |
+
|
339 |
+
try:
|
340 |
+
response = rag_chain.invoke(context)
|
341 |
+
return eval(response.content.strip("```json\n").strip("\n```"))
|
342 |
+
except Exception as e:
|
343 |
+
logger.error(f"Error generating RAG suggestion: {e}")
|
344 |
+
raise HTTPException(status_code=500, detail=f"Error generating RAG suggestion: {str(e)}")
|
345 |
+
|
346 |
+
# Direct suggestion function
|
347 |
+
@traceable(run_type="chain", name="Direct Suggestion")
|
348 |
+
def generate_suggestion_direct(context, response_type, crisis_flag):
|
349 |
+
prompt_template = ChatPromptTemplate.from_template(
|
350 |
+
"""
|
351 |
+
You are an expert mental health counseling assistant. A counselor has provided the following patient situation:
|
352 |
+
|
353 |
+
Patient Situation: {context}
|
354 |
+
|
355 |
+
Predicted Response Type: {response_type}
|
356 |
+
Crisis Flag: {crisis_flag}
|
357 |
+
|
358 |
+
Provide a suggested response for the counselor to use with the patient, aligned with the response type ({response_type}) and sensitive to the crisis level.
|
359 |
+
|
360 |
+
Guidelines:
|
361 |
+
- If Crisis Flag is True, prioritize safety, empathy, and suggest immediate resources (e.g., National Suicide Prevention Lifeline at 988).
|
362 |
+
- For 'Empathetic Listening', focus on validating feelings without giving direct advice or questions.
|
363 |
+
- For 'Advice', provide practical, actionable suggestions.
|
364 |
+
- For 'Question', pose an open-ended question to encourage further discussion.
|
365 |
+
- For 'Validation', affirm the patient's efforts or feelings.
|
366 |
+
- Strictly adhere to the response type. For 'Empathetic Listening', do not include questions or advice.
|
367 |
+
|
368 |
+
Output in the following format:
|
369 |
+
```json
|
370 |
+
{{
|
371 |
+
"suggested_response": "Your suggested response here",
|
372 |
+
"risk_level": "Low/Moderate/High"
|
373 |
+
}}
|
374 |
+
```
|
375 |
+
"""
|
376 |
+
)
|
377 |
+
|
378 |
+
direct_chain = (
|
379 |
+
{
|
380 |
+
"context": RunnablePassthrough(),
|
381 |
+
"response_type": lambda x: response_type,
|
382 |
+
"crisis_flag": lambda x: "Crisis" if crisis_flag else "No Crisis"
|
383 |
+
}
|
384 |
+
| prompt_template
|
385 |
+
| llm
|
386 |
+
)
|
387 |
+
|
388 |
+
try:
|
389 |
+
response = direct_chain.invoke(context)
|
390 |
+
return eval(response.content.strip("```json\n").strip("\n```"))
|
391 |
+
except Exception as e:
|
392 |
+
logger.error(f"Error generating direct suggestion: {e}")
|
393 |
+
raise HTTPException(status_code=500, detail=f"Error generating direct suggestion: {str(e)}")
|
394 |
+
|
395 |
+
# User Profile Endpoints
|
396 |
+
@app.post("/users/create", response_model=UserProfile)
|
397 |
+
async def create_user(profile: UserProfile):
|
398 |
+
"""Create a new counselor profile with preferences and specializations."""
|
399 |
+
try:
|
400 |
+
saved_profile = save_user_profile(profile)
|
401 |
+
logger.info(f"Created user profile: {saved_profile.user_id}")
|
402 |
+
return saved_profile
|
403 |
+
except Exception as e:
|
404 |
+
logger.error(f"Error creating user profile: {e}")
|
405 |
+
raise HTTPException(status_code=500, detail=f"Error creating user profile: {str(e)}")
|
406 |
+
|
407 |
+
@app.get("/users/{user_id}", response_model=UserProfile)
|
408 |
+
async def get_user(user_id: str):
|
409 |
+
"""Get a counselor profile by user ID."""
|
410 |
+
profile = get_user_profile(user_id)
|
411 |
+
if not profile:
|
412 |
+
raise HTTPException(status_code=404, detail=f"User profile not found: {user_id}")
|
413 |
+
return profile
|
414 |
+
|
415 |
+
@app.put("/users/{user_id}", response_model=UserProfile)
|
416 |
+
async def update_user(user_id: str, profile_update: UserProfile):
|
417 |
+
"""Update a counselor profile."""
|
418 |
+
existing_profile = get_user_profile(user_id)
|
419 |
+
if not existing_profile:
|
420 |
+
raise HTTPException(status_code=404, detail=f"User profile not found: {user_id}")
|
421 |
+
|
422 |
+
# Preserve the original user_id
|
423 |
+
profile_update.user_id = user_id
|
424 |
+
# Preserve the original created_at timestamp
|
425 |
+
profile_update.created_at = existing_profile.created_at
|
426 |
+
|
427 |
+
try:
|
428 |
+
updated_profile = save_user_profile(profile_update)
|
429 |
+
logger.info(f"Updated user profile: {user_id}")
|
430 |
+
return updated_profile
|
431 |
+
except Exception as e:
|
432 |
+
logger.error(f"Error updating user profile: {e}")
|
433 |
+
raise HTTPException(status_code=500, detail=f"Error updating user profile: {str(e)}")
|
434 |
+
|
435 |
+
# Session Management Endpoints
|
436 |
+
@app.post("/sessions/create", response_model=SessionData)
|
437 |
+
async def create_session(session_data: SessionData):
|
438 |
+
"""Create a new session with patient identifier (anonymized)."""
|
439 |
+
try:
|
440 |
+
# Verify counselor exists
|
441 |
+
counselor = get_user_profile(session_data.counselor_id)
|
442 |
+
if not counselor:
|
443 |
+
raise HTTPException(status_code=404, detail=f"Counselor not found: {session_data.counselor_id}")
|
444 |
+
|
445 |
+
# If counselor has custom crisis keywords, add them to the session
|
446 |
+
if counselor.custom_crisis_keywords:
|
447 |
+
session_data.crisis_keywords.extend(counselor.custom_crisis_keywords)
|
448 |
+
|
449 |
+
saved_session = save_session(session_data)
|
450 |
+
logger.info(f"Created session: {saved_session.session_id}")
|
451 |
+
return saved_session
|
452 |
+
except HTTPException:
|
453 |
+
raise
|
454 |
+
except Exception as e:
|
455 |
+
logger.error(f"Error creating session: {e}")
|
456 |
+
raise HTTPException(status_code=500, detail=f"Error creating session: {str(e)}")
|
457 |
+
|
458 |
+
@app.get("/sessions/{session_id}", response_model=SessionData)
|
459 |
+
async def get_session_by_id(session_id: str):
|
460 |
+
"""Get a session by ID."""
|
461 |
+
session = get_session(session_id)
|
462 |
+
if not session:
|
463 |
+
raise HTTPException(status_code=404, detail=f"Session not found: {session_id}")
|
464 |
+
return session
|
465 |
+
|
466 |
+
@app.get("/sessions/counselor/{counselor_id}", response_model=List[SessionData])
|
467 |
+
async def get_counselor_sessions(counselor_id: str):
|
468 |
+
"""Get all sessions for a counselor."""
|
469 |
+
sessions = get_user_sessions(counselor_id)
|
470 |
+
return sessions
|
471 |
+
|
472 |
+
@app.put("/sessions/{session_id}", response_model=SessionData)
|
473 |
+
async def update_session(session_id: str, session_update: SessionData):
|
474 |
+
"""Update a session."""
|
475 |
+
existing_session = get_session(session_id)
|
476 |
+
if not existing_session:
|
477 |
+
raise HTTPException(status_code=404, detail=f"Session not found: {session_id}")
|
478 |
+
|
479 |
+
# Preserve the original session_id and created_at
|
480 |
+
session_update.session_id = session_id
|
481 |
+
session_update.created_at = existing_session.created_at
|
482 |
+
|
483 |
+
try:
|
484 |
+
updated_session = save_session(session_update)
|
485 |
+
logger.info(f"Updated session: {session_id}")
|
486 |
+
return updated_session
|
487 |
+
except Exception as e:
|
488 |
+
logger.error(f"Error updating session: {e}")
|
489 |
+
raise HTTPException(status_code=500, detail=f"Error updating session: {str(e)}")
|
490 |
+
|
491 |
+
# Conversation History Endpoints
|
492 |
+
@app.post("/conversations/add", response_model=str)
|
493 |
+
async def add_conversation_entry(entry: ConversationEntry):
|
494 |
+
"""Add a new entry to a conversation."""
|
495 |
+
try:
|
496 |
+
# Verify session exists
|
497 |
+
session = get_session(entry.session_id)
|
498 |
+
if not session:
|
499 |
+
raise HTTPException(status_code=404, detail=f"Session not found: {entry.session_id}")
|
500 |
+
|
501 |
+
entry_id = save_conversation_entry(entry)
|
502 |
+
logger.info(f"Added conversation entry: {entry_id}")
|
503 |
+
return entry_id
|
504 |
+
except HTTPException:
|
505 |
+
raise
|
506 |
+
except Exception as e:
|
507 |
+
logger.error(f"Error adding conversation entry: {e}")
|
508 |
+
raise HTTPException(status_code=500, detail=f"Error adding conversation entry: {str(e)}")
|
509 |
+
|
510 |
+
@app.get("/conversations/{session_id}", response_model=List[ConversationEntry])
|
511 |
+
async def get_conversation(session_id: str):
|
512 |
+
"""Get conversation history for a session."""
|
513 |
+
try:
|
514 |
+
# Verify session exists
|
515 |
+
session = get_session(session_id)
|
516 |
+
if not session:
|
517 |
+
raise HTTPException(status_code=404, detail=f"Session not found: {session_id}")
|
518 |
+
|
519 |
+
entries = get_conversation_history(session_id)
|
520 |
+
return entries
|
521 |
+
except HTTPException:
|
522 |
+
raise
|
523 |
+
except Exception as e:
|
524 |
+
logger.error(f"Error retrieving conversation history: {e}")
|
525 |
+
raise HTTPException(status_code=500, detail=f"Error retrieving conversation history: {str(e)}")
|
526 |
+
|
527 |
+
# API Endpoints
|
528 |
+
@app.post("/suggest")
|
529 |
+
async def get_suggestion(context: PatientContext):
|
530 |
+
logger.info(f"Received suggestion request for context: {context.context}")
|
531 |
+
prediction = predict_response_type(context.context)
|
532 |
+
suggestion_rag = generate_suggestion_rag(context.context, prediction["response_type"], prediction["crisis_flag"])
|
533 |
+
suggestion_direct = generate_suggestion_direct(context.context, prediction["response_type"], prediction["crisis_flag"])
|
534 |
+
|
535 |
+
return {
|
536 |
+
"context": context.context,
|
537 |
+
"response_type": prediction["response_type"],
|
538 |
+
"crisis_flag": prediction["crisis_flag"],
|
539 |
+
"confidence": prediction["confidence"],
|
540 |
+
"rag_suggestion": suggestion_rag["suggested_response"],
|
541 |
+
"rag_risk_level": suggestion_rag["risk_level"],
|
542 |
+
"direct_suggestion": suggestion_direct["suggested_response"],
|
543 |
+
"direct_risk_level": suggestion_direct["risk_level"]
|
544 |
+
}
|
545 |
+
|
546 |
+
@app.post("/session/suggest")
|
547 |
+
async def get_session_suggestion(request: dict):
|
548 |
+
"""Get suggestion within a session context, with enhanced crisis detection based on session keywords."""
|
549 |
+
try:
|
550 |
+
session_id = request.get("session_id")
|
551 |
+
if not session_id:
|
552 |
+
raise HTTPException(status_code=400, detail="session_id is required")
|
553 |
+
|
554 |
+
context = request.get("context")
|
555 |
+
if not context:
|
556 |
+
raise HTTPException(status_code=400, detail="context is required")
|
557 |
+
|
558 |
+
# Get session for custom crisis keywords
|
559 |
+
session = get_session(session_id)
|
560 |
+
if not session:
|
561 |
+
raise HTTPException(status_code=404, detail=f"Session not found: {session_id}")
|
562 |
+
|
563 |
+
# Get conversation history for context
|
564 |
+
conversation_history = get_conversation_history(session_id)
|
565 |
+
|
566 |
+
# Regular prediction
|
567 |
+
prediction = predict_response_type(context)
|
568 |
+
crisis_flag = prediction["crisis_flag"]
|
569 |
+
|
570 |
+
# Enhanced crisis detection with custom keywords
|
571 |
+
if not crisis_flag and session.crisis_keywords:
|
572 |
+
for keyword in session.crisis_keywords:
|
573 |
+
if keyword.lower() in context.lower():
|
574 |
+
crisis_flag = True
|
575 |
+
logger.info(f"Crisis flag triggered by custom keyword: {keyword}")
|
576 |
+
break
|
577 |
+
|
578 |
+
# Generate suggestions
|
579 |
+
suggestion_rag = generate_suggestion_rag(context, prediction["response_type"], crisis_flag)
|
580 |
+
suggestion_direct = generate_suggestion_direct(context, prediction["response_type"], crisis_flag)
|
581 |
+
|
582 |
+
# Create response
|
583 |
+
response = {
|
584 |
+
"context": context,
|
585 |
+
"response_type": prediction["response_type"],
|
586 |
+
"crisis_flag": crisis_flag,
|
587 |
+
"confidence": prediction["confidence"],
|
588 |
+
"rag_suggestion": suggestion_rag["suggested_response"],
|
589 |
+
"rag_risk_level": suggestion_rag["risk_level"],
|
590 |
+
"direct_suggestion": suggestion_direct["suggested_response"],
|
591 |
+
"direct_risk_level": suggestion_direct["risk_level"],
|
592 |
+
"session_id": session_id
|
593 |
+
}
|
594 |
+
|
595 |
+
# Save the conversation entry
|
596 |
+
entry = ConversationEntry(
|
597 |
+
session_id=session_id,
|
598 |
+
message=context,
|
599 |
+
sender="patient",
|
600 |
+
suggested_response=suggestion_rag["suggested_response"],
|
601 |
+
response_type=prediction["response_type"],
|
602 |
+
crisis_flag=crisis_flag,
|
603 |
+
risk_level=suggestion_rag["risk_level"]
|
604 |
+
)
|
605 |
+
save_conversation_entry(entry)
|
606 |
+
|
607 |
+
return response
|
608 |
+
except HTTPException:
|
609 |
+
raise
|
610 |
+
except Exception as e:
|
611 |
+
logger.error(f"Error getting session suggestion: {e}")
|
612 |
+
raise HTTPException(status_code=500, detail=f"Error getting session suggestion: {str(e)}")
|
613 |
+
|
614 |
+
# Feedback Endpoints
|
615 |
+
@app.post("/feedback")
|
616 |
+
async def add_feedback(feedback: FeedbackData):
|
617 |
+
"""Add feedback about a suggestion's effectiveness."""
|
618 |
+
try:
|
619 |
+
feedback_id = save_feedback(feedback)
|
620 |
+
logger.info(f"Added feedback: {feedback_id}")
|
621 |
+
return {"feedback_id": feedback_id}
|
622 |
+
except Exception as e:
|
623 |
+
logger.error(f"Error adding feedback: {e}")
|
624 |
+
raise HTTPException(status_code=500, detail=f"Error adding feedback: {str(e)}")
|
625 |
+
|
626 |
+
# Tone & Cultural Sensitivity Analysis
|
627 |
+
@traceable(run_type="chain", name="Cultural Sensitivity Analysis")
|
628 |
+
def analyze_cultural_sensitivity(text: str, cultural_context: Optional[str] = None):
|
629 |
+
"""Analyze text for cultural appropriateness and sensitivity."""
|
630 |
+
prompt_template = ChatPromptTemplate.from_template(
|
631 |
+
"""
|
632 |
+
You are a cultural sensitivity expert. Analyze the following text for cultural appropriateness:
|
633 |
+
|
634 |
+
Text: {text}
|
635 |
+
|
636 |
+
Cultural Context: {cultural_context}
|
637 |
+
|
638 |
+
Provide an analysis of:
|
639 |
+
1. Cultural appropriateness
|
640 |
+
2. Potential bias or insensitivity
|
641 |
+
3. Suggestions for improvement
|
642 |
+
|
643 |
+
Output in the following format:
|
644 |
+
```json
|
645 |
+
{{
|
646 |
+
"cultural_appropriateness_score": 0-10,
|
647 |
+
"issues_detected": ["issue1", "issue2"],
|
648 |
+
"suggestions": ["suggestion1", "suggestion2"],
|
649 |
+
"explanation": "Brief explanation of analysis"
|
650 |
+
}}
|
651 |
+
```
|
652 |
+
"""
|
653 |
+
)
|
654 |
+
|
655 |
+
analysis_chain = (
|
656 |
+
{
|
657 |
+
"text": RunnablePassthrough(),
|
658 |
+
"cultural_context": lambda x: cultural_context if cultural_context else "General"
|
659 |
+
}
|
660 |
+
| prompt_template
|
661 |
+
| llm
|
662 |
+
)
|
663 |
+
|
664 |
+
try:
|
665 |
+
response = analysis_chain.invoke(text)
|
666 |
+
return eval(response.content.strip("```json\n").strip("\n```"))
|
667 |
+
except Exception as e:
|
668 |
+
logger.error(f"Error analyzing cultural sensitivity: {e}")
|
669 |
+
raise HTTPException(status_code=500, detail=f"Error analyzing cultural sensitivity: {str(e)}")
|
670 |
+
|
671 |
+
@traceable(run_type="chain", name="Age Appropriate Analysis")
|
672 |
+
def analyze_age_appropriateness(text: str, age: Optional[int] = None):
|
673 |
+
"""Analyze text for age-appropriate language."""
|
674 |
+
prompt_template = ChatPromptTemplate.from_template(
|
675 |
+
"""
|
676 |
+
You are an expert in age-appropriate communication. Analyze the following text for age appropriateness:
|
677 |
+
|
678 |
+
Text: {text}
|
679 |
+
|
680 |
+
Target Age: {age}
|
681 |
+
|
682 |
+
Provide an analysis of:
|
683 |
+
1. Age appropriateness
|
684 |
+
2. Complexity level
|
685 |
+
3. Suggestions for improvement
|
686 |
+
|
687 |
+
Output in the following format:
|
688 |
+
```json
|
689 |
+
{{
|
690 |
+
"age_appropriateness_score": 0-10,
|
691 |
+
"complexity_level": "Simple/Moderate/Complex",
|
692 |
+
"issues_detected": ["issue1", "issue2"],
|
693 |
+
"suggestions": ["suggestion1", "suggestion2"],
|
694 |
+
"explanation": "Brief explanation of analysis"
|
695 |
+
}}
|
696 |
+
```
|
697 |
+
"""
|
698 |
+
)
|
699 |
+
|
700 |
+
analysis_chain = (
|
701 |
+
{
|
702 |
+
"text": RunnablePassthrough(),
|
703 |
+
"age": lambda x: str(age) if age else "Adult"
|
704 |
+
}
|
705 |
+
| prompt_template
|
706 |
+
| llm
|
707 |
+
)
|
708 |
+
|
709 |
+
try:
|
710 |
+
response = analysis_chain.invoke(text)
|
711 |
+
return eval(response.content.strip("```json\n").strip("\n```"))
|
712 |
+
except Exception as e:
|
713 |
+
logger.error(f"Error analyzing age appropriateness: {e}")
|
714 |
+
raise HTTPException(status_code=500, detail=f"Error analyzing age appropriateness: {str(e)}")
|
715 |
+
|
716 |
+
@app.post("/analyze/sensitivity")
|
717 |
+
async def analyze_text_sensitivity(request: AnalysisRequest):
|
718 |
+
"""Analyze text for cultural sensitivity and age appropriateness."""
|
719 |
+
try:
|
720 |
+
cultural_analysis = analyze_cultural_sensitivity(request.text, request.cultural_context)
|
721 |
+
age_analysis = analyze_age_appropriateness(request.text, request.patient_age)
|
722 |
+
|
723 |
+
return {
|
724 |
+
"text": request.text,
|
725 |
+
"cultural_analysis": cultural_analysis,
|
726 |
+
"age_analysis": age_analysis
|
727 |
+
}
|
728 |
+
except Exception as e:
|
729 |
+
logger.error(f"Error analyzing text sensitivity: {e}")
|
730 |
+
raise HTTPException(status_code=500, detail=f"Error analyzing text sensitivity: {str(e)}")
|
731 |
+
|
732 |
+
# Guided Intervention Workflows
|
733 |
+
@traceable(run_type="chain", name="Generate Intervention")
|
734 |
+
def generate_intervention_workflow(issue: str, intervention_type: Optional[str] = None, background: Optional[Dict] = None):
|
735 |
+
"""Generate a structured intervention workflow for a specific issue."""
|
736 |
+
prompt_template = ChatPromptTemplate.from_template(
|
737 |
+
"""
|
738 |
+
You are an expert mental health counselor. Generate a structured intervention workflow for the following patient issue:
|
739 |
+
|
740 |
+
Patient Issue: {issue}
|
741 |
+
|
742 |
+
Intervention Type: {intervention_type}
|
743 |
+
Patient Background: {background}
|
744 |
+
|
745 |
+
Provide a step-by-step intervention plan based on evidence-based practices. Include:
|
746 |
+
1. Initial assessment questions
|
747 |
+
2. Specific techniques to apply
|
748 |
+
3. Homework or practice exercises
|
749 |
+
4. Follow-up guidance
|
750 |
+
|
751 |
+
Output in the following format:
|
752 |
+
```json
|
753 |
+
{{
|
754 |
+
"intervention_type": "CBT/DBT/ACT/Mindfulness/etc.",
|
755 |
+
"assessment_questions": ["question1", "question2", "question3"],
|
756 |
+
"techniques": [
|
757 |
+
{{
|
758 |
+
"name": "technique name",
|
759 |
+
"description": "brief description",
|
760 |
+
"instructions": "step-by-step instructions"
|
761 |
+
}}
|
762 |
+
],
|
763 |
+
"exercises": [
|
764 |
+
{{
|
765 |
+
"name": "exercise name",
|
766 |
+
"description": "brief description",
|
767 |
+
"instructions": "step-by-step instructions"
|
768 |
+
}}
|
769 |
+
],
|
770 |
+
"follow_up": ["follow-up step 1", "follow-up step 2"],
|
771 |
+
"resources": ["resource1", "resource2"]
|
772 |
+
}}
|
773 |
+
```
|
774 |
+
"""
|
775 |
+
)
|
776 |
+
|
777 |
+
intervention_chain = (
|
778 |
+
{
|
779 |
+
"issue": RunnablePassthrough(),
|
780 |
+
"intervention_type": lambda x: intervention_type if intervention_type else "Best fit",
|
781 |
+
"background": lambda x: str(background) if background else "Not provided"
|
782 |
+
}
|
783 |
+
| prompt_template
|
784 |
+
| llm
|
785 |
+
)
|
786 |
+
|
787 |
+
try:
|
788 |
+
response = intervention_chain.invoke(issue)
|
789 |
+
return eval(response.content.strip("```json\n").strip("\n```"))
|
790 |
+
except Exception as e:
|
791 |
+
logger.error(f"Error generating intervention workflow: {e}")
|
792 |
+
raise HTTPException(status_code=500, detail=f"Error generating intervention workflow: {str(e)}")
|
793 |
+
|
794 |
+
@app.post("/interventions/generate")
|
795 |
+
async def get_intervention_workflow(request: InterventionRequest):
|
796 |
+
"""Get a structured intervention workflow for a specific patient issue."""
|
797 |
+
try:
|
798 |
+
intervention = generate_intervention_workflow(
|
799 |
+
request.patient_issue,
|
800 |
+
request.intervention_type,
|
801 |
+
request.patient_background
|
802 |
+
)
|
803 |
+
|
804 |
+
return {
|
805 |
+
"patient_issue": request.patient_issue,
|
806 |
+
"intervention": intervention
|
807 |
+
}
|
808 |
+
except Exception as e:
|
809 |
+
logger.error(f"Error generating intervention workflow: {e}")
|
810 |
+
raise HTTPException(status_code=500, detail=f"Error generating intervention workflow: {str(e)}")
|
811 |
+
|
812 |
+
@app.get("/health")
|
813 |
+
async def health_check():
|
814 |
+
if all([response_clf, crisis_clf, vectorizer, le, selector, lda, vector_store, llm]):
|
815 |
+
return {"status": "healthy", "message": "All models and vector store loaded successfully"}
|
816 |
+
logger.error("Health check failed: One or more components not loaded")
|
817 |
+
raise HTTPException(status_code=500, detail="One or more components failed to load")
|
818 |
+
|
819 |
+
@app.get("/metadata")
|
820 |
+
async def get_metadata():
|
821 |
+
try:
|
822 |
+
collection = vector_store._client.get_collection("mental_health_conversations")
|
823 |
+
count = collection.count()
|
824 |
+
return {"collection_name": "mental_health_conversations", "document_count": count}
|
825 |
+
except Exception as e:
|
826 |
+
logger.error(f"Error retrieving metadata: {e}")
|
827 |
+
raise HTTPException(status_code=500, detail=f"Error retrieving metadata: {str(e)}")
|
828 |
+
|
829 |
+
# Database utility functions
|
830 |
+
def save_user_profile(profile: UserProfile):
|
831 |
+
if not profile.user_id:
|
832 |
+
profile.user_id = str(uuid4())
|
833 |
+
|
834 |
+
if not profile.created_at:
|
835 |
+
profile.created_at = datetime.now()
|
836 |
+
|
837 |
+
profile.updated_at = datetime.now()
|
838 |
+
|
839 |
+
file_path = os.path.join(DATA_DIR, "users", f"{profile.user_id}.json")
|
840 |
+
with open(file_path, "w") as f:
|
841 |
+
# Convert datetime to string for JSON serialization
|
842 |
+
profile_dict = profile.dict()
|
843 |
+
for key in ["created_at", "updated_at"]:
|
844 |
+
if profile_dict[key]:
|
845 |
+
profile_dict[key] = profile_dict[key].isoformat()
|
846 |
+
f.write(json.dumps(profile_dict, indent=2))
|
847 |
+
|
848 |
+
return profile
|
849 |
+
|
850 |
+
def get_user_profile(user_id: str) -> Optional[UserProfile]:
|
851 |
+
file_path = os.path.join(DATA_DIR, "users", f"{user_id}.json")
|
852 |
+
if not os.path.exists(file_path):
|
853 |
+
return None
|
854 |
+
|
855 |
+
with open(file_path, "r") as f:
|
856 |
+
data = json.loads(f.read())
|
857 |
+
# Convert string dates back to datetime
|
858 |
+
for key in ["created_at", "updated_at"]:
|
859 |
+
if data[key]:
|
860 |
+
data[key] = datetime.fromisoformat(data[key])
|
861 |
+
return UserProfile(**data)
|
862 |
+
|
863 |
+
def save_session(session: SessionData):
|
864 |
+
if not session.session_id:
|
865 |
+
session.session_id = str(uuid4())
|
866 |
+
|
867 |
+
if not session.created_at:
|
868 |
+
session.created_at = datetime.now()
|
869 |
+
|
870 |
+
session.updated_at = datetime.now()
|
871 |
+
|
872 |
+
file_path = os.path.join(DATA_DIR, "sessions", f"{session.session_id}.json")
|
873 |
+
with open(file_path, "w") as f:
|
874 |
+
# Convert datetime to string for JSON serialization
|
875 |
+
session_dict = session.dict()
|
876 |
+
for key in ["created_at", "updated_at"]:
|
877 |
+
if session_dict[key]:
|
878 |
+
session_dict[key] = session_dict[key].isoformat()
|
879 |
+
f.write(json.dumps(session_dict, indent=2))
|
880 |
+
|
881 |
+
return session
|
882 |
+
|
883 |
+
def get_session(session_id: str) -> Optional[SessionData]:
|
884 |
+
file_path = os.path.join(DATA_DIR, "sessions", f"{session_id}.json")
|
885 |
+
if not os.path.exists(file_path):
|
886 |
+
return None
|
887 |
+
|
888 |
+
with open(file_path, "r") as f:
|
889 |
+
data = json.loads(f.read())
|
890 |
+
# Convert string dates back to datetime
|
891 |
+
for key in ["created_at", "updated_at"]:
|
892 |
+
if data[key]:
|
893 |
+
data[key] = datetime.fromisoformat(data[key])
|
894 |
+
return SessionData(**data)
|
895 |
+
|
896 |
+
def get_user_sessions(counselor_id: str) -> List[SessionData]:
|
897 |
+
sessions = []
|
898 |
+
sessions_dir = os.path.join(DATA_DIR, "sessions")
|
899 |
+
for filename in os.listdir(sessions_dir):
|
900 |
+
if not filename.endswith(".json"):
|
901 |
+
continue
|
902 |
+
|
903 |
+
file_path = os.path.join(sessions_dir, filename)
|
904 |
+
with open(file_path, "r") as f:
|
905 |
+
data = json.loads(f.read())
|
906 |
+
if data["counselor_id"] == counselor_id:
|
907 |
+
for key in ["created_at", "updated_at"]:
|
908 |
+
if data[key]:
|
909 |
+
data[key] = datetime.fromisoformat(data[key])
|
910 |
+
sessions.append(SessionData(**data))
|
911 |
+
|
912 |
+
return sessions
|
913 |
+
|
914 |
+
def save_conversation_entry(entry: ConversationEntry):
|
915 |
+
conversation_dir = os.path.join(DATA_DIR, "conversations", entry.session_id)
|
916 |
+
os.makedirs(conversation_dir, exist_ok=True)
|
917 |
+
|
918 |
+
if not entry.timestamp:
|
919 |
+
entry.timestamp = datetime.now()
|
920 |
+
|
921 |
+
entry_id = str(uuid4())
|
922 |
+
file_path = os.path.join(conversation_dir, f"{entry_id}.json")
|
923 |
+
|
924 |
+
with open(file_path, "w") as f:
|
925 |
+
# Convert datetime to string for JSON serialization
|
926 |
+
entry_dict = entry.dict()
|
927 |
+
entry_dict["entry_id"] = entry_id
|
928 |
+
if entry_dict["timestamp"]:
|
929 |
+
entry_dict["timestamp"] = entry_dict["timestamp"].isoformat()
|
930 |
+
f.write(json.dumps(entry_dict, indent=2))
|
931 |
+
|
932 |
+
return entry_id
|
933 |
+
|
934 |
+
def get_conversation_history(session_id: str) -> List[ConversationEntry]:
|
935 |
+
conversation_dir = os.path.join(DATA_DIR, "conversations", session_id)
|
936 |
+
if not os.path.exists(conversation_dir):
|
937 |
+
return []
|
938 |
+
|
939 |
+
entries = []
|
940 |
+
for filename in os.listdir(conversation_dir):
|
941 |
+
if not filename.endswith(".json"):
|
942 |
+
continue
|
943 |
+
|
944 |
+
file_path = os.path.join(conversation_dir, filename)
|
945 |
+
with open(file_path, "r") as f:
|
946 |
+
data = json.loads(f.read())
|
947 |
+
if data["timestamp"]:
|
948 |
+
data["timestamp"] = datetime.fromisoformat(data["timestamp"])
|
949 |
+
entries.append(ConversationEntry(**data))
|
950 |
+
|
951 |
+
# Sort by timestamp
|
952 |
+
entries.sort(key=lambda x: x.timestamp)
|
953 |
+
return entries
|
954 |
+
|
955 |
+
def save_feedback(feedback: FeedbackData):
|
956 |
+
feedback_id = str(uuid4())
|
957 |
+
file_path = os.path.join(DATA_DIR, "feedback", f"{feedback_id}.json")
|
958 |
+
|
959 |
+
with open(file_path, "w") as f:
|
960 |
+
feedback_dict = feedback.dict()
|
961 |
+
feedback_dict["feedback_id"] = feedback_id
|
962 |
+
feedback_dict["timestamp"] = datetime.now().isoformat()
|
963 |
+
f.write(json.dumps(feedback_dict, indent=2))
|
964 |
+
|
965 |
+
return feedback_id
|
966 |
+
|
967 |
+
# Multi-modal Input Support
|
968 |
+
@app.post("/input/process")
|
969 |
+
async def process_multimodal_input(input_data: MultiModalInput):
|
970 |
+
"""Process multi-modal input (text, audio, video)."""
|
971 |
+
try:
|
972 |
+
if input_data.input_type not in ["text", "audio", "video"]:
|
973 |
+
raise HTTPException(status_code=400, detail=f"Unsupported input type: {input_data.input_type}")
|
974 |
+
|
975 |
+
# For now, handle text directly and simulate processing for audio/video
|
976 |
+
if input_data.input_type == "text":
|
977 |
+
# Process text normally
|
978 |
+
prediction = predict_response_type(input_data.content)
|
979 |
+
|
980 |
+
return {
|
981 |
+
"input_type": "text",
|
982 |
+
"processed_content": input_data.content,
|
983 |
+
"analysis": {
|
984 |
+
"response_type": prediction["response_type"],
|
985 |
+
"crisis_flag": prediction["crisis_flag"],
|
986 |
+
"confidence": prediction["confidence"]
|
987 |
+
}
|
988 |
+
}
|
989 |
+
elif input_data.input_type == "audio":
|
990 |
+
# Simulate audio transcription and emotion detection
|
991 |
+
# In a production system, this would use a speech-to-text API and emotion analysis
|
992 |
+
prompt_template = ChatPromptTemplate.from_template(
|
993 |
+
"""
|
994 |
+
Simulate audio processing for this description: {content}
|
995 |
+
|
996 |
+
Generate a simulated transcription and emotion detection as if this were real audio.
|
997 |
+
|
998 |
+
Output in the following format:
|
999 |
+
```json
|
1000 |
+
{{
|
1001 |
+
"transcription": "Simulated transcription of the audio",
|
1002 |
+
"emotion_detected": "primary emotion",
|
1003 |
+
"secondary_emotions": ["emotion1", "emotion2"],
|
1004 |
+
"confidence": 0.85
|
1005 |
+
}}
|
1006 |
+
```
|
1007 |
+
"""
|
1008 |
+
)
|
1009 |
+
|
1010 |
+
process_chain = prompt_template | llm
|
1011 |
+
response = process_chain.invoke({"content": input_data.content})
|
1012 |
+
audio_result = eval(response.content.strip("```json\n").strip("\n```"))
|
1013 |
+
|
1014 |
+
# Now process the transcription
|
1015 |
+
prediction = predict_response_type(audio_result["transcription"])
|
1016 |
+
|
1017 |
+
return {
|
1018 |
+
"input_type": "audio",
|
1019 |
+
"processed_content": audio_result["transcription"],
|
1020 |
+
"emotion_analysis": {
|
1021 |
+
"primary_emotion": audio_result["emotion_detected"],
|
1022 |
+
"secondary_emotions": audio_result["secondary_emotions"],
|
1023 |
+
"confidence": audio_result["confidence"]
|
1024 |
+
},
|
1025 |
+
"analysis": {
|
1026 |
+
"response_type": prediction["response_type"],
|
1027 |
+
"crisis_flag": prediction["crisis_flag"],
|
1028 |
+
"confidence": prediction["confidence"]
|
1029 |
+
}
|
1030 |
+
}
|
1031 |
+
elif input_data.input_type == "video":
|
1032 |
+
# Simulate video analysis
|
1033 |
+
# In a production system, this would use video analytics API
|
1034 |
+
prompt_template = ChatPromptTemplate.from_template(
|
1035 |
+
"""
|
1036 |
+
Simulate video processing for this description: {content}
|
1037 |
+
|
1038 |
+
Generate a simulated analysis as if this were real video with facial expressions and body language.
|
1039 |
+
|
1040 |
+
Output in the following format:
|
1041 |
+
```json
|
1042 |
+
{{
|
1043 |
+
"transcription": "Simulated transcription of speech in the video",
|
1044 |
+
"facial_expressions": ["expression1", "expression2"],
|
1045 |
+
"body_language": ["posture observation", "gesture observation"],
|
1046 |
+
"primary_emotion": "primary emotion",
|
1047 |
+
"confidence": 0.80
|
1048 |
+
}}
|
1049 |
+
```
|
1050 |
+
"""
|
1051 |
+
)
|
1052 |
+
|
1053 |
+
process_chain = prompt_template | llm
|
1054 |
+
response = process_chain.invoke({"content": input_data.content})
|
1055 |
+
video_result = eval(response.content.strip("```json\n").strip("\n```"))
|
1056 |
+
|
1057 |
+
# Process the transcription
|
1058 |
+
prediction = predict_response_type(video_result["transcription"])
|
1059 |
+
|
1060 |
+
return {
|
1061 |
+
"input_type": "video",
|
1062 |
+
"processed_content": video_result["transcription"],
|
1063 |
+
"nonverbal_analysis": {
|
1064 |
+
"facial_expressions": video_result["facial_expressions"],
|
1065 |
+
"body_language": video_result["body_language"],
|
1066 |
+
"primary_emotion": video_result["primary_emotion"],
|
1067 |
+
"confidence": video_result["confidence"]
|
1068 |
+
},
|
1069 |
+
"analysis": {
|
1070 |
+
"response_type": prediction["response_type"],
|
1071 |
+
"crisis_flag": prediction["crisis_flag"],
|
1072 |
+
"confidence": prediction["confidence"]
|
1073 |
+
}
|
1074 |
+
}
|
1075 |
+
except Exception as e:
|
1076 |
+
logger.error(f"Error processing multimodal input: {e}")
|
1077 |
+
raise HTTPException(status_code=500, detail=f"Error processing multimodal input: {str(e)}")
|
1078 |
+
|
1079 |
+
# Therapeutic Technique Suggestions
|
1080 |
+
@traceable(run_type="chain", name="Therapeutic Techniques")
|
1081 |
+
def suggest_therapeutic_techniques(context: str, technique_type: Optional[str] = None):
|
1082 |
+
"""Suggest specific therapeutic techniques based on the patient context."""
|
1083 |
+
prompt_template = ChatPromptTemplate.from_template(
|
1084 |
+
"""
|
1085 |
+
You are an expert mental health professional with extensive knowledge of therapeutic techniques. Based on the following patient context, suggest therapeutic techniques that would be appropriate:
|
1086 |
+
|
1087 |
+
Patient Context: {context}
|
1088 |
+
|
1089 |
+
Technique Type (if specified): {technique_type}
|
1090 |
+
|
1091 |
+
Suggest specific therapeutic techniques, exercises, or interventions that would be helpful for this patient. Include:
|
1092 |
+
1. Name of technique
|
1093 |
+
2. Brief description
|
1094 |
+
3. How to apply it in this specific case
|
1095 |
+
4. Expected benefits
|
1096 |
+
|
1097 |
+
Provide a range of options from different therapeutic approaches (CBT, DBT, ACT, mindfulness, motivational interviewing, etc.) unless a specific technique type was requested.
|
1098 |
+
|
1099 |
+
Output in the following format:
|
1100 |
+
```json
|
1101 |
+
{{
|
1102 |
+
"primary_approach": "The most appropriate therapeutic approach",
|
1103 |
+
"techniques": [
|
1104 |
+
{{
|
1105 |
+
"name": "Technique name",
|
1106 |
+
"approach": "CBT/DBT/ACT/etc.",
|
1107 |
+
"description": "Brief description",
|
1108 |
+
"application": "How to apply to this specific case",
|
1109 |
+
"benefits": "Expected benefits"
|
1110 |
+
}}
|
1111 |
+
],
|
1112 |
+
"rationale": "Brief explanation of why these techniques were selected"
|
1113 |
+
}}
|
1114 |
+
```
|
1115 |
+
"""
|
1116 |
+
)
|
1117 |
+
|
1118 |
+
technique_chain = (
|
1119 |
+
{
|
1120 |
+
"context": RunnablePassthrough(),
|
1121 |
+
"technique_type": lambda x: technique_type if technique_type else "Any appropriate"
|
1122 |
+
}
|
1123 |
+
| prompt_template
|
1124 |
+
| llm
|
1125 |
+
)
|
1126 |
+
|
1127 |
+
try:
|
1128 |
+
response = technique_chain.invoke(context)
|
1129 |
+
return eval(response.content.strip("```json\n").strip("\n```"))
|
1130 |
+
except Exception as e:
|
1131 |
+
logger.error(f"Error suggesting therapeutic techniques: {e}")
|
1132 |
+
raise HTTPException(status_code=500, detail=f"Error suggesting therapeutic techniques: {str(e)}")
|
1133 |
+
|
1134 |
+
@app.post("/techniques/suggest")
|
1135 |
+
async def get_therapeutic_techniques(request: dict):
|
1136 |
+
"""Get suggested therapeutic techniques for a patient context."""
|
1137 |
+
try:
|
1138 |
+
context = request.get("context")
|
1139 |
+
if not context:
|
1140 |
+
raise HTTPException(status_code=400, detail="context is required")
|
1141 |
+
|
1142 |
+
technique_type = request.get("technique_type")
|
1143 |
+
|
1144 |
+
techniques = suggest_therapeutic_techniques(context, technique_type)
|
1145 |
+
|
1146 |
+
return {
|
1147 |
+
"context": context,
|
1148 |
+
"techniques": techniques
|
1149 |
+
}
|
1150 |
+
except Exception as e:
|
1151 |
+
logger.error(f"Error getting therapeutic techniques: {e}")
|
1152 |
+
raise HTTPException(status_code=500, detail=f"Error getting therapeutic techniques: {str(e)}")
|
1153 |
+
|
1154 |
+
# Ethical AI Guardrails - Confidence Indicator
|
1155 |
+
@app.post("/suggest/with_confidence")
|
1156 |
+
async def get_suggestion_with_confidence(context: PatientContext):
|
1157 |
+
"""Get suggestion with detailed confidence indicators and uncertainty flags."""
|
1158 |
+
try:
|
1159 |
+
# Get standard prediction
|
1160 |
+
prediction = predict_response_type(context.context)
|
1161 |
+
|
1162 |
+
# Set confidence thresholds
|
1163 |
+
high_confidence = 0.8
|
1164 |
+
medium_confidence = 0.6
|
1165 |
+
|
1166 |
+
# Determine confidence level
|
1167 |
+
confidence_value = prediction["confidence"]
|
1168 |
+
if confidence_value >= high_confidence:
|
1169 |
+
confidence_level = "High"
|
1170 |
+
elif confidence_value >= medium_confidence:
|
1171 |
+
confidence_level = "Medium"
|
1172 |
+
else:
|
1173 |
+
confidence_level = "Low"
|
1174 |
+
|
1175 |
+
# Analyze for potential biases
|
1176 |
+
bias_prompt = ChatPromptTemplate.from_template(
|
1177 |
+
"""
|
1178 |
+
You are an AI ethics expert. Analyze the following patient context and proposed response type for potential biases:
|
1179 |
+
|
1180 |
+
Patient Context: {context}
|
1181 |
+
Predicted Response Type: {response_type}
|
1182 |
+
|
1183 |
+
Identify any potential biases in interpretation or response. Consider gender, cultural, socioeconomic, and other potential biases.
|
1184 |
+
|
1185 |
+
Output in the following format:
|
1186 |
+
```json
|
1187 |
+
{{
|
1188 |
+
"bias_detected": true/false,
|
1189 |
+
"bias_types": ["bias type 1", "bias type 2"],
|
1190 |
+
"explanation": "Brief explanation of potential biases"
|
1191 |
+
}}
|
1192 |
+
```
|
1193 |
+
"""
|
1194 |
+
)
|
1195 |
+
|
1196 |
+
bias_chain = (
|
1197 |
+
{
|
1198 |
+
"context": lambda x: context.context,
|
1199 |
+
"response_type": lambda x: prediction["response_type"]
|
1200 |
+
}
|
1201 |
+
| bias_prompt
|
1202 |
+
| llm
|
1203 |
+
)
|
1204 |
+
|
1205 |
+
bias_analysis = eval(bias_chain.invoke({}).content.strip("```json\n").strip("\n```"))
|
1206 |
+
|
1207 |
+
# Generate suggestions
|
1208 |
+
suggestion_rag = generate_suggestion_rag(context.context, prediction["response_type"], prediction["crisis_flag"])
|
1209 |
+
suggestion_direct = generate_suggestion_direct(context.context, prediction["response_type"], prediction["crisis_flag"])
|
1210 |
+
|
1211 |
+
return {
|
1212 |
+
"context": context.context,
|
1213 |
+
"response_type": prediction["response_type"],
|
1214 |
+
"crisis_flag": prediction["crisis_flag"],
|
1215 |
+
"confidence": {
|
1216 |
+
"value": prediction["confidence"],
|
1217 |
+
"level": confidence_level,
|
1218 |
+
"uncertainty_flag": confidence_level == "Low"
|
1219 |
+
},
|
1220 |
+
"bias_analysis": bias_analysis,
|
1221 |
+
"rag_suggestion": suggestion_rag["suggested_response"],
|
1222 |
+
"rag_risk_level": suggestion_rag["risk_level"],
|
1223 |
+
"direct_suggestion": suggestion_direct["suggested_response"],
|
1224 |
+
"direct_risk_level": suggestion_direct["risk_level"],
|
1225 |
+
"attribution": {
|
1226 |
+
"ai_generated": True,
|
1227 |
+
"model_version": "Mental Health Counselor API v2.0",
|
1228 |
+
"human_reviewed": False
|
1229 |
+
}
|
1230 |
+
}
|
1231 |
+
except Exception as e:
|
1232 |
+
logger.error(f"Error getting suggestion with confidence: {e}")
|
1233 |
+
raise HTTPException(status_code=500, detail=f"Error getting suggestion with confidence: {str(e)}")
|
1234 |
+
|
1235 |
+
# Text to Speech with Eleven Labs API
|
1236 |
+
@app.post("/api/text-to-speech")
|
1237 |
+
async def text_to_speech(request: dict):
|
1238 |
+
"""Convert text to speech using Eleven Labs API."""
|
1239 |
+
try:
|
1240 |
+
text = request.get("text")
|
1241 |
+
voice_id = request.get("voice_id", "pNInz6obpgDQGcFmaJgB") # Default to "Adam" voice
|
1242 |
+
|
1243 |
+
if not text:
|
1244 |
+
raise HTTPException(status_code=400, detail="Text is required")
|
1245 |
+
|
1246 |
+
# Get API key from environment
|
1247 |
+
api_key = os.getenv("ELEVEN_API_KEY")
|
1248 |
+
if not api_key:
|
1249 |
+
raise HTTPException(status_code=500, detail="Eleven Labs API key not configured")
|
1250 |
+
|
1251 |
+
# Prepare the request to Eleven Labs
|
1252 |
+
url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
|
1253 |
+
|
1254 |
+
headers = {
|
1255 |
+
"Accept": "audio/mpeg",
|
1256 |
+
"Content-Type": "application/json",
|
1257 |
+
"xi-api-key": api_key
|
1258 |
+
}
|
1259 |
+
|
1260 |
+
payload = {
|
1261 |
+
"text": text,
|
1262 |
+
"model_id": "eleven_multilingual_v2",
|
1263 |
+
"voice_settings": {
|
1264 |
+
"stability": 0.5,
|
1265 |
+
"similarity_boost": 0.75
|
1266 |
+
}
|
1267 |
+
}
|
1268 |
+
|
1269 |
+
# Make the request to Eleven Labs
|
1270 |
+
response = requests.post(url, json=payload, headers=headers)
|
1271 |
+
|
1272 |
+
if response.status_code != 200:
|
1273 |
+
logger.error(f"Eleven Labs API error: {response.text}")
|
1274 |
+
raise HTTPException(status_code=response.status_code,
|
1275 |
+
detail=f"Eleven Labs API error: {response.text}")
|
1276 |
+
|
1277 |
+
# Return audio as streaming response
|
1278 |
+
return StreamingResponse(
|
1279 |
+
BytesIO(response.content),
|
1280 |
+
media_type="audio/mpeg"
|
1281 |
+
)
|
1282 |
+
|
1283 |
+
except Exception as e:
|
1284 |
+
logger.error(f"Error in text-to-speech: {str(e)}")
|
1285 |
+
if not isinstance(e, HTTPException):
|
1286 |
+
raise HTTPException(status_code=500, detail=f"Text-to-speech error: {str(e)}")
|
1287 |
+
raise e
|
1288 |
+
|
1289 |
+
# Multimedia file processing (speech to text)
|
1290 |
+
@app.post("/api/input/process")
|
1291 |
+
async def process_audio_input(
|
1292 |
+
audio: UploadFile = File(...),
|
1293 |
+
session_id: str = Form(...)
|
1294 |
+
):
|
1295 |
+
"""Process audio input for speech-to-text using Eleven Labs."""
|
1296 |
+
try:
|
1297 |
+
# Get API key from environment
|
1298 |
+
api_key = os.getenv("ELEVEN_API_KEY")
|
1299 |
+
if not api_key:
|
1300 |
+
raise HTTPException(status_code=500, detail="Eleven Labs API key not configured")
|
1301 |
+
|
1302 |
+
# Read the audio file content
|
1303 |
+
audio_content = await audio.read()
|
1304 |
+
|
1305 |
+
# Call Eleven Labs Speech-to-Text API
|
1306 |
+
url = "https://api.elevenlabs.io/v1/speech-to-text"
|
1307 |
+
|
1308 |
+
headers = {
|
1309 |
+
"xi-api-key": api_key
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
# Create form data with the audio file
|
1313 |
+
files = {
|
1314 |
+
'audio': ('audio.webm', audio_content, 'audio/webm')
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
data = {
|
1318 |
+
'model_id': 'whisper-1' # Using Whisper model
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
# Make the request to Eleven Labs
|
1322 |
+
response = requests.post(url, headers=headers, files=files, data=data)
|
1323 |
+
|
1324 |
+
if response.status_code != 200:
|
1325 |
+
logger.error(f"Eleven Labs API error: {response.text}")
|
1326 |
+
raise HTTPException(status_code=response.status_code,
|
1327 |
+
detail=f"Eleven Labs API error: {response.text}")
|
1328 |
+
|
1329 |
+
result = response.json()
|
1330 |
+
|
1331 |
+
# Extract the transcribed text
|
1332 |
+
text = result.get('text', '')
|
1333 |
+
|
1334 |
+
# Return the transcribed text
|
1335 |
+
return {
|
1336 |
+
"text": text,
|
1337 |
+
"session_id": session_id
|
1338 |
+
}
|
1339 |
+
|
1340 |
+
except Exception as e:
|
1341 |
+
logger.error(f"Error processing audio: {str(e)}")
|
1342 |
+
if not isinstance(e, HTTPException):
|
1343 |
+
raise HTTPException(status_code=500, detail=f"Audio processing error: {str(e)}")
|
1344 |
+
raise e
|
1345 |
+
|
1346 |
+
# Add a custom encoder for bytes objects to prevent UTF-8 decode errors
|
1347 |
+
def custom_encoder(obj):
|
1348 |
+
if isinstance(obj, bytes):
|
1349 |
+
try:
|
1350 |
+
return obj.decode('utf-8')
|
1351 |
+
except UnicodeDecodeError:
|
1352 |
+
return base64.b64encode(obj).decode('ascii')
|
1353 |
+
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
1354 |
+
|
1355 |
+
# Override the jsonable_encoder function to handle bytes properly
|
1356 |
+
from fastapi.encoders import jsonable_encoder as original_jsonable_encoder
|
1357 |
+
|
1358 |
+
def safe_jsonable_encoder(*args, **kwargs):
|
1359 |
+
try:
|
1360 |
+
return original_jsonable_encoder(*args, **kwargs)
|
1361 |
+
except UnicodeDecodeError:
|
1362 |
+
# If the standard encoder fails with a decode error,
|
1363 |
+
# ensure all bytes are properly handled
|
1364 |
+
if args and isinstance(args[0], bytes):
|
1365 |
+
return custom_encoder(args[0])
|
1366 |
+
raise
|
1367 |
+
|
1368 |
+
# Monkey patch the jsonable_encoder in FastAPI
|
1369 |
+
import fastapi.encoders
|
1370 |
+
fastapi.encoders.jsonable_encoder = safe_jsonable_encoder
|
create_vector_db.py
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import chromadb
|
3 |
+
from chromadb.config import Settings
|
4 |
+
from langchain_openai import OpenAIEmbeddings
|
5 |
+
import os
|
6 |
+
import getpass
|
7 |
+
import shutil
|
8 |
+
import re
|
9 |
+
import nltk
|
10 |
+
from nltk.tokenize import word_tokenize
|
11 |
+
from nltk.corpus import stopwords
|
12 |
+
from nltk.stem import WordNetLemmatizer
|
13 |
+
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
|
14 |
+
import kagglehub
|
15 |
+
from pathlib import Path
|
16 |
+
from langsmith import Client, traceable
|
17 |
+
|
18 |
+
# Download required NLTK data
|
19 |
+
nltk.download('punkt')
|
20 |
+
nltk.download('punkt_tab')
|
21 |
+
nltk.download('wordnet')
|
22 |
+
nltk.download('stopwords')
|
23 |
+
|
24 |
+
# Initialize NLTK and VADER
|
25 |
+
STOPWORDS = set(stopwords.words("english"))
|
26 |
+
lemmatizer = WordNetLemmatizer()
|
27 |
+
analyzer = SentimentIntensityAnalyzer()
|
28 |
+
|
29 |
+
# Set OpenAI API key
|
30 |
+
if not os.environ.get("OPENAI_API_KEY"):
|
31 |
+
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter OpenAI API Key: ")
|
32 |
+
|
33 |
+
# Set LangSmith environment variables
|
34 |
+
if not os.environ.get("LANGCHAIN_API_KEY"):
|
35 |
+
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass("Enter LangSmith API Key: ")
|
36 |
+
os.environ["LANGCHAIN_TRACING_V2"] = "true"
|
37 |
+
os.environ["LANGCHAIN_PROJECT"] = "MentalHealthCounselorPOC"
|
38 |
+
|
39 |
+
# Initialize LangSmith client
|
40 |
+
langsmith_client = Client()
|
41 |
+
|
42 |
+
# Define default paths
|
43 |
+
DEFAULT_OUTPUT_DIR = os.environ.get("MH_OUTPUT_DIR", "mental_health_model_artifacts")
|
44 |
+
DEFAULT_DATASET_PATH = os.environ.get("MH_DATASET_PATH", None)
|
45 |
+
|
46 |
+
# Parse command-line arguments (ignore unknown args for Jupyter/Colab)
|
47 |
+
import argparse
|
48 |
+
parser = argparse.ArgumentParser(description="Create ChromaDB vector database for Mental Health Counselor POC")
|
49 |
+
parser.add_argument('--output-dir', default=DEFAULT_OUTPUT_DIR, help="Directory for model artifacts")
|
50 |
+
parser.add_argument('--dataset-path', default=DEFAULT_DATASET_PATH, help="Path to train.csv (if already downloaded)")
|
51 |
+
args, unknown = parser.parse_known_args() # Ignore unknown args like -f
|
52 |
+
|
53 |
+
# Set paths
|
54 |
+
output_dir = args.output_dir
|
55 |
+
chroma_db_path = os.path.join(output_dir, "chroma_db")
|
56 |
+
dataset_path = args.dataset_path
|
57 |
+
|
58 |
+
# Text preprocessing function
|
59 |
+
@traceable(run_type="tool", name="Clean Text")
|
60 |
+
def clean_text(text):
|
61 |
+
if pd.isna(text):
|
62 |
+
return ""
|
63 |
+
text = str(text).lower()
|
64 |
+
text = re.sub(r"[^a-zA-Z']", " ", text)
|
65 |
+
tokens = word_tokenize(text)
|
66 |
+
tokens = [lemmatizer.lemmatize(tok) for tok in tokens if tok not in STOPWORDS and len(tok) > 2]
|
67 |
+
return " ".join(tokens)
|
68 |
+
|
69 |
+
# Response categorization function
|
70 |
+
@traceable(run_type="tool", name="Categorize Response")
|
71 |
+
def categorize_response(text):
|
72 |
+
text = str(text).lower()
|
73 |
+
labels = []
|
74 |
+
if re.search(r"\?$", text.strip()):
|
75 |
+
return "Question"
|
76 |
+
if any(phrase in text for phrase in ["i understand", "that sounds", "i hear"]):
|
77 |
+
labels.append("Validation")
|
78 |
+
if any(phrase in text for phrase in ["should", "could", "try", "recommend"]):
|
79 |
+
labels.append("Advice")
|
80 |
+
if not labels:
|
81 |
+
sentiment = analyzer.polarity_scores(text)
|
82 |
+
if sentiment['compound'] > 0.3:
|
83 |
+
labels.append("Empathetic Listening")
|
84 |
+
else:
|
85 |
+
labels.append("Advice")
|
86 |
+
return "|".join(labels)
|
87 |
+
|
88 |
+
# Load dataset
|
89 |
+
@traceable(run_type="tool", name="Load Dataset")
|
90 |
+
def load_dataset():
|
91 |
+
try:
|
92 |
+
if dataset_path and os.path.exists(dataset_path):
|
93 |
+
df = pd.read_csv(dataset_path)
|
94 |
+
else:
|
95 |
+
# Download dataset using kagglehub
|
96 |
+
dataset = kagglehub.dataset_download("thedevastator/nlp-mental-health-conversations", path="train.csv")
|
97 |
+
df = pd.read_csv(dataset)
|
98 |
+
print("First 5 records:\n", df.head())
|
99 |
+
return df
|
100 |
+
except Exception as e:
|
101 |
+
print(f"Error loading dataset: {e}")
|
102 |
+
exit(1)
|
103 |
+
|
104 |
+
# Main vector database creation
|
105 |
+
@traceable(run_type="chain", name="Create Vector Database")
|
106 |
+
def create_vector_db():
|
107 |
+
df = load_dataset()
|
108 |
+
|
109 |
+
# Validate and clean dataset
|
110 |
+
if not all(col in df.columns for col in ['Context', 'Response']):
|
111 |
+
print("Error: Dataset missing required columns ('Context', 'Response')")
|
112 |
+
exit(1)
|
113 |
+
|
114 |
+
df = df.dropna(subset=['Context', 'Response']).drop_duplicates()
|
115 |
+
print(f"Cleaned Dataset Shape: {df.shape}")
|
116 |
+
|
117 |
+
# Compute response type and crisis flag
|
118 |
+
crisis_keywords = ['suicide', 'hopeless', 'worthless', 'kill', 'harm', 'desperate', 'overwhelmed', 'alone']
|
119 |
+
df["response_type"] = df["Response"].apply(categorize_response)
|
120 |
+
df["response_type_single"] = df["response_type"].apply(lambda x: x.split("|")[0])
|
121 |
+
df["crisis_flag"] = df["Context"].apply(
|
122 |
+
lambda x: sum(1 for word in crisis_keywords if word in str(x).lower()) > 0
|
123 |
+
)
|
124 |
+
|
125 |
+
# Initialize ChromaDB client
|
126 |
+
try:
|
127 |
+
if os.path.exists(chroma_db_path):
|
128 |
+
print(f"Clearing existing ChromaDB at {chroma_db_path}")
|
129 |
+
shutil.rmtree(chroma_db_path)
|
130 |
+
os.makedirs(chroma_db_path, exist_ok=True)
|
131 |
+
chroma_client = chromadb.PersistentClient(
|
132 |
+
path=chroma_db_path,
|
133 |
+
settings=Settings(anonymized_telemetry=False)
|
134 |
+
)
|
135 |
+
except Exception as e:
|
136 |
+
print(f"Error initializing ChromaDB: {e}")
|
137 |
+
print("Ensure ChromaDB version is compatible (e.g., 0.5.x) and no other processes are accessing the database.")
|
138 |
+
exit(1)
|
139 |
+
|
140 |
+
# Initialize OpenAI embeddings
|
141 |
+
try:
|
142 |
+
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
|
143 |
+
except Exception as e:
|
144 |
+
print(f"Error initializing OpenAI embeddings: {e}")
|
145 |
+
exit(1)
|
146 |
+
|
147 |
+
# Create or reset collection
|
148 |
+
collection_name = "mental_health_conversations"
|
149 |
+
try:
|
150 |
+
chroma_client.delete_collection(collection_name)
|
151 |
+
print(f"Deleted existing collection '{collection_name}' if it existed")
|
152 |
+
except:
|
153 |
+
print(f"No existing collection '{collection_name}' to delete")
|
154 |
+
try:
|
155 |
+
collection = chroma_client.create_collection(name=collection_name)
|
156 |
+
print(f"Created new collection '{collection_name}'")
|
157 |
+
except Exception as e:
|
158 |
+
print(f"Error creating Chroma collection: {e}")
|
159 |
+
exit(1)
|
160 |
+
|
161 |
+
# Prepare documents
|
162 |
+
documents = df["Context"].tolist()
|
163 |
+
metadatas = [
|
164 |
+
{
|
165 |
+
"response": row["Response"],
|
166 |
+
"response_type": row["response_type_single"],
|
167 |
+
"crisis_flag": bool(row["crisis_flag"])
|
168 |
+
}
|
169 |
+
for _, row in df.iterrows()
|
170 |
+
]
|
171 |
+
ids = [f"doc_{i}" for i in range(len(documents))]
|
172 |
+
|
173 |
+
# Generate embeddings and add to collection
|
174 |
+
try:
|
175 |
+
embeddings_vectors = embeddings.embed_documents(documents)
|
176 |
+
collection.add(
|
177 |
+
documents=documents,
|
178 |
+
embeddings=embeddings_vectors,
|
179 |
+
metadatas=metadatas,
|
180 |
+
ids=ids
|
181 |
+
)
|
182 |
+
print(f"Vector database created in {chroma_db_path} with {len(documents)} documents")
|
183 |
+
except Exception as e:
|
184 |
+
print(f"Error generating embeddings or adding to collection: {e}")
|
185 |
+
exit(1)
|
186 |
+
|
187 |
+
if __name__ == "__main__":
|
188 |
+
create_vector_db()
|
data/conversations/2ae6de8a-bf2d-4d01-8269-e13d5cf3d927/9b2ff07a-def6-43a7-9efd-e8ed6e8cdfd6.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "2ae6de8a-bf2d-4d01-8269-e13d5cf3d927",
|
3 |
+
"message": "he said he sick and crazy ",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-29T23:29:05.936691",
|
6 |
+
"suggested_response": "It sounds like you're feeling really overwhelmed right now, and it's completely understandable to feel that way. When someone says they feel sick and crazy, it can be really distressing. I'm here to listen to how you're feeling and to support you through this. Can you tell me more about what you mean by feeling sick and crazy?",
|
7 |
+
"response_type": "Empathetic Listening",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "9b2ff07a-def6-43a7-9efd-e8ed6e8cdfd6"
|
11 |
+
}
|
data/conversations/33f954fb-f60d-4ea0-bd59-1396253c8cdc/71bea3ca-82ef-4857-93cf-84955c3eddb3.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "33f954fb-f60d-4ea0-bd59-1396253c8cdc",
|
3 |
+
"message": "hello",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-29T23:49:20.830853",
|
6 |
+
"suggested_response": "It sounds like you might be feeling a bit lost or overwhelmed right now. I'm here to listen and support you as you navigate through these feelings. It\u2019s okay to share whatever is on your mind, and I\u2019m here to help you process those emotions.",
|
7 |
+
"response_type": "Empathetic Listening",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "71bea3ca-82ef-4857-93cf-84955c3eddb3"
|
11 |
+
}
|
data/conversations/5fc04208-6f76-4774-9c40-bda7e0a28caf/2c256fde-2660-453e-b404-37bcc8a8f93a.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "5fc04208-6f76-4774-9c40-bda7e0a28caf",
|
3 |
+
"message": "jjj\n",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-29T23:32:53.999866",
|
6 |
+
"suggested_response": "It sounds like you're feeling overwhelmed and perhaps caught in a cycle of negative thoughts and anxiety. One approach you might consider is practicing mindfulness techniques, such as deep breathing or meditation, which can help center your thoughts and reduce anxiety. Additionally, try setting small, manageable goals for your day to help regain a sense of control. Journaling your thoughts can also be beneficial; it allows you to express and process what you're feeling. If these strategies don't seem to help, consider reaching out to a therapist who can provide you with tailored support.",
|
7 |
+
"response_type": "Advice",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "2c256fde-2660-453e-b404-37bcc8a8f93a"
|
11 |
+
}
|
data/conversations/5fc04208-6f76-4774-9c40-bda7e0a28caf/72ebbd21-33dc-4148-8905-1d944451c3ba.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "5fc04208-6f76-4774-9c40-bda7e0a28caf",
|
3 |
+
"message": "nn",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-29T23:33:14.386950",
|
6 |
+
"suggested_response": "It sounds like you're feeling overwhelmed and stuck right now. One approach that might help is to break down your situation into smaller, manageable steps. For instance, consider setting specific, realistic goals for yourself each week, whether it's applying for a certain number of jobs, reaching out to a housing organization for support, or even engaging in self-care activities that bring you some joy. Also, communicating openly with your partner about your feelings and struggles could strengthen your connection and provide the support you need during this challenging time. Remember, it's okay to seek help from professionals or support groups that can offer guidance and resources tailored to your situation.",
|
7 |
+
"response_type": "Advice",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "72ebbd21-33dc-4148-8905-1d944451c3ba"
|
11 |
+
}
|
data/conversations/67e763f8-5a97-491d-977b-2071af8a8abe/ef88b6ba-c13b-49ff-9dfd-33c6e58d1487.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "67e763f8-5a97-491d-977b-2071af8a8abe",
|
3 |
+
"message": "I've been feeling anxious about work lately and having trouble sleeping.",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-30T00:00:09.873980",
|
6 |
+
"suggested_response": "It sounds like work has been weighing heavily on you lately, and it's understandable to feel anxious about it. Sleep difficulties can really add to that stress, making it even harder to cope with daily challenges. You're not alone in feeling this way, and it\u2019s okay to acknowledge how tough this is for you right now. I'm here to listen and support you as you navigate these feelings.",
|
7 |
+
"response_type": "Empathetic Listening",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "ef88b6ba-c13b-49ff-9dfd-33c6e58d1487"
|
11 |
+
}
|
data/conversations/72f347f1-ba29-4050-bb6c-02e0851e088a/75c4bb13-daa6-415b-8913-6cc584324940.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "72f347f1-ba29-4050-bb6c-02e0851e088a",
|
3 |
+
"message": "ss",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-29T23:46:13.232213",
|
6 |
+
"suggested_response": "It sounds like you\u2019re feeling stuck with your current situation. One approach you might consider is to take some time to reflect on what specifically hasn\u2019t worked for you in the past and why that might be. Identifying any patterns can help you figure out new strategies to try. Additionally, it might be beneficial to set some realistic, small goals for yourself that can lead to gradual progress. Have you thought about discussing your feelings and frustrations with someone you trust or a professional? They can provide additional support and perspective.",
|
7 |
+
"response_type": "Advice",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "75c4bb13-daa6-415b-8913-6cc584324940"
|
11 |
+
}
|
data/conversations/b132a94e-553b-47cd-a855-fcf853dc8320/6465a103-6163-40e7-b7a5-acb2e475181c.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "b132a94e-553b-47cd-a855-fcf853dc8320",
|
3 |
+
"message": "I feel so anxious all the time, especially at work. My heart races and I can barely breathe.",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-29T23:09:09.291362",
|
6 |
+
"suggested_response": "It sounds like you're experiencing a lot of intense anxiety at work, and that must be incredibly overwhelming. Your feelings of racing heart and difficulty breathing are really important to acknowledge. It's completely understandable to feel this way, especially in a work environment. You're not alone in this experience, and many people struggle with anxiety in similar situations. I'm here to listen and support you as you navigate through these feelings.",
|
7 |
+
"response_type": "Empathetic Listening",
|
8 |
+
"crisis_flag": false,
|
9 |
+
"risk_level": "Low",
|
10 |
+
"entry_id": "6465a103-6163-40e7-b7a5-acb2e475181c"
|
11 |
+
}
|
data/conversations/d5072833-ef63-4ed2-92ab-d1476f81e17d/34e2ac93-7fef-467f-a672-6a5e0638440f.json
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "d5072833-ef63-4ed2-92ab-d1476f81e17d",
|
3 |
+
"message": "ss",
|
4 |
+
"sender": "patient",
|
5 |
+
"timestamp": "2025-04-30T00:39:25.082732",
|
6 |
+
"suggested_response": "I\u2019m really sorry to hear that you\u2019re feeling this way. It\u2019s important to talk about what you\u2019re experiencing. I strongly encourage you to reach out to someone who can help you right now, like a trusted friend or a mental health professional. If you\u2019re feeling overwhelmed, please consider contacting the National Suicide Prevention Lifeline at 988, where someone is available to listen and support you 24/7. You don\u2019t have to go through this alone, and there are people who want to help you through this tough time.",
|
7 |
+
"response_type": "Advice",
|
8 |
+
"crisis_flag": true,
|
9 |
+
"risk_level": "High",
|
10 |
+
"entry_id": "34e2ac93-7fef-467f-a672-6a5e0638440f"
|
11 |
+
}
|
data/sessions/08f51081-9d82-4a8c-a3f4-c159a584c3a4.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "08f51081-9d82-4a8c-a3f4-c159a584c3a4",
|
3 |
+
"counselor_id": "4ba9d533-2d2a-4f1b-b00e-bd4f58f38fa1",
|
4 |
+
"patient_identifier": "jkl;",
|
5 |
+
"session_notes": "jj",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"kill"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:33:44.792776",
|
11 |
+
"updated_at": "2025-04-29T23:33:44.792785"
|
12 |
+
}
|
data/sessions/139f262d-769d-4c3f-8d04-0cefc2cdd33e.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "139f262d-769d-4c3f-8d04-0cefc2cdd33e",
|
3 |
+
"counselor_id": "4ba9d533-2d2a-4f1b-b00e-bd4f58f38fa1",
|
4 |
+
"patient_identifier": "aaa",
|
5 |
+
"session_notes": "aaa",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"kill"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:31:21.528490",
|
11 |
+
"updated_at": "2025-04-29T23:31:21.528502"
|
12 |
+
}
|
data/sessions/261e6110-82a3-4f26-bc1e-bbebabea059d.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "261e6110-82a3-4f26-bc1e-bbebabea059d",
|
3 |
+
"counselor_id": "4d352ce8-fd66-4756-9ee1-617406668f93",
|
4 |
+
"patient_identifier": "a",
|
5 |
+
"session_notes": "a",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"aa"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-30T00:44:03.891455",
|
11 |
+
"updated_at": "2025-04-30T00:44:03.891479"
|
12 |
+
}
|
data/sessions/2ae6de8a-bf2d-4d01-8269-e13d5cf3d927.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "2ae6de8a-bf2d-4d01-8269-e13d5cf3d927",
|
3 |
+
"counselor_id": "ddb6322a-4f73-435b-8734-ff8d32a23bcd",
|
4 |
+
"patient_identifier": "patioan doctor",
|
5 |
+
"session_notes": "intial",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"harm"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:28:49.602630",
|
11 |
+
"updated_at": "2025-04-29T23:28:49.602641"
|
12 |
+
}
|
data/sessions/33f954fb-f60d-4ea0-bd59-1396253c8cdc.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "33f954fb-f60d-4ea0-bd59-1396253c8cdc",
|
3 |
+
"counselor_id": "1dc2a9f3-0e67-4086-9119-be87d1fcf147",
|
4 |
+
"patient_identifier": "said",
|
5 |
+
"session_notes": "anxirty",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"sss"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:49:00.598796",
|
11 |
+
"updated_at": "2025-04-29T23:49:00.598853"
|
12 |
+
}
|
data/sessions/37f38f66-78af-4544-88b9-3c31734c429d.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "37f38f66-78af-4544-88b9-3c31734c429d",
|
3 |
+
"counselor_id": "d31a3577-b6b8-4e2a-a292-5a1a3c373dc5",
|
4 |
+
"patient_identifier": "dd",
|
5 |
+
"session_notes": "dd",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"aa"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:44:41.289712",
|
11 |
+
"updated_at": "2025-04-29T23:44:41.289725"
|
12 |
+
}
|
data/sessions/5fc04208-6f76-4774-9c40-bda7e0a28caf.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "5fc04208-6f76-4774-9c40-bda7e0a28caf",
|
3 |
+
"counselor_id": "4ba9d533-2d2a-4f1b-b00e-bd4f58f38fa1",
|
4 |
+
"patient_identifier": "j",
|
5 |
+
"session_notes": "j",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"kill"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:32:42.976743",
|
11 |
+
"updated_at": "2025-04-29T23:32:42.976795"
|
12 |
+
}
|
data/sessions/67e763f8-5a97-491d-977b-2071af8a8abe.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "67e763f8-5a97-491d-977b-2071af8a8abe",
|
3 |
+
"counselor_id": "3989efcc-7bd0-49a6-8f26-53b0f9169fd5",
|
4 |
+
"patient_identifier": "k",
|
5 |
+
"session_notes": "k",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"aa"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:58:20.679295",
|
11 |
+
"updated_at": "2025-04-29T23:58:20.679338"
|
12 |
+
}
|
data/sessions/72f347f1-ba29-4050-bb6c-02e0851e088a.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "72f347f1-ba29-4050-bb6c-02e0851e088a",
|
3 |
+
"counselor_id": "d31a3577-b6b8-4e2a-a292-5a1a3c373dc5",
|
4 |
+
"patient_identifier": "aa",
|
5 |
+
"session_notes": "aa",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"aa"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-29T23:44:35.406566",
|
11 |
+
"updated_at": "2025-04-29T23:44:35.406580"
|
12 |
+
}
|
data/sessions/75289adb-550c-4a4d-9035-ec54f1c954c6.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "75289adb-550c-4a4d-9035-ec54f1c954c6",
|
3 |
+
"counselor_id": "13c3dcfc-d8ff-4305-88fe-8e610bd8457b",
|
4 |
+
"patient_identifier": "s",
|
5 |
+
"session_notes": "ss",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"aa"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-30T00:10:28.631177",
|
11 |
+
"updated_at": "2025-04-30T00:10:28.631195"
|
12 |
+
}
|
data/sessions/b0408071-2a67-4cf4-bdf8-593b71dba326.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "b0408071-2a67-4cf4-bdf8-593b71dba326",
|
3 |
+
"counselor_id": "79a850ac-bee6-45e9-8aef-f47fa20dc669",
|
4 |
+
"patient_identifier": "ss",
|
5 |
+
"session_notes": "ss",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"ss"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-30T00:39:07.050280",
|
11 |
+
"updated_at": "2025-04-30T00:39:07.050297"
|
12 |
+
}
|
data/sessions/b132a94e-553b-47cd-a855-fcf853dc8320.json
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "b132a94e-553b-47cd-a855-fcf853dc8320",
|
3 |
+
"counselor_id": "e2d0468b-f229-4b8f-9fa1-95bd9be68eba",
|
4 |
+
"patient_identifier": "anonymous-123",
|
5 |
+
"session_notes": "Initial session with patient experiencing anxiety",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"harm",
|
9 |
+
"end it all"
|
10 |
+
],
|
11 |
+
"created_at": "2025-04-29T23:08:48.266989",
|
12 |
+
"updated_at": "2025-04-29T23:08:48.266999"
|
13 |
+
}
|
data/sessions/d5072833-ef63-4ed2-92ab-d1476f81e17d.json
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"session_id": "d5072833-ef63-4ed2-92ab-d1476f81e17d",
|
3 |
+
"counselor_id": "79a850ac-bee6-45e9-8aef-f47fa20dc669",
|
4 |
+
"patient_identifier": "ss",
|
5 |
+
"session_notes": "ss",
|
6 |
+
"session_preferences": {},
|
7 |
+
"crisis_keywords": [
|
8 |
+
"ss"
|
9 |
+
],
|
10 |
+
"created_at": "2025-04-30T00:39:16.212728",
|
11 |
+
"updated_at": "2025-04-30T00:39:16.212739"
|
12 |
+
}
|
data/users/13c3dcfc-d8ff-4305-88fe-8e610bd8457b.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "13c3dcfc-d8ff-4305-88fe-8e610bd8457b",
|
3 |
+
"username": "aa",
|
4 |
+
"name": "aa",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"aa"
|
8 |
+
],
|
9 |
+
"years_experience": 2,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"aa"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-30T00:10:20.447044",
|
15 |
+
"updated_at": "2025-04-30T00:10:20.447082"
|
16 |
+
}
|
data/users/1dc2a9f3-0e67-4086-9119-be87d1fcf147.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "1dc2a9f3-0e67-4086-9119-be87d1fcf147",
|
3 |
+
"username": "ss",
|
4 |
+
"name": "ss",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"ss"
|
8 |
+
],
|
9 |
+
"years_experience": 2,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"sss"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-29T23:48:35.799622",
|
15 |
+
"updated_at": "2025-04-29T23:48:35.799684"
|
16 |
+
}
|
data/users/3989efcc-7bd0-49a6-8f26-53b0f9169fd5.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "3989efcc-7bd0-49a6-8f26-53b0f9169fd5",
|
3 |
+
"username": "aa",
|
4 |
+
"name": "aa",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"aa"
|
8 |
+
],
|
9 |
+
"years_experience": 3,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"aa"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-29T23:57:13.574632",
|
15 |
+
"updated_at": "2025-04-29T23:57:13.574662"
|
16 |
+
}
|
data/users/4ba9d533-2d2a-4f1b-b00e-bd4f58f38fa1.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "4ba9d533-2d2a-4f1b-b00e-bd4f58f38fa1",
|
3 |
+
"username": "aa",
|
4 |
+
"name": "aa",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"aa"
|
8 |
+
],
|
9 |
+
"years_experience": 2,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"kill"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-29T23:31:15.551397",
|
15 |
+
"updated_at": "2025-04-29T23:31:15.551477"
|
16 |
+
}
|
data/users/4d352ce8-fd66-4756-9ee1-617406668f93.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "4d352ce8-fd66-4756-9ee1-617406668f93",
|
3 |
+
"username": "a",
|
4 |
+
"name": "a",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"a"
|
8 |
+
],
|
9 |
+
"years_experience": 3,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"aa"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-30T00:43:58.059383",
|
15 |
+
"updated_at": "2025-04-30T00:43:58.059439"
|
16 |
+
}
|
data/users/70c8bae7-d171-4c2f-a27a-0ba4f3b1778b.json
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "70c8bae7-d171-4c2f-a27a-0ba4f3b1778b",
|
3 |
+
"username": "counselor1",
|
4 |
+
"name": "Dr. Smith",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"anxiety",
|
8 |
+
"depression"
|
9 |
+
],
|
10 |
+
"years_experience": null,
|
11 |
+
"custom_crisis_keywords": [
|
12 |
+
"harm",
|
13 |
+
"end it all"
|
14 |
+
],
|
15 |
+
"preferences": {},
|
16 |
+
"created_at": "2025-04-29T23:08:25.633537",
|
17 |
+
"updated_at": "2025-04-29T23:08:25.633570"
|
18 |
+
}
|
data/users/79a850ac-bee6-45e9-8aef-f47fa20dc669.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "79a850ac-bee6-45e9-8aef-f47fa20dc669",
|
3 |
+
"username": "ss",
|
4 |
+
"name": "ss",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"ss"
|
8 |
+
],
|
9 |
+
"years_experience": 2,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"ss"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-30T00:39:01.718893",
|
15 |
+
"updated_at": "2025-04-30T00:39:01.719090"
|
16 |
+
}
|
data/users/c22207e8-0863-4592-be45-0c68d0ce8df3.json
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "c22207e8-0863-4592-be45-0c68d0ce8df3",
|
3 |
+
"username": "counselor1",
|
4 |
+
"name": "Dr. Smith",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"anxiety",
|
8 |
+
"depression"
|
9 |
+
],
|
10 |
+
"years_experience": null,
|
11 |
+
"custom_crisis_keywords": [
|
12 |
+
"harm",
|
13 |
+
"end it all"
|
14 |
+
],
|
15 |
+
"preferences": {},
|
16 |
+
"created_at": "2025-04-29T23:21:04.749779",
|
17 |
+
"updated_at": "2025-04-29T23:21:04.749840"
|
18 |
+
}
|
data/users/d31a3577-b6b8-4e2a-a292-5a1a3c373dc5.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "d31a3577-b6b8-4e2a-a292-5a1a3c373dc5",
|
3 |
+
"username": "aa",
|
4 |
+
"name": "aaa",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"aa"
|
8 |
+
],
|
9 |
+
"years_experience": 2,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"aa"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-29T23:44:25.851112",
|
15 |
+
"updated_at": "2025-04-29T23:44:25.851163"
|
16 |
+
}
|
data/users/ddb6322a-4f73-435b-8734-ff8d32a23bcd.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "ddb6322a-4f73-435b-8734-ff8d32a23bcd",
|
3 |
+
"username": "Saod",
|
4 |
+
"name": "Dr. Said",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"trauma"
|
8 |
+
],
|
9 |
+
"years_experience": 2,
|
10 |
+
"custom_crisis_keywords": [
|
11 |
+
"harm"
|
12 |
+
],
|
13 |
+
"preferences": {},
|
14 |
+
"created_at": "2025-04-29T23:28:33.424887",
|
15 |
+
"updated_at": "2025-04-29T23:28:33.424918"
|
16 |
+
}
|
data/users/e2d0468b-f229-4b8f-9fa1-95bd9be68eba.json
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"user_id": "e2d0468b-f229-4b8f-9fa1-95bd9be68eba",
|
3 |
+
"username": "counselor1",
|
4 |
+
"name": "Dr. Smith",
|
5 |
+
"role": "counselor",
|
6 |
+
"specializations": [
|
7 |
+
"anxiety",
|
8 |
+
"depression"
|
9 |
+
],
|
10 |
+
"years_experience": null,
|
11 |
+
"custom_crisis_keywords": [
|
12 |
+
"harm",
|
13 |
+
"end it all"
|
14 |
+
],
|
15 |
+
"preferences": {},
|
16 |
+
"created_at": "2025-04-29T23:08:36.108291",
|
17 |
+
"updated_at": "2025-04-29T23:08:36.108301"
|
18 |
+
}
|
mental_health_model_artifacts/chroma_db/chroma.sqlite3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0909d32b23bd9ff2d51a06887e46eebe144fe1c465686d7bcbac169d043b4342
|
3 |
+
size 40247296
|
mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/data_level0.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:6d2ff57e249a7cba65242d2ca6d9a1ad9eea523eebdeb5bb8435fa5befbc6c05
|
3 |
+
size 12568000
|
mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/header.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b254e99be7ad660da7bebc7decb50244dcd2bf9ba281d2d4c496c03734612e7a
|
3 |
+
size 100
|
mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/index_metadata.pickle
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1fc8ed8aee380194233dff20e3d1d8f99d3dce1ab366d83a1405d441274287b9
|
3 |
+
size 56848
|
mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/length.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a6a4b30f8353fe95c05d3eb4042e94a215d24a341e2f1ea4f0d1055f39f7a8a5
|
3 |
+
size 8000
|
mental_health_model_artifacts/chroma_db/e454ec48-8871-48f0-af58-914c5aace95d/link_lists.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b64aafb055de969104a3d45c63af6836a3ce63e5d9b5558f4fb825419b5d180a
|
3 |
+
size 17316
|
mental_health_model_artifacts/crisis_classifier.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3ad22c3bdc09d39ae4723d2bb1ba62e2af490fd3b0008f6bb3a12a68a5517462
|
3 |
+
size 346020
|
mental_health_model_artifacts/feature_selector.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:74d7063ff7201a6c318e447556bc586db73b21abe2063cc1e2951fe77686eb5d
|
3 |
+
size 35715
|