Spaces:
Running
Running
Download app.py from nvikou/polyglot-rag: direct link, hf CLI and curl.
- Browser
- Download file 7.48 kB
-
https://huggingface.co/spaces/nvikou/polyglot-rag/resolve/main/app.py
- Command line
-
hf download hf://spaces/nvikou/polyglot-rag/app.py
-
curl -L -o app.py https://huggingface.co/spaces/nvikou/polyglot-rag/resolve/main/app.py
7.48 kB
| """ | |
| PolyglotRAG — assistant documentaire multilingue cross-lingue. | |
| Point d'entrée de l'application Gradio (Hugging Face Spaces, SDK docker). | |
| Onglets : | |
| - Assistant : conversation multilingue avec citations et analyse. | |
| - Architecture : schéma du pipeline complet. | |
| - Administration: métriques du RAG (volume, latence, langues, abstention). | |
| L'interface elle-même est traduite dans les 6 langues du projet | |
| (sélecteur en haut à droite ; choix mémorisé dans le navigateur, détection | |
| automatique de la langue du navigateur, paramètre d'URL `?lang=xx`). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| from pathlib import Path | |
| import gradio as gr | |
| from config import settings | |
| from core.retrieval.vector_store import get_vector_store | |
| from core.ui.admin_tab import admin_i18n_updates, build_admin_tab | |
| from core.ui.architecture_tab import architecture_i18n_updates, build_architecture_tab | |
| from core.ui.chat_tab import build_chat_tab, chat_i18n_updates | |
| from core.ui.components import ( | |
| render_brand, | |
| render_config_banner, | |
| render_footer, | |
| render_status, | |
| ) | |
| from core.ui.i18n import UI_LANGUAGES, normalize_lang, t, ui_language_choices | |
| from core.ui.theme import CUSTOM_CSS, HEAD_HTML, build_theme | |
| ASSETS_DIR = Path(__file__).resolve().parent / "core" / "assets" | |
| AVATAR_PATH = ASSETS_DIR / "assistant.svg" | |
| DEFAULT_LANG = normalize_lang(settings.default_ui_language) | |
| _TAB_KEYS = ("tab_chat", "tab_arch", "tab_admin") | |
| _TAB_LABELS_JSON = json.dumps( | |
| {code: [t(code, k) for k in _TAB_KEYS] for code in UI_LANGUAGES}, ensure_ascii=False | |
| ) | |
| _SUPPORTED_JSON = json.dumps(list(UI_LANGUAGES)) | |
| # Applique côté navigateur : sens d'écriture (RTL pour l'arabe), attribut | |
| # `lang` du document, libellés des onglets et mémorisation du choix. | |
| APPLY_LANG_JS = f""" | |
| (lang) => {{ | |
| const labels = {_TAB_LABELS_JSON}; | |
| const root = document.querySelector('.gradio-container'); | |
| if (root) root.setAttribute('dir', lang === 'ar' ? 'rtl' : 'ltr'); | |
| document.documentElement.setAttribute('lang', lang); | |
| try {{ localStorage.setItem('pg-ui-lang', lang); }} catch (e) {{}} | |
| const tabs = document.querySelectorAll('#pg-tabs button[role="tab"]'); | |
| if (labels[lang] && tabs.length === labels[lang].length) {{ | |
| tabs.forEach((b, i) => {{ b.textContent = labels[lang][i]; }}); | |
| }} | |
| return lang; | |
| }} | |
| """ | |
| # Au chargement : ?lang=xx > choix mémorisé > langue du navigateur > défaut. | |
| INIT_LANG_JS = f""" | |
| () => {{ | |
| const supported = {_SUPPORTED_JSON}; | |
| let lang = new URLSearchParams(window.location.search).get('lang'); | |
| if (!lang) {{ try {{ lang = localStorage.getItem('pg-ui-lang'); }} catch (e) {{}} }} | |
| if (!lang) lang = (navigator.language || '').slice(0, 2).toLowerCase(); | |
| if (!supported.includes(lang)) lang = '{DEFAULT_LANG}'; | |
| const root = document.querySelector('.gradio-container'); | |
| if (root) root.setAttribute('dir', lang === 'ar' ? 'rtl' : 'ltr'); | |
| document.documentElement.setAttribute('lang', lang); | |
| return lang; | |
| }} | |
| """ | |
| def _config_ok() -> bool: | |
| return not settings.validate() | |
| def status_html(lang: str) -> str: | |
| try: | |
| corpus = get_vector_store().corpus_stats() | |
| except Exception: # noqa: BLE001 - l'en-tête ne doit jamais casser l'app | |
| corpus = {"total_chunks": 0, "documents": 0} | |
| return render_status(normalize_lang(lang), corpus, _config_ok()) | |
| def build_app() -> gr.Blocks: | |
| lang = DEFAULT_LANG | |
| config_ok = _config_ok() | |
| with gr.Blocks( | |
| title="PolyglotRAG — Multilingual Document Assistant", | |
| theme=build_theme(), | |
| css=CUSTOM_CSS, | |
| head=HEAD_HTML, | |
| analytics_enabled=False, | |
| ) as demo: | |
| # ------------------------------ En-tête ------------------------------ | |
| with gr.Row(elem_id="pg-header", equal_height=True): | |
| with gr.Column(scale=1, min_width=320): | |
| brand = gr.HTML(render_brand(lang), elem_classes=["pg-html-flush"]) | |
| with gr.Column(scale=0, min_width=240, elem_id="pg-header-right"): | |
| ui_lang_dd = gr.Dropdown( | |
| choices=ui_language_choices(), | |
| value=lang, | |
| label=t(lang, "ui_language"), | |
| show_label=False, | |
| container=False, | |
| interactive=True, | |
| elem_id="pg-ui-lang", | |
| ) | |
| status = gr.HTML( | |
| render_status(lang, {"total_chunks": 0}, config_ok) if not config_ok else "", | |
| elem_classes=["pg-html-flush"], | |
| ) | |
| banner = gr.HTML(render_config_banner(lang, config_ok), elem_classes=["pg-html-flush"]) | |
| # ------------------------------ Onglets ------------------------------ | |
| with gr.Tabs(elem_id="pg-tabs"): | |
| with gr.Tab(t(lang, "tab_chat"), id="chat") as tab_chat: | |
| chat = build_chat_tab( | |
| lang, ui_lang_dd, str(AVATAR_PATH) if AVATAR_PATH.exists() else None | |
| ) | |
| with gr.Tab(t(lang, "tab_arch"), id="architecture") as tab_arch: | |
| arch = build_architecture_tab(lang) | |
| with gr.Tab(t(lang, "tab_admin"), id="admin") as tab_admin: | |
| admin = build_admin_tab(lang, ui_lang_dd) | |
| footer = gr.HTML(render_footer(lang), elem_classes=["pg-html-flush"]) | |
| # ------------------------ Changement de langue ------------------------ | |
| def apply_language(new_lang, last, admin_state, response_value, scope_value): | |
| new_lang = normalize_lang(new_lang) | |
| return [ | |
| render_brand(new_lang), | |
| status_html(new_lang), | |
| render_config_banner(new_lang, _config_ok()), | |
| gr.update(label=t(new_lang, "tab_chat")), | |
| gr.update(label=t(new_lang, "tab_arch")), | |
| gr.update(label=t(new_lang, "tab_admin")), | |
| render_footer(new_lang), | |
| *chat_i18n_updates(new_lang, last, response_value, scope_value), | |
| *architecture_i18n_updates(new_lang), | |
| *admin_i18n_updates(new_lang, admin_state), | |
| ] | |
| i18n_outputs = [ | |
| brand, | |
| status, | |
| banner, | |
| tab_chat, | |
| tab_arch, | |
| tab_admin, | |
| footer, | |
| *chat["i18n_outputs"], | |
| *arch["i18n_outputs"], | |
| *admin["i18n_outputs"], | |
| ] | |
| ui_lang_dd.change(None, inputs=[ui_lang_dd], outputs=None, js=APPLY_LANG_JS) | |
| ui_lang_dd.change( | |
| apply_language, | |
| inputs=[ui_lang_dd, chat["last_state"], admin["state"], chat["response_lang"], chat["scope"]], | |
| outputs=i18n_outputs, | |
| queue=False, | |
| ) | |
| # ------------------------------ Chargement ---------------------------- | |
| demo.load(None, inputs=None, outputs=[ui_lang_dd], js=INIT_LANG_JS) | |
| demo.load(status_html, inputs=[ui_lang_dd], outputs=[status]) | |
| return demo | |
| demo = build_app() | |
| if __name__ == "__main__": | |
| # 0.0.0.0 + $PORT (par défaut 7860) : fonctionne à l'identique en local, | |
| # dans le conteneur Docker fourni, et sur un Hugging Face Space. | |
| demo.queue(default_concurrency_limit=8).launch( | |
| server_name="0.0.0.0", | |
| server_port=int(os.environ.get("PORT", 7860)), | |
| show_api=False, | |
| allowed_paths=[str(ASSETS_DIR)], | |
| favicon_path=str(ASSETS_DIR / "favicon.svg") if (ASSETS_DIR / "favicon.svg").exists() else None, | |
| ) | |