ajamous commited on
Commit
a6e116a
·
verified ·
1 Parent(s): 2955315

TCXC brand look and feel; say many languages without a count

Browse files
Files changed (2) hide show
  1. README.md +2 -2
  2. app.py +120 -46
README.md CHANGED
@@ -8,7 +8,7 @@ sdk_version: 5.49.1
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
- short_description: Detect SMS spam & phishing in 100+ languages
12
  tags:
13
  - sms
14
  - spam-detection
@@ -24,7 +24,7 @@ models:
24
  # OpenTextShield — SMS spam and phishing detection
25
 
26
  OpenTextShield is an open-source model that detects SMS spam and phishing
27
- (smishing) in more than 100 languages. A fine-tuned multilingual BERT labels a
28
  message as `ham` (legitimate), `spam` or `phishing`. It is used by telecom
29
  carriers to screen live SMS traffic and protect subscribers, and runs entirely
30
  on your own infrastructure as a REST API or an SMPP proxy in front of your
 
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
+ short_description: Detect SMS spam & phishing in many languages
12
  tags:
13
  - sms
14
  - spam-detection
 
24
  # OpenTextShield — SMS spam and phishing detection
25
 
26
  OpenTextShield is an open-source model that detects SMS spam and phishing
27
+ (smishing) in many languages. A fine-tuned multilingual BERT labels a
28
  message as `ham` (legitimate), `spam` or `phishing`. It is used by telecom
29
  carriers to screen live SMS traffic and protect subscribers, and runs entirely
30
  on your own infrastructure as a REST API or an SMPP proxy in front of your
app.py CHANGED
@@ -2,7 +2,8 @@
2
 
3
  Loads the OpenTextShield mBERT model from the Hub and classifies an SMS as
4
  ham (legitimate), spam or phishing, applying the same text normalisation as
5
- the production API so obfuscated messages are handled identically.
 
