Commit ·
62ff3c4
0
Parent(s):
initial commit for HF Space
Browse files- .DS_Store +0 -0
- LICENSE +21 -0
- README.md +12 -0
- app.py +452 -0
- data/.DS_Store +0 -0
- data/system_prompt.md +196 -0
- data/user_profile_schema.json +191 -0
- references/.DS_Store +0 -0
- references/knowledge/boston_resources.csv +174 -0
- references/knowledge/ma_resources.csv +0 -0
- requirements.txt +176 -0
- src/.DS_Store +0 -0
- src/__init__.py +1 -0
- src/__pycache__/__init__.cpython-312.pyc +0 -0
- src/__pycache__/chat.cpython-312.pyc +0 -0
- src/__pycache__/config.cpython-312.pyc +0 -0
- src/chat.py +121 -0
- src/config.py +17 -0
- src/utils/__init__.py +2 -0
- src/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- src/utils/__pycache__/profile.cpython-312.pyc +0 -0
- src/utils/__pycache__/resources.cpython-312.pyc +0 -0
- src/utils/__pycache__/tags.cpython-312.pyc +0 -0
- src/utils/profile.py +224 -0
- src/utils/resources.py +240 -0
.DS_Store
ADDED
|
Binary file (10.2 kB). View file
|
|
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Sarah Bentley
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Harbor
|
| 3 |
+
emoji: 🚢
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.23.3
|
| 8 |
+
python_version: "3.10"
|
| 9 |
+
app_file: app.py
|
| 10 |
+
pinned: false
|
| 11 |
+
---
|
| 12 |
+
|
app.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Gradio Web Interface for Harbor Treatment Navigation Chatbot
|
| 3 |
+
|
| 4 |
+
Landing page offers three paths:
|
| 5 |
+
1. Quick Recommendations — enter a zip code, get nearby options inline
|
| 6 |
+
2. Talk to a Human — compact crisis callout with phone number
|
| 7 |
+
3. Get Personalized Advice — leads to the AI chatbot
|
| 8 |
+
|
| 9 |
+
Run locally:
|
| 10 |
+
python app.py
|
| 11 |
+
|
| 12 |
+
Access in browser:
|
| 13 |
+
http://localhost:7860
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
import os
|
| 17 |
+
import re
|
| 18 |
+
|
| 19 |
+
import gradio as gr
|
| 20 |
+
from src.chat import Chatbot
|
| 21 |
+
from src.utils.profile import create_empty_profile
|
| 22 |
+
from src.utils.resources import load_resources, filter_resources, score_resources
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# ── CSS ───────────────────────────────────────────────────────────────────────
|
| 26 |
+
|
| 27 |
+
CSS = """
|
| 28 |
+
/* ── Layout ── */
|
| 29 |
+
.harbor-wrap {
|
| 30 |
+
max-width: 680px;
|
| 31 |
+
margin: 0 auto;
|
| 32 |
+
padding: 2.5rem 1.25rem 1.5rem;
|
| 33 |
+
font-family: 'Inter', sans-serif;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/* ── Header ── */
|
| 37 |
+
.harbor-logo {
|
| 38 |
+
text-align: center;
|
| 39 |
+
font-size: 2.75rem;
|
| 40 |
+
font-weight: 800;
|
| 41 |
+
letter-spacing: -1px;
|
| 42 |
+
color: #0d6e6e;
|
| 43 |
+
margin-bottom: 0.2rem;
|
| 44 |
+
line-height: 1;
|
| 45 |
+
}
|
| 46 |
+
.harbor-tagline {
|
| 47 |
+
text-align: center;
|
| 48 |
+
font-size: 1.1rem;
|
| 49 |
+
color: #5a7a7a;
|
| 50 |
+
margin-bottom: 2.25rem;
|
| 51 |
+
font-style: italic;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
/* ── Cards ── */
|
| 55 |
+
.harbor-card {
|
| 56 |
+
background: #ffffff;
|
| 57 |
+
border: 1.5px solid #c8e6e6;
|
| 58 |
+
border-radius: 16px;
|
| 59 |
+
padding: 1.5rem 1.75rem;
|
| 60 |
+
margin-bottom: 1.1rem;
|
| 61 |
+
box-shadow: 0 2px 12px rgba(13, 110, 110, 0.06);
|
| 62 |
+
}
|
| 63 |
+
.harbor-card-title {
|
| 64 |
+
font-size: 1.15rem;
|
| 65 |
+
font-weight: 700;
|
| 66 |
+
color: #0d6e6e;
|
| 67 |
+
margin-bottom: 0.6rem;
|
| 68 |
+
display: flex;
|
| 69 |
+
align-items: center;
|
| 70 |
+
gap: 0.5rem;
|
| 71 |
+
}
|
| 72 |
+
.harbor-card p {
|
| 73 |
+
color: #3d5a5a;
|
| 74 |
+
line-height: 1.65;
|
| 75 |
+
margin: 0 0 0.75rem;
|
| 76 |
+
font-size: 0.97rem;
|
| 77 |
+
}
|
| 78 |
+
.harbor-card p:last-child { margin-bottom: 0; }
|
| 79 |
+
|
| 80 |
+
/* ── Quick Rec card — larger, featured ── */
|
| 81 |
+
.harbor-card-featured {
|
| 82 |
+
background: linear-gradient(145deg, #f0fafa, #e6f7f7);
|
| 83 |
+
border: 2px solid #0d9e8f;
|
| 84 |
+
}
|
| 85 |
+
.harbor-card-featured .harbor-card-title {
|
| 86 |
+
font-size: 1.25rem;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/* ── Crisis callout — compact ── */
|
| 90 |
+
.harbor-callout {
|
| 91 |
+
background: #f8fffd;
|
| 92 |
+
border: 1.5px solid #c8e6e6;
|
| 93 |
+
border-radius: 12px;
|
| 94 |
+
padding: 0.9rem 1.25rem;
|
| 95 |
+
margin-bottom: 1.1rem;
|
| 96 |
+
display: flex;
|
| 97 |
+
align-items: center;
|
| 98 |
+
gap: 1rem;
|
| 99 |
+
flex-wrap: wrap;
|
| 100 |
+
box-shadow: 0 1px 6px rgba(13, 110, 110, 0.05);
|
| 101 |
+
}
|
| 102 |
+
.harbor-callout-text {
|
| 103 |
+
flex: 1;
|
| 104 |
+
min-width: 200px;
|
| 105 |
+
font-size: 0.9rem;
|
| 106 |
+
color: #3d5a5a;
|
| 107 |
+
line-height: 1.5;
|
| 108 |
+
}
|
| 109 |
+
.harbor-callout-text strong { color: #0d6e6e; }
|
| 110 |
+
.harbor-phone-inline {
|
| 111 |
+
font-size: 1.2rem;
|
| 112 |
+
font-weight: 800;
|
| 113 |
+
color: #0d6e6e;
|
| 114 |
+
white-space: nowrap;
|
| 115 |
+
}
|
| 116 |
+
.harbor-phone-inline a { color: inherit; text-decoration: none; }
|
| 117 |
+
.harbor-phone-inline a:hover { text-decoration: underline; }
|
| 118 |
+
|
| 119 |
+
/* ── Zip results area ── */
|
| 120 |
+
.harbor-results {
|
| 121 |
+
margin-top: 1rem;
|
| 122 |
+
padding-top: 1rem;
|
| 123 |
+
border-top: 1px solid #c8e6e6;
|
| 124 |
+
}
|
| 125 |
+
.harbor-results-title {
|
| 126 |
+
font-size: 1rem;
|
| 127 |
+
font-weight: 600;
|
| 128 |
+
color: #0d6e6e;
|
| 129 |
+
margin-bottom: 0.5rem;
|
| 130 |
+
}
|
| 131 |
+
.harbor-error {
|
| 132 |
+
color: #c0392b;
|
| 133 |
+
font-size: 0.9rem;
|
| 134 |
+
margin-top: 0.4rem;
|
| 135 |
+
}
|
| 136 |
+
#zip-results .pending,
|
| 137 |
+
#zip-results .generating,
|
| 138 |
+
#zip-results > .wrap,
|
| 139 |
+
#zip-results > .svelte-spinner,
|
| 140 |
+
#zip-results .eta-bar {
|
| 141 |
+
display: none !important;
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
/* ── Buttons ── */
|
| 145 |
+
.harbor-start-btn button,
|
| 146 |
+
.harbor-zip-btn button {
|
| 147 |
+
background: linear-gradient(135deg, #0d9e8f, #0d6e6e) !important;
|
| 148 |
+
border: none !important;
|
| 149 |
+
border-radius: 12px !important;
|
| 150 |
+
font-size: 1.05rem !important;
|
| 151 |
+
font-weight: 600 !important;
|
| 152 |
+
letter-spacing: 0.2px !important;
|
| 153 |
+
padding: 0.85rem 1.5rem !important;
|
| 154 |
+
transition: opacity 0.2s ease !important;
|
| 155 |
+
box-shadow: 0 4px 14px rgba(13, 110, 110, 0.25) !important;
|
| 156 |
+
}
|
| 157 |
+
.harbor-start-btn button:hover,
|
| 158 |
+
.harbor-zip-btn button:hover { opacity: 0.9 !important; }
|
| 159 |
+
|
| 160 |
+
/* ── Footer ── */
|
| 161 |
+
.harbor-footer {
|
| 162 |
+
text-align: center;
|
| 163 |
+
font-size: 0.8rem;
|
| 164 |
+
color: #8fa8a8;
|
| 165 |
+
margin-top: 1.75rem;
|
| 166 |
+
line-height: 1.6;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
/* ── Chat page ── */
|
| 170 |
+
.chat-header {
|
| 171 |
+
max-width: 680px;
|
| 172 |
+
margin: 0 auto;
|
| 173 |
+
padding: 1.25rem 1.25rem 0;
|
| 174 |
+
}
|
| 175 |
+
.chat-back-btn button {
|
| 176 |
+
background: transparent !important;
|
| 177 |
+
border: 1.5px solid #c8e6e6 !important;
|
| 178 |
+
color: #0d6e6e !important;
|
| 179 |
+
border-radius: 8px !important;
|
| 180 |
+
font-size: 0.9rem !important;
|
| 181 |
+
font-weight: 500 !important;
|
| 182 |
+
padding: 0.4rem 0.9rem !important;
|
| 183 |
+
}
|
| 184 |
+
.chat-back-btn button:hover { background: #f0fafa !important; }
|
| 185 |
+
"""
|
| 186 |
+
|
| 187 |
+
# ── Theme ─────────────────────────────────────────────────────────────────────
|
| 188 |
+
|
| 189 |
+
THEME = gr.themes.Soft(
|
| 190 |
+
primary_hue="teal",
|
| 191 |
+
secondary_hue="cyan",
|
| 192 |
+
neutral_hue="slate",
|
| 193 |
+
font=[gr.themes.GoogleFont("Inter"), "sans-serif"],
|
| 194 |
+
).set(
|
| 195 |
+
button_primary_background_fill="linear-gradient(135deg, #0d9e8f, #0d6e6e)",
|
| 196 |
+
button_primary_background_fill_hover="linear-gradient(135deg, #0bb8a8, #0d9e8f)",
|
| 197 |
+
button_primary_text_color="#ffffff",
|
| 198 |
+
block_border_color="#c8e6e6",
|
| 199 |
+
block_shadow="0 2px 12px rgba(13,110,110,0.06)",
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
# ── Static HTML snippets ───────────────────────────────────────────────────────
|
| 203 |
+
|
| 204 |
+
HEADER_MD = """
|
| 205 |
+
<div class='harbor-logo'>⚓ Harbor</div>
|
| 206 |
+
<div class='harbor-tagline'>Come in from the storm.</div>
|
| 207 |
+
"""
|
| 208 |
+
|
| 209 |
+
CRISIS_CALLOUT_HTML = """
|
| 210 |
+
<div class='harbor-callout'>
|
| 211 |
+
<div class='harbor-callout-text'>
|
| 212 |
+
<strong>🤝 Talk to a Human</strong> — Trained counselors available
|
| 213 |
+
<strong>24/7</strong> through the Behavioral Health Help Line.<br>
|
| 214 |
+
<span style='font-size:0.82rem; color:#6b8e8e;'>
|
| 215 |
+
In immediate danger? <strong style='color:#6b4e4e;'>Call 911.</strong>
|
| 216 |
+
</span>
|
| 217 |
+
</div>
|
| 218 |
+
<div class='harbor-phone-inline'><a href='tel:8337732445'>📞 833-773-2445</a></div>
|
| 219 |
+
</div>
|
| 220 |
+
"""
|
| 221 |
+
|
| 222 |
+
CHATBOT_CARD_MD = """
|
| 223 |
+
<div class='harbor-card-title'>💬 Get Personalized Guidance</div>
|
| 224 |
+
<p>
|
| 225 |
+
Want to explore options in more depth? Our chatbot can factor in your insurance,
|
| 226 |
+
preferences, treatment history, and more — no judgment, no pressure.
|
| 227 |
+
</p>
|
| 228 |
+
"""
|
| 229 |
+
|
| 230 |
+
FOOTER_MD = """
|
| 231 |
+
<div class='harbor-footer'>
|
| 232 |
+
Harbor does not provide medical advice, diagnosis, or treatment.<br>
|
| 233 |
+
If you are in crisis, please call 911 or the BHHL at 833-773-2445.
|
| 234 |
+
</div>
|
| 235 |
+
"""
|
| 236 |
+
|
| 237 |
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
| 238 |
+
|
| 239 |
+
ZIPCODE_RE = re.compile(r"^\d{5}$")
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def is_valid_zip(zipcode: str) -> bool:
|
| 243 |
+
"""Return True if zipcode is exactly 5 digits."""
|
| 244 |
+
return bool(ZIPCODE_RE.match(zipcode.strip()))
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def _load_resources_once():
|
| 248 |
+
"""Load resource CSVs once and cache."""
|
| 249 |
+
if not hasattr(_load_resources_once, "_cache"):
|
| 250 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 251 |
+
paths = [
|
| 252 |
+
os.path.join(current_dir, "references", "knowledge", "ma_resources.csv"),
|
| 253 |
+
os.path.join(current_dir, "references", "knowledge", "boston_resources.csv"),
|
| 254 |
+
]
|
| 255 |
+
_load_resources_once._cache = load_resources(paths)
|
| 256 |
+
return _load_resources_once._cache
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
def get_recommendations(zipcode: str) -> list[dict]:
|
| 260 |
+
"""
|
| 261 |
+
Return a list of treatment recommendations for the given zip code.
|
| 262 |
+
|
| 263 |
+
Uses the same filter/score logic as the chatbot, but with a minimal
|
| 264 |
+
profile containing only the zipcode.
|
| 265 |
+
"""
|
| 266 |
+
profile = create_empty_profile()
|
| 267 |
+
profile["logistics"]["zipcode"] = zipcode.strip()
|
| 268 |
+
|
| 269 |
+
resources = _load_resources_once()
|
| 270 |
+
filtered = filter_resources(resources, profile)
|
| 271 |
+
top = score_resources(filtered, profile)
|
| 272 |
+
return top
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def format_recommendations(zipcode: str, results: list[dict]) -> str:
|
| 276 |
+
"""Render recommendations as an HTML snippet for display."""
|
| 277 |
+
if not results:
|
| 278 |
+
return (
|
| 279 |
+
f"<div class='harbor-results'>"
|
| 280 |
+
f"<div class='harbor-results-title'>Results near {zipcode}</div>"
|
| 281 |
+
f"<p style='color:#5a7a7a; font-size:0.93rem;'>"
|
| 282 |
+
f"No results found for that zip code yet. Try the chatbot below for "
|
| 283 |
+
f"more personalised help.</p>"
|
| 284 |
+
f"</div>"
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
items_html = ""
|
| 288 |
+
for r in results:
|
| 289 |
+
name = r.get("name", "Unknown Facility")
|
| 290 |
+
# Build address from parts
|
| 291 |
+
addr_parts = [r.get("address", ""), r.get("city", ""),
|
| 292 |
+
r.get("state", ""), r.get("zip", "")]
|
| 293 |
+
address = ", ".join(p.strip() for p in addr_parts if p.strip())
|
| 294 |
+
phone = r.get("phone", "").strip()
|
| 295 |
+
# Type from primary_focus
|
| 296 |
+
focus = r.get("primary_focus", "").strip()
|
| 297 |
+
type_label = ", ".join(
|
| 298 |
+
v.strip().replace("_", " ").title() for v in focus.split("|")
|
| 299 |
+
) if focus else ""
|
| 300 |
+
|
| 301 |
+
items_html += (
|
| 302 |
+
f"<div style='margin-bottom:0.75rem; padding:0.75rem; background:#f8fffd; "
|
| 303 |
+
f"border-radius:10px; border:1px solid #c8e6e6;'>"
|
| 304 |
+
f"<strong style='color:#0d6e6e;'>{name}</strong><br>"
|
| 305 |
+
)
|
| 306 |
+
if type_label or address:
|
| 307 |
+
items_html += (
|
| 308 |
+
f"<span style='font-size:0.88rem; color:#5a7a7a;'>"
|
| 309 |
+
f"{type_label + ' · ' if type_label else ''}{address}</span><br>"
|
| 310 |
+
)
|
| 311 |
+
if phone:
|
| 312 |
+
items_html += (
|
| 313 |
+
f"<a href='tel:{phone}' style='font-size:0.88rem; color:#0d9e8f;'>{phone}</a>"
|
| 314 |
+
)
|
| 315 |
+
items_html += "</div>"
|
| 316 |
+
return (
|
| 317 |
+
f"<div class='harbor-results'>"
|
| 318 |
+
f"<div class='harbor-results-title'>📍 Options near {zipcode}</div>"
|
| 319 |
+
f"{items_html}"
|
| 320 |
+
f"</div>"
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
# ── App ───────────────────────────────────────────────────────────────────────
|
| 325 |
+
|
| 326 |
+
def create_chatbot():
|
| 327 |
+
"""Creates the Harbor interface with a landing page and chatbot."""
|
| 328 |
+
_load_resources_once() # pre-load CSVs so first zip lookup is fast
|
| 329 |
+
chatbot = Chatbot()
|
| 330 |
+
|
| 331 |
+
def chat(message, history):
|
| 332 |
+
"""
|
| 333 |
+
Generate a response for the current message.
|
| 334 |
+
|
| 335 |
+
Args:
|
| 336 |
+
message (str): The current message from the user
|
| 337 |
+
history (list): List of previous [user, assistant] message pairs
|
| 338 |
+
|
| 339 |
+
Returns:
|
| 340 |
+
str: The assistant's response
|
| 341 |
+
"""
|
| 342 |
+
return chatbot.get_response(message)
|
| 343 |
+
|
| 344 |
+
def handle_zip_submit(zipcode: str):
|
| 345 |
+
"""Validate zip and return inline results HTML."""
|
| 346 |
+
zipcode = zipcode.strip()
|
| 347 |
+
if not is_valid_zip(zipcode):
|
| 348 |
+
return gr.update(
|
| 349 |
+
value="<div class='harbor-error'>⚠️ Please enter a valid 5-digit zip code.</div>",
|
| 350 |
+
visible=True,
|
| 351 |
+
)
|
| 352 |
+
results = get_recommendations(zipcode)
|
| 353 |
+
|
| 354 |
+
# Log recommendations to console
|
| 355 |
+
if results:
|
| 356 |
+
print(f"[Harbor] Zip lookup ({zipcode}) — {len(results)} recommendation(s):")
|
| 357 |
+
for i, r in enumerate(results, 1):
|
| 358 |
+
print(f" {i}. {r.get('name', 'Unknown')} — {r.get('city', '')}, {r.get('state', '')} {r.get('zip', '')}")
|
| 359 |
+
else:
|
| 360 |
+
print(f"[Harbor] Zip lookup ({zipcode}) — no results found.")
|
| 361 |
+
|
| 362 |
+
return gr.update(value=format_recommendations(zipcode, results), visible=True)
|
| 363 |
+
|
| 364 |
+
def show_chat():
|
| 365 |
+
return gr.update(visible=False), gr.update(visible=True)
|
| 366 |
+
|
| 367 |
+
def show_landing():
|
| 368 |
+
return gr.update(visible=True), gr.update(visible=False)
|
| 369 |
+
|
| 370 |
+
with gr.Blocks(title="Harbor", theme=THEME, css=CSS) as demo:
|
| 371 |
+
|
| 372 |
+
# ── Landing Page ──────────────────────────────────────────────
|
| 373 |
+
with gr.Column(visible=True) as landing_page:
|
| 374 |
+
with gr.Column(elem_classes="harbor-wrap"):
|
| 375 |
+
gr.HTML(HEADER_MD)
|
| 376 |
+
|
| 377 |
+
# Card 1 — Quick Recommendations (featured)
|
| 378 |
+
with gr.Group(elem_classes="harbor-card harbor-card-featured"):
|
| 379 |
+
gr.HTML("<div class='harbor-card-title'>📍 Find Options Near You</div>")
|
| 380 |
+
gr.HTML(
|
| 381 |
+
"<p>Enter your zip code and we'll show you nearby treatment "
|
| 382 |
+
"programs right away — no account needed.</p>"
|
| 383 |
+
)
|
| 384 |
+
with gr.Row():
|
| 385 |
+
zip_input = gr.Textbox(
|
| 386 |
+
placeholder="e.g. 02134",
|
| 387 |
+
max_lines=1,
|
| 388 |
+
show_label=False,
|
| 389 |
+
container=False,
|
| 390 |
+
scale=3,
|
| 391 |
+
)
|
| 392 |
+
zip_btn = gr.Button(
|
| 393 |
+
"Find Options →",
|
| 394 |
+
variant="primary",
|
| 395 |
+
scale=1,
|
| 396 |
+
elem_classes="harbor-zip-btn",
|
| 397 |
+
)
|
| 398 |
+
# Results rendered outside the card so the loading spinner
|
| 399 |
+
# does not overlay the input card above.
|
| 400 |
+
results_html = gr.HTML(visible=False, elem_id="zip-results")
|
| 401 |
+
|
| 402 |
+
# Card 2 — Crisis callout (compact)
|
| 403 |
+
gr.HTML(CRISIS_CALLOUT_HTML)
|
| 404 |
+
|
| 405 |
+
# Card 3 — Chatbot
|
| 406 |
+
with gr.Group(elem_classes="harbor-card"):
|
| 407 |
+
gr.HTML(CHATBOT_CARD_MD)
|
| 408 |
+
start_chat_btn = gr.Button(
|
| 409 |
+
"Start a Conversation →",
|
| 410 |
+
variant="primary",
|
| 411 |
+
size="lg",
|
| 412 |
+
elem_classes="harbor-start-btn",
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
gr.HTML(FOOTER_MD)
|
| 416 |
+
|
| 417 |
+
# ── Chat Page ─────────────────────────────────────────────────
|
| 418 |
+
with gr.Column(visible=False) as chat_page:
|
| 419 |
+
with gr.Column(elem_classes="chat-header"):
|
| 420 |
+
back_btn = gr.Button(
|
| 421 |
+
"← Back to Home",
|
| 422 |
+
size="sm",
|
| 423 |
+
variant="secondary",
|
| 424 |
+
elem_classes="chat-back-btn",
|
| 425 |
+
)
|
| 426 |
+
gr.ChatInterface(
|
| 427 |
+
chat,
|
| 428 |
+
title="⚓ Harbor",
|
| 429 |
+
description=(
|
| 430 |
+
"Tell me a little about your situation and I'll help you find "
|
| 431 |
+
"treatment options that match your needs. Everything is confidential."
|
| 432 |
+
),
|
| 433 |
+
examples=[
|
| 434 |
+
"What treatment options are available near me?",
|
| 435 |
+
"I'm looking for outpatient help with alcohol use.",
|
| 436 |
+
"I need support but I don't have insurance.",
|
| 437 |
+
"How do I know which type of program is right for me?",
|
| 438 |
+
],
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
# ── Events ────────────────────────────────────────────────────
|
| 442 |
+
zip_btn.click(handle_zip_submit, inputs=zip_input, outputs=results_html)
|
| 443 |
+
zip_input.submit(handle_zip_submit, inputs=zip_input, outputs=results_html)
|
| 444 |
+
start_chat_btn.click(show_chat, outputs=[landing_page, chat_page])
|
| 445 |
+
back_btn.click(show_landing, outputs=[landing_page, chat_page])
|
| 446 |
+
|
| 447 |
+
return demo
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
if __name__ == "__main__":
|
| 451 |
+
demo = create_chatbot()
|
| 452 |
+
demo.launch()
|
data/.DS_Store
ADDED
|
Binary file (6.15 kB). View file
|
|
|
data/system_prompt.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
Treatment Navigation Chatbot — System Prompt
|
| 4 |
+
|
| 5 |
+
Role / Identity
|
| 6 |
+
|
| 7 |
+
You are a supportive assistant that helps people find treatment options for substance use or mental health care and guides them toward contacting appropriate treatment facilities.
|
| 8 |
+
|
| 9 |
+
Scope / Boundaries
|
| 10 |
+
|
| 11 |
+
Only assist with discovering and accessing mental health or substance use treatment services. If users ask unrelated questions, politely redirect the conversation back to treatment support.
|
| 12 |
+
|
| 13 |
+
If a user indicates they may be in immediate crisis or danger, pause the normal conversation and encourage them to contact the Behavioral Health Help Line (BHHL).
|
| 14 |
+
|
| 15 |
+
Crisis support information:
|
| 16 |
+
|
| 17 |
+
Behavioral Health Help Line (BHHL)
|
| 18 |
+
|
| 19 |
+
Call or text: 833-773-2445
|
| 20 |
+
|
| 21 |
+
Available 24 hours a day, 7 days a week, 365 days a year.
|
| 22 |
+
|
| 23 |
+
Anyone may contact the Help Line if they or a family member are experiencing a mental health or substance use disorder crisis.
|
| 24 |
+
|
| 25 |
+
Tone / Style
|
| 26 |
+
|
| 27 |
+
Be warm, patient, supportive, and non-judgmental. Use plain language and avoid clinical jargon.
|
| 28 |
+
|
| 29 |
+
Use motivational interviewing techniques:
|
| 30 |
+
|
| 31 |
+
\- Ask open-ended questions
|
| 32 |
+
|
| 33 |
+
\- Reflect the user’s concerns
|
| 34 |
+
|
| 35 |
+
\- Affirm their effort in seeking help
|
| 36 |
+
|
| 37 |
+
\- Avoid lecturing, pushing, or shaming
|
| 38 |
+
|
| 39 |
+
Respect the user’s autonomy and pace.
|
| 40 |
+
|
| 41 |
+
Key Facts
|
| 42 |
+
|
| 43 |
+
Users often hesitate to seek treatment due to stigma, fear, cost concerns, or uncertainty about what treatment involves.
|
| 44 |
+
|
| 45 |
+
Treatment settings may include:
|
| 46 |
+
|
| 47 |
+
\- outpatient care
|
| 48 |
+
|
| 49 |
+
\- intensive outpatient care
|
| 50 |
+
|
| 51 |
+
\- residential/inpatient programs
|
| 52 |
+
|
| 53 |
+
\- telehealth services
|
| 54 |
+
|
| 55 |
+
Matching users to facilities requires understanding:
|
| 56 |
+
|
| 57 |
+
\- type of help needed
|
| 58 |
+
|
| 59 |
+
\- preferred treatment setting
|
| 60 |
+
|
| 61 |
+
\- payment method or insurance
|
| 62 |
+
|
| 63 |
+
\- location
|
| 64 |
+
|
| 65 |
+
\- special preferences such as language, LGBTQ+ affirming care, veterans services, adolescent services, or pregnancy-related care
|
| 66 |
+
|
| 67 |
+
Behavior Rules
|
| 68 |
+
|
| 69 |
+
1\. Engage
|
| 70 |
+
|
| 71 |
+
Build rapport and understand the user’s situation with open-ended questions.
|
| 72 |
+
|
| 73 |
+
2\. Educate
|
| 74 |
+
|
| 75 |
+
If the user is uncertain about treatment, normalize help-seeking and explain what treatment typically involves.
|
| 76 |
+
|
| 77 |
+
3\. Assess
|
| 78 |
+
|
| 79 |
+
Ask a short set of questions to understand their needs:
|
| 80 |
+
|
| 81 |
+
\- type of help
|
| 82 |
+
|
| 83 |
+
\- treatment setting
|
| 84 |
+
|
| 85 |
+
\- payment method
|
| 86 |
+
|
| 87 |
+
\- location
|
| 88 |
+
|
| 89 |
+
\- optional preferences
|
| 90 |
+
|
| 91 |
+
4\. Match
|
| 92 |
+
|
| 93 |
+
Present 3–5 treatment facilities that best match the user’s needs. Explain why each facility fits.
|
| 94 |
+
|
| 95 |
+
5\. Empower
|
| 96 |
+
|
| 97 |
+
Explain what happens when contacting a facility and what the intake process usually looks like.
|
| 98 |
+
|
| 99 |
+
6\. Plan
|
| 100 |
+
|
| 101 |
+
Encourage a concrete next step, such as calling a facility. Provide a simple call script.
|
| 102 |
+
|
| 103 |
+
7\. Follow-through
|
| 104 |
+
|
| 105 |
+
Offer encouragement and help the user make a plan for when they will contact the facility.
|
| 106 |
+
|
| 107 |
+
Guardrails
|
| 108 |
+
|
| 109 |
+
Source of Truth
|
| 110 |
+
|
| 111 |
+
Only recommend facilities that come from an approved, up-to-date treatment directory or database provided to the assistant. Do not invent, infer, or guess facility names, addresses, phone numbers, hours, or services. If verified facility data is unavailable, say so clearly and offer general guidance instead.
|
| 112 |
+
|
| 113 |
+
Verification and Uncertainty
|
| 114 |
+
|
| 115 |
+
Do not claim that a facility has current availability, accepts a specific insurance plan, or offers a specific service unless that information is present in the source data. Remind the user to call the facility before visiting to confirm services, eligibility, hours, and payment details.
|
| 116 |
+
|
| 117 |
+
Crisis Escalation
|
| 118 |
+
|
| 119 |
+
If the user describes immediate danger, overdose risk, suicidal intent, intent to harm others, severe withdrawal, or another urgent behavioral health crisis, do not continue with routine matching questions until safety is addressed. First encourage immediate contact with the Behavioral Health Help Line at 833-773-2445 by call or text. If the situation appears life-threatening or time-sensitive, urge the user to call emergency services right away.
|
| 120 |
+
|
| 121 |
+
Crisis Threshold Protocol
|
| 122 |
+
|
| 123 |
+
The assistant must switch to crisis support mode if the user indicates:
|
| 124 |
+
|
| 125 |
+
Self-harm or suicide risk
|
| 126 |
+
|
| 127 |
+
\- expressing suicidal thoughts
|
| 128 |
+
|
| 129 |
+
\- wanting to die
|
| 130 |
+
|
| 131 |
+
\- planning or considering self-harm
|
| 132 |
+
|
| 133 |
+
\- expressing hopelessness about continuing
|
| 134 |
+
|
| 135 |
+
Risk of harming others
|
| 136 |
+
|
| 137 |
+
\- stating intent to harm another person
|
| 138 |
+
|
| 139 |
+
\- describing imminent violent behavior
|
| 140 |
+
|
| 141 |
+
Overdose or severe substance use emergency
|
| 142 |
+
|
| 143 |
+
\- possible overdose
|
| 144 |
+
|
| 145 |
+
\- severe withdrawal symptoms
|
| 146 |
+
|
| 147 |
+
\- loss of consciousness
|
| 148 |
+
|
| 149 |
+
\- inability to breathe normally
|
| 150 |
+
|
| 151 |
+
Acute mental health crisis
|
| 152 |
+
|
| 153 |
+
\- panic that feels uncontrollable
|
| 154 |
+
|
| 155 |
+
\- hallucinations or extreme confusion
|
| 156 |
+
|
| 157 |
+
\- inability to stay safe
|
| 158 |
+
|
| 159 |
+
Third-party crisis
|
| 160 |
+
|
| 161 |
+
\- the user says a family member or friend is currently in danger or crisis
|
| 162 |
+
|
| 163 |
+
Crisis Response Template
|
| 164 |
+
|
| 165 |
+
I'm really sorry you're going through something this difficult. You don't have to handle it alone.
|
| 166 |
+
|
| 167 |
+
If you or someone near you may be in immediate danger, please call 911 right now.
|
| 168 |
+
|
| 169 |
+
You can also reach the Behavioral Health Help Line (BHHL) for immediate support. They are available 24 hours a day, 7 days a week, 365 days a year.
|
| 170 |
+
|
| 171 |
+
Call or text: 833-773-2445
|
| 172 |
+
|
| 173 |
+
Trained counselors can talk with you and help figure out the next step.
|
| 174 |
+
|
| 175 |
+
If you'd like, you can also tell me a little about what's happening and I can help you think through what to do next.
|
| 176 |
+
|
| 177 |
+
Output Format
|
| 178 |
+
|
| 179 |
+
When recommending facilities, present them clearly:
|
| 180 |
+
|
| 181 |
+
Facility Name
|
| 182 |
+
|
| 183 |
+
Address
|
| 184 |
+
|
| 185 |
+
Phone number
|
| 186 |
+
|
| 187 |
+
Why this matches you:
|
| 188 |
+
|
| 189 |
+
Short explanation referencing the user’s needs.
|
| 190 |
+
|
| 191 |
+
Services:
|
| 192 |
+
|
| 193 |
+
Key treatment services offered.
|
| 194 |
+
|
| 195 |
+
After listing facilities, ask whether the user would like help contacting one of them or making a plan for next steps.
|
| 196 |
+
|
data/user_profile_schema.json
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"demographics": {
|
| 3 |
+
"population": {
|
| 4 |
+
"description": "User's age group or life stage",
|
| 5 |
+
"type": "single",
|
| 6 |
+
"options": [
|
| 7 |
+
{"value": "adolescent", "label": "Adolescent / Minor", "keywords": ["teen", "teenager", "adolescent", "minor", "kid", "child", "youth", "young", "high school", "middle school"], "cite": [7, 627]},
|
| 8 |
+
{"value": "young_adult", "label": "Young Adult / College Student", "keywords": ["college", "university", "student", "young adult", "campus", "dorm", "undergrad", "graduate student"], "cite": [120, 346]},
|
| 9 |
+
{"value": "adult", "label": "General Adult", "keywords": [], "cite": [212]},
|
| 10 |
+
{"value": "older_adult", "label": "Older Adult", "keywords": ["older", "elderly", "senior", "retired", "retirement", "aging"], "cite": [446, 967]}
|
| 11 |
+
]
|
| 12 |
+
},
|
| 13 |
+
"identity_factors": {
|
| 14 |
+
"description": "Core identity factors relevant to treatment matching",
|
| 15 |
+
"type": "multi",
|
| 16 |
+
"options": [
|
| 17 |
+
{"value": "veteran", "label": "Veteran", "keywords": ["veteran", "military", "army", "navy", "marines", "air force", "service member", "deployed", "combat", "VA"], "cite": [65]},
|
| 18 |
+
{"value": "lgbtq", "label": "LGBTQ+", "keywords": ["lgbtq", "gay", "lesbian", "bisexual", "transgender", "trans", "queer", "nonbinary", "non-binary"], "cite": [258]},
|
| 19 |
+
{"value": "wheelchair_user", "label": "Wheelchair User", "keywords": ["wheelchair", "mobility aid", "mobility impairment"], "cite": [918]},
|
| 20 |
+
{"value": "woman", "label": "Woman", "keywords": ["woman", "female", "mother", "mom", "daughter", "wife", "girl"], "cite": []},
|
| 21 |
+
{"value": "homeless", "label": "Homeless / Unhoused", "keywords": ["homeless", "unhoused", "shelter", "no place to stay", "living on the street", "couch surfing"], "cite": [860]},
|
| 22 |
+
{"value": "autistic", "label": "Autistic / Neurodivergent", "keywords": ["autistic", "autism", "neurodivergent", "adhd", "on the spectrum"], "cite": []}
|
| 23 |
+
]
|
| 24 |
+
},
|
| 25 |
+
"language": {
|
| 26 |
+
"description": "Preferred language for treatment",
|
| 27 |
+
"type": "single",
|
| 28 |
+
"options": [
|
| 29 |
+
{"value": "english", "label": "English", "keywords": ["english"], "cite": [173]},
|
| 30 |
+
{"value": "spanish", "label": "Spanish", "keywords": ["spanish", "español", "espanol", "habla español"], "cite": [171, 173]}
|
| 31 |
+
]
|
| 32 |
+
},
|
| 33 |
+
"pronouns": {
|
| 34 |
+
"description": "User's pronouns",
|
| 35 |
+
"type": "single",
|
| 36 |
+
"options": [
|
| 37 |
+
{"value": "he", "label": "He/Him", "keywords": ["he/him"], "cite": []},
|
| 38 |
+
{"value": "she", "label": "She/Her", "keywords": ["she/her"], "cite": []},
|
| 39 |
+
{"value": "they", "label": "They/Them", "keywords": ["they/them"], "cite": [267]}
|
| 40 |
+
]
|
| 41 |
+
}
|
| 42 |
+
},
|
| 43 |
+
"logistics": {
|
| 44 |
+
"zipcode": {
|
| 45 |
+
"description": "User's ZIP code for proximity-based facility matching",
|
| 46 |
+
"type": "extracted",
|
| 47 |
+
"pattern": "\\b\\d{5}\\b"
|
| 48 |
+
},
|
| 49 |
+
"region": {
|
| 50 |
+
"description": "Broader region or area description (city, county, state, or rural designation)",
|
| 51 |
+
"type": "extracted",
|
| 52 |
+
"examples": ["Boston", "Pocahontas County", "rural West Virginia"]
|
| 53 |
+
},
|
| 54 |
+
"profession": {
|
| 55 |
+
"description": "Long-term profession relevant to treatment (e.g., licensing fears, scheduling)",
|
| 56 |
+
"type": "single",
|
| 57 |
+
"options": [
|
| 58 |
+
{"value": "healthcare_worker", "label": "Healthcare Worker / Nurse", "keywords": ["nurse", "doctor", "healthcare worker", "hospital", "medical professional", "EMT", "paramedic", "physician"], "cite": [793]},
|
| 59 |
+
{"value": "mental_health_professional", "label": "Mental Health Professional / Therapist", "keywords": ["therapist", "counselor", "psychologist", "psychiatrist", "social worker", "mental health professional"], "cite": [1021]},
|
| 60 |
+
{"value": "first_responder", "label": "First Responder", "keywords": ["firefighter", "police", "officer", "first responder", "law enforcement"], "cite": []}
|
| 61 |
+
]
|
| 62 |
+
},
|
| 63 |
+
"accessibility_needs": {
|
| 64 |
+
"description": "Physical accessibility requirements for facilities",
|
| 65 |
+
"type": "multi",
|
| 66 |
+
"options": [
|
| 67 |
+
{"value": "wheelchair_accessible", "label": "Wheelchair Accessible / ADA", "keywords": ["wheelchair accessible", "ada accessible", "accessibility", "ramp", "elevator"], "cite": [918]}
|
| 68 |
+
]
|
| 69 |
+
},
|
| 70 |
+
"insurance": {
|
| 71 |
+
"description": "Payment or insurance method",
|
| 72 |
+
"type": "single",
|
| 73 |
+
"options": [
|
| 74 |
+
{"value": "private", "label": "Private Insurance", "keywords": ["insurance", "blue cross", "aetna", "cigna", "united health", "anthem", "humana", "private insurance", "employer insurance", "work insurance"], "cite": [26, 527, 607]},
|
| 75 |
+
{"value": "medicaid_medicare", "label": "Medicaid / Medicare", "keywords": ["medicaid", "medicare", "masshealth", "state insurance"], "cite": [171, 464, 527]},
|
| 76 |
+
{"value": "va_tricare", "label": "VA Benefits / TRICARE", "keywords": ["va benefits", "tricare", "veterans affairs", "va insurance", "military insurance"], "cite": [84, 89]},
|
| 77 |
+
{"value": "uninsured", "label": "Uninsured / Self-Pay", "keywords": ["uninsured", "no insurance", "self-pay", "self pay", "pay out of pocket", "can't afford", "sliding scale", "free"], "cite": [120, 527]}
|
| 78 |
+
]
|
| 79 |
+
},
|
| 80 |
+
"treatment_history": {
|
| 81 |
+
"description": "Previous treatment experiences",
|
| 82 |
+
"type": "extracted",
|
| 83 |
+
"examples": ["prior 12-step residential", "been to rehab before", "tried outpatient"],
|
| 84 |
+
"cite": [752, 759, 762]
|
| 85 |
+
}
|
| 86 |
+
},
|
| 87 |
+
"status": {
|
| 88 |
+
"current_state": {
|
| 89 |
+
"description": "User's current sobriety or intoxication state",
|
| 90 |
+
"type": "single",
|
| 91 |
+
"options": [
|
| 92 |
+
{"value": "sober", "label": "Sober", "keywords": ["sober", "clean", "not using", "in recovery"], "cite": [459]},
|
| 93 |
+
{"value": "intoxicated", "label": "Currently Intoxicated / Incoherent", "keywords": ["drunk", "high", "intoxicated", "wasted", "messed up", "using right now"], "cite": [564, 567]}
|
| 94 |
+
]
|
| 95 |
+
},
|
| 96 |
+
"crisis_level": {
|
| 97 |
+
"description": "Level of distress or crisis",
|
| 98 |
+
"type": "single",
|
| 99 |
+
"options": [
|
| 100 |
+
{"value": "general_distress", "label": "General Distress", "keywords": ["stressed", "overwhelmed", "struggling", "having a hard time", "not doing well"], "cite": [122]},
|
| 101 |
+
{"value": "acute_crisis", "label": "Acute Distress / Suicidal Ideation", "keywords": ["suicidal", "kill myself", "want to die", "end it", "overdose", "can't go on", "hurt myself", "self-harm", "no reason to live"], "cite": [212, 215]}
|
| 102 |
+
]
|
| 103 |
+
},
|
| 104 |
+
"temporary_factors": {
|
| 105 |
+
"description": "Temporary life circumstances affecting treatment needs",
|
| 106 |
+
"type": "multi",
|
| 107 |
+
"options": [
|
| 108 |
+
{"value": "pregnant", "label": "Pregnant / Postpartum", "keywords": ["pregnant", "pregnancy", "postpartum", "expecting", "baby on the way", "just had a baby"], "cite": [300]},
|
| 109 |
+
{"value": "homeless", "label": "Homeless / Unhoused", "keywords": ["homeless", "unhoused", "shelter", "no place to stay", "living on the street"], "cite": [860]},
|
| 110 |
+
{"value": "court_mandated", "label": "Court-Mandated", "keywords": ["court", "mandated", "judge", "probation", "parole", "court-ordered", "legal requirement"], "cite": [828, 831]}
|
| 111 |
+
]
|
| 112 |
+
}
|
| 113 |
+
},
|
| 114 |
+
"clinical": {
|
| 115 |
+
"primary_focus": {
|
| 116 |
+
"description": "Primary reason for seeking help",
|
| 117 |
+
"type": "single",
|
| 118 |
+
"options": [
|
| 119 |
+
{"value": "substance_use", "label": "Alcohol or Drug Use", "keywords": ["drinking", "drug", "substance", "addiction", "using", "habit", "dependence"], "cite": [518]},
|
| 120 |
+
{"value": "mental_health", "label": "Mental Health", "keywords": ["anxiety", "depression", "trauma", "ptsd", "bipolar", "panic", "mental health", "therapy", "counseling"], "cite": [518]},
|
| 121 |
+
{"value": "dual_diagnosis", "label": "Both (Co-occurring)", "keywords": ["both", "dual diagnosis", "co-occurring", "mental health and substance", "depression and drinking"], "cite": [518, 522]},
|
| 122 |
+
{"value": "unsure", "label": "Unsure", "keywords": ["not sure", "don't know", "unsure", "maybe", "i think"], "cite": [518]}
|
| 123 |
+
]
|
| 124 |
+
},
|
| 125 |
+
"substances": {
|
| 126 |
+
"description": "Specific substances involved",
|
| 127 |
+
"type": "multi",
|
| 128 |
+
"options": [
|
| 129 |
+
{"value": "alcohol", "label": "Alcohol", "keywords": ["alcohol", "drinking", "beer", "wine", "liquor", "booze", "drunk"], "cite": [65]},
|
| 130 |
+
{"value": "opioids", "label": "Opioids", "keywords": ["opioid", "heroin", "fentanyl", "pills", "oxy", "oxycontin", "percocet", "vicodin", "painkiller", "opiate"], "cite": [171, 867]},
|
| 131 |
+
{"value": "stimulants", "label": "Meth / Stimulants", "keywords": ["meth", "methamphetamine", "cocaine", "crack", "stimulant", "adderall", "amphetamine", "speed", "coke"], "cite": [595, 867]},
|
| 132 |
+
{"value": "cannabis", "label": "Cannabis", "keywords": ["marijuana", "cannabis", "weed", "pot", "thc", "edibles", "smoking weed"], "cite": [7]},
|
| 133 |
+
{"value": "benzodiazepines", "label": "Benzodiazepines", "keywords": ["benzo", "benzodiazepine", "xanax", "valium", "klonopin", "ativan"], "cite": []},
|
| 134 |
+
{"value": "other", "label": "Other Substance", "keywords": [], "cite": []}
|
| 135 |
+
]
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
"preferences": {
|
| 139 |
+
"setting": {
|
| 140 |
+
"description": "Preferred treatment setting",
|
| 141 |
+
"type": "single",
|
| 142 |
+
"options": [
|
| 143 |
+
{"value": "outpatient", "label": "Outpatient", "keywords": ["outpatient", "appointments", "weekly visits", "not live there"], "cite": [523]},
|
| 144 |
+
{"value": "intensive_outpatient", "label": "Intensive Outpatient (IOP)", "keywords": ["intensive outpatient", "iop", "partial hospitalization", "day program"], "cite": [523]},
|
| 145 |
+
{"value": "residential", "label": "Residential / Inpatient", "keywords": ["residential", "inpatient", "rehab", "live-in", "live there", "stay there", "30-day", "90-day"], "cite": [523]},
|
| 146 |
+
{"value": "telehealth", "label": "Telehealth", "keywords": ["telehealth", "online", "virtual", "video", "remote", "from home", "phone appointment"], "cite": [523]}
|
| 147 |
+
]
|
| 148 |
+
},
|
| 149 |
+
"therapy_approach": {
|
| 150 |
+
"description": "Preferred therapeutic approach",
|
| 151 |
+
"type": "single",
|
| 152 |
+
"options": [
|
| 153 |
+
{"value": "mat", "label": "Medication-Assisted Treatment (MAT)", "keywords": ["mat", "medication-assisted", "suboxone", "methadone", "naltrexone", "vivitrol", "medication treatment", "medically assisted"], "cite": [171, 765]},
|
| 154 |
+
{"value": "cbt", "label": "Cognitive Behavioral Therapy (CBT)", "keywords": ["cbt", "cognitive behavioral", "talk therapy", "behavioral therapy"], "cite": [31, 146]},
|
| 155 |
+
{"value": "twelve_step", "label": "12-Step", "keywords": ["12-step", "twelve step", "aa", "na", "alcoholics anonymous", "narcotics anonymous", "sponsor", "higher power"], "cite": [759]},
|
| 156 |
+
{"value": "medication_only", "label": "Medication Only (No Counseling)", "keywords": ["medication only", "just medication", "no counseling", "no therapy", "just pills", "just meds"], "cite": [991, 993]}
|
| 157 |
+
]
|
| 158 |
+
},
|
| 159 |
+
"scheduling": {
|
| 160 |
+
"description": "Scheduling constraints",
|
| 161 |
+
"type": "multi",
|
| 162 |
+
"options": [
|
| 163 |
+
{"value": "evening_weekend", "label": "Evening / Weekend Hours", "keywords": ["evening", "weekend", "after work", "night", "saturday", "sunday", "after hours"], "cite": [535]},
|
| 164 |
+
{"value": "after_school", "label": "After-School Scheduling", "keywords": ["after school", "after class", "afternoon"], "cite": [31, 35]}
|
| 165 |
+
]
|
| 166 |
+
},
|
| 167 |
+
"barriers": {
|
| 168 |
+
"description": "Primary barriers to seeking treatment",
|
| 169 |
+
"type": "multi",
|
| 170 |
+
"options": [
|
| 171 |
+
{"value": "stigma", "label": "Stigma / Shame", "keywords": ["ashamed", "embarrassed", "stigma", "shame", "what people think", "judged", "judgment"], "cite": [7, 301]},
|
| 172 |
+
{"value": "fear_of_labeling", "label": "Fear of Labeling", "keywords": ["label", "labeled", "addict", "alcoholic", "don't want to be called"], "cite": [7]},
|
| 173 |
+
{"value": "denial", "label": "Denial / Uncertainty", "keywords": ["not sure if", "maybe i don't", "is it really", "not that bad", "i can stop"], "cite": [346]},
|
| 174 |
+
{"value": "cost", "label": "Cost", "keywords": ["cost", "expensive", "afford", "money", "price", "cheap", "budget"], "cite": [120]},
|
| 175 |
+
{"value": "legal_fear", "label": "Fear of Legal Consequences", "keywords": ["custody", "lose my kids", "license", "lose my job", "arrested", "legal", "fired"], "cite": [301, 796]},
|
| 176 |
+
{"value": "geographic_isolation", "label": "Geographic Isolation", "keywords": ["rural", "far away", "no providers", "nothing nearby", "middle of nowhere", "long drive"], "cite": [684, 686]},
|
| 177 |
+
{"value": "phone_anxiety", "label": "Phone Anxiety", "keywords": ["hate calling", "don't want to call", "phone anxiety", "can't call", "nervous to call", "scared to call"], "cite": [396, 419]}
|
| 178 |
+
]
|
| 179 |
+
},
|
| 180 |
+
"contact_channel": {
|
| 181 |
+
"description": "Preferred way to be contacted or reach out",
|
| 182 |
+
"type": "single",
|
| 183 |
+
"options": [
|
| 184 |
+
{"value": "text", "label": "Text Message", "keywords": ["text", "text me", "sms", "message"], "cite": [164, 555]},
|
| 185 |
+
{"value": "phone", "label": "Phone Call", "keywords": ["call", "phone", "call me"], "cite": [488, 712]},
|
| 186 |
+
{"value": "email", "label": "Email", "keywords": ["email", "e-mail"], "cite": [295]},
|
| 187 |
+
{"value": "discreet", "label": "Discreet / Confidential Messaging", "keywords": ["discreet", "confidential", "private", "secret", "no one know"], "cite": [665, 821]}
|
| 188 |
+
]
|
| 189 |
+
}
|
| 190 |
+
}
|
| 191 |
+
}
|
references/.DS_Store
ADDED
|
Binary file (6.15 kB). View file
|
|
|
references/knowledge/boston_resources.csv
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name,address,city,state,zip,phone,website,primary_focus,substances,age_groups,identity_factors,insurance,settings,therapy_approaches,languages,dmh_region,notes
|
| 2 |
+
"Several DMH site offices also have VideoPhones, which can be used for direct communication between staff and",,Statewide,MA,,(978) 627-4468,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 3 |
+
North County Site Office,,Statewide,MA,,(978) 627-4468,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 4 |
+
Springfield Site Office,,Statewide,MA,,(413) 301-0914,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 5 |
+
Metro North Site Office,,Statewide,MA,,(339) 219-6119,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 6 |
+
DMH Site Office,,Statewide,MA,,(339) 219-6119,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 7 |
+
Westborough Site Office,,Statewide,MA,,(508) 948-0605,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 8 |
+
Lindemann/Cambridge/Somerville Site Office,,Statewide,MA,,(857) 366 4215,,mental_health,,,,,,,english,Statewide,DMH Resource Directory 2025
|
| 9 |
+
"Lemuel Shattuck Hospital (617) 522-8110 Dr. Romas Buivydas, Ph.D.","7888 Chief of Psychiatric Operations
|
| 10 |
+
Jamaica Pl",ain,MA,02130,(617) 522-8110,,mental_health,,,,,,,english,Metro Boston,DMH Resource Directory 2025
|
| 11 |
+
Dr. Solomon Carter Fuller Mental Health Center (617) 626-8700 Valencia Reid,"8929 Chief Operating Officer
|
| 12 |
+
Bost",on,MA,02118,(617) 626-8700,,mental_health,,,,,,,english,Metro Boston,DMH Resource Directory 2025
|
| 13 |
+
Taunton State Hospital Telephone # Person in Charge,,"Chief Operating Officer
|
| 14 |
+
Taunton",MA,02780,(508) 977-3000,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 15 |
+
Tewksbury Hospital 2859 Chief of Psychiatric Operations,"4000 Kathy Wenzel
|
| 16 |
+
Worcest",er,MA,01604,(978) 851-1029,,mental_health,,adult,,,,,english,Northeast,DMH Resource Directory 2025
|
| 17 |
+
Worcester Recovery Center and Hospital Telephone # Person in Charge,"4000 Kathy Wenzel
|
| 18 |
+
Worcest",er,MA,01604,(508) 368-4000,,mental_health,,adolescent|adult,,,,,english,Central MA,DMH Resource Directory 2025
|
| 19 |
+
Fuller/Bay Cove Site Office Telephone # Person in Charge,"8884 Site Director
|
| 20 |
+
Bost",on,MA,02118,(617) 626-8944,,mental_health,,,,,,,english,Metro Boston,DMH Resource Directory 2025
|
| 21 |
+
Dr. Solomon Carter Fuller Mental Health Center (617) 626-8944 Patricia Cadet,"8884 Site Director
|
| 22 |
+
Bost",on,MA,02118,(617) 626-8944,,mental_health,,,,,,,english,Metro Boston,DMH Resource Directory 2025
|
| 23 |
+
"Center/Cambridge/Somerville Site Office (617) 626-8500 Luis Borrero, MSW, LICSW","8515 Site Director
|
| 24 |
+
Bost",on,MA,02114,(617) 626-8500,,mental_health,,,,,,,english,Metro Boston,DMH Resource Directory 2025
|
| 25 |
+
Massachusetts Mental Health Center Site Office Telephone # Person in Charge,"9300 Rachel Steiner
|
| 26 |
+
Bost",on,MA,02115,(617) 626-9300,,mental_health,,,,,,,english,Metro Boston,DMH Resource Directory 2025
|
| 27 |
+
Pocasset Mental Health Center Telephone # Person in Charge,,"LICSW
|
| 28 |
+
Pocasset",MA,02559,(508) 564-9600,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 29 |
+
Corrigan Mental Health Center Telephone # Person in Charge,,"LICSW
|
| 30 |
+
Fall River",MA,02720,(508) 235-7200,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 31 |
+
Brockton Site Office Telephone # Person in Charge,2075 Site Direct,"or
|
| 32 |
+
Brockton",MA,02302,(508) 897-2000,,mental_health,,,lgbtq,,,,english,Southeast,DMH Resource Directory 2025
|
| 33 |
+
Cape Cod & The Islands Site Office Telephone # Person in Charge,7346 Site Direct,"or
|
| 34 |
+
Fall River",MA,02720,(508) 957-0900,,mental_health,,,lgbtq,,,,english,Southeast,DMH Resource Directory 2025
|
| 35 |
+
Fall River Site Office Telephone # Person in Charge,7346 Site Direct,"or
|
| 36 |
+
Fall River",MA,02720,(508) 235-7200,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 37 |
+
"Corrigan Mental Health Center (508) 235-7200 Jeanne Crespi, LICSW",7346 Site Direct,"or
|
| 38 |
+
Fall River",MA,02720,(508) 235-7200,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 39 |
+
New Bedford Site Office Telephone # Person in Charge,,"LMHC
|
| 40 |
+
New Bedford",MA,02740,(508) 996-7900,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 41 |
+
Plymouth Site Office Telephone # Person in Charge,,"MSW
|
| 42 |
+
Plymouth",MA,02360,(508) 732-3000,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 43 |
+
Quincy Site Office Telephone # Person in Charge,,"Site Director
|
| 44 |
+
Quincy",MA,02169,(617) 984-1000,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 45 |
+
"Quincy Mental Health Center (617) 984-1000 Laura Ruscio, MSW, LICSW",,"Site Director
|
| 46 |
+
Quincy",MA,02169,(617) 984-1000,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 47 |
+
Taunton/Attleboro Site Office Telephone # Person in Charge,,"MA
|
| 48 |
+
Taunton",MA,02780,(508) 977-3174,,mental_health,,,,,,,english,Southeast,DMH Resource Directory 2025
|
| 49 |
+
Acton Site Office Telephone # Person in Charge,"2100 Alicia Stacy, MPA,","LSW
|
| 50 |
+
Acton",MA,01720,(978) 206-2100,,mental_health,,,,,,,english,Northeast,DMH Resource Directory 2025
|
| 51 |
+
Essex North Site Office Telephone # Person in Charge,"4500 Steven Noroian,","LMHC
|
| 52 |
+
Lawrence",MA,01843,(978) 738-4500,,mental_health,,,,,,,english,Northeast,DMH Resource Directory 2025
|
| 53 |
+
Lowell Site Office Telephone # Person in Charge,"5000 Gerard Frater, MSW,","LICSW
|
| 54 |
+
Lowell",MA,01851,(978) 322-5000,,mental_health,,,,,,,english,Northeast,DMH Resource Directory 2025
|
| 55 |
+
Lynn Site Office Telephone # Person in Charge,"178 Albion Street, Suite 410 Telephone #","Person in Charge
|
| 56 |
+
Wakefield",MA,01880,(781) 477-2070,,mental_health,,,,,,,english,Northeast,DMH Resource Directory 2025
|
| 57 |
+
Metro North Site Office,"178 Albion Street, Suite 410 Telephone #","Person in Charge
|
| 58 |
+
Wakefield",MA,01880,(781) 224-7900,,mental_health,,,,,,,english,Northeast,DMH Resource Directory 2025
|
| 59 |
+
North Shore Site Office Telephone # Person in Charge,,"LMHC
|
| 60 |
+
Salem",MA,01970,(978) 741-7300,,mental_health,,,,,,,english,Northeast,DMH Resource Directory 2025
|
| 61 |
+
North County Site Office Telephone # Person in Charge,,"LMHC
|
| 62 |
+
Fitchburg",MA,01420,(978) 353-4400,,mental_health,,adolescent,,,,,english,Central MA,DMH Resource Directory 2025
|
| 63 |
+
South County Site Office Telephone # Person in Charge,9721 Site Direct,"or
|
| 64 |
+
Canton",MA,02021,(508) 887-1100,,mental_health,,adolescent,,,,,english,Central MA,DMH Resource Directory 2025
|
| 65 |
+
Worcester Site Office Telephone # Person in Charge,9721 Site Direct,"or
|
| 66 |
+
Canton",MA,02021,(774) 420-3100,,mental_health,,adolescent,,,,,english,Central MA,DMH Resource Directory 2025
|
| 67 |
+
Canton Site Office,9721 Site Direct,"or
|
| 68 |
+
Canton",MA,02021,(781) 401-9700,,mental_health,,adolescent,,,,,english,Central MA,DMH Resource Directory 2025
|
| 69 |
+
Pappas Rehabilitation Hospital for Children Telephone # Person in Charge,9721 Site Direct,"or
|
| 70 |
+
Canton",MA,02021,(781) 401-9700,,mental_health,,adolescent,,,,,english,Central MA,DMH Resource Directory 2025
|
| 71 |
+
Westborough Site Office Telephone # Person in Charge,"2864 Site Director
|
| 72 |
+
West",borough,MA,01581,(508) 616-2801,,mental_health,,,,,,,english,Central MA,DMH Resource Directory 2025
|
| 73 |
+
Berkshire Site Office Telephone # Person in Charge,,"LICSW
|
| 74 |
+
Pittsfield",MA,01201,(413) 395-2000,,mental_health,,,,,,,english,Western MA,DMH Resource Directory 2025
|
| 75 |
+
Franklin/North Quabbin Site Office Telephone # Person in Charge,,"LICSW
|
| 76 |
+
Greenfield",MA,01301,(413) 772-5600,,mental_health,,,,,,,english,Western MA,DMH Resource Directory 2025
|
| 77 |
+
Hampshire Site Office Telephone # Person in Charge,,"LMHC
|
| 78 |
+
Northampton",MA,01060,(413) 587-6200,,mental_health,,,,,,,english,Western MA,DMH Resource Directory 2025
|
| 79 |
+
Holyoke/Chicopee Site Office Telephone # Person in Charge,,"LCSW
|
| 80 |
+
Springfield",MA,01104,(413) 452-2300,,mental_health,,,,,,,english,Western MA,DMH Resource Directory 2025
|
| 81 |
+
Springfield Site Office Telephone # Person in Charge,"6200 Christine Fowle,","B.A.
|
| 82 |
+
Northampton",MA,01060,(413) 452-2300,,mental_health,,,,,,,english,Western MA,DMH Resource Directory 2025
|
| 83 |
+
Westfield Site Office Telephone # Person in Charge,"6200 Christine Fowle,","B.A.
|
| 84 |
+
Northampton",MA,01060,(413) 587-6200,,mental_health,,,,,,,english,Western MA,DMH Resource Directory 2025
|
| 85 |
+
"If you have any questions or comments about this directory, please contact the Office of Race,",,,MA,,(617) 626-8134,https://mass.gov/service-,mental_health,,,accessibility|immigrant_refugee|lgbtq,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 86 |
+
EMERGENCY LINE,,,MA,,877-382-1609,https://mass.gov/dmh,dual_diagnosis,,,,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 87 |
+
MASSACHUSETTS DEPARTMENT OF MENTAL HEALTH (DMH),"25 Staniford Street,",Boston,MA,02114,800-221-0053,https://mass.gov/dmh,mental_health,,,,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 88 |
+
Help is available for those with a drug or alcohol problem at helpline-online.org or,"250 Washington Street,",Boston,MA,02108,800-720-3480,https://mass.gov/eohhs/gov/departments/dph/programs/substance-abuse,dual_diagnosis,alcohol,adolescent,accessibility,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 89 |
+
HEARING (MCDHH),"600 Washington Street,",Boston,MA,02111,617-740-1600,https://mass.gov/eohhs/gov/departments/mcdhh,dual_diagnosis,,,accessibility|immigrant_refugee,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 90 |
+
MASSACHUSETTS OFFICE FOR REFUGEES AND IMMIGRANTS (MORI),"600 Washington Street,",Boston,MA,02111,617-727-7888,https://mass.gov/eohhs/gov/departments/ori,dual_diagnosis,,,accessibility|immigrant_refugee,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 91 |
+
MASS 2-1-1,,,MA,,877-211-6277,https://mass.gov/eohhs/gov/departments/ori/key-resources.html,dual_diagnosis,,,immigrant_refugee,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 92 |
+
MASSACHUSETTS REHABILITATION COMMISSION (MRC),"600 Washington Street,",Boston,MA,02111,617-204-3600,https://massoptions.org,dual_diagnosis,,older_adult,accessibility,,,,english|spanish,Statewide,DMH Multicultural Directory 2019
|
| 93 |
+
ADVOCATES,,Framingham,MA,01701,800-640-5432,https://advocates.org,dual_diagnosis,,,accessibility,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 94 |
+
ASPERGER / AUTISM NETWORK,"51 Water Street, Suite 206,",Watertown,MA,02472,617- 393-3824,https://advocates.org,dual_diagnosis,,,,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 95 |
+
"BROCKTON AREA MULTI-SERVICES, INC. (BAMSI)",,Brockton,MA,02301,508-580-8700,https://baycove.org,dual_diagnosis,alcohol,adolescent|older_adult,accessibility,,,,cape_verdean_creole|english|portuguese,Statewide,DMH Multicultural Directory 2019
|
| 96 |
+
"Helpline for health care, social services, financial assistance, housing, and food pantries:",,Brockton,MA,02301,508-584-4357,https://baycove.org,dual_diagnosis,alcohol,adolescent|older_adult,accessibility,,,,cape_verdean_creole|english|portuguese,Statewide,DMH Multicultural Directory 2019
|
| 97 |
+
CAMBRIDGE HEALTH ALLIANCE,"1493 Cambridge Street,",Cambridge,MA,02139,617-665-1305,https://bamsi.org,dual_diagnosis,,adolescent,accessibility,,,,cape_verdean_creole|english|portuguese,Statewide,DMH Multicultural Directory 2019
|
| 98 |
+
“LINK-KID”,,,MA,,855-456-5543,https://challiance.org,dual_diagnosis,,adolescent,accessibility,,,,cape_verdean_creole|english|portuguese,Statewide,DMH Multicultural Directory 2019
|
| 99 |
+
DISABILITY LAW CENTER,"11 Beacon Street, Suite 925,",Boston,MA,02108,617-723-8455,https://deafinconline.org,mental_health,,adolescent,accessibility|lgbtq,,,,english|mandarin|portuguese|spanish,Statewide,DMH Multicultural Directory 2019
|
| 100 |
+
FEDERATION FOR CHILDREN WITH SPECIAL NEEDS,"529 Main Street,",Boston,MA,02129,617-236-7210,https://frcma.org,mental_health,,adolescent,accessibility|lgbtq,,,,english|french|haitian_creole|mandarin|portuguese|spanish,Statewide,DMH Multicultural Directory 2019
|
| 101 |
+
JAPANESE BOSTONIANS SUPPORT LINE,,,MA,,781-296-1800,https://hmhnetwork.org/boston-based-agencies,mental_health,,adult,accessibility,,residential,,english|japanese,Statewide,DMH Multicultural Directory 2019
|
| 102 |
+
JUSTICE RESOURCE INSTITUTE,"160 Gould Street, Suite 300,",Needham,MA,02494,781-559-4900,https://hmhnetwork.org/boston-based-agencies,mental_health,,adult,accessibility,,residential,,english|japanese,Statewide,DMH Multicultural Directory 2019
|
| 103 |
+
MASSACHUSETTS ADULT DAY SERVICES ASSOCIATION,"1 Florence Street,",Boston,MA,02131,617-469-5848,https://jri.org,mental_health,,adult,accessibility,,residential,,english,Statewide,DMH Multicultural Directory 2019
|
| 104 |
+
MASSACHUSETTS LEAGUE OF COMMUNITY HEALTH CENTERS,"40 Court Street #10,",Boston,MA,02108,617-426-2225,https://masspartnership.com,dual_diagnosis,alcohol,adolescent,homeless|immigrant_refugee,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 105 |
+
MASSACHUSETTS ORGANIZATION FOR ADDICTION RECOVERY,"29 Winter Street, 2nd Floor,",Boston,MA,02108,617-423-6627,https://moar-recovery.org,dual_diagnosis,alcohol,adolescent,accessibility|homeless|immigrant_refugee,,,,english,Statewide,DMH Multicultural Directory 2019
|
| 106 |
+
PARENT/PROFESSIONAL ADVOCACY LEAGUE (PPAL),,Boston,MA,02108,866-815-8122,https://nami.org,mental_health,,adolescent|adult|young_adult,woman,,,,bengali|english|gujarati|haitian_creole|hindi|nepali|punjabi|spanish|tamil|urdu,Statewide,DMH Multicultural Directory 2019
|
| 107 |
+
SAHELI,,Burlington,MA,01803,866-472-4354,https://ppal.net/wp-content/uploads/2011/01/Child-,mental_health,,adolescent|adult|young_adult,accessibility|woman,,,,bengali|english|gujarati|haitian_creole|hindi|nepali|punjabi|spanish|tamil|urdu,Statewide,DMH Multicultural Directory 2019
|
| 108 |
+
"1 Frederick Abbott Way, Framingham, MA 01701","1 Frederick Abbott Way,",Framingham,MA,01701,508-879-9800,https://transformation-center.org,dual_diagnosis,,adolescent|adult|older_adult|young_adult,accessibility|immigrant_refugee,,residential,,english|spanish,Statewide,DMH Multicultural Directory 2019
|
| 109 |
+
Arbour Counseling Services ns/arbour-counseling-serv Asian American Civi Association 617-426-9492 Asian Task Force Agai Domestic Violence 617-338-2350 24 hour Multilingual Helpli 617-338-2355 2019 DMH MULTICU,P.O. Box 120108,Boston,MA,02112,617-782-6460,https://arbourhealth.com/organiz,mental_health,,adolescent|young_adult,,,residential,,bengali|cantonese|english|french|hebrew|hindi|italian|japanese|khmer|korean|lao|mandarin|nepali|russian|spanish|tagalog|thai|urdu|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 110 |
+
"Association of Haitia Women in Boston Boston Alliance of Ga Lesbian, Bisexual, an Transgender Youth In 617-227-4313 Boston Asian: Youth Essential Service, Inc 617-482-4243 Boston Center for Refugee Health and Human Rights 617-414-4794 Boston Chinatown Neighborhood Cente 617-635-5129 2019 DMH MULTICU",38 Ash Street,Boston,MA,02111,617-287-0096,https://afab-kafanm.org,dual_diagnosis,,adolescent|adult,immigrant_refugee|lgbtq|woman,,,,asl|cantonese|english|haitian_creole|mandarin|multilingual_interpretation|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 111 |
+
"Boston Children’s Hospital Department Psychiatry and-services/department- psychiatry Boston GLASS For LGBTQ+ youth, homel youth, and youth living wi HIV ages 13-29 857-399-1920 housing/health/boston-gl Boston Mayor’s Heal Line floor 800-847-0710 health-line Boston Medical Cente Immigrant & Refuge Program 617-414-5951 2019 DMH MULTICU",725 Albany Street,Boston,MA,02118,617-355-6680,https://childrenshospital.org/cent,dual_diagnosis,,adolescent,accessibility|immigrant_refugee|lgbtq|woman,,outpatient|residential,,cape_verdean_creole|english|haitian_creole|multilingual_interpretation|portuguese|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 112 |
+
"Brazilian Immigran Center Brazilian Women’s Group 617-202-5775 Casa Esperanza 617-445-1123 Casa Myrna Vazquez, I 617-521-0100 Domestic Violence Hotlin 877-785-2020 2019 DMH MULTICU",P.O. Box 180019,Boston,MA,02118,617-783-8001,https://braziliancenter.org,dual_diagnosis,,adolescent,woman,,residential,,english|portuguese|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 113 |
+
"Center Club, a progra of Bay Cove Human Services 617-788-1091 (Spanish) Center for Cross-Cultu Student Emotional Wellness 617-726-2000 ervices/ccsew_home.asp Children’s Services o Roxbury Specializes in serving the African-American populatio Greater Boston 617-445-6655 2019 DMH MULTICU",504 Dudley Street,Roxbury,MA,02119,617-788-1000,https://centerclubboston.org,dual_diagnosis,,adolescent|adult|young_adult,,,outpatient,,cantonese|cape_verdean_creole|english|haitian_creole|mandarin|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 114 |
+
"Community Legal Services and Counseli Center Deaf and Hard of Hearing Service, Cambridge Health Alliance 617-665-3458 Dorchester House Hea Multi-Service Cente 617-740-2212 Emerge: Counseling a Education to Stop Domestic Violence Suite 101 617-547-9879 2019 DMH MULTICU",2464 Massachusetts Avenu,Cambridge,MA,02140,617-661-1010,https://clsacc.org,dual_diagnosis,,,accessibility|immigrant_refugee,,,,asl|english|french|spanish|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 115 |
+
Eritrean Community Center Ethiopian Communit Mutual Assistance Association 617-492-4232 Fenway Health Healthcare for the LGBTQ community 617-267-0900 617-247-7555 617-457-8140 2019 DMH MULTICU,75 Kneeland Street,Boston,MA,02111,617-427-1210,https://eccboston.org,dual_diagnosis,,adult,lgbtq,,outpatient,,english|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 116 |
+
Freedom Trail Clinic Deaf and Hard of Hearing Outpatient Mental Health lt-services/deaf-and-hard- hearing-outpatient-clinic services Frosina Information Network Albanian Cultural Resource Information Center 617-482-2002 Guidance Center 617-354-2275 2019 DMH MULTICU,5 Sacramento Street,Cambridge,MA,02138,617-912-7800,https://northsuffolk.org/services/,mental_health,,adolescent,accessibility,,outpatient,,asl|cantonese|english|haitian_creole|mandarin|portuguese|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 117 |
+
Haitian American Pub Health Initiative 1601-1603 Blue Hill Avenu Haitian Mental Healt Network Email:HMHnetwork@gmail.c Harvard Program in Refugee Trauma 617-876-7879 Home for Little Wanderers 617-267-3700 Multiple sites in Boston ar 2019 DMH MULTICUL,10 Guest Street,Boston,MA,02135,617-298-8076,https://haphi.org,mental_health,,adolescent,immigrant_refugee,,residential,,english|french|haitian_creole|mandarin|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 118 |
+
Inquilinos Boricuas E Accion Irish International Immigrant Center 617-542-7654 Irish Pastoral Cente 617-265-5300 Italian Home for Children 617-787-1901 2019 DMH MULTICUL,77 Warren Street,Brighton,MA,01235,617-927-1707,https://iba-etc.org,dual_diagnosis,alcohol,adolescent,immigrant_refugee,,,,english|french|haitian_creole|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 119 |
+
"Kit Clark Senior Servi La Alianza Hispana, In 617-427-7175 Latino Health Insuran Program Iglesia Cristiana Nueva Vid 617-567-0235 Ext. 2 Massachusetts Alliance Portuguese Speaker 617-864-7600 Locations in Somerville, Brighton, and Dorchester 2019 DMH MULTICUL",1046 Cambridge Street,Cambridge,MA,02139,617-788-1083,https://kitclark.org,dual_diagnosis,,adolescent|older_adult,immigrant_refugee,,outpatient,,english|haitian_creole|portuguese|spanish|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 120 |
+
"Massachusetts Societ for the Prevention o Cruelty to Children Native American Lifelines of Boston 857-203-9680 nativeamericanlifelines.o North American India Center of Boston, Inc 617-232-0343 North Suffolk Menta Health Association, In 617-889-3300 Locations in East Boston a Revere 2019 DMH MULTICUL",301 Broadway Street,Chelsea,MA,02128,617-983-5800,https://mspcc.org,dual_diagnosis,,adolescent|adult|older_adult,,,outpatient,,asl|cantonese|english|haitian_creole|khmer|mandarin|portuguese|spanish|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 121 |
+
"Osiris Family Institu Priority Professiona Care 857-598-4774 Pyramid Builders Counseling Services, I 857-267-2059 Refugee and Immigra Assistance Center 617-238-2430 2019 DMH MULTICUL",31 Heath Street,Jamaica Plain,MA,02130,617-442-2002,https://osirisinstitute.com,dual_diagnosis,,adolescent,immigrant_refugee,,,,arabic|cape_verdean_creole|english|french|haitian_creole|portuguese|somali|spanish,Metro Boston,DMH Multicultural Directory 2019
|
| 122 |
+
"Roca, Inc. Roxbury Multi-Servic Center 617-989-0292 Somali Developmen Center 10 Malcolm X Blvd. 2nd Flo 617-522-0700 South Bay Communi Service, Dorchester Mental Health Clinic 857-217-3700 southbaycommunityservices. 2019 DMH MULTICUL","415 Neponset Avenue, 3rd Fl",Dorchester,MA,02122,617-889-5210,https://rocainc.org,dual_diagnosis,,adolescent|adult,,,,,arabic|asl|cantonese|english|haitian_creole|mandarin|portuguese|somali|spanish|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 123 |
+
Trauma Center Ummah Health 617-858-6114 Vietnamese America Civic Association 617-288-7344 Vietnamese America Initiative for Development 617-822-3717 Women’s Center 617-354-6394 cambridgewomenscenter. 2019 DMH MULTICUL,46 Pleasant Street,Cambridge,MA,02139,617-232-1303,https://traumacenter.org,mental_health,alcohol,adolescent|adult|young_adult,woman,,outpatient,,arabic|english|spanish|vietnamese,Metro Boston,DMH Multicultural Directory 2019
|
| 124 |
+
"Advocates, Inc. and Advo Deaf Services Video Phone: 774-999-06 Email: DeafInquiries@Advoca African Children Educa 508-799-3653 ARISE (Advocacy for Re and Immigrant Service Empowerment) 617-446-3706 Ascentria Care Allian 774-243-3100 2019 DMH MULTICUL",11 Shattuck Street,Worcester,MA,01605,508-628-6300,https://advocates.org,mental_health,,adolescent,accessibility|immigrant_refugee,,outpatient|residential,,arabic|asl|english|haitian_creole|nepali|somali,Central MA,DMH Multicultural Directory 2019
|
| 125 |
+
"Boston Health Care Callahan Center 508-532-5980 Center for Health Imp 508-756-6676 centerforhealthimpact. Center for Living an Working, Inc.: Deaf and of Hearing Independ Living Services Denholm Building, Suite 3 Voice: 508-798-0350 Video Phone: 508-762-11 2019 DMH MULTICUL",484 Main Street,Worcester,MA,01608,508-660-7974,https://bostonhealthcareinc.co,dual_diagnosis,,adolescent|adult|older_adult,accessibility,,,,arabic|asl|english|spanish|urdu,Central MA,DMH Multicultural Directory 2019
|
| 126 |
+
"Centro Las America Children’s Friend 508-753-5425 s-friend Community Healthlink 508-791-3261 communityhealthlink.o Counseling and Assessm Clinic of Worcester, L 508-756-5400 978-345-9400 2019 DMH MULTICUL","33 Electric Avenue, Suite 2",Fitchburg,MA,01420,508-798-1900,https://centroinc.org,dual_diagnosis,,adolescent|adult,,,outpatient,,arabic|english|french|haitian_creole|italian|portuguese|spanish|thai|vietnamese,Central MA,DMH Multicultural Directory 2019
|
| 127 |
+
Everyday Miracles everydaymiraclesprsc. Family Continuity Prog (FCP) 508-755-0556 508-234-4181 Friendly House 508-755-4362 2019 DMH MULTICUL,36 Wall Street,Worcester,MA,01604,508-799-6227,https://familycontinuity.org,mental_health,,,homeless,,outpatient,,arabic|english|nepali|somali|spanish,Central MA,DMH Multicultural Directory 2019
|
| 128 |
+
"Hearing Loss Associati Central Massachuset Headquarters address: Bethesda, MD 20814 Meetings at Northborough Li Northborough, MA 0153 Email: info@hearinglosscentra .com Hector Reyes Hous Latin American Heal Alliance 508-459-1801 Jewish Community Ce 508-756-7109 2019 DMH MULTICUL",633 Salisbury Street,Worcester,MA,01609,301-657-2248,https://hearinglosscentralma.wor,dual_diagnosis,alcohol,adolescent|older_adult,,,residential,,asl|english|portuguese|spanish,Central MA,DMH Multicultural Directory 2019
|
| 129 |
+
"Kiva Center Latino Health Insura Program 508-875-1237 Locations in Milford and Wor Learning Center for the Framingham, MA 0170 508-879-5110 Video Phone: 774-999-09 LUK Crisis Center 978-345-0685 508-640-0011 508-672-3000 2019 DMH MULTICUL",40 Southbridge Street,Worcester,MA,01608,508-751-9600,https://centralmassrlc.org,dual_diagnosis,,adolescent|adult|young_adult,accessibility,,residential,,asl|english|portuguese|spanish,Central MA,DMH Multicultural Directory 2019
|
| 130 |
+
"Martin Luther King J Opportunity Center, SM Multicultural Wellness C 508-752-4665 978-343-3336 multiculturalwellness. Our Deaf Survivors Ce 978-451-7225 Email: CBosdc414@gmail. 2019 DMH MULTICUL",12 Meade Street,Worcester,MA,01610,508-756-6330,https://mlkj-oc.org,dual_diagnosis,,,accessibility|homeless,,outpatient,,arabic|asl|bengali|cape_verdean_creole|english|french|haitian_creole|hindi|japanese|portuguese|punjabi|russian|spanish,Central MA,DMH Multicultural Directory 2019
|
| 131 |
+
Pathways for Change: Survivors Program Video Phone: 508-502-76 ncy-programs/deaf-survi program Refugee & Immigran Assistance Center Riverside Community 781-329-0909 2019 DMH MULTICUL,"270 Bridge Street, Suite 3",Dedham,MA,02026,508-756-7557,https://pathwaysforchange.help/p,mental_health,,adolescent,accessibility|immigrant_refugee,,outpatient,,arabic|asl|cantonese|english|french|korean|mandarin|portuguese|russian|somali|spanish,Central MA,DMH Multicultural Directory 2019
|
| 132 |
+
Sarah Care /Adult Day H SMOC Behavioral Hea Services 508-872-4853 508-460-9699 Southeast Asian Coaliti Central Massachuset 508-791-4373 2019 DMH MULTICULT,"484 Main Street, Suite 40",Worcester,MA,01608,508-756-7272,https://sarahcare.com/Worces,dual_diagnosis,,adolescent|adult|young_adult,immigrant_refugee|woman,,residential,,english|khmer|lao|mandarin|portuguese|spanish|thai|vietnamese,Central MA,DMH Multicultural Directory 2019
|
| 133 |
+
"Spanish American Cen spanishamericancenter UMass Memorial Health Behavioral/Psychiat Services 508-334-1000 Other locations in Worces umassmemorialhealthcar United Hmong of Massachusetts, Inc 978-343-3831 2019 DMH MULTICULT","21 Culley Street, Suite 10",Fitchburg,MA,01420,978-534-3145,https://unitedhmong.org,dual_diagnosis,,older_adult,homeless|woman,,outpatient|residential,,asl|english|spanish,Central MA,DMH Multicultural Directory 2019
|
| 134 |
+
Worcester Adult Day He Renaissance daycare/worcester_ma_ci l Youth Opportunities Up Inc. 508-849-5600 Zia Access Center At The Bridge of Central M 508-751-9600 At the Kiva Center 774-242-6364 2019 DMH MULTICULT,209 Shrewsbury Street,Worcester,MA,01602,508-831-7500,https://daycarein.com/adult,dual_diagnosis,,adolescent|adult|young_adult,lgbtq,,intensive_outpatient|outpatient|residential,,asl|english|portuguese|russian|spanish,Central MA,DMH Multicultural Directory 2019
|
| 135 |
+
"Alternative House, In Arbour Counseling Services 978-373-7010 978-686-8202 978-453-5736 Arbour Counseling Services Latino Program 617-855-2000 /Latino-services 2019 DMH MULTICULTURA",115 Mill Street,Belmont,MA,02478,978-937-5777,https://alternative-house.org,dual_diagnosis,,adult,lgbtq|woman,,,,english|khmer|portuguese|russian|spanish,Northeast,DMH Multicultural Directory 2019
|
| 136 |
+
"Bosnian Community Center for Resource Development, Inc. Cambodian Mutual Assistance Associatio 978-454-6200 Children’s Friend & Family Services—A Division of Justice Research Institute 781-592-5691 978-682-7289 2019 DMH MULTICULTURA",15 Union Street,Lawrence,MA,01840,781-593-0100,https://bccrd.org,mental_health,,adolescent,accessibility|immigrant_refugee,,outpatient,,arabic|cantonese|english|haitian_creole|hindi|khmer|lao|mandarin|nepali|somali|spanish|thai|vietnamese,Northeast,DMH Multicultural Directory 2019
|
| 137 |
+
Congolese Development Center Eliot Community Human Services 781-395-0704 Family Continuity Programs (FCP) 978-687-1617 978-927-9410 2019 DMH MULTICULTURA,"9 Centennial Drive, Suite 20",Peabody,MA,01960,781-593-0100,https://cd-c.org,dual_diagnosis,,adolescent,,,outpatient,,english|french|haitian_creole|mandarin|portuguese|spanish,Northeast,DMH Multicultural Directory 2019
|
| 138 |
+
International Institut of Lowell Jewish Family and Children’s Services 781-647-5327 Lahey/Northeast Behavioral Health 978-374-0414 978-922-0025 2019 DMH MULTICULTURA,298 Washington Street,Gloucester,MA,01930,978-459-9031,https://iine.org,dual_diagnosis,,adolescent|adult,immigrant_refugee,,intensive_outpatient|outpatient|residential,,english|french|haitian_creole|hebrew|japanese|russian|spanish,Northeast,DMH Multicultural Directory 2019
|
| 139 |
+
Lowell Community Health Metta Health Center Massachusetts Allianc of Portuguese Speake (MAPS) 978-970-1250 2019 DMH MULTICULTURA,11 Mill Street,Lowell,MA,01852,978-441-1700,https://lchealth.org,mental_health,,older_adult,immigrant_refugee,,,,english|french|hindi|khmer|lao|mandarin|portuguese|vietnamese,Northeast,DMH Multicultural Directory 2019
|
| 140 |
+
McLean Hospital Mystic Valley Elder Services 781-324-7705 Northeast Independe Living Program 978-687-4288 2019 DMH MULTICULTURA,20 Ballard Road,Lawrence,MA,01843,800-333-0338,https://mcleanhospital.org,mental_health,alcohol,adolescent|adult|older_adult,accessibility|lgbtq,,outpatient|residential,,asl|english|french|haitian_creole|russian|spanish,Northeast,DMH Multicultural Directory 2019
|
| 141 |
+
Refugee & Immigran Assistance Center (RIAC) Russian Community Association of Massachusetts 781-593-0100 South Bay Communit Services 978-542-1951 978-674-5400 Other locations in Lawrenc and Malden southbaycommunityser 2019 DMH MULTICULTURA,22 Old Canal Drive,Lowell,MA,01851,781-593-0100,https://riacboston.org,dual_diagnosis,,adolescent|adult|older_adult,immigrant_refugee,,,,arabic|english|japanese|khmer|portuguese|russian|somali|spanish,Northeast,DMH Multicultural Directory 2019
|
| 142 |
+
Wayside Youth & Family Support Network YouForward 978-989-2047 Drop-in Centers: Mondays through Wednesdays 1:00-6:00pm YouForward-Entrance B i the back of the building Thursdays 2:00-5:00pm Haverhill YMCA 2019 DMH MULTICULTURA,81 Winter Street,Haverhill,MA,01830,978-460-8712,https://waysideyouth.org,mental_health,,adolescent|adult|young_adult,lgbtq,,outpatient,,english|khmer|portuguese|spanish,Northeast,DMH Multicultural Directory 2019
|
| 143 |
+
"Abelard Psychothera LLC Arbour Counseling Center West Yarmouth, MA 026 774-470-2294 508-678-2833 BAMSI (Brockton Ar Multi-Services, Inc. 508-580-8700",24 Mercedes Road,Brockton,MA,02301,781-344-0057,https://abelardpsychotherapy.co,dual_diagnosis,,adolescent|adult,,,intensive_outpatient|outpatient,,cape_verdean_creole|english|haitian_creole|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 144 |
+
"Boston Chinatown Neighborhood Cente 617-770-0091 Bristol Elder Service 1 Father Devalles Blvd, # 508-675-2101 Cape Cod Behaviora Health Services Psychiatric Center 508-862-5566",27 Park Street,Hyannis,MA,02601,617-635-5129,https://bcnc.net,mental_health,,adolescent|adult|older_adult,,,outpatient|residential,,asl|cantonese|english|khmer|mandarin|portuguese|spanish|vietnamese,Southeast,DMH Multicultural Directory 2019
|
| 145 |
+
Catholic Charities Child & Family Servic 508-990-0894 508-996-8572 508-996-3154,543 North Street,New Bedford,MA,02740,508-587-0815,https://ccab.org,mental_health,,adolescent,immigrant_refugee,,,,cape_verdean_creole|english|french|haitian_creole|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 146 |
+
"Community Counseli of Bristol County 508-823-6124 508-222-8812 Fairwinds-Nantucke Counseling Center 20 Vesper Lane, #L1 508-228-2689",5 Bank Street,Nantucket,MA,02554,508-828-9116,https://comcounseling.org,dual_diagnosis,,adolescent|adult,,,outpatient,,asl|english|korean|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 147 |
+
"Family Continuity Program (FCP) 29 Bassett lane 508-862-0273 Family Service Association 508-677-3822 Health Imperatives 508-583-3005 Other locations in Falmou Vineyard Haven, Nantuck Plymouth, and New Bedfo",942 West Chestnut Stre,Brockton,MA,02301,508-747-6762,https://familycontinuity.org,mental_health,,adolescent|adult,lgbtq,,outpatient,,cape_verdean_creole|english|khmer|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 148 |
+
High Point Treatme Center Immigrants Assistan Center 508-996-8113 immigrantsassistancecenter Independence Hous Inc. 160 Bassett Lane 508-771-6507,58 Crapo Street,Hyannis,MA,02601,508-224-7701,https://hptc.org,dual_diagnosis,,adolescent,immigrant_refugee|woman,,outpatient,,english|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 149 |
+
"Italian Home for Children 5 Palmer Court East Freetown, MA 0271 Luminosity Behavior Health 781-344-0102 Martha’s Vineyard Community Service 508-693-7900",111 Edgartown Road,Oak Bluffs,MA,02557,508-763-4217,https://italianhome.org,dual_diagnosis,,adolescent,veteran,,,,asl|cape_verdean_creole|english|french|haitian_creole|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 150 |
+
New Life Counselin 400-402 North Main Stre newlifecounselingcenter. Northeast Family Services 774-206-1125 northeastfamilyservices.c Pyramid Builders Counseling 617-481-8566 pyramidbuilderscounseling.,425 Pleasant street,Brockton,MA,02301,781-986-4800,,mental_health,,,,,outpatient,,cantonese|cape_verdean_creole|english|haitian_creole|mandarin|spanish,Southeast,DMH Multicultural Directory 2019
|
| 151 |
+
"Seven Hills Behavior Health New Bedford, MA 0274 South Shore Menta Health 617-847-1950 St. Vincent’s Home 508-679-8511",2425 Highland Avenue,Fall River,MA,02720,508-995-6360,https://sevenhills.org,dual_diagnosis,,adolescent|adult,,,intensive_outpatient|outpatient|residential,,asl|cantonese|cape_verdean_creole|english|french|haitian_creole|mandarin|portuguese|spanish|vietnamese,Southeast,DMH Multicultural Directory 2019
|
| 152 |
+
The Women’s Cente (Greater New Bedfo Women’s Center),405 County Street,New Bedford,MA,02740,508-996-3343,https://thewomenscenters.com,mental_health,,adolescent,woman,,residential,,english|portuguese|spanish,Southeast,DMH Multicultural Directory 2019
|
| 153 |
+
"A Positive Place Northampton, MA 0106 ms-services/hivaids AdCare West Springfield, MA 010 413-209-3124 AdLib 413-442-7047 2019 DMH MUL",215 North Street,Pittsfield,MA,01201,413-586-8288,https://cooleydickinson.org/pr,mental_health,,,accessibility|lgbtq|woman,,outpatient,,asl|english,Western MA,DMH Multicultural Directory 2019
|
| 154 |
+
"Anchor House Northampton, MA 0106 anchorhouseartists.o Ascentria Service for N Americans 413-787-0725 services/services-new americans Behavioral Health Network 413-747-0705 Other locations in Westfie Ware, Greenfield, and Holy 2019 DMH MUL",417 Liberty Street,Springfield,MA,01104,413-588-4337,https://ascentria.org/our-,dual_diagnosis,,adolescent,immigrant_refugee|lgbtq,,,,english|italian|russian|spanish,Western MA,DMH Multicultural Directory 2019
|
| 155 |
+
"Berkshire Immigran Center Brick House Commun Resource Center Turners Falls, MA 0137 413-863-9576 brickhousecommunity. Brien Center 413-499-0412 2019 DMH MUL",333 East Street,Pittsfield,MA,01201,413-445-4881,https://berkshireic.com,mental_health,,adolescent|adult,immigrant_refugee,,outpatient,,asl|english|russian|spanish,Western MA,DMH Multicultural Directory 2019
|
| 156 |
+
"Casa Latina Catholic Charities, Diocese of Springfie 413-452-0605 es/cca Center for Human Development 413-733-6624 Center for New Americans Northampton, MA 0106 413-587-0084 2019 DMH MUL",42 Gothic Street,Springfield,MA,01107,413-586-1569,https://casalatinainc.org,mental_health,,adolescent|older_adult,accessibility|immigrant_refugee|lgbtq,,,,english|mandarin|russian|spanish|urdu|vietnamese,Western MA,DMH Multicultural Directory 2019
|
| 157 |
+
"ORGANIZATIONS Center for Women a Community Clinical & Support Options Northampton, MA 0106 413-773-1314 Other locations include Greenfield, Pittsfield, Spring Athol, Amherst, Florenc Orange, and Gardner Community Action 413-774-2318 Northampton, MA 0106 413-582-4230 978-544-5423 2019 DMH MUL",167 South Main Street,Orange,MA,01364,413-545-0883,https://umass.edu/cwc,mental_health,,adolescent|adult,lgbtq|woman,,outpatient,,english|nepali|russian|spanish,Western MA,DMH Multicultural Directory 2019
|
| 158 |
+
ORGANIZATIONS DIAL/SELF Youth an Community Service Elizabeth Freeman Ce 413-499-2425 elizabethfreemancenter Fair Housing Cente 413-539-9796 2019 DMH MUL,57 Suffolk Street,Holyoke,MA,01040,413-774-7054,https://dialself.org,mental_health,,adolescent|adult|young_adult,homeless|immigrant_refugee|lgbtq,,,,english|spanish,Western MA,DMH Multicultural Directory 2019
|
| 159 |
+
"Franklin County Lif Path Gandara Center West Springfield, MA 010 413-736-8329 Guidewire Inc. 413-733-6100 413-443-3295 2019 DMH MUL",34 Depot Street,Pittsfield,MA,01201,413-773-5555,https://lifepathma.org,dual_diagnosis,,adolescent|adult|older_adult|young_adult,accessibility|lgbtq,,intensive_outpatient|residential,,asl|english|spanish,Western MA,DMH Multicultural Directory 2019
|
| 160 |
+
"Impact Center Springfield, MA youth-access-center Jewish Family Servic Western Massachuse 413-737-2601 2019 DMH MUL",15 Lenox Street,Springfield,MA,01108,413-654-1566,https://gandaracenter.org/imp,dual_diagnosis,,adolescent,immigrant_refugee,,,,arabic|english|french|nepali|somali|spanish|vietnamese,Western MA,DMH Multicultural Directory 2019
|
| 161 |
+
Martin Luther King J Family Services Massachusetts Society the Prevention of Cru to Children 413-532-9446 Mental Health Association 413-734-5376 2019 DMH MULT,995 Worthington Stree,Springfield,MA,01109,413-746-3655,https://mlkjrfamilyservices.org,mental_health,,adolescent|adult|young_adult,accessibility|homeless,,residential,,english|spanish,Western MA,DMH Multicultural Directory 2019
|
| 162 |
+
"Montague Catholic So Ministries Turners Falls, MA 0137 New England Learni Center for Women i Transition 413-772-0871 Northeast Center fo Youth and Familie Easthampton, MA 0102 800-360-6210 2019 DMH MULT",203 East Street,Greenfield,MA,01301,413-863-4804,https://mcsmcommunity.or,mental_health,,adolescent,woman,,intensive_outpatient|outpatient|residential,,english|spanish,Western MA,DMH Multicultural Directory 2019
|
| 163 |
+
"Northeast Family Serv West Springfield, MA 010 northeastfamilyservices Nuestras Raices, In 413-535-1789 Picknelly Adult an Family Education Cen 413-552-2990 progams/adult-basic education/picknelly-ad and-family-educatin-ce Roca Inc. 413-846-4301 2019 DMH MULT",29 School Street,Springfield,MA,01105,774-206-1125,https://nuestras-raices.org,mental_health,,adolescent|adult,,,outpatient,,english|spanish,Western MA,DMH Multicultural Directory 2019
|
| 164 |
+
"Safe Passage Northampton, MA 0106 ServiceNet Northampton, MA 0106 413-585-1300 Springfield Vietnam Cultural Center 413-733-9373 2019 DMH MULT",433 Belmont Avenue,Springfield,MA,01108,413-586-1125,https://safepass.org,dual_diagnosis,,adolescent|adult|older_adult|young_adult,accessibility|immigrant_refugee|lgbtq,,residential,,english|khmer|spanish|vietnamese,Western MA,DMH Multicultural Directory 2019
|
| 165 |
+
"Stavros Tapestry Health 413-586-2016 Other locations includ Greenfield, Holyoke, No Adams, Northampton, Pitts Springfield, and West Sprin Valley Opportunit Council 413-552-1554 413-552-1554 2019 DMH MULT",300 High Street,Holyoke,MA,01040,413-256-0473,https://stavros.org,mental_health,,adolescent|adult|older_adult|young_adult,accessibility|lgbtq,,,,arabic|asl|english|french|russian|spanish,Western MA,DMH Multicultural Directory 2019
|
| 166 |
+
"Viability, Inc. Walden Community Serv Learning Center for the Phone and TTY: 508-879-5 Video Phone: 774-999-094 Western Mass Recove Learning Communit 413-539-5941 413-372-5652 413-772-0715 Afiya (Peer-run respite hou Northampton, MA 2019 DMH MULT",74 Federal Street,Greenfield,MA,01301,413-584-1460,https://viability.org,mental_health,,adolescent|young_adult,accessibility,,,,asl|english|spanish,Western MA,DMH Multicultural Directory 2019
|
| 167 |
+
services (/how-to/apply-for-dmh-services),,,MA,,(413) 587-6200,https://www.mass.gov/how-to/apply-for-dmh-,mental_health,,young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 168 |
+
training and presentation programs for community members.,,A Springfield Street Agawam,MA,01001,413-786-9139,https://namimass.org/support-and-,mental_health,,adolescent|young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 169 |
+
"21 Olander Dr., Northampton, MA 01060","235 Chestnut St,",Springfield,MA,01103,(413) 585-1300,https://www.servicenet.org/services/child-and-adolescent-services/prevention-recovery-early-psychosis-prep-2/,mental_health,,adolescent|young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 170 |
+
"Bowen Resource & Wellness Center, 235 Chestnut St, Springfield, MA","235 Chestnut St,",Springfield,MA,01103,(413) 372-5652,https://wildfloweralliance.org,mental_health,,adolescent|young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 171 |
+
772-0715,"235 Chestnut St,",Springfield,MA,01103,866-641-2853,https://wildfloweralliance.org,mental_health,,adolescent|adult|young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 172 |
+
Central Intake,,Boston,MA,02108,(413) 737-2439,https://www.mass.gov/childrens-,dual_diagnosis,,adolescent|young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 173 |
+
Department of Mental Health and the Department of Public Health.,"41 Taylor Street,",Springfield,MA,01103,(413) 654-1566,https://www.springfieldimpactcenter.org/,mental_health,,adolescent|adult|young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
| 174 |
+
sufficiency for themselves and their families.,,,MA,,(413) 552-1554,https://www.valleyopp.com/about-voc/our-,mental_health,,young_adult,,,,,english,Western MA,Western Mass Young Adult Guide
|
references/knowledge/ma_resources.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
accelerate==1.13.0
|
| 2 |
+
aiofiles==24.1.0
|
| 3 |
+
aiohappyeyeballs==2.6.1
|
| 4 |
+
aiohttp==3.13.3
|
| 5 |
+
aiosignal==1.4.0
|
| 6 |
+
annotated-doc==0.0.4
|
| 7 |
+
annotated-types==0.7.0
|
| 8 |
+
anyio==4.12.1
|
| 9 |
+
appnope==0.1.4
|
| 10 |
+
argon2-cffi==25.1.0
|
| 11 |
+
argon2-cffi-bindings==25.1.0
|
| 12 |
+
arrow==1.4.0
|
| 13 |
+
asttokens==3.0.1
|
| 14 |
+
async-lru==2.2.0
|
| 15 |
+
attrs==25.4.0
|
| 16 |
+
babel==2.18.0
|
| 17 |
+
beautifulsoup4==4.14.3
|
| 18 |
+
bitsandbytes==0.49.2
|
| 19 |
+
bleach==6.3.0
|
| 20 |
+
brotli==1.2.0
|
| 21 |
+
certifi==2026.2.25
|
| 22 |
+
cffi==2.0.0
|
| 23 |
+
charset-normalizer==3.4.5
|
| 24 |
+
click==8.3.1
|
| 25 |
+
comm==0.2.3
|
| 26 |
+
cut-cross-entropy==25.1.1
|
| 27 |
+
datasets==4.3.0
|
| 28 |
+
debugpy==1.8.20
|
| 29 |
+
decorator==5.2.1
|
| 30 |
+
defusedxml==0.7.1
|
| 31 |
+
diffusers==0.37.0
|
| 32 |
+
dill==0.4.0
|
| 33 |
+
docstring_parser==0.17.0
|
| 34 |
+
executing==2.2.1
|
| 35 |
+
fastapi==0.135.1
|
| 36 |
+
fastjsonschema==2.21.2
|
| 37 |
+
ffmpy==1.0.0
|
| 38 |
+
filelock==3.25.1
|
| 39 |
+
fqdn==1.5.1
|
| 40 |
+
frozenlist==1.8.0
|
| 41 |
+
fsspec==2025.9.0
|
| 42 |
+
git-filter-repo==2.47.0
|
| 43 |
+
gradio==6.9.0
|
| 44 |
+
gradio_client==2.3.0
|
| 45 |
+
groovy==0.1.2
|
| 46 |
+
h11==0.16.0
|
| 47 |
+
hf-xet==1.4.2
|
| 48 |
+
hf_transfer==0.1.9
|
| 49 |
+
httpcore==1.0.9
|
| 50 |
+
httpx==0.28.1
|
| 51 |
+
huggingface_hub==1.7.1
|
| 52 |
+
idna==3.11
|
| 53 |
+
importlib_metadata==8.7.1
|
| 54 |
+
ipykernel==7.2.0
|
| 55 |
+
ipython==9.11.0
|
| 56 |
+
ipython_pygments_lexers==1.1.1
|
| 57 |
+
ipywidgets==8.1.8
|
| 58 |
+
isoduration==20.11.0
|
| 59 |
+
jedi==0.19.2
|
| 60 |
+
Jinja2==3.1.6
|
| 61 |
+
json5==0.13.0
|
| 62 |
+
jsonpointer==3.0.0
|
| 63 |
+
jsonschema==4.26.0
|
| 64 |
+
jsonschema-specifications==2025.9.1
|
| 65 |
+
jupyter==1.1.1
|
| 66 |
+
jupyter-console==6.6.3
|
| 67 |
+
jupyter-events==0.12.0
|
| 68 |
+
jupyter-lsp==2.3.0
|
| 69 |
+
jupyter_client==8.8.0
|
| 70 |
+
jupyter_core==5.9.1
|
| 71 |
+
jupyter_server==2.17.0
|
| 72 |
+
jupyter_server_terminals==0.5.4
|
| 73 |
+
jupyterlab==4.5.6
|
| 74 |
+
jupyterlab_pygments==0.3.0
|
| 75 |
+
jupyterlab_server==2.28.0
|
| 76 |
+
jupyterlab_widgets==3.0.16
|
| 77 |
+
lark==1.3.1
|
| 78 |
+
markdown-it-py==4.0.0
|
| 79 |
+
MarkupSafe==3.0.3
|
| 80 |
+
matplotlib-inline==0.2.1
|
| 81 |
+
mdurl==0.1.2
|
| 82 |
+
mistune==3.2.0
|
| 83 |
+
mpmath==1.3.0
|
| 84 |
+
msgspec==0.20.0
|
| 85 |
+
multidict==6.7.1
|
| 86 |
+
multiprocess==0.70.16
|
| 87 |
+
nbclient==0.10.4
|
| 88 |
+
nbconvert==7.17.0
|
| 89 |
+
nbformat==5.10.4
|
| 90 |
+
nest-asyncio==1.6.0
|
| 91 |
+
networkx==3.6.1
|
| 92 |
+
notebook==7.5.5
|
| 93 |
+
notebook_shim==0.2.4
|
| 94 |
+
numpy==1.26.4
|
| 95 |
+
orjson==3.11.7
|
| 96 |
+
packaging==26.0
|
| 97 |
+
pandas==3.0.1
|
| 98 |
+
pandocfilters==1.5.1
|
| 99 |
+
parso==0.8.6
|
| 100 |
+
peft==0.18.1
|
| 101 |
+
pexpect==4.9.0
|
| 102 |
+
pillow==12.1.1
|
| 103 |
+
platformdirs==4.9.4
|
| 104 |
+
prometheus_client==0.24.1
|
| 105 |
+
prompt_toolkit==3.0.52
|
| 106 |
+
propcache==0.4.1
|
| 107 |
+
protobuf==7.34.0
|
| 108 |
+
psutil==7.2.2
|
| 109 |
+
ptyprocess==0.7.0
|
| 110 |
+
pure_eval==0.2.3
|
| 111 |
+
pyarrow==23.0.1
|
| 112 |
+
pycparser==3.0
|
| 113 |
+
pydantic==2.12.5
|
| 114 |
+
pydantic_core==2.41.5
|
| 115 |
+
pydub==0.25.1
|
| 116 |
+
Pygments==2.19.2
|
| 117 |
+
python-dateutil==2.9.0.post0
|
| 118 |
+
python-dotenv==1.2.2
|
| 119 |
+
python-json-logger==4.0.0
|
| 120 |
+
python-multipart==0.0.22
|
| 121 |
+
pytz==2026.1.post1
|
| 122 |
+
PyYAML==6.0.3
|
| 123 |
+
pyzmq==27.1.0
|
| 124 |
+
referencing==0.37.0
|
| 125 |
+
regex==2026.2.28
|
| 126 |
+
requests==2.32.5
|
| 127 |
+
rfc3339-validator==0.1.4
|
| 128 |
+
rfc3986-validator==0.1.1
|
| 129 |
+
rfc3987-syntax==1.1.0
|
| 130 |
+
rich==14.3.3
|
| 131 |
+
rpds-py==0.30.0
|
| 132 |
+
safehttpx==0.1.7
|
| 133 |
+
safetensors==0.7.0
|
| 134 |
+
semantic-version==2.10.0
|
| 135 |
+
Send2Trash==2.1.0
|
| 136 |
+
sentencepiece==0.2.1
|
| 137 |
+
setuptools==82.0.1
|
| 138 |
+
shellingham==1.5.4
|
| 139 |
+
six==1.17.0
|
| 140 |
+
soupsieve==2.8.3
|
| 141 |
+
stack-data==0.6.3
|
| 142 |
+
starlette==0.52.1
|
| 143 |
+
sympy==1.14.0
|
| 144 |
+
terminado==0.18.1
|
| 145 |
+
tinycss2==1.4.0
|
| 146 |
+
tokenizers==0.22.2
|
| 147 |
+
tomlkit==0.13.3
|
| 148 |
+
torch==2.10.0
|
| 149 |
+
torchao==0.16.0
|
| 150 |
+
torchvision==0.25.0
|
| 151 |
+
tornado==6.5.5
|
| 152 |
+
tqdm==4.67.3
|
| 153 |
+
traitlets==5.14.3
|
| 154 |
+
transformers==5.2.0
|
| 155 |
+
trl==0.24.0
|
| 156 |
+
typeguard==4.5.1
|
| 157 |
+
typer==0.24.1
|
| 158 |
+
typer-slim==0.24.0
|
| 159 |
+
typing-inspection==0.4.2
|
| 160 |
+
typing_extensions==4.15.0
|
| 161 |
+
tyro==1.0.8
|
| 162 |
+
tzdata==2025.3
|
| 163 |
+
unsloth==2026.3.4
|
| 164 |
+
unsloth_zoo==2026.3.2
|
| 165 |
+
uri-template==1.3.0
|
| 166 |
+
urllib3==2.6.3
|
| 167 |
+
uvicorn==0.41.0
|
| 168 |
+
wcwidth==0.6.0
|
| 169 |
+
webcolors==25.10.0
|
| 170 |
+
webencodings==0.5.1
|
| 171 |
+
websocket-client==1.9.0
|
| 172 |
+
wheel==0.46.3
|
| 173 |
+
widgetsnbextension==4.0.15
|
| 174 |
+
xxhash==3.6.0
|
| 175 |
+
yarl==1.23.0
|
| 176 |
+
zipp==3.23.0
|
src/.DS_Store
ADDED
|
Binary file (6.15 kB). View file
|
|
|
src/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
src/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (164 Bytes). View file
|
|
|
src/__pycache__/chat.cpython-312.pyc
ADDED
|
Binary file (6.13 kB). View file
|
|
|
src/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (390 Bytes). View file
|
|
|
src/chat.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from huggingface_hub import InferenceClient
|
| 2 |
+
from src.config import BASE_MODEL, MY_MODEL, HF_TOKEN
|
| 3 |
+
import os
|
| 4 |
+
from src.utils.profile import load_schema, create_empty_profile, extract_profile_updates, merge_profile, profile_to_summary
|
| 5 |
+
from src.utils.resources import load_resources, filter_resources, score_resources, format_recommendations
|
| 6 |
+
|
| 7 |
+
class Chatbot:
|
| 8 |
+
|
| 9 |
+
def __init__(self):
|
| 10 |
+
"""
|
| 11 |
+
Initialize the chatbot with a HF model ID
|
| 12 |
+
"""
|
| 13 |
+
model_id = MY_MODEL if MY_MODEL else BASE_MODEL # define MY_MODEL in config.py if you create a new model in the HuggingFace Hub
|
| 14 |
+
self.client = InferenceClient(model=model_id, token=HF_TOKEN)
|
| 15 |
+
# Initialize user profile
|
| 16 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 17 |
+
data_dir = os.path.join(current_dir, '..', 'data')
|
| 18 |
+
self.profile_schema = load_schema(os.path.join(data_dir, 'user_profile_schema.json'))
|
| 19 |
+
self.user_profile = create_empty_profile()
|
| 20 |
+
# Load treatment resources once
|
| 21 |
+
knowledge_dir = os.path.join(data_dir, '..', 'references', 'knowledge')
|
| 22 |
+
resources_paths = [
|
| 23 |
+
os.path.join(knowledge_dir, 'ma_resources.csv'),
|
| 24 |
+
os.path.join(knowledge_dir, 'boston_resources.csv'),
|
| 25 |
+
]
|
| 26 |
+
self.resources = load_resources(resources_paths)
|
| 27 |
+
|
| 28 |
+
def update_profile(self, user_input):
|
| 29 |
+
"""
|
| 30 |
+
Scan user input for profile-relevant information and merge it
|
| 31 |
+
into the running user profile.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
user_input (str): The user's message text.
|
| 35 |
+
"""
|
| 36 |
+
updates = extract_profile_updates(self.profile_schema, user_input)
|
| 37 |
+
merge_profile(self.user_profile, updates)
|
| 38 |
+
|
| 39 |
+
def format_prompt(self, user_input):
|
| 40 |
+
"""
|
| 41 |
+
Format the user's input into a list of chat messages with system context.
|
| 42 |
+
Updates the user profile with any new information detected.
|
| 43 |
+
|
| 44 |
+
This method:
|
| 45 |
+
1. Loads system prompt from system_prompt.md
|
| 46 |
+
2. Updates user profile from schema-based keyword matching
|
| 47 |
+
3. Injects profile summary into the system prompt so the model knows what's been gathered
|
| 48 |
+
4. Returns a list of message dicts for the chat completion API
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
user_input (str): The user's question
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
list[dict]: A list of message dicts with 'role' and 'content' keys
|
| 55 |
+
"""
|
| 56 |
+
# Get the directory where this file is located
|
| 57 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 58 |
+
|
| 59 |
+
# Load system prompt
|
| 60 |
+
system_prompt_path = os.path.join(current_dir, '../data/system_prompt.md')
|
| 61 |
+
with open(system_prompt_path, 'r', encoding='utf-8') as f:
|
| 62 |
+
system_prompt = f.read().strip()
|
| 63 |
+
|
| 64 |
+
# Update user profile from this message
|
| 65 |
+
self.update_profile(user_input)
|
| 66 |
+
|
| 67 |
+
# Build profile summary for the prompt
|
| 68 |
+
profile_summary = profile_to_summary(self.user_profile)
|
| 69 |
+
|
| 70 |
+
# Build system message with profile context
|
| 71 |
+
system_content = system_prompt
|
| 72 |
+
if profile_summary:
|
| 73 |
+
system_content = system_content + "\n\n" + profile_summary
|
| 74 |
+
|
| 75 |
+
# Return structured messages for chat completion API
|
| 76 |
+
messages = [
|
| 77 |
+
{"role": "system", "content": system_content},
|
| 78 |
+
{"role": "user", "content": user_input},
|
| 79 |
+
]
|
| 80 |
+
|
| 81 |
+
return messages
|
| 82 |
+
|
| 83 |
+
def get_response(self, user_input):
|
| 84 |
+
"""
|
| 85 |
+
Generate a response to the user's question, with resource recommendations
|
| 86 |
+
appended when the user profile contains enough information to match.
|
| 87 |
+
|
| 88 |
+
Args:
|
| 89 |
+
user_input (str): The user's question
|
| 90 |
+
|
| 91 |
+
Returns:
|
| 92 |
+
str: The chatbot's response, optionally followed by top 3 resources
|
| 93 |
+
"""
|
| 94 |
+
# 1. Format messages (also updates profile)
|
| 95 |
+
messages = self.format_prompt(user_input)
|
| 96 |
+
|
| 97 |
+
# 2. Generate LLM response via chat completion API
|
| 98 |
+
result = self.client.chat_completion(
|
| 99 |
+
messages=messages,
|
| 100 |
+
max_tokens=512,
|
| 101 |
+
temperature=0.7,
|
| 102 |
+
)
|
| 103 |
+
response = result.choices[0].message.content.strip()
|
| 104 |
+
|
| 105 |
+
# 3. Filter resources by profile, score, and append top 3
|
| 106 |
+
filtered = filter_resources(self.resources, self.user_profile)
|
| 107 |
+
top_resources = score_resources(filtered, self.user_profile)
|
| 108 |
+
recommendations = format_recommendations(top_resources)
|
| 109 |
+
|
| 110 |
+
# Log recommendations to console
|
| 111 |
+
if top_resources:
|
| 112 |
+
print(f"[Harbor] Chat recommendations ({len(top_resources)}) for profile:")
|
| 113 |
+
for i, r in enumerate(top_resources, 1):
|
| 114 |
+
print(f" {i}. {r.get('name', 'Unknown')} — {r.get('city', '')}, {r.get('state', '')} {r.get('zip', '')}")
|
| 115 |
+
else:
|
| 116 |
+
print("[Harbor] No recommendations matched current profile.")
|
| 117 |
+
|
| 118 |
+
if recommendations:
|
| 119 |
+
response = response + "\n\n" + recommendations
|
| 120 |
+
|
| 121 |
+
return response
|
src/config.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from dotenv import load_dotenv
|
| 3 |
+
|
| 4 |
+
# Load from .env file. Store your HF token in the .env file.
|
| 5 |
+
load_dotenv()
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
BASE_MODEL = "Qwen/Qwen2.5-7B-Instruct"
|
| 9 |
+
# Other options:
|
| 10 |
+
# BASE_MODEL = "meta-llama/Llama-3.2-3B-Instruct" # gated — requires Meta license approval
|
| 11 |
+
# BASE_MODEL = "Qwen/Qwen2.5-1.5B-Instruct" # ungated, smaller/faster
|
| 12 |
+
# BASE_MODEL = "HuggingFaceH4/zephyr-7b-beta" # ungated
|
| 13 |
+
|
| 14 |
+
# If you finetune the model or change it in any way, save it to huggingface hub, then set MY_MODEL to your model ID. The model ID is in the format "your-username/your-model-name".
|
| 15 |
+
MY_MODEL = "" #"amitashukla/harbor-qwn25-lora"
|
| 16 |
+
|
| 17 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
src/utils/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Utils package for helper functions
|
| 2 |
+
|
src/utils/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (166 Bytes). View file
|
|
|
src/utils/__pycache__/profile.cpython-312.pyc
ADDED
|
Binary file (7.23 kB). View file
|
|
|
src/utils/__pycache__/resources.cpython-312.pyc
ADDED
|
Binary file (11.6 kB). View file
|
|
|
src/utils/__pycache__/tags.cpython-312.pyc
ADDED
|
Binary file (1.56 kB). View file
|
|
|
src/utils/profile.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import re
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def load_schema(schema_path):
|
| 6 |
+
"""Load the user profile schema from a JSON file."""
|
| 7 |
+
with open(schema_path, 'r', encoding='utf-8') as f:
|
| 8 |
+
return json.load(f)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def create_empty_profile():
|
| 12 |
+
"""
|
| 13 |
+
Create an empty user profile with all fields set to null/empty.
|
| 14 |
+
This represents a user we know nothing about yet.
|
| 15 |
+
"""
|
| 16 |
+
return {
|
| 17 |
+
"demographics": {
|
| 18 |
+
"population": None,
|
| 19 |
+
"identity_factors": [],
|
| 20 |
+
"language": None,
|
| 21 |
+
"pronouns": None
|
| 22 |
+
},
|
| 23 |
+
"logistics": {
|
| 24 |
+
"zipcode": None,
|
| 25 |
+
"region": None,
|
| 26 |
+
"profession": None,
|
| 27 |
+
"accessibility_needs": [],
|
| 28 |
+
"insurance": None,
|
| 29 |
+
"treatment_history": None
|
| 30 |
+
},
|
| 31 |
+
"status": {
|
| 32 |
+
"current_state": None,
|
| 33 |
+
"crisis_level": None,
|
| 34 |
+
"temporary_factors": []
|
| 35 |
+
},
|
| 36 |
+
"clinical": {
|
| 37 |
+
"primary_focus": None,
|
| 38 |
+
"substances": []
|
| 39 |
+
},
|
| 40 |
+
"preferences": {
|
| 41 |
+
"setting": None,
|
| 42 |
+
"therapy_approach": None,
|
| 43 |
+
"scheduling": [],
|
| 44 |
+
"barriers": [],
|
| 45 |
+
"contact_channel": None
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def extract_profile_updates(schema, user_input):
|
| 51 |
+
"""
|
| 52 |
+
Scan user input against the schema and return a dict of detected profile updates.
|
| 53 |
+
|
| 54 |
+
For 'single' type fields, returns the first matched option value.
|
| 55 |
+
For 'multi' type fields, returns a list of all matched option values.
|
| 56 |
+
For 'extracted' type fields (zipcode, region, treatment_history), uses
|
| 57 |
+
pattern matching or returns raw text snippets.
|
| 58 |
+
|
| 59 |
+
Args:
|
| 60 |
+
schema: The loaded profile schema dict.
|
| 61 |
+
user_input: The user's message text.
|
| 62 |
+
|
| 63 |
+
Returns:
|
| 64 |
+
dict: Nested dict mirroring the profile structure, containing only
|
| 65 |
+
fields where matches were found.
|
| 66 |
+
"""
|
| 67 |
+
input_lower = user_input.lower()
|
| 68 |
+
updates = {}
|
| 69 |
+
|
| 70 |
+
for category_name, category in schema.items():
|
| 71 |
+
category_updates = {}
|
| 72 |
+
|
| 73 |
+
for field_name, field_def in category.items():
|
| 74 |
+
field_type = field_def.get("type")
|
| 75 |
+
|
| 76 |
+
if field_type == "extracted":
|
| 77 |
+
# Special handling for pattern-based or free-text fields
|
| 78 |
+
value = _extract_field(field_name, field_def, user_input, input_lower)
|
| 79 |
+
if value is not None:
|
| 80 |
+
category_updates[field_name] = value
|
| 81 |
+
|
| 82 |
+
elif field_type in ("single", "multi"):
|
| 83 |
+
matches = []
|
| 84 |
+
for option in field_def.get("options", []):
|
| 85 |
+
for keyword in option.get("keywords", []):
|
| 86 |
+
if keyword and keyword.lower() in input_lower:
|
| 87 |
+
matches.append(option["value"])
|
| 88 |
+
break # one keyword match per option is enough
|
| 89 |
+
|
| 90 |
+
if matches:
|
| 91 |
+
if field_type == "single":
|
| 92 |
+
category_updates[field_name] = matches[0]
|
| 93 |
+
else:
|
| 94 |
+
category_updates[field_name] = matches
|
| 95 |
+
|
| 96 |
+
if category_updates:
|
| 97 |
+
updates[category_name] = category_updates
|
| 98 |
+
|
| 99 |
+
return updates
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def _extract_field(field_name, field_def, user_input, input_lower):
|
| 103 |
+
"""Handle extraction for non-option fields like zipcode and treatment_history."""
|
| 104 |
+
if field_name == "zipcode":
|
| 105 |
+
pattern = field_def.get("pattern", r"\b\d{5}\b")
|
| 106 |
+
match = re.search(pattern, user_input)
|
| 107 |
+
if match:
|
| 108 |
+
return match.group()
|
| 109 |
+
return None
|
| 110 |
+
|
| 111 |
+
if field_name == "region":
|
| 112 |
+
# Region is typically set explicitly or by the LLM, not keyword-matched.
|
| 113 |
+
# We do a lightweight check for common geographic indicators.
|
| 114 |
+
geo_patterns = [
|
| 115 |
+
r"\bin\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)", # "in Boston", "in Pocahontas County"
|
| 116 |
+
r"\bnear\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)", # "near Springfield"
|
| 117 |
+
r"\bfrom\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)", # "from Cambridge"
|
| 118 |
+
]
|
| 119 |
+
for pattern in geo_patterns:
|
| 120 |
+
match = re.search(pattern, user_input)
|
| 121 |
+
if match:
|
| 122 |
+
return match.group(1)
|
| 123 |
+
return None
|
| 124 |
+
|
| 125 |
+
if field_name == "treatment_history":
|
| 126 |
+
history_keywords = ["rehab", "treatment before", "been to", "tried",
|
| 127 |
+
"previous treatment", "went to", "was in",
|
| 128 |
+
"12-step", "residential before", "relapsed"]
|
| 129 |
+
for keyword in history_keywords:
|
| 130 |
+
if keyword in input_lower:
|
| 131 |
+
return user_input # store the raw message as context
|
| 132 |
+
return None
|
| 133 |
+
|
| 134 |
+
return None
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def merge_profile(profile, updates):
|
| 138 |
+
"""
|
| 139 |
+
Merge new updates into the existing profile.
|
| 140 |
+
|
| 141 |
+
- For 'single' fields (non-list values): new values overwrite old ones.
|
| 142 |
+
- For 'multi' fields (list values): new values are appended (no duplicates).
|
| 143 |
+
- None values in updates are ignored (don't clear existing data).
|
| 144 |
+
|
| 145 |
+
Args:
|
| 146 |
+
profile: The current user profile dict (modified in place).
|
| 147 |
+
updates: The updates dict from extract_profile_updates().
|
| 148 |
+
|
| 149 |
+
Returns:
|
| 150 |
+
dict: The updated profile (same object as input).
|
| 151 |
+
"""
|
| 152 |
+
for category_name, category_updates in updates.items():
|
| 153 |
+
if category_name not in profile:
|
| 154 |
+
continue
|
| 155 |
+
|
| 156 |
+
for field_name, new_value in category_updates.items():
|
| 157 |
+
if field_name not in profile[category_name]:
|
| 158 |
+
continue
|
| 159 |
+
|
| 160 |
+
if new_value is None:
|
| 161 |
+
continue
|
| 162 |
+
|
| 163 |
+
existing = profile[category_name][field_name]
|
| 164 |
+
|
| 165 |
+
if isinstance(existing, list) and isinstance(new_value, list):
|
| 166 |
+
# Append new values, skip duplicates
|
| 167 |
+
for v in new_value:
|
| 168 |
+
if v not in existing:
|
| 169 |
+
existing.append(v)
|
| 170 |
+
elif isinstance(existing, list) and not isinstance(new_value, list):
|
| 171 |
+
# Single value going into a list field
|
| 172 |
+
if new_value not in existing:
|
| 173 |
+
existing.append(new_value)
|
| 174 |
+
else:
|
| 175 |
+
# Single value field: overwrite
|
| 176 |
+
profile[category_name][field_name] = new_value
|
| 177 |
+
|
| 178 |
+
return profile
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def profile_to_summary(profile):
|
| 182 |
+
"""
|
| 183 |
+
Convert a user profile dict into a concise text summary for injection
|
| 184 |
+
into the system prompt. Only includes fields that have been filled in.
|
| 185 |
+
|
| 186 |
+
Returns:
|
| 187 |
+
str: A human-readable summary, or empty string if profile is empty.
|
| 188 |
+
"""
|
| 189 |
+
lines = []
|
| 190 |
+
|
| 191 |
+
category_labels = {
|
| 192 |
+
"demographics": "Demographics",
|
| 193 |
+
"logistics": "Logistics & History",
|
| 194 |
+
"status": "Current Status",
|
| 195 |
+
"clinical": "Clinical Needs",
|
| 196 |
+
"preferences": "Preferences & Barriers"
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
for category_name, category_label in category_labels.items():
|
| 200 |
+
category = profile.get(category_name, {})
|
| 201 |
+
category_lines = []
|
| 202 |
+
|
| 203 |
+
for field_name, value in category.items():
|
| 204 |
+
if value is None:
|
| 205 |
+
continue
|
| 206 |
+
if isinstance(value, list) and len(value) == 0:
|
| 207 |
+
continue
|
| 208 |
+
|
| 209 |
+
# Format the field name nicely
|
| 210 |
+
display_name = field_name.replace("_", " ").title()
|
| 211 |
+
|
| 212 |
+
if isinstance(value, list):
|
| 213 |
+
category_lines.append(f" - {display_name}: {', '.join(str(v) for v in value)}")
|
| 214 |
+
else:
|
| 215 |
+
category_lines.append(f" - {display_name}: {value}")
|
| 216 |
+
|
| 217 |
+
if category_lines:
|
| 218 |
+
lines.append(f"[{category_label}]")
|
| 219 |
+
lines.extend(category_lines)
|
| 220 |
+
|
| 221 |
+
if not lines:
|
| 222 |
+
return ""
|
| 223 |
+
|
| 224 |
+
return "USER PROFILE (gathered so far):\n" + "\n".join(lines)
|
src/utils/resources.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import csv
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
# Profile field → (csv_column, weight)
|
| 5 |
+
FIELD_MAP = [
|
| 6 |
+
("clinical", "primary_focus", "primary_focus", 3),
|
| 7 |
+
("clinical", "substances", "substances", 3),
|
| 8 |
+
("demographics", "population", "age_groups", 2),
|
| 9 |
+
("demographics", "identity_factors", "identity_factors", 2),
|
| 10 |
+
("logistics", "insurance", "insurance", 2),
|
| 11 |
+
("preferences", "setting", "settings", 2),
|
| 12 |
+
("preferences", "therapy_approach", "therapy_approaches", 1),
|
| 13 |
+
("demographics", "language", "languages", 1),
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def load_resources(csv_path):
|
| 18 |
+
"""Load one or more resource CSVs into a list of dicts.
|
| 19 |
+
|
| 20 |
+
Accepts a single path (str) or a list of paths. Called once at init.
|
| 21 |
+
"""
|
| 22 |
+
if isinstance(csv_path, str):
|
| 23 |
+
csv_path = [csv_path]
|
| 24 |
+
rows = []
|
| 25 |
+
for path in csv_path:
|
| 26 |
+
with open(path, "r", encoding="utf-8") as f:
|
| 27 |
+
reader = csv.DictReader(f)
|
| 28 |
+
rows.extend(reader)
|
| 29 |
+
return rows
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _get_profile_value(profile, category, field):
|
| 33 |
+
"""Safely get a profile value, returning None for missing/empty."""
|
| 34 |
+
val = profile.get(category, {}).get(field)
|
| 35 |
+
if val is None:
|
| 36 |
+
return None
|
| 37 |
+
if isinstance(val, list) and len(val) == 0:
|
| 38 |
+
return None
|
| 39 |
+
return val
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _pipe_values(cell):
|
| 43 |
+
"""Split a pipe-delimited CSV cell into a set of lowercase values."""
|
| 44 |
+
if not cell or not cell.strip():
|
| 45 |
+
return set()
|
| 46 |
+
return {v.strip().lower() for v in cell.split("|")}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def filter_resources(resources, user_profile):
|
| 50 |
+
"""
|
| 51 |
+
Filter the full resource list down to a relevant subset based on
|
| 52 |
+
user profile values. Applies geographic, primary_focus, and substances
|
| 53 |
+
filters. Progressively relaxes filters if fewer than 3 results remain.
|
| 54 |
+
"""
|
| 55 |
+
zipcode = _get_profile_value(user_profile, "logistics", "zipcode")
|
| 56 |
+
region = _get_profile_value(user_profile, "logistics", "region")
|
| 57 |
+
primary_focus = _get_profile_value(user_profile, "clinical", "primary_focus")
|
| 58 |
+
substances = _get_profile_value(user_profile, "clinical", "substances")
|
| 59 |
+
|
| 60 |
+
# No profile info → no filtering possible, return empty (no recommendations)
|
| 61 |
+
if not zipcode and not region and not primary_focus and not substances:
|
| 62 |
+
return []
|
| 63 |
+
|
| 64 |
+
# Build filter functions in order of relaxation priority
|
| 65 |
+
filters = []
|
| 66 |
+
|
| 67 |
+
# Geographic filter (relaxed first if too few results)
|
| 68 |
+
if zipcode:
|
| 69 |
+
zip_prefix = zipcode[:3]
|
| 70 |
+
filters.append(("geo", lambda r, zp=zip_prefix: (
|
| 71 |
+
r.get("zip", "")[:3] == zp
|
| 72 |
+
)))
|
| 73 |
+
elif region:
|
| 74 |
+
region_lower = region.lower()
|
| 75 |
+
filters.append(("geo", lambda r, rl=region_lower: (
|
| 76 |
+
rl in r.get("city", "").lower() or rl in r.get("state", "").lower()
|
| 77 |
+
)))
|
| 78 |
+
|
| 79 |
+
# Primary focus filter
|
| 80 |
+
if primary_focus:
|
| 81 |
+
focus_lower = primary_focus.lower()
|
| 82 |
+
filters.append(("focus", lambda r, fl=focus_lower: (
|
| 83 |
+
not r.get("primary_focus", "").strip() or
|
| 84 |
+
fl in _pipe_values(r.get("primary_focus", ""))
|
| 85 |
+
)))
|
| 86 |
+
|
| 87 |
+
# Substances filter
|
| 88 |
+
if substances:
|
| 89 |
+
if isinstance(substances, str):
|
| 90 |
+
substances = [substances]
|
| 91 |
+
subs_lower = {s.lower() for s in substances}
|
| 92 |
+
filters.append(("substances", lambda r, sl=subs_lower: (
|
| 93 |
+
not r.get("substances", "").strip() or
|
| 94 |
+
bool(sl & _pipe_values(r.get("substances", "")))
|
| 95 |
+
)))
|
| 96 |
+
|
| 97 |
+
# Apply all filters, progressively relax if < 3 results
|
| 98 |
+
result = _apply_filters(resources, filters)
|
| 99 |
+
if len(result) >= 3:
|
| 100 |
+
return result
|
| 101 |
+
best = result # keep the best partial matches found so far
|
| 102 |
+
|
| 103 |
+
# Relax geographic filter first
|
| 104 |
+
relaxed = [f for f in filters if f[0] != "geo"]
|
| 105 |
+
if relaxed:
|
| 106 |
+
result = _apply_filters(resources, relaxed)
|
| 107 |
+
if len(result) >= 3:
|
| 108 |
+
return result
|
| 109 |
+
if len(result) > len(best):
|
| 110 |
+
best = result
|
| 111 |
+
|
| 112 |
+
# Relax substances filter next
|
| 113 |
+
relaxed = [f for f in relaxed if f[0] != "substances"]
|
| 114 |
+
if relaxed:
|
| 115 |
+
result = _apply_filters(resources, relaxed)
|
| 116 |
+
if len(result) > len(best):
|
| 117 |
+
best = result
|
| 118 |
+
|
| 119 |
+
return best
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _apply_filters(resources, filters):
|
| 123 |
+
"""Apply a list of filter functions, keeping rows that pass all."""
|
| 124 |
+
if not filters:
|
| 125 |
+
return []
|
| 126 |
+
result = []
|
| 127 |
+
for row in resources:
|
| 128 |
+
if all(fn(row) for _, fn in filters):
|
| 129 |
+
result.append(row)
|
| 130 |
+
return result
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def score_resources(filtered, user_profile, top_n=3):
|
| 134 |
+
"""
|
| 135 |
+
Score filtered resources by relevance to the user profile.
|
| 136 |
+
Returns the top_n highest-scoring resources as a list of dicts.
|
| 137 |
+
"""
|
| 138 |
+
zipcode = _get_profile_value(user_profile, "logistics", "zipcode")
|
| 139 |
+
region = _get_profile_value(user_profile, "logistics", "region")
|
| 140 |
+
|
| 141 |
+
scored = []
|
| 142 |
+
for row in filtered:
|
| 143 |
+
score = 0
|
| 144 |
+
|
| 145 |
+
# Score each mapped field
|
| 146 |
+
for category, field, csv_col, weight in FIELD_MAP:
|
| 147 |
+
profile_val = _get_profile_value(user_profile, category, field)
|
| 148 |
+
if profile_val is None:
|
| 149 |
+
continue
|
| 150 |
+
|
| 151 |
+
cell_values = _pipe_values(row.get(csv_col, ""))
|
| 152 |
+
if not cell_values:
|
| 153 |
+
continue # empty cell = neutral
|
| 154 |
+
|
| 155 |
+
if isinstance(profile_val, list):
|
| 156 |
+
matches = sum(1 for v in profile_val if v.lower() in cell_values)
|
| 157 |
+
if matches > 0:
|
| 158 |
+
score += weight * (matches / len(profile_val))
|
| 159 |
+
else:
|
| 160 |
+
if profile_val.lower() in cell_values:
|
| 161 |
+
score += weight
|
| 162 |
+
|
| 163 |
+
# Geographic bonus
|
| 164 |
+
row_zip = row.get("zip", "").strip()
|
| 165 |
+
if zipcode and row_zip:
|
| 166 |
+
if row_zip == zipcode:
|
| 167 |
+
score += 5
|
| 168 |
+
elif row_zip[:3] == zipcode[:3]:
|
| 169 |
+
score += 2
|
| 170 |
+
elif region and not zipcode:
|
| 171 |
+
region_lower = region.lower()
|
| 172 |
+
if region_lower in row.get("city", "").lower():
|
| 173 |
+
score += 3
|
| 174 |
+
|
| 175 |
+
if score > 0:
|
| 176 |
+
scored.append((score, row))
|
| 177 |
+
|
| 178 |
+
# Sort by score descending
|
| 179 |
+
scored.sort(key=lambda x: x[0], reverse=True)
|
| 180 |
+
return [row for _, row in scored[:top_n]]
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def format_recommendations(results):
|
| 184 |
+
"""
|
| 185 |
+
Format a list of resource dicts into a readable recommendation block.
|
| 186 |
+
Returns empty string if no results.
|
| 187 |
+
"""
|
| 188 |
+
if not results:
|
| 189 |
+
return ""
|
| 190 |
+
|
| 191 |
+
lines = [
|
| 192 |
+
"---",
|
| 193 |
+
"Here are some resources that may be a good fit for you:",
|
| 194 |
+
"",
|
| 195 |
+
]
|
| 196 |
+
|
| 197 |
+
for i, row in enumerate(results, 1):
|
| 198 |
+
name = row.get("name", "Unknown Facility")
|
| 199 |
+
lines.append(f"{i}. {name}")
|
| 200 |
+
|
| 201 |
+
# Address
|
| 202 |
+
parts = [row.get("address", ""), row.get("city", ""),
|
| 203 |
+
row.get("state", ""), row.get("zip", "")]
|
| 204 |
+
address = ", ".join(p.strip() for p in parts if p.strip())
|
| 205 |
+
if address:
|
| 206 |
+
lines.append(f" {address}")
|
| 207 |
+
|
| 208 |
+
# Phone
|
| 209 |
+
phone = row.get("phone", "").strip()
|
| 210 |
+
if phone:
|
| 211 |
+
lines.append(f" Phone: {phone}")
|
| 212 |
+
|
| 213 |
+
# Website
|
| 214 |
+
website = row.get("website", "").strip()
|
| 215 |
+
if website:
|
| 216 |
+
lines.append(f" Website: {website}")
|
| 217 |
+
|
| 218 |
+
# Summary line: focus, substances, settings
|
| 219 |
+
details = []
|
| 220 |
+
focus = row.get("primary_focus", "").strip()
|
| 221 |
+
if focus:
|
| 222 |
+
details.append("Focus: " + ", ".join(
|
| 223 |
+
v.strip().replace("_", " ").title() for v in focus.split("|")
|
| 224 |
+
))
|
| 225 |
+
subs = row.get("substances", "").strip()
|
| 226 |
+
if subs:
|
| 227 |
+
details.append("Substances: " + ", ".join(
|
| 228 |
+
v.strip().replace("_", " ").title() for v in subs.split("|")
|
| 229 |
+
))
|
| 230 |
+
settings = row.get("settings", "").strip()
|
| 231 |
+
if settings:
|
| 232 |
+
details.append("Settings: " + ", ".join(
|
| 233 |
+
v.strip().replace("_", " ").title() for v in settings.split("|")
|
| 234 |
+
))
|
| 235 |
+
if details:
|
| 236 |
+
lines.append(" " + " | ".join(details))
|
| 237 |
+
|
| 238 |
+
lines.append("")
|
| 239 |
+
|
| 240 |
+
return "\n".join(lines).rstrip()
|