Scadiva commited on
Commit
26f1c3a
·
verified ·
1 Parent(s): 7ac109b
Files changed (3) hide show
  1. app.py +3 -3
  2. requirements.txt +1 -1
  3. 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 '?'. NOT a hard guarantee -- if every generated sentence was a
49
- question, it falls back to the raw text with '?' replaced.
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 text
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.8.0
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 or "").lower().replace("\u2018", "'").replace("\u2019", "'")
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):