cstr commited on
Commit
c193dfd
·
verified ·
1 Parent(s): 92ef58f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -71
app.py CHANGED
@@ -21,6 +21,7 @@ MODEL_INFO: Dict[str, Tuple[str, str, str]] = {
21
  "es": ("Spanish", "es_core_news_md", "spacy"),
22
  "grc-proiel-trf": ("Ancient Greek (PROIEL TRF)", "grc_proiel_trf", "grecy"),
23
  "grc-perseus-trf": ("Ancient Greek (Perseus TRF)", "grc_perseus_trf", "grecy"),
 
24
  "grc-proiel-lg": ("Ancient Greek (PROIEL LG)", "grc_proiel_lg", "grecy"),
25
  "grc-perseus-lg": ("Ancient Greek (Perseus LG)", "grc_perseus_lg", "grecy"),
26
  "grc-proiel-sm": ("Ancient Greek (PROIEL SM)", "grc_proiel_sm", "grecy"),
@@ -40,10 +41,11 @@ UI_TEXT = {
40
  "tab_graphic": "Grafische Darstellung",
41
  "tab_table": "Tabelle",
42
  "tab_json": "JSON",
 
43
  "html_label": "Abhängigkeitsparsing",
44
  "table_label": "Morphologische Analyse",
45
- "table_headers": ["Wort", "Lemma", "POS", "Tag", "Morphologie", "Abhängigkeit"],
46
  "json_label": "JSON-Ausgabe",
 
47
  "error_message": "Fehler: "
48
  },
49
  "en": {
@@ -58,10 +60,11 @@ UI_TEXT = {
58
  "tab_graphic": "Graphic View",
59
  "tab_table": "Table",
60
  "tab_json": "JSON",
 
61
  "html_label": "Dependency Parsing",
62
  "table_label": "Morphological Analysis",
63
- "table_headers": ["Word", "Lemma", "POS", "Tag", "Morphology", "Dependency"],
64
  "json_label": "JSON Output",
 
65
  "error_message": "Error: "
66
  },
67
  "es": {
@@ -76,15 +79,15 @@ UI_TEXT = {
76
  "tab_graphic": "Vista Gráfica",
77
  "tab_table": "Tabla",
78
  "tab_json": "JSON",
 
79
  "html_label": "Análisis de Dependencias",
80
  "table_label": "Análisis Morfológico",
81
- "table_headers": ["Palabra", "Lema", "POS", "Etiqueta", "Morfología", "Dependencia"],
82
  "json_label": "Salida JSON",
 
83
  "error_message": "Error: "
84
  }
85
  }
86
 
87
- # This global dict holds the *loaded* models
88
  MODELS: Dict[str, Optional[spacy.Language]] = {}
89
 
90
  # ============================================================================
@@ -92,9 +95,7 @@ MODELS: Dict[str, Optional[spacy.Language]] = {}
92
  # ============================================================================
93
 
94
  def install_spacy_transformers_once():
95
- """
96
- Installs spacy-transformers, required for all _trf models.
97
- """
98
  marker_file = Path(".spacy_transformers_installed")
99
  if marker_file.exists():
100
  print("✓ spacy-transformers already installed (marker found)")
@@ -113,10 +114,7 @@ def install_spacy_transformers_once():
113
  return False
114
 
115
  def install_grecy_model_from_github(model_name: str) -> bool:
116
- """
117
- Installs a greCy model from your specific GitHub Release.
118
- Uses --no-deps to prevent dependency conflicts.
119
- """
120
  marker_file = Path(f".{model_name}_installed")
121
  if marker_file.exists():
122
  print(f"✓ {model_name} already installed (marker found)")
@@ -124,16 +122,15 @@ def install_grecy_model_from_github(model_name: str) -> bool:
124
 
125
  print(f"Installing grecy model: {model_name}...")
126
 
127
- # --- *** THIS IS THE CRITICAL FILENAME FIX *** ---
128
- # The wheel filename's "distribution" part uses underscores,
129
- # not hyphens, to be PEP 427 compliant.
130
  if model_name == "grc_proiel_trf":
131
  wheel_filename = "grc_proiel_trf-3.7.5-py3-none-any.whl"
132
- else:
133
- # All other models use the 0.0.0 version and underscore in name
134
- # e.g., grc_perseus_trf-0.0.0-py3-none-any.whl
135
  wheel_filename = f"{model_name}-0.0.0-py3-none-any.whl"
136
- # --- *** END FIX *** ---
 
 
137
 
138
  install_url = f"https://github.com/CrispStrobe/greCy/releases/download/v1.0-models/{wheel_filename}"
139
  cmd = [sys.executable, "-m", "pip", "install", install_url, "--no-deps"]
@@ -150,11 +147,6 @@ def install_grecy_model_from_github(model_name: str) -> bool:
150
  print(f"✗ Installation subprocess FAILED with code {e.returncode}")
151
  print("STDOUT:", e.stdout)
152
  print("STDERR:", e.stderr)
153
- print("\n\n!! --- NOTE --- !!")
154
- print(f"If the error is 'Invalid wheel filename', it means the files on your GitHub Release are named incorrectly.")
155
- print(f"You must delete the release and re-run the fixed 'create_grecy_release.sh' script.")
156
- print(f"The correct filename should be: '{wheel_filename}'.")
157
- print("!! --- END NOTE --- !!\n\n")
158
  return False
159
  except Exception as e:
160
  print(f"✗ Installation exception: {e}")
@@ -162,7 +154,7 @@ def install_grecy_model_from_github(model_name: str) -> bool:
162
  return False
163
 
164
  # ============================================================================
165
- # MODEL LOADING (NOW WITH LAZY LOADING)
166
  # ============================================================================
167
 
168
  def load_spacy_model(model_name: str) -> Optional[spacy.Language]:
@@ -179,10 +171,7 @@ def load_spacy_model(model_name: str) -> Optional[spacy.Language]:
179
  return None
180
 
181
  def load_grecy_model(model_name: str) -> Optional[spacy.Language]:
182
- """
183
- Load a grecy model.
184
- First, installs it from our GitHub. Then, loads it.
185
- """
186
  if not install_grecy_model_from_github(model_name):
187
  print(f"✗ Cannot load {model_name} because installation failed.")
188
  return None
@@ -190,7 +179,7 @@ def load_grecy_model(model_name: str) -> Optional[spacy.Language]:
190
  print("Refreshing importlib to find new package...")
191
  importlib.invalidate_caches()
192
  try: importlib.reload(site)
193
- except Exception: pass # Fails in some envs, but that's ok
194
 
195
  print(f"Trying: spacy.load('{model_name}')")
196
  nlp = spacy.load(model_name)
@@ -203,16 +192,11 @@ def load_grecy_model(model_name: str) -> Optional[spacy.Language]:
203
  return None
204
 
205
  def initialize_models():
206
- """
207
- Load all *non-grecy* models at startup for speed.
208
- Grecy models will be lazy-loaded on first use.
209
- Also ensures spacy-transformers is installed.
210
- """
211
  print("\n" + "="*70)
212
  print("INITIALIZING MODELS")
213
  print("="*70 + "\n")
214
 
215
- # First, ensure dependencies are met
216
  install_spacy_transformers_once()
217
 
218
  loaded_count = 0
@@ -223,22 +207,21 @@ def initialize_models():
223
  spacy_model_count += 1
224
  print(f"Loading {lang_name} ({model_name})...")
225
  nlp = load_spacy_model(model_name)
226
- MODELS[lang_code] = nlp # Store the loaded model
227
  if nlp:
228
  print(f"✓ {lang_name} ready\n")
229
  loaded_count += 1
230
  else:
231
  print(f"✗ {lang_name} FAILED\n")
232
  else:
233
- # It's a grecy model, just mark as not loaded yet
234
  print(f"✓ {lang_name} ({model_name}) will be loaded on first use.\n")
235
- MODELS[lang_code] = None # Mark as available but not loaded
236
 
237
  print(f"Pre-loaded {loaded_count}/{spacy_model_count} standard models.")
238
  print("="*70 + "\n")
239
 
240
  # ============================================================================
241
- # ANALYSIS (WITH LAZY LOADING)
242
  # ============================================================================
243
 
244
  def get_analysis(ui_lang: str, model_lang_key: str, text: str):
@@ -248,36 +231,29 @@ def get_analysis(ui_lang: str, model_lang_key: str, text: str):
248
 
249
  try:
250
  if not text.strip():
251
- return ([], [], "<p style='color: orange;'>No text provided.</p>",
 
252
  gr.Button(value=ui_config["button_text"], interactive=True))
253
 
254
- # --- LAZY LOADING LOGIC ---
255
  nlp = MODELS.get(model_lang_key)
256
 
257
  if nlp is None:
258
- # Model hasn't been loaded yet
259
  print(f"First use of {model_lang_key}. Loading model...")
260
-
261
  if model_lang_key not in MODEL_INFO:
262
  raise ValueError(f"Unknown model key: {model_lang_key}")
263
-
264
  _, model_name, model_type = MODEL_INFO[model_lang_key]
265
 
266
  if model_type == "grecy":
267
  nlp = load_grecy_model(model_name)
268
  else:
269
- # This case should be pre-loaded, but as a fallback
270
  nlp = load_spacy_model(model_name)
271
 
272
  if nlp is None:
273
- # Failed to load
274
- MODELS.pop(model_lang_key, None) # Remove from cache to try again next time
275
  raise ValueError(f"Model for {model_lang_key} ({model_name}) FAILED to load. Check logs.")
276
  else:
277
- # Store in cache
278
  MODELS[model_lang_key] = nlp
279
  print(f"✓ {model_lang_key} is now loaded and cached.")
280
- # --- END LAZY LOADING ---
281
 
282
  doc = nlp(text)
283
 
@@ -296,32 +272,48 @@ def get_analysis(ui_lang: str, model_lang_key: str, text: str):
296
  "tag": tag_str, "morphology": morph_str, "dependency": dep_str,
297
  "is_stopword": token.is_stop
298
  })
