boffire commited on
Commit
2999d26
·
verified ·
1 Parent(s): 36e285c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +292 -67
app.py CHANGED
@@ -5,55 +5,241 @@ import gradio as gr
5
  import os
6
  import asyncio
7
  import atexit
 
 
 
 
8
 
9
- # Constants
10
- MAX_INPUT_LENGTH = 10000 # Maximum characters allowed
 
11
 
12
- # Preprocessing patterns
 
 
 
 
 
 
 
 
 
13
  NONWORD_REPLACE_STR = r"[^\p{Word}\p{Zs}]|\d"
14
  NONWORD_REPLACE_PATTERN = regex.compile(NONWORD_REPLACE_STR)
15
  SPACE_PATTERN = regex.compile(r"\s\s+")
16
 
17
- def preprocess(text):
18
- """Preprocess text for language identification."""
19
  text = text.strip().replace('\n', ' ').lower()
20
  text = regex.sub(SPACE_PATTERN, " ", text)
21
  text = regex.sub(NONWORD_REPLACE_PATTERN, "", text)
22
  return text
23
 
24
- # Load model once at startup
25
- print("Loading OpenLID-v3 model...")
26
- model_path = hf_hub_download(
27
- repo_id="HPLT/OpenLID-v3",
28
- filename="openlid-v3.bin"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  )
30
- model = fasttext.load_model(model_path)
31
- print("Model loaded successfully!")
 
 
 
 
 
 
32
 
33
- def predict_language(text, top_k=3, threshold=0.5):
34
- """
35
- Predict language of input text.
36
-
37
- Args:
38
- text: Input text to analyze
39
- top_k: Number of top predictions to return (1-10)
40
- threshold: Confidence threshold (0.0-1.0)
41
- """
42
- # Check input length first
43
- if len(text) > MAX_INPUT_LENGTH:
44
- return f"**Error**: Input too long ({len(text):,} characters). Maximum allowed is {MAX_INPUT_LENGTH:,} characters."
45
-
46
  if not text or not text.strip():
47
  return "Please enter some text to analyze."
48
 
49
- # Preprocess
50
- processed_text = preprocess(text)
51
-
52
  if not processed_text.strip():
53
  return "Text contains no valid characters for language identification."
54
 
55
- # Get predictions
56
- predictions = model.predict(
57
  text=processed_text,
58
  k=min(top_k, 10),
59
  threshold=threshold,
@@ -61,18 +247,58 @@ def predict_language(text, top_k=3, threshold=0.5):
61
  )
62
 
63
  labels, scores = predictions
64
-
65
- # Format results
66
  results = []
67
  for label, score in zip(labels, scores):
68
- # Remove __label__ prefix and format
69
  lang_code = label.replace("__label__", "")
70
  confidence = float(score) * 100
71
  results.append(f"**{lang_code}**: {confidence:.2f}%")
72
 
73
- return "\n\n".join(results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- # Cleanup function to prevent async errors on shutdown
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def cleanup():
77
  try:
78
  loop = asyncio.get_event_loop()
@@ -85,14 +311,15 @@ def cleanup():
85
 
86
  atexit.register(cleanup)
87
 
88
- # Create Gradio interface
89
- with gr.Blocks(title="OpenLID-v3 Language Identification") as demo:
90
- # Use HTML with target="_blank" to open in new tab
91
  gr.HTML("""
92
- <h1>OpenLID-v3 Language Identifier</h1>
93
- <p>Identify the language of any text with state-of-the-art accuracy.<br>
94
- Supports 194+ language varieties.</p>
95
- <p><em>Model: <a href="https://huggingface.co/HPLT/OpenLID-v3" target="_blank" rel="noopener noreferrer">HPLT/OpenLID-v3</a></em></p>
 
 
96
  """)
97
 
98
  with gr.Row():
@@ -102,29 +329,27 @@ with gr.Blocks(title="OpenLID-v3 Language Identification") as demo:
102
  placeholder="Enter text to identify its language...",
103
  lines=5,
104
  max_lines=10,
105
- max_length=MAX_INPUT_LENGTH # Also enforce in UI
106
  )