6
  """
7
 
8
  import gradio as gr
@@ -19,9 +20,9 @@ model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
19
  model.eval()
20
 
21
  DISPLAY = {
22
- "ham": "Legitimate (ham)",
23
- "spam": "Spam",
24
- "phishing": "Phishing",
25
  }
26
 
27
  EXAMPLES = [
@@ -36,7 +37,7 @@ EXAMPLES = [
36
  def classify(message: str):
37
  message = (message or "").strip()
38
  if not message:
39
- return {}, ""
40
 
41
  normalized = normalize_unicode(message)
42
  inputs = tokenizer(
@@ -48,34 +49,54 @@ def classify(message: str):
48
  with torch.inference_mode():
49
  probs = torch.softmax(model(**inputs).logits, dim=-1)[0]
50
 
51
- scores = {
52
- DISPLAY[model.config.id2label[i]]: float(p) for i, p in enumerate(probs)
53
- }
 
 
 
 
 
 
 
54
  note = (
55
  ""
56
  if normalized == message
57
- else f"Obfuscation detected — classified as the text it imitates:\n\n> {normalized}"
58
  )
59
- return scores, note
60
-
61
-
62
- DESCRIPTION = """
63
- **OpenTextShield is an open-source model that detects SMS spam and phishing
64
- (smishing) in more than 100 languages.**
65
-
66
- A fine-tuned multilingual BERT (~180M parameters) labels a text message as
67
- **legitimate**, **spam** or **phishing**. It is used by telecom carriers to
68
- screen live SMS traffic and protect subscribers in real networks, and it runs
69
- entirely on your own servers — as a REST API, an SMPP proxy in front of your
70
- SMSC, or both. No third-party AI service is involved.
71
-
72
- [GitHub](https://github.com/TelecomsXChangeAPi/OpenTextShield) ·
73
- [Model weights](https://huggingface.co/telecomsxchange/OpenTextShield) ·
74
- [Docker image](https://hub.docker.com/r/telecomsxchange/opentextshield) ·
75
- [Live API demo](https://ots.telecomsxchange.com)
 
 
 
76
  """
77
 
78
  ARTICLE = """
 
 
 
 
 
 
 
 
 
 
79
  ### Run it yourself
80
 
81
  ```bash
@@ -95,32 +116,85 @@ classifier = pipeline("text-classification", model="telecomsxchange/OpenTextShie
95
  classifier("Your package is held. Pay the fee: http://usps-redelivery.top/pay")
96
  ```
97
 
98
- This demo applies the same Unicode normalisation as the production API, so
99
- zero-width, full-width, homoglyph and leetspeak disguises are classified as the
100
- text they imitate. Built by [TelecomsXChange (TCXC)](https://telecomsxchange.com),
101
- MIT licensed.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  """
103
 
104
- demo = gr.Interface(
105
- fn=classify,
106
- inputs=gr.Textbox(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  lines=3,
108
  max_lines=8,
109
  label="Text message",
110
  placeholder="Paste an SMS to check…",
111
- ),
112
- outputs=[
113
- gr.Label(num_top_classes=3, label="Verdict"),
114
- gr.Markdown(label="Normalisation"),
115
- ],
116
- examples=[[e] for e in EXAMPLES],
117
- cache_examples=False,
118
- title="OpenTextShield — SMS Spam & Phishing Detection",
119
- description=DESCRIPTION,
120
- article=ARTICLE,
121
- theme=gr.themes.Soft(),
122
- flagging_mode="never",
123
- )
 
 
 
 
 
124
 
125
  if __name__ == "__main__":
126
  demo.launch()
 
2
 
3
  Loads the OpenTextShield mBERT model from the Hub and classifies an SMS as
4
  ham (legitimate), spam or phishing, applying the same text normalisation as
5
+ the production API so obfuscated messages are handled identically. Styled to
6
+ match the OpenTextShield / TelecomsXChange (TCXC) brand.
7
  """
8
 
9
  import gradio as gr
 
20
  model.eval()
21
 
22
  DISPLAY = {
23
+ "ham": ("Legitimate", "ham", "reads like a normal message"),
24
+ "spam": ("Spam", "spam", "unwanted promotional or bulk content"),
25
+ "phishing": ("Phishing", "phishing", "an attempt to steal credentials, money or personal data"),
26
  }
27
 
28
  EXAMPLES = [
 
37
  def classify(message: str):
38
  message = (message or "").strip()
39
  if not message:
40
+ return "", {}, ""
41
 
42
  normalized = normalize_unicode(message)
43
  inputs = tokenizer(
 
49
  with torch.inference_mode():
50
  probs = torch.softmax(model(**inputs).logits, dim=-1)[0]
51
 
52
+ top = int(probs.argmax())
53
+ raw = model.config.id2label[top]
54
+ word, css_class, meaning = DISPLAY[raw]
55
+ verdict_html = (
56
+ f'<div class="ots-verdict"><span class="ots-word ots-{css_class}">{word}</span>'
57
+ f'<span class="ots-conf">{probs[top]:.1%}</span>'
58
+ f'<span class="ots-note">&mdash; {meaning}</span></div>'
59
+ )
60
+
61
+ scores = {DISPLAY[model.config.id2label[i]][0]: float(p) for i, p in enumerate(probs)}
62
  note = (
63
  ""
64
  if normalized == message
65
+ else f"**Obfuscation detected** — classified as the text it imitates:\n\n> {normalized}"
66
  )
67
+ return verdict_html, scores, note
68
+
69
+
70
+ MARK_SVG = """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 112" class="ots-mark" aria-hidden="true"><path d="M14 0H86Q100 0 100 14V62Q100 78 88 86L56 108Q50 112 44 108L12 86Q0 78 0 62V14Q0 0 14 0Z" class="ots-mark-shield"/><path d="M30 56 L44 70 L72 40" fill="none" class="ots-mark-check" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/></svg>"""
71
+
72
+ HEADER = f"""
73
+ <div class="ots-header">
74
+ <div class="ots-brand">{MARK_SVG}
75
+ <div>
76
+ <div class="ots-title">OpenTextShield</div>
77
+ <div class="ots-sub">Open-source SMS spam &amp; phishing detection, in many languages</div>
78
+ </div>
79
+ </div>
80
+ <div class="ots-links">
81
+ <a href="https://github.com/TelecomsXChangeAPi/OpenTextShield" target="_blank" rel="noopener">GitHub</a>
82
+ <a href="https://huggingface.co/telecomsxchange/OpenTextShield" target="_blank" rel="noopener">Model</a>
83
+ <a href="https://hub.docker.com/r/telecomsxchange/opentextshield" target="_blank" rel="noopener">Docker</a>
84
+ <a href="https://ots.telecomsxchange.com" target="_blank" rel="noopener">Live API</a>
85
+ </div>
86
+ </div>
87
  """
88
 
89
  ARTICLE = """
90
+ ### About
91
+
92
+ OpenTextShield is an open-source model that detects SMS spam and phishing
93
+ (smishing) in many languages. It is used by telecom carriers to screen live
94
+ SMS traffic and protect subscribers in real networks, and it runs entirely on
95
+ your own servers — as a REST API, an SMPP proxy in front of your SMSC, or
96
+ both. No third-party AI service is involved. This demo applies the same
97
+ Unicode normalisation as the production API, so zero-width, full-width,
98
+ homoglyph and leetspeak disguises are classified as the text they imitate.
99
+
100
  ### Run it yourself
101
 
102
  ```bash
 
116
  classifier("Your package is held. Pay the fee: http://usps-redelivery.top/pay")
117
  ```
118
 
119
+ Built by [TelecomsXChange (TCXC)](https://telecomsxchange.com) · MIT licensed
120
+ """
121
+
122
+ CSS = """
123
+ :root {
124
+ --ots-paper:#F2F3F1; --ots-surface:#FAFAF9; --ots-ink:#16181A; --ots-muted:#6B7076;
125
+ --ots-hair:#D9DCD8; --ots-ham:#1E6E50; --ots-spam:#A6621A; --ots-phishing:#B42318;
126
+ }
127
+ .dark {
128
+ --ots-paper:#15171A; --ots-surface:#1C1F23; --ots-ink:#E9EBEC; --ots-muted:#9AA0A6;
129
+ --ots-hair:#2C3035; --ots-ham:#4CC38A; --ots-spam:#E5A04D; --ots-phishing:#F0655A;
130
+ }
131
+ .gradio-container { max-width: 780px !important; margin: 0 auto !important; }
132
+ .ots-header { display:flex; align-items:flex-end; justify-content:space-between;
133
+ flex-wrap:wrap; gap:12px; padding:8px 0 4px; border-bottom:1px solid var(--ots-hair); }
134
+ .ots-brand { display:flex; align-items:center; gap:14px; }
135
+ .ots-mark { width:40px; height:45px; flex:none; }
136
+ .ots-mark-shield { fill: var(--ots-ink); }
137
+ .ots-mark-check { stroke: var(--ots-paper); }
138
+ .ots-title { font-size:22px; font-weight:700; letter-spacing:-0.02em; color:var(--ots-ink); }
139
+ .ots-sub { font-size:13.5px; color:var(--ots-muted); }
140
+ .ots-links { display:flex; gap:14px; font-size:13.5px; padding-bottom:4px; }
141
+ .ots-links a { color:var(--ots-muted) !important; text-decoration:none !important;
142
+ border-bottom:1px solid var(--ots-hair); }
143
+ .ots-links a:hover { color:var(--ots-ink) !important; border-color:var(--ots-ink); }
144
+ .ots-verdict { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
145
+ padding:6px 2px 2px; min-height:40px; }
146
+ .ots-word { font-size:30px; font-weight:700; letter-spacing:-0.02em; line-height:1.1; }
147
+ .ots-ham { color:var(--ots-ham); } .ots-spam { color:var(--ots-spam); }
148
+ .ots-phishing { color:var(--ots-phishing); }
149
+ .ots-conf { font-size:17px; color:var(--ots-ink); font-variant-numeric:tabular-nums; }
150
+ .ots-note { font-size:13.5px; color:var(--ots-muted); }
151
  """
152
 
153
+ theme = gr.themes.Soft(
154
+ primary_hue=gr.themes.colors.stone,
155
+ neutral_hue=gr.themes.colors.stone,
156
+ font=[gr.themes.GoogleFont("Schibsted Grotesk"), "system-ui", "sans-serif"],
157
+ font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace"],
158
+ ).set(
159
+ body_background_fill="#F2F3F1",
160
+ body_background_fill_dark="#15171A",
161
+ block_background_fill="#FAFAF9",
162
+ block_background_fill_dark="#1C1F23",
163
+ button_primary_background_fill="#16181A",
164
+ button_primary_background_fill_hover="#2C3035",
165
+ button_primary_text_color="#FAFAF9",
166
+ button_primary_background_fill_dark="#E9EBEC",
167
+ button_primary_background_fill_hover_dark="#FFFFFF",
168
+ button_primary_text_color_dark="#15171A",
169
+ block_title_text_color="#6B7076",
170
+ block_title_text_color_dark="#9AA0A6",
171
+ )
172
+
173
+ with gr.Blocks(theme=theme, css=CSS, title="OpenTextShield — SMS Spam & Phishing Detection") as demo:
174
+ gr.HTML(HEADER)
175
+ msg = gr.Textbox(
176
  lines=3,
177
  max_lines=8,
178
  label="Text message",
179
  placeholder="Paste an SMS to check…",
180
+ )
181
+ check = gr.Button("Check message", variant="primary")
182
+ verdict = gr.HTML(label="Verdict")
183
+ scores = gr.Label(num_top_classes=3, label="Confidence", show_label=True)
184
+ note = gr.Markdown()
185
+ gr.Examples(
186
+ examples=[[e] for e in EXAMPLES],
187
+ inputs=msg,
188
+ outputs=[verdict, scores, note],
189
+ fn=classify,
190
+ run_on_click=True,
191
+ cache_examples=False,
192
+ label="Try a sample",
193
+ )
194
+ gr.Markdown(ARTICLE)
195
+
196
+ check.click(classify, inputs=msg, outputs=[verdict, scores, note])
197
+ msg.submit(classify, inputs=msg, outputs=[verdict, scores, note])
198
 
199
  if __name__ == "__main__":
200
  demo.launch()