299
-
300
  dataframe_output.append([token.text, lemma_str, pos_str, tag_str, morph_str, dep_str])
301
 
302
- html_out = ""
303
- if doc.has_annotation("DEP"):
 
304
  try:
305
  options = {"compact": True, "bg": "#ffffff", "color": "#000000", "font": "Source Sans Pro"}
306
  html_svg = displacy.render(doc, style="dep", jupyter=False, options=options)
307
  svg_b64 = base64.b64encode(html_svg.encode("utf-8")).decode("utf-8")
308
- html_out = f'<div style="background-color: #ffffff; overflow-x: auto; border: 1px solid #e6e9ef; border-radius: 0.25rem; padding: 1rem; line-height: 2.5;"><img src="data:image/svg+xml;base64,{svg_b64}" /></div>'
309
  except Exception as e:
310
- html_out = f"<p style='color: orange;'>Visualization error: {e}</p>"
311
  else:
312
- html_out = "<p style='color: orange;'>Dependency parsing not available for this model.</p>"
313
 
314
- return (dataframe_output, json_output, html_out,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  gr.Button(value=ui_config["button_text"], interactive=True))
316
 
317
  except Exception as e:
318
  traceback.print_exc()
319
  error_html = f"<div style='color: red; border: 1px solid red; padding: 10px; border-radius: 5px; background-color: #fff5f5;'><strong>{error_prefix}</strong> {str(e)}</div>"
