"""
Gradio интерфейс для NER-приложения.
"""
import gradio as gr
from config import MAX_CHARS, MAX_BATCH_ROWS, HISTORY_SIZE, MODELS, COLOR_MAP
from processing import (
process_single_text,
compare_models,
process_batch,
get_history_df,
clear_history
)
def create_interface():
"""Создание Gradio интерфейса."""
with gr.Blocks(
theme=gr.themes.Soft(),
title="Russian NER — Извлечение сущностей",
css="""
.entity-legend {
display: flex;
gap: 20px;
margin: 10px 0;
flex-wrap: wrap;
}
.entity-legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.entity-color {
width: 16px;
height: 16px;
border-radius: 3px;
}
"""
) as demo:
# Заголовок
gr.Markdown("""
# Russian NER — Извлечение именованных сущностей
Приложение для автоматического извлечения именованных сущностей из русского текста:
**персоны (ФИО)**, **организации**, **локации (города, страны)** и **прочее**.
---
""")
# Легенда цветов
gr.HTML("""
""")
with gr.Tabs():
# ==================== ВКЛАДКА 1: АНАЛИЗ ТЕКСТА ====================
with gr.Tab("Анализ текста"):
gr.Markdown("### Введите текст для извлечения сущностей")
with gr.Row():
with gr.Column(scale=2):
model_dropdown = gr.Dropdown(
choices=list(MODELS.keys()),
value=list(MODELS.keys())[0],
label="Выберите модель NER",
interactive=True
)
with gr.Column(scale=1):
latency_box = gr.Textbox(
label="Время обработки",
value="—",
interactive=False
)
text_input = gr.Textbox(
label=f"Текст для анализа (максимум {MAX_CHARS} символов)",
placeholder="Введите или вставьте текст на русском языке...",
lines=5
)
process_btn = gr.Button("Обработать", variant="primary", size="lg")
status_box = gr.Textbox(label="Статус", interactive=False)
gr.Markdown("### Результат с подсветкой сущностей")
highlighted_text = gr.HighlightedText(
label="Найденные сущности в тексте",
combine_adjacent=True,
color_map=COLOR_MAP
)
gr.Markdown("### Таблица извлечённых сущностей")
entities_table = gr.Dataframe(
headers=["Текст", "Тип", "Описание", "Уверенность"],
label="Извлечённые сущности",
wrap=True
)
# Обработчик
process_btn.click(
fn=process_single_text,
inputs=[text_input, model_dropdown],
outputs=[status_box, highlighted_text, entities_table, latency_box]
)
# Примеры
gr.Markdown("### Примеры текстов")
gr.Examples(
examples=[
["Владимир Путин встретился с президентом Франции Эммануэлем Макроном в Москве для обсуждения вопросов безопасности."],
["Компания Яндекс открыла новый офис в Санкт-Петербурге рядом с Невским проспектом."],
["Сбербанк и ВТБ объявили о запуске совместного проекта в Казани при поддержке Министерства финансов."],
["Иван Петров работает программистом в компании Mail.ru Group в Москве с 2020 года."],
["Александр Сергеевич Пушкин родился в Москве в 1799 году и стал величайшим русским поэтом."]
],
inputs=text_input,
label="Нажмите на пример для автозаполнения"
)
# ==================== ВКЛАДКА 2: СРАВНЕНИЕ МОДЕЛЕЙ ====================
with gr.Tab("Сравнение моделей"):
gr.Markdown("""
### Сравнение результатов двух моделей
Введите текст, чтобы увидеть, как разные модели распознают сущности.
""")
compare_input = gr.Textbox(
label=f"Текст для сравнения (максимум {MAX_CHARS} символов)",
placeholder="Введите текст для сравнения моделей...",
lines=4
)
compare_btn = gr.Button("Сравнить модели", variant="primary", size="lg")
compare_status = gr.Textbox(label="Статус сравнения", interactive=False)
model_keys = list(MODELS.keys())
with gr.Row():
with gr.Column():
gr.Markdown(f"#### {model_keys[0]}")
highlight_1 = gr.HighlightedText(
label="Результат модели 1",
color_map=COLOR_MAP
)
table_1 = gr.Dataframe(label="Сущности (модель 1)")
latency_1 = gr.Textbox(label="Время", interactive=False)
with gr.Column():
gr.Markdown(f"#### {model_keys[1]}")
highlight_2 = gr.HighlightedText(
label="Результат модели 2",
color_map=COLOR_MAP
)
table_2 = gr.Dataframe(label="Сущности (модель 2)")
latency_2 = gr.Textbox(label="Время", interactive=False)
compare_btn.click(
fn=compare_models,
inputs=[compare_input],
outputs=[compare_status, highlight_1, table_1, latency_1, highlight_2, table_2, latency_2]
)
# ==================== ВКЛАДКА 3: ПАКЕТНАЯ ОБРАБОТКА ====================
with gr.Tab("Пакетная обработка"):
gr.Markdown(f"""
### Массовая обработка текстов из файла
Загрузите файл **CSV** (с колонкой `text`) или **TXT** (каждая строка — отдельный текст).
**Ограничения:** максимум {MAX_BATCH_ROWS} строк, {MAX_CHARS} символов на текст.
""")
with gr.Row():
batch_model = gr.Dropdown(
choices=list(MODELS.keys()),
value=list(MODELS.keys())[0],
label="Модель для обработки"
)
batch_file = gr.File(
label="Загрузите CSV или TXT файл",
file_types=[".csv", ".txt"]
)
batch_btn = gr.Button("Обработать файл", variant="primary", size="lg")
batch_status = gr.Textbox(label="Статус обработки", interactive=False)
batch_results = gr.Dataframe(
label="Результаты обработки",
wrap=True
)
batch_download = gr.Textbox(
label="CSV для скачивания (скопируйте содержимое)",
lines=5,
visible=True
)
batch_btn.click(
fn=process_batch,
inputs=[batch_file, batch_model],
outputs=[batch_status, batch_results, batch_download]
)
# ==================== ВКЛАДКА 4: ИСТОРИЯ ====================
with gr.Tab("История запросов"):
gr.Markdown(f"""
### История последних {HISTORY_SIZE} запросов
Здесь отображаются ваши недавние запросы с результатами.
""")
refresh_btn = gr.Button("Обновить историю", variant="secondary")
clear_btn = gr.Button("Очистить историю", variant="stop")
history_status = gr.Textbox(label="Статус", interactive=False, visible=False)
history_table = gr.Dataframe(
label="История запросов",
headers=["Время", "Модель", "Текст", "Найдено", "Типы", "Latency"],
wrap=True
)
refresh_btn.click(
fn=get_history_df,
outputs=[history_table]
)
clear_btn.click(
fn=clear_history,
outputs=[history_table, history_status]
)
# Футер
gr.Markdown("""
---
**Модели:**
- [Babelscape/wikineural-multilingual-ner](https://huggingface.co/Babelscape/wikineural-multilingual-ner) — мультиязычная модель NER
- [Gherman/bert-base-NER-Russian](https://huggingface.co/Gherman/bert-base-NER-Russian) — BERT для русского NER
**Ограничения:** CPU-режим, максимум 2000 символов на текст.
**Внимание:** Не вводите реальные персональные данные в демонстрационных целях.
""")
return demo