Spaces:
Running
Running
| """LMAF (Legal Multi-Agent Framework) Gradio chat app. | |
| Chat interface for multi-agent legal consultation pipeline. | |
| Runs on prod (agents.legal.org.ua) and HuggingFace Space. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).parent / "src")) | |
| import gradio as gr | |
| EXAMPLES = [ | |
| "Покупець не оплатив товар на 150 000 грн протягом 6 місяців. Як стягнути пеню, 3% річних та інфляційні?", | |
| "Працівника звільнено під час воєнного стану без попередження. Чи є підстави для поновлення?", | |
| "Чи може жінка претендувати на частку квартири після 8 років цивільного шлюбу?", | |
| "Забудовник затримує введення будинку в експлуатацію на 2 роки. Які компенсації можна вимагати?", | |
| ] | |
| def has_api_keys() -> bool: | |
| return bool( | |
| os.environ.get("AWS_REGION") | |
| or os.environ.get("ANTHROPIC_API_KEY") | |
| or os.environ.get("OPENAI_API_KEY") | |
| ) | |
| async def _run_pipeline(question: str): | |
| """Run a fast 3-agent pipeline for the web demo. | |
| Full research loop (15 iterations) is available via the CLI. | |
| The web demo runs: Surveyor -> Planner -> Formatter (~60-90s). | |
| """ | |
| from lmaf.core.config import Config | |
| from lmaf.engine import LMAF | |
| config = Config.from_env() | |
| lmaf = LMAF(question, config) | |
| yield "**Surveyor**: аналізую правовий ландшафт..." | |
| await lmaf.surveyor.run(lmaf.state) | |
| if lmaf.state.survey_summary: | |
| yield f"**Surveyor**: {lmaf.state.survey_summary[:400]}" | |
| yield "**Planner**: розробляю стратегію дослідження..." | |
| await lmaf.planner.run(lmaf.state) | |
| if lmaf.state.strategy.approach: | |
| yield f"**Planner**: {lmaf.state.strategy.approach[:400]}" | |
| yield "**Formatter**: оформлюю консультацію..." | |
| await lmaf.formatter.run(lmaf.state) | |
| yield lmaf.state.answer or "Не вдалося сформувати відповідь." | |
| async def stream_chat(message: str, history: list[dict]): | |
| """Async streaming chat handler -- yields incremental updates.""" | |
| if not message.strip(): | |
| yield "Будь ласка, опишіть вашу правову ситуацію." | |
| return | |
| if not has_api_keys(): | |
| yield ( | |
| "API ключі не налаштовано. Цей інстанс працює в демо-режимі.\n\n" | |
| "Для реальних консультацій використовуйте " | |
| "[agents.legal.org.ua](https://agents.legal.org.ua)." | |
| ) | |
| return | |
| accumulated = "" | |
| async for update in _run_pipeline(message): | |
| if update.startswith("**"): | |
| accumulated += f"\n\n{update}" | |
| else: | |
| accumulated = update | |
| yield accumulated | |
| _arch_raw = (Path(__file__).parent / "architecture.html").read_text(encoding="utf-8") | |
| import html as _html_mod | |
| _arch_escaped = _html_mod.escape(_arch_raw, quote=True) | |
| ARCHITECTURE_HTML = ( | |
| f'<iframe srcdoc="{_arch_escaped}" ' | |
| 'style="width:100%;height:700px;border:none;border-radius:12px;" ' | |
| 'sandbox="allow-scripts allow-same-origin"></iframe>' | |
| ) | |
| DATASETS_MD = """ | |
| ## Пов'язані датасети на HuggingFace | |
| | Датасет | Розмір | Опис | | |
| |---------|--------|------| | |
| | [ua-case-outcome-6m](https://huggingface.co/datasets/overthelex/ua-case-outcome-6m) | 6.7M | Повний датасет рішень суду з темпоральними спліттами | | |
| | [ukrainian-court-decisions](https://huggingface.co/datasets/overthelex/ukrainian-court-decisions) | 428K | Збалансований бенчмарк для LEXTREME | | |
| | [ua-court-citation-graph](https://huggingface.co/datasets/overthelex/ua-court-citation-graph) | 2.3M | Граф ко-цитування з 99.5М рішень | | |
| | [ua-statute-retrieval](https://huggingface.co/datasets/overthelex/ua-statute-retrieval) | 396M citations | Бенчмарк пошуку законодавства | | |
| | [ua-temporal-drift](https://huggingface.co/datasets/overthelex/ua-temporal-drift) | 428K | Дані про темпоральний дрифт | | |
| ## Пов'язані статті | |
| - [Temporal Decay of Co-Citation Predictability](https://arxiv.org/abs/2605.17639) (arXiv, 2025) | |
| - [A Citation Graph from 100M Court Decisions](https://arxiv.org/abs/2605.15362) (arXiv, 2025) | |
| - [Tokenizer Fertility on Ukrainian Legal Text](https://arxiv.org/abs/2605.14890) (arXiv, 2025) | |
| """ | |
| def build_app() -> gr.Blocks: | |
| with gr.Blocks(title="LMAF") as app: | |
| gr.Markdown( | |
| "# LMAF -- Legal Multi-Agent Framework\n" | |
| "### Мульти-агентна система для складних правових консультацій\n\n" | |
| "Дев'ять спеціалізованих LLM-агентів аналізують правові питання, " | |
| "шукають судову практику у 100М+ рішень ЄДРСР та формують структуровану консультацію.\n\n" | |
| "*[GitHub](https://github.com/overthelex/secondlayer-agents) | " | |
| "[SecondLayer](https://legal.org.ua) | " | |
| "[Датасети](https://huggingface.co/overthelex)*" | |
| ) | |
| with gr.Tabs(): | |
| with gr.Tab("Чат"): | |
| chatbot = gr.ChatInterface( | |
| fn=stream_chat, | |
| examples=EXAMPLES, | |
| title=None, | |
| chatbot=gr.Chatbot( | |
| height=600, | |
| placeholder="Опишіть правову ситуацію -- 9 агентів проаналізують та підготують консультацію", | |
| ), | |
| textbox=gr.Textbox( | |
| placeholder="Опишіть вашу правову ситуацію...", | |
| scale=7, | |
| ), | |
| ) | |
| with gr.Tab("Архітектура"): | |
| gr.HTML(ARCHITECTURE_HTML) | |
| with gr.Tab("Датасети"): | |
| gr.Markdown(DATASETS_MD) | |
| return app | |
| if __name__ == "__main__": | |
| demo = build_app() | |
| demo.launch() | |