320
- return ([[f"{error_prefix}{str(e)}"]], {"error": str(e)}, error_html,
 
321
  gr.Button(value=ui_config["button_text"], interactive=True))
322
 
323
  # ============================================================================
324
- # UI
325
  # ============================================================================
326
 
327
  def update_ui(ui_lang: str):
@@ -337,26 +329,25 @@ def update_ui(ui_lang: str):
337
  gr.Tab(label=ui_config["tab_graphic"]),
338
  gr.Tab(label=ui_config["tab_table"]),
339
  gr.Tab(label=ui_config["tab_json"]),
 
340
  gr.HTML(label=ui_config["html_label"]),
341
  gr.DataFrame(label=ui_config["table_label"], headers=ui_config["table_headers"], interactive=False),
342
- gr.JSON(label=ui_config["json_label"])
 
343
  ]
344
 
345
  def create_interface():
346
  """Create Gradio interface."""
347
  config = UI_TEXT["en"]
348
-
349
- # Use the keys from MODEL_INFO as the choices
350
  model_choices = list(MODEL_INFO.keys())
351
 
352
  with gr.Blocks(title="Multilingual Morpho-Syntactic Analyzer") as demo:
353
  with gr.Row():
354
  ui_lang_radio = gr.Radio(["DE", "EN", "ES"], label=config["ui_lang_label"], value="EN")