107
  with gr.Row():
108
  top_k = gr.Slider(
109
- minimum=1,
110
- maximum=10,
111
- value=3,
112
- step=1,
113
  label="Top-K Predictions"
114
  )
115
  threshold = gr.Slider(
116
- minimum=0.0,
117
- maximum=1.0,
118
- value=0.5,
119
- step=0.05,
120
- label="Confidence Threshold"
121
  )
122
- submit_btn = gr.Button("Identify Language", variant="primary")
 
123
 
 
 
 
124
  with gr.Column():
125
- output = gr.Markdown(label="Predictions")
126
 
127
- # Examples with Kabyle and Occitan as defaults
128
  gr.Examples(
129
  examples=[
130
  ["Asebter-a yura s wudem awurman d amagrad s tutlayt taqbaylit."],
@@ -135,42 +360,42 @@ with gr.Blocks(title="OpenLID-v3 Language Identification") as demo:
135
  ["El rápido zorro marrón salta sobre el perro perezoso."],
136
  ["Быстрая коричневая лисица прыгает через ленивую собаку."],
137
  ["快速的棕色狐狸跳过了懒惰的狗。"],
 
 
138
  ],
139
  inputs=input_text,
140
- label="Try these examples (Kabyle and Occitan featured)"
141
  )
142
 
143
  gr.Markdown(f"""
144
  ### Tips for best results:
145
- - Text is automatically preprocessed (lowercased, normalized)
146
- - Longer texts generally give more accurate predictions
147
- - The model supports 194+ language varieties
148
- - Use higher thresholds to filter out uncertain predictions
149
- - **Maximum input length: {MAX_INPUT_LENGTH:,} characters**
150
  """)
151
 
152
  # Event handlers
153
  submit_btn.click(
154
- fn=predict_language,
155
  inputs=[input_text, top_k, threshold],
156
- outputs=output
157
  )
158
 
159
  input_text.submit(
160
- fn=predict_language,
161
  inputs=[input_text, top_k, threshold],
162
- outputs=output
163
  )
164
 
165
  if __name__ == "__main__":
166
- # Get port from environment (HF Spaces sets this)
167
  port = int(os.environ.get("PORT", 7860))
168
 
169
  try:
170
  demo.launch(
171
  server_name="0.0.0.0",
172
  server_port=port,
173
- ssr_mode=False, # Disable experimental SSR to prevent the error
174
  share=False,
175
  show_error=True
176
  )
 
5
  import os
6
  import asyncio
7
  import atexit
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ import numpy as np
12
 
13
+ # ==================== Constants ====================
14
+ MAX_INPUT_LENGTH = 10000 # OpenLID character limit
15
+ COMMONLINGUA_MAX_BYTES = 512 # CommonLingua byte limit
16
 
17
+ # ==================== OpenLID Setup ====================
18
+ print("Loading OpenLID-v3 model...")
19
+ openlid_path = hf_hub_download(
20
+ repo_id="HPLT/OpenLID-v3",
21
+ filename="openid-v3.bin"
22
+ )
23
+ openlid_model = fasttext.load_model(openlid_path)
24
+ print("OpenLID-v3 loaded successfully!")
25
+
26
+ # Preprocessing patterns for OpenLID
27
  NONWORD_REPLACE_STR = r"[^\p{Word}\p{Zs}]|\d"
28
  NONWORD_REPLACE_PATTERN = regex.compile(NONWORD_REPLACE_STR)
29
  SPACE_PATTERN = regex.compile(r"\s\s+")
30
 
31
+ def openlid_preprocess(text):
32
+ """Preprocess text for OpenLID-v3."""
33
  text = text.strip().replace('\n', ' ').lower()
34
  text = regex.sub(SPACE_PATTERN, " ", text)
35
  text = regex.sub(NONWORD_REPLACE_PATTERN, "", text)
36
  return text
37
 
