Spaces:
Sleeping
Sleeping
Deploy
Browse files- app.py +3 -3
- requirements.txt +1 -1
- safety.py +20 -5
app.py
CHANGED
|
@@ -45,12 +45,12 @@ BERMED_SYSTEM_PROMPT = (
|
|
| 45 |
def enforce_declarative_lock(text: str) -> str:
|
| 46 |
"""
|
| 47 |
Post-hoc syntactic guardrail: drops sentences ending in '?' and neutralizes
|
| 48 |
-
'!' / stray '?'.
|
| 49 |
-
|
| 50 |
"""
|
| 51 |
sentences = re.split(r"(?<=[.!?])\s+", text.strip())
|
| 52 |
safe = [s for s in sentences if not s.endswith("?")]
|
| 53 |
-
cleaned = " ".join(safe) if safe else
|
| 54 |
return cleaned.replace("?", ".").replace("!", ".")
|
| 55 |
|
| 56 |
|
|
|
|
| 45 |
def enforce_declarative_lock(text: str) -> str:
|
| 46 |
"""
|
| 47 |
Post-hoc syntactic guardrail: drops sentences ending in '?' and neutralizes
|
| 48 |
+
'!' / stray '?'. If every generated sentence was an interrogative, it falls back
|
| 49 |
+
to a calm neutral observation rather than converting questions into dotted sentences.
|
| 50 |
"""
|
| 51 |
sentences = re.split(r"(?<=[.!?])\s+", text.strip())
|
| 52 |
safe = [s for s in sentences if not s.endswith("?")]
|
| 53 |
+
cleaned = " ".join(safe) if safe else "This space remains neutral and unhurried. No response is needed."
|
| 54 |
return cleaned.replace("?", ".").replace("!", ".")
|
| 55 |
|
| 56 |
|
requirements.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
spaces>=0.30.0
|
| 2 |
-
torch>=2.
|
| 3 |
transformers>=4.46.0,<5
|
| 4 |
accelerate>=0.34.0
|
| 5 |
gradio==5.50.0
|
|
|
|
| 1 |
spaces>=0.30.0
|
| 2 |
+
torch>=2.4.0
|
| 3 |
transformers>=4.46.0,<5
|
| 4 |
accelerate>=0.34.0
|
| 5 |
gradio==5.50.0
|
safety.py
CHANGED
|
@@ -12,10 +12,11 @@ is far cheaper than a miss. It needs review by a clinician before public use.
|
|
| 12 |
"""
|
| 13 |
|
| 14 |
import re
|
|
|
|
| 15 |
|
| 16 |
CRISIS_PATTERNS = [
|
| 17 |
r"\bsuicid\w*",
|
| 18 |
-
r"\bkill(?:ing)? my ?self\b",
|
| 19 |
r"\bkill me\b",
|
| 20 |
r"\bend(?:ing)? (?:it all|my life|my own life|things for good)\b",
|
| 21 |
r"\btake my (?:own )?life\b",
|
|
@@ -24,10 +25,10 @@ CRISIS_PATTERNS = [
|
|
| 24 |
r"\bwish i (?:could |would )?(?:just )?(?:die|not wake up|never wake up)\b",
|
| 25 |
r"\b(?:do not|don'?t|dont) want to (?:be alive|live|exist|be here anymore)\b",
|
| 26 |
r"\bno reason to (?:live|go on|be alive)\b",
|
| 27 |
-
r"\b(?:hurt|harm|cut|cutting|burn|burning|punish) (?:my ?self)\b",
|
| 28 |
r"\bself[- ]?harm\w*",
|
| 29 |
r"\boverdos\w*",
|
| 30 |
-
r"\bhang(?:ing)? my ?self\b",
|
| 31 |
r"\bjump(?:ing)? (?:off|from) (?:a |the )?(?:bridge|building|roof|balcony)\b",
|
| 32 |
r"\bbetter off dead\b",
|
| 33 |
r"\bend my suffering\b",
|
|
@@ -51,7 +52,6 @@ WATCH_PATTERNS = [
|
|
| 51 |
r"\bcannot (?:do|take) this anymore\b",
|
| 52 |
r"\bgive up on (?:life|everything)\b",
|
| 53 |
r"\bhopeless\b",
|
| 54 |
-
r"\bbetter off without me\b",
|
| 55 |
r"\bwant to disappear forever\b",
|
| 56 |
r"\bdisappear forever\b",
|
| 57 |
r"\bnot wake up\b",
|
|
@@ -81,9 +81,24 @@ WATCH_NOTE = (
|
|
| 81 |
)
|
| 82 |
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
def assess_risk(text: str) -> str:
|
| 85 |
"""Returns "crisis", "watch" or "none"."""
|
| 86 |
-
t = (text
|
| 87 |
if any(r.search(t) for r in _CRISIS_RES):
|
| 88 |
return "crisis"
|
| 89 |
if any(r.search(t) for r in _WATCH_RES):
|
|
|
|
| 12 |
"""
|
| 13 |
|
| 14 |
import re
|
| 15 |
+
import unicodedata
|
| 16 |
|
| 17 |
CRISIS_PATTERNS = [
|
| 18 |
r"\bsuicid\w*",
|
| 19 |
+
r"\bkill(?:ing)? ?my ?self\b",
|
| 20 |
r"\bkill me\b",
|
| 21 |
r"\bend(?:ing)? (?:it all|my life|my own life|things for good)\b",
|
| 22 |
r"\btake my (?:own )?life\b",
|
|
|
|
| 25 |
r"\bwish i (?:could |would )?(?:just )?(?:die|not wake up|never wake up)\b",
|
| 26 |
r"\b(?:do not|don'?t|dont) want to (?:be alive|live|exist|be here anymore)\b",
|
| 27 |
r"\bno reason to (?:live|go on|be alive)\b",
|
| 28 |
+
r"\b(?:hurt|harm|cut|cutting|burn|burning|punish) ?(?:my ?self)\b",
|
| 29 |
r"\bself[- ]?harm\w*",
|
| 30 |
r"\boverdos\w*",
|
| 31 |
+
r"\bhang(?:ing)? ?my ?self\b",
|
| 32 |
r"\bjump(?:ing)? (?:off|from) (?:a |the )?(?:bridge|building|roof|balcony)\b",
|
| 33 |
r"\bbetter off dead\b",
|
| 34 |
r"\bend my suffering\b",
|
|
|
|
| 52 |
r"\bcannot (?:do|take) this anymore\b",
|
| 53 |
r"\bgive up on (?:life|everything)\b",
|
| 54 |
r"\bhopeless\b",
|
|
|
|
| 55 |
r"\bwant to disappear forever\b",
|
| 56 |
r"\bdisappear forever\b",
|
| 57 |
r"\bnot wake up\b",
|
|
|
|
| 81 |
)
|
| 82 |
|
| 83 |
|
| 84 |
+
_ZERO_WIDTH = re.compile(r"[\u200b\u200c\u200d\u2060\ufeff]")
|
| 85 |
+
_QUOTES = re.compile(r"['\u2019\u2018`]")
|
| 86 |
+
_SPACED = re.compile(r"(?:\b\w\s+){3,}\w\b")
|
| 87 |
+
_PUNCT_COLLAPSE = re.compile(r"(?<=\b\w)[.\-_/](?=\w\b)")
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def normalize(text: str) -> str:
|
| 91 |
+
t = unicodedata.normalize("NFKC", text or "").lower()
|
| 92 |
+
t = _ZERO_WIDTH.sub("", t)
|
| 93 |
+
t = _PUNCT_COLLAPSE.sub("", t)
|
| 94 |
+
t = _QUOTES.sub("'", t)
|
| 95 |
+
t = _SPACED.sub(lambda m: re.sub(r"\s+", "", m.group(0)), t)
|
| 96 |
+
return t
|
| 97 |
+
|
| 98 |
+
|
| 99 |
def assess_risk(text: str) -> str:
|
| 100 |
"""Returns "crisis", "watch" or "none"."""
|
| 101 |
+
t = normalize(text)
|
| 102 |
if any(r.search(t) for r in _CRISIS_RES):
|
| 103 |
return "crisis"
|
| 104 |
if any(r.search(t) for r in _WATCH_RES):
|