355
- # Update the radio to pass the key, but display the friendly name
356
  model_lang_radio = gr.Radio(
357
- choices=[(MODEL_INFO[k][0], k) for k in model_choices], # (Label, Value)
358
  label=config["model_lang_label"],
359
- value=model_choices[0] # Default to the first key
360
  )
361
 
362
  markdown_title = gr.Markdown(config["title"])
@@ -366,19 +357,28 @@ def create_interface():
366
 
367
  with gr.Tabs():
368
  with gr.Tab(config["tab_graphic"]) as tab_graphic:
369
- html_out = gr.HTML(label=config["html_label"])
 
 
 
 
370
  with gr.Tab(config["tab_table"]) as tab_table:
371
  df_out = gr.DataFrame(label=config["table_label"], headers=config["table_headers"], interactive=False)
372
  with gr.Tab(config["tab_json"]) as tab_json:
373
  json_out = gr.JSON(label=config["json_label"])
374
 
375
- analyze_button.click(fn=get_analysis, inputs=[ui_lang_radio, model_lang_radio, text_input],
376
- outputs=[df_out, json_out, html_out, analyze_button], api_name="get_morphology")
 
 
 
377
 
378
- ui_lang_radio.change(fn=update_ui, inputs=ui_lang_radio,
379
- outputs=[markdown_title, markdown_subtitle, ui_lang_radio, model_lang_radio,
380
- text_input, analyze_button, tab_graphic, tab_table, tab_json,
381
- html_out, df_out, json_out])
 
 
382
  return demo
383
 
384
  # ============================================================================
 
21
  "es": ("Spanish", "es_core_news_md", "spacy"),
22
  "grc-proiel-trf": ("Ancient Greek (PROIEL TRF)", "grc_proiel_trf", "grecy"),
23
  "grc-perseus-trf": ("Ancient Greek (Perseus TRF)", "grc_perseus_trf", "grecy"),
24
+ "grc_ner_trf": ("Ancient Greek (NER TRF)", "grc_ner_trf", "grecy"),
25
  "grc-proiel-lg": ("Ancient Greek (PROIEL LG)", "grc_proiel_lg", "grecy"),
26
  "grc-perseus-lg": ("Ancient Greek (Perseus LG)", "grc_perseus_lg", "grecy"),
27
  "grc-proiel-sm": ("Ancient Greek (PROIEL SM)", "grc_proiel_sm", "grecy"),
 
41
  "tab_graphic": "Grafische Darstellung",
42
  "tab_table": "Tabelle",
43
  "tab_json": "JSON",
44
+ "tab_ner": "Entitäten", # <-- ADDED
45
  "html_label": "Abhängigkeitsparsing",
46
  "table_label": "Morphologische Analyse",
 
47
  "json_label": "JSON-Ausgabe",
48
+ "ner_label": "Benannte Entitäten", # <-- ADDED
49
  "error_message": "Fehler: "
50
  },