38
+ # ==================== CommonLingua Setup ====================
39
+ # Inline model architecture (from model.py) so no extra file is needed
40
+ class ByteNgramEmbed(nn.Module):
41
+ def __init__(self, num_buckets=4096, embed_dim=64, n=3):
42
+ super().__init__()
43
+ self.n = n
44
+ self.num_buckets = num_buckets
45
+ self.embed = nn.Embedding(num_buckets, embed_dim)
46
+
47
+ def forward(self, byte_ids):
48
+ B, T = byte_ids.shape
49
+ clamped = byte_ids.clamp(max=255)
50
+ padded = F.pad(clamped, (0, self.n - 1), value=0)
51
+ h = torch.zeros(B, T, dtype=torch.long, device=byte_ids.device)
52
+ for i in range(self.n):
53
+ h = h * 257 + padded[:, i:i + T]
54
+ return self.embed(h % self.num_buckets)
55
+
56
+
57
+ class ByteConvBlock(nn.Module):
58
+ def __init__(self, d_model, kernel_size=15, expand=2):
59
+ super().__init__()
60
+ self.norm1 = nn.LayerNorm(d_model)
61
+ self.pad = kernel_size - 1
62
+ self.conv = nn.Conv1d(d_model, d_model, kernel_size, groups=d_model)
63
+ self.norm2 = nn.LayerNorm(d_model)
64
+ ffn = d_model * expand
65
+ self.ffn_gate = nn.Linear(d_model, ffn, bias=False)
66
+ self.ffn_up = nn.Linear(d_model, ffn, bias=False)
67
+ self.ffn_down = nn.Linear(ffn, d_model, bias=False)
68
+
69
+ def forward(self, x):
70
+ residual = x
71
+ x = self.norm1(x).transpose(1, 2)
72
+ x = F.pad(x, (self.pad, 0))
73
+ x = F.silu(self.conv(x)).transpose(1, 2)
74
+ x = residual + x
75
+
76
+ residual = x
77
+ x = self.norm2(x)
78
+ x = self.ffn_down(F.silu(self.ffn_gate(x)) * self.ffn_up(x))
79
+ return residual + x
80
+
81
+
82
+ def _rope(q, k):
83
+ head_dim = q.shape[-1]
84
+ seq_len = q.shape[-2]
85
+ freqs = 1.0 / (10000.0 ** (torch.arange(0, head_dim, 2, device=q.device).float() / head_dim))
86
+ t = torch.arange(seq_len, device=q.device)
87
+ a = torch.outer(t, freqs)
88
+ cos = a.cos().to(q.dtype)
89
+ sin = a.sin().to(q.dtype)
90
+
91
+ def rot(x):
92
+ x1, x2 = x[..., : head_dim // 2], x[..., head_dim // 2:]
93
+ return torch.cat([x1 * cos - x2 * sin, x2 * cos + x1 * sin], dim=-1)
94
+
95
+ return rot(q), rot(k)
96
+
97
+
98
+ class ByteAttnBlock(nn.Module):
99
+ def __init__(self, d_model, n_heads=4, expand=2):
100
+ super().__init__()
101
+ self.n_heads = n_heads
102
+ self.head_dim = d_model // n_heads
103
+ self.norm1 = nn.LayerNorm(d_model)
104
+ self.qkv = nn.Linear(d_model, 3 * d_model, bias=False)
105
+ self.out_proj = nn.Linear(d_model, d_model, bias=False)
106
+ self.norm2 = nn.LayerNorm(d_model)
107
+ ffn = d_model * expand
108
+ self.ffn_gate = nn.Linear(d_model, ffn, bias=False)
109
+ self.ffn_up = nn.Linear(d_model, ffn, bias=False)
110
+ self.ffn_down = nn.Linear(ffn, d_model, bias=False)
111
+
112
+ def forward(self, x):
113
+ B, T, D = x.shape
114
+ residual = x
115
+ h = self.norm1(x)
116
+ qkv = self.qkv(h).reshape(B, T, 3, self.n_heads, self.head_dim)
117
+ q, k, v = (t.transpose(1, 2) for t in qkv.unbind(dim=2))
118
+ q, k = _rope(q, k)
119
+ attn = (q @ k.transpose(-2, -1)) / (self.head_dim ** 0.5)
120
+ attn = attn.softmax(dim=-1)
121
+ out = (attn @ v).transpose(1, 2).contiguous().view(B, T, D)
122
+ x = residual + self.out_proj(out)
123
+
124
+ residual = x
125
+ h = self.norm2(x)
126
+ h = self.ffn_down(F.silu(self.ffn_gate(h)) * self.ffn_up(h))
127
+ return residual + h
128
+
129
+
130
+ class ByteHybrid(nn.Module):
131
+ def __init__(
132
+ self,
133
+ num_classes,
134
+ d_model=256,
135
+ n_conv=3,
136
+ n_attn=1,
137
+ n_heads=4,
138
+ ffn_expand=2,
139
+ max_len=512,
140
+ conv_kernel=15,
141
+ ngram_buckets=0,
142
+ ngram_dim=64,
143
+ ):
144
+ super().__init__()
145
+ self.max_len = max_len
146
+ self.embed = nn.Embedding(257, d_model, padding_idx=256)
147
+
148
+ self.ngram_embed = None
149
+ if ngram_buckets > 0:
150
+ self.ngram_embed = ByteNgramEmbed(ngram_buckets, ngram_dim, n=3)
151
+ self.ngram_proj = nn.Linear(ngram_dim, d_model, bias=False)
152
+
153
+ self.conv_layers = nn.ModuleList(
154
+ [ByteConvBlock(d_model, conv_kernel, ffn_expand) for _ in range(n_conv)]
155
+ )
156
+ self.attn_layers = nn.ModuleList(
157
+ [ByteAttnBlock(d_model, n_heads, ffn_expand) for _ in range(n_attn)]
158
+ )
159
+ self.final_norm = nn.LayerNorm(d_model)
160
+ self.head = nn.Sequential(
161
+ nn.Linear(d_model, d_model),
162
+ nn.GELU(),
163
+ nn.Dropout(0.1),
164
+ nn.Linear(d_model, num_classes),
165
+ )
166
+
167
+ def forward(self, byte_ids):
168
+ pad_mask = byte_ids != 256
169
+ x = self.embed(byte_ids)
170
+ if self.ngram_embed is not None:
171
+ x = x + self.ngram_proj(self.ngram_embed(byte_ids))
172
+ for layer in self.conv_layers:
173
+ x = layer(x)
174
+ for layer in self.attn_layers:
175
+ x = layer(x)
176
+ x = self.final_norm(x)
177
+ mask = pad_mask.unsqueeze(-1).to(x.dtype)
178
+ x = (x * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1)
179
+ return self.head(x)
180
+
181
+
182
+ CONFIGS = {
183
+ "base_ngram": dict(
184
+ d_model=256, n_conv=3, n_attn=1, n_heads=4, conv_kernel=15,
185
+ ngram_buckets=4096, ngram_dim=64,
186
+ ),
187
+ }
188
+
189
+ def commonlingua_encode(texts, max_len):
190
+ out = np.full((len(texts), max_len), 256, dtype=np.int64)
191
+ for i, t in enumerate(texts):
192
+ if not isinstance(t, str):
193
+ t = "" if t is None else str(t)
194
+ raw = t.encode("utf-8", errors="replace")[:max_len]
195
+ if raw:
196
+ out[i, :len(raw)] = np.frombuffer(raw, dtype=np.uint8)
197
+ return torch.from_numpy(out)
198
+
199
+
200
+ @torch.no_grad()
201
+ def commonlingua_predict(model, texts, idx2lang, max_len, device, top_k=3):
202
+ """Returns a list of [(lang, prob), ...] (one list per text, top-k entries each)."""
203
+ out = []
204
+ batch = commonlingua_encode(texts, max_len).to(device)
205
+ probs = torch.softmax(model(batch).float(), dim=-1)
206
+ top_p, top_idx = probs.topk(top_k, dim=-1)
207
+ for p_row, idx_row in zip(top_p.cpu().tolist(), top_idx.cpu().tolist()):
208
+ out.append([(idx2lang[j], float(p)) for p, j in zip(p_row, idx_row)])
209
+ return out
210
+
211
+
212
+ print("Loading CommonLingua model...")
213
+ commonlingua_path = hf_hub_download(
214
+ repo_id="PleIAs/CommonLingua",
215
+ filename="model.pt"
216
  )
217
+ ckpt = torch.load(commonlingua_path, map_location="cpu", weights_only=False)
218
+ commonlingua_model = ByteHybrid(
219
+ num_classes=ckpt["num_classes"],
220
+ max_len=ckpt["max_len"],
221
+ **CONFIGS[ckpt["config"]]
222
+ )
223
+ commonlingua_model.load_state_dict(ckpt["model_state_dict"])
224
+ commonlingua_model.eval()
225
 
226
+ device = "cuda" if torch.cuda.is_available() else "cpu"
227
+ commonlingua_model = commonlingua_model.to(device)
228
+ commonlingua_idx2lang = {v: k for k, v in ckpt["lang2idx"].items()}
229
+ commonlingua_max_len = ckpt["max_len"]
230
+ print(f"CommonLingua loaded successfully! ({len(commonlingua_idx2lang)} languages, device={device})")
231
+
232
+ # ==================== Prediction Functions ====================
233
+ def predict_openlid(text, top_k=3, threshold=0.5):
234
+ """Predict language using OpenLID-v3."""
 
 
 
 
235
  if not text or not text.strip():
236
  return "Please enter some text to analyze."
237
 
238
+ processed_text = openlid_preprocess(text)
 
 
239
  if not processed_text.strip():
240
  return "Text contains no valid characters for language identification."
241
 
242
+ predictions = openlid_model.predict(
 
243
  text=processed_text,
244
  k=min(top_k, 10),
245
  threshold=threshold,
 
247
  )
248
 
249
  labels, scores = predictions
 
 
250
  results = []
251
  for label, score in zip(labels, scores):
 
252
  lang_code = label.replace("__label__", "")
253
  confidence = float(score) * 100
254
  results.append(f"**{lang_code}**: {confidence:.2f}%")
255
 
256
+ return "\n\n".join(results) if results else "No predictions above threshold."
257
+
258
+
259
+ def predict_commonlingua(text, top_k=3):
260
+ """Predict language using CommonLingua."""
261
+ if not text or not text.strip():
262
+ return "Please enter some text to analyze."
263
+
264
+ results = commonlingua_predict(
265
+ commonlingua_model, [text], commonlingua_idx2lang,
266
+ commonlingua_max_len, device, top_k=min(top_k, 10)
267
+ )
268
+
269
+ formatted = []
270
+ for lang, prob in results[0]:
271
+ formatted.append(f"**{lang}**: {prob*100:.2f}%")
272
+ return "\n\n".join(formatted)
273
+
274
 
275
+ def predict_both(text, top_k=3, threshold=0.5):
276
+ """
277
+ Run both models and return combined results.
278
+ Returns tuple: (openlid_result, commonlingua_result, status_message)
279
+ """
280
+ # Check OpenLID length limit
281
+ if len(text) > MAX_INPUT_LENGTH:
282
+ return (
283
+ f"**Error**: Input too long ({len(text):,} characters). Maximum allowed is {MAX_INPUT_LENGTH:,} characters.",
284
+ f"**Error**: Input too long ({len(text):,} characters). Maximum allowed is {MAX_INPUT_LENGTH:,} characters.",
285
+ "❌ Input exceeds maximum length."
286
+ )
287
+
288
+ # Check CommonLingua byte limit
289
+ byte_length = len(text.encode('utf-8'))
290
+ if byte_length > COMMONLINGUA_MAX_BYTES:
291
+ status = f"⚠️ Warning: Input is {byte_length} bytes. CommonLingua works best with ≤{COMMONLINGUA_MAX_BYTES} bytes (first {COMMONLINGUA_MAX_BYTES} bytes will be used)."
292
+ else:
293
+ status = f"✅ Input length: {len(text):,} chars | {byte_length} bytes"
294
+
295
+ openlid_result = predict_openlid(text, top_k, threshold)
296
+ commonlingua_result = predict_commonlingua(text, top_k)
297
+
298
+ return openlid_result, commonlingua_result, status
299
+
300
+
301
+ # ==================== Cleanup ====================
302
  def cleanup():
303
  try:
304
  loop = asyncio.get_event_loop()
 
311
 
312
  atexit.register(cleanup)
313
 
314
+ # ==================== Gradio Interface ====================
315
+ with gr.Blocks(title="OpenLID-v3 vs CommonLingua") as demo:
 
316
  gr.HTML("""
317
+ <h1>🔍 Language Identification: OpenLID-v3 vs CommonLingua</h1>
318
+ <p>Compare two state-of-the-art language identification models side-by-side.</p>
319
+ <p>
320
+ <em>OpenLID-v3</em>: <a href="https://huggingface.co/HPLT/OpenLID-v3" target="_blank">HPLT/OpenLID-v3</a> (fastText, 194+ languages)<br>
321
+ <em>CommonLingua</em>: <a href="https://huggingface.co/PleIAs/CommonLingua" target="_blank">PleIAs/CommonLingua</a> (byte-level CNN+Attention, 334 languages, 2.35M params)
322
+ </p>
323
  """)
324
 
325
  with gr.Row():
 
329
  placeholder="Enter text to identify its language...",
330
  lines=5,
331
  max_lines=10,
332
+ max_length=MAX_INPUT_LENGTH
333
  )