51
  "en": {
 
60
  "tab_graphic": "Graphic View",
61
  "tab_table": "Table",
62
  "tab_json": "JSON",
63
+ "tab_ner": "Entities", # <-- ADDED
64
  "html_label": "Dependency Parsing",
65
  "table_label": "Morphological Analysis",
 
66
  "json_label": "JSON Output",
67
+ "ner_label": "Named Entities", # <-- ADDED
68
  "error_message": "Error: "
69
  },
70
  "es": {
 
79
  "tab_graphic": "Vista Gráfica",
80
  "tab_table": "Tabla",
81
  "tab_json": "JSON",
82
+ "tab_ner": "Entidades", # <-- ADDED
83
  "html_label": "Análisis de Dependencias",
84
  "table_label": "Análisis Morfológico",
 
85
  "json_label": "Salida JSON",
86
+ "ner_label": "Entidades Nombradas", # <-- ADDED
87
  "error_message": "Error: "
88
  }
89
  }
90
 
 
91
  MODELS: Dict[str, Optional[spacy.Language]] = {}
92
 
93
  # ============================================================================
 
95
  # ============================================================================
96
 
97
  def install_spacy_transformers_once():
98
+ """ Installs spacy-transformers, required for all _trf models. """
 
 
99
  marker_file = Path(".spacy_transformers_installed")
100
  if marker_file.exists():
101
  print("✓ spacy-transformers already installed (marker found)")
 
114
  return False
115
 
116
  def install_grecy_model_from_github(model_name: str) -> bool:
117
+ """ Installs a greCy model from your specific GitHub Release. """
 
 
 
118
  marker_file = Path(f".{model_name}_installed")
119
  if marker_file.exists():
120
  print(f"✓ {model_name} already installed (marker found)")
 
122
 
123
  print(f"Installing grecy model: {model_name}...")
124
 
 
 
 
125
  if model_name == "grc_proiel_trf":
126
  wheel_filename = "grc_proiel_trf-3.7.5-py3-none-any.whl"
127
+ # --- UPDATED: Added grc_ner_trf to this list ---
128
+ elif model_name in ["grc_perseus_trf", "grc_proiel_lg", "grc_perseus_lg",
129
+ "grc_proiel_sm", "grc_perseus_sm", "grc_ner_trf"]:
130
  wheel_filename = f"{model_name}-0.0.0-py3-none-any.whl"
131
+ else:
132
+ print(f"✗ Unknown grecy model: {model_name}")
133
+ return False
134
 
135
  install_url = f"https://github.com/CrispStrobe/greCy/releases/download/v1.0-models/{wheel_filename}"
136
  cmd = [sys.executable, "-m", "pip", "install", install_url, "--no-deps"]
 
147
  print(f"✗ Installation subprocess FAILED with code {e.returncode}")
148
  print("STDOUT:", e.stdout)
149
  print("STDERR:", e.stderr)
 
 
 
 
 
150
  return False
151
  except Exception as e:
152
  print(f"✗ Installation exception: {e}")
 
154
  return False
155
 
156
  # ============================================================================
157
+ # MODEL LOADING (LAZY LOADING)
158
  # ============================================================================
159
 
160
  def load_spacy_model(model_name: str) -> Optional[spacy.Language]:
 
171
  return None
172
 
173
  def load_grecy_model(model_name: str) -> Optional[spacy.Language]:
174
+ """ Load a grecy model, installing from GitHub if needed. """
 
 
 
175
  if not install_grecy_model_from_github(model_name):
176
  print(f"✗ Cannot load {model_name} because installation failed.")
177
  return None
 
179
  print("Refreshing importlib to find new package...")
180
  importlib.invalidate_caches()
181
  try: importlib.reload(site)
182
+ except Exception: pass
183
 
184
  print(f"Trying: spacy.load('{model_name}')")
185
  nlp = spacy.load(model_name)
 
192
  return None
193
 
194
  def initialize_models():
195
+ """ Pre-load standard models and ensure _trf dependencies are ready. """
 
 
 
 
196
  print("\n" + "="*70)
197
  print("INITIALIZING MODELS")
198
  print("="*70 + "\n")
199
 
 
200
  install_spacy_transformers_once()
201
 
202
  loaded_count = 0
 