334
  with gr.Row():
335
  top_k = gr.Slider(
336
+ minimum=1, maximum=10, value=3, step=1,
 
 
 
337
  label="Top-K Predictions"
338
  )
339
  threshold = gr.Slider(
340
+ minimum=0.0, maximum=1.0, value=0.5, step=0.05,
341
+ label="OpenLID Confidence Threshold"
 
 
 
342
  )
343
+ submit_btn = gr.Button("🔍 Identify Language", variant="primary")
344
+ status = gr.Textbox(label="Status", interactive=False)
345
 
346
+ with gr.Row():
347
+ with gr.Column():
348
+ openlid_output = gr.Markdown(label="OpenLID-v3 Predictions")
349
  with gr.Column():
350
+ commonlingua_output = gr.Markdown(label="CommonLingua Predictions")
351
 
352
+ # Examples
353
  gr.Examples(
354
  examples=[
355
  ["Asebter-a yura s wudem awurman d amagrad s tutlayt taqbaylit."],
 
360
  ["El rápido zorro marrón salta sobre el perro perezoso."],
361
  ["Быстрая коричневая лисица прыгает через ленивую собаку."],
362
  ["快速的棕色狐狸跳过了懒惰的狗。"],
363
+ ["Wikipédia est une encyclopédie universelle, multilingue."],
364
+ ["CommonLingua est un modèle d'identification de langue très léger."],
365
  ],
366
  inputs=input_text,
367
+ label="Try these examples"
368
  )
369
 
370
  gr.Markdown(f"""
371
  ### Tips for best results:
372
+ - **OpenLID-v3**: Text is automatically preprocessed (lowercased, normalized). Longer texts generally give more accurate predictions. Max {MAX_INPUT_LENGTH:,} characters.
373
+ - **CommonLingua**: Operates directly on raw UTF-8 bytes (no tokenizer). Designed for paragraph-level corpus curation. Works best with ≤{COMMONLINGUA_MAX_BYTES} bytes. Not assessed on very short segments.
374
+ - Use the **Top-K** slider to see more alternative predictions.
375
+ - Use the **Threshold** slider to filter out uncertain OpenLID predictions (does not affect CommonLingua).
 
376
  """)
377
 
378
  # Event handlers
379
  submit_btn.click(
380
+ fn=predict_both,
381
  inputs=[input_text, top_k, threshold],
382
+ outputs=[openlid_output, commonlingua_output, status]
383
  )
384
 
385
  input_text.submit(
386
+ fn=predict_both,
387
  inputs=[input_text, top_k, threshold],
388
+ outputs=[openlid_output, commonlingua_output, status]
389
  )
390
 
391
  if __name__ == "__main__":
 
392
  port = int(os.environ.get("PORT", 7860))
393
 
394
  try:
395
  demo.launch(
396
  server_name="0.0.0.0",
397
  server_port=port,
398
+ ssr_mode=False,
399
  share=False,
400
  show_error=True
401
  )