207
  spacy_model_count += 1
208
  print(f"Loading {lang_name} ({model_name})...")
209
  nlp = load_spacy_model(model_name)
210
+ MODELS[lang_code] = nlp
211
  if nlp:
212
  print(f"✓ {lang_name} ready\n")
213
  loaded_count += 1
214
  else:
215
  print(f"✗ {lang_name} FAILED\n")
216
  else:
 
217
  print(f"✓ {lang_name} ({model_name}) will be loaded on first use.\n")
218
+ MODELS[lang_code] = None
219
 
220
  print(f"Pre-loaded {loaded_count}/{spacy_model_count} standard models.")
221
  print("="*70 + "\n")
222
 
223
  # ============================================================================
224
+ # ANALYSIS (WITH NER)
225
  # ============================================================================
226
 
227
  def get_analysis(ui_lang: str, model_lang_key: str, text: str):
 
231
 
232
  try:
233
  if not text.strip():
234
+ # Return empty values for all outputs
235
+ return ([], [], "<p style='color: orange;'>No text provided.</p>", "",
236
  gr.Button(value=ui_config["button_text"], interactive=True))
237
 
 
238
  nlp = MODELS.get(model_lang_key)
239
 
240
  if nlp is None:
 
241
  print(f"First use of {model_lang_key}. Loading model...")
 
242
  if model_lang_key not in MODEL_INFO:
243
  raise ValueError(f"Unknown model key: {model_lang_key}")
 
244
  _, model_name, model_type = MODEL_INFO[model_lang_key]
245
 
246
  if model_type == "grecy":
247
  nlp = load_grecy_model(model_name)
248
  else:
 
249
  nlp = load_spacy_model(model_name)
250
 
251
  if nlp is None:
252
+ MODELS.pop(model_lang_key, None)
 
253
  raise ValueError(f"Model for {model_lang_key} ({model_name}) FAILED to load. Check logs.")
254
  else:
 
255
  MODELS[model_lang_key] = nlp
256
  print(f"✓ {model_lang_key} is now loaded and cached.")
 
257
 
258
  doc = nlp(text)
259
 
 
272
  "tag": tag_str, "morphology": morph_str, "dependency": dep_str,
273
  "is_stopword": token.is_stop
274
  })
 
275
  dataframe_output.append([token.text, lemma_str, pos_str, tag_str, morph_str, dep_str])
276
 
277
+ # --- DEPENDENCY PARSE VISUALIZATION ---
278
+ html_dep_out = ""
279
+ if "parser" in nlp.pipe_names:
280
  try:
281
  options = {"compact": True, "bg": "#ffffff", "color": "#000000", "font": "Source Sans Pro"}
282
  html_svg = displacy.render(doc, style="dep", jupyter=False, options=options)
283
  svg_b64 = base64.b64encode(html_svg.encode("utf-8")).decode("utf-8")
284
+ html_dep_out = f'<div style="overflow-x: auto; border: 1px solid #e6e9ef; border-radius: 0.25rem; padding: 1rem; line-height: 2.5;"><img src="data:image/svg+xml;base64,{svg_b64}" /></div>'
285
  except Exception as e:
286
+ html_dep_out = f"<p style='color: orange;'>Visualization error (DEP): {e}</p>"
287
  else:
288
+ html_dep_out = "<p style='color: orange;'>Dependency parsing ('parser') not available for this model.</p>"
289
 
290
+ # --- NAMED ENTITY VISUALIZATION (NEW) ---
291
+ html_ner_out = ""
292
+ if "ner" in nlp.pipe_names:
293
+ if doc.ents:
294
+ try:
295
+ # Let displacy use its default colors
296
+ html_ner_out = displacy.render(doc, style="ent", jupyter=False)
297
+ html_ner_out = f'<div style="overflow-x: auto; border: 1px solid #e6e9ef; border-radius: 0.25rem; padding: 1rem; line-height: 2.5;">{html_ner_out}</div>'
298
+ except Exception as e:
299
+ html_ner_out = f"<p style='color: orange;'>Visualization error (NER): {e}</p>"
300
+ else:
301
+ html_ner_out = "<p>No named entities found in this text.</p>"
302
+ else:
303
+ html_ner_out = "<p style='color: orange;'>Named Entity Recognition ('ner') not available for this model.</p>"
304
+
305
+ return (dataframe_output, json_output, html_dep_out, html_ner_out,
306
  gr.Button(value=ui_config["button_text"], interactive=True))
307
 
308
  except Exception as e:
309
  traceback.print_exc()
310
  error_html = f"<div style='color: red; border: 1px solid red; padding: 10px; border-radius: 5px; background-color: #fff5f5;'><strong>{error_prefix}</strong> {str(e)}</div>"
311
+ # Return error for all 4 outputs
312
+ return ([[f"{error_prefix}{str(e)}"]], {"error": str(e)}, error_html, error_html,
313
  gr.Button(value=ui_config["button_text"], interactive=True))
314
 
315
  # ============================================================================
316
+ # UI (UPDATED FOR NER)
317
  # ============================================================================
318
 
319
  def update_ui(ui_lang: str):
 
329
  gr.Tab(label=ui_config["tab_graphic"]),
330
  gr.Tab(label=ui_config["tab_table"]),
331
  gr.Tab(label=ui_config["tab_json"]),
332
+ gr.Tab(label=ui_config["tab_ner"]), # <-- ADDED
333
  gr.HTML(label=ui_config["html_label"]),
334
  gr.DataFrame(label=ui_config["table_label"], headers=ui_config["table_headers"], interactive=False),
335
+ gr.JSON(label=ui_config["json_label"]),
336
+ gr.HTML(label=ui_config["ner_label"]) # <-- ADDED
337
  ]
338
 
339
  def create_interface():
340
  """Create Gradio interface."""
341
  config = UI_TEXT["en"]
 
 
342
  model_choices = list(MODEL_INFO.keys())
343
 
344
  with gr.Blocks(title="Multilingual Morpho-Syntactic Analyzer") as demo:
345
  with gr.Row():
346
  ui_lang_radio = gr.Radio(["DE", "EN", "ES"], label=config["ui_lang_label"], value="EN")
 
347
  model_lang_radio = gr.Radio(
348
+ choices=[(MODEL_INFO[k][0], k) for k in model_choices],
349
  label=config["model_lang_label"],
350
+ value=model_choices[0]
351
  )
352
 
353
  markdown_title = gr.Markdown(config["title"])
 
357
 
358
  with gr.Tabs():
359
  with gr.Tab(config["tab_graphic"]) as tab_graphic:
360
+ html_dep_out = gr.HTML(label=config["html_label"])
361
+ # --- ADDED NEW TAB ---
362
+ with gr.Tab(config["tab_ner"]) as tab_ner:
363
+ html_ner_out = gr.HTML(label=config["ner_label"])
364
+ # --- END ADDITION ---
365
  with gr.Tab(config["tab_table"]) as tab_table:
366
  df_out = gr.DataFrame(label=config["table_label"], headers=config["table_headers"], interactive=False)
367
  with gr.Tab(config["tab_json"]) as tab_json:
368
  json_out = gr.JSON(label=config["json_label"])
369
 
370
+ # --- UPDATED OUTPUTS ---
371
+ analyze_button.click(fn=get_analysis,
372
+ inputs=[ui_lang_radio, model_lang_radio, text_input],
373
+ outputs=[df_out, json_out, html_dep_out, html_ner_out, analyze_button],
374
+ api_name="get_morphology")
375
 
376
+ # --- UPDATED OUTPUTS ---
377
+ ui_lang_radio.change(fn=update_ui,
378
+ inputs=ui_lang_radio,
379
+ outputs=[markdown_title, markdown_subtitle, ui_lang_radio, model_lang_radio,
380
+ text_input, analyze_button, tab_graphic, tab_table, tab_json, tab_ner,
381
+ html_dep_out, df_out, json_out, html_ner_out])
382
  return demo
383
 
384
  # ============================================================================