Spaces:
Running
Running
File size: 6,554 Bytes
3bd9d04 a992ecb be2046c 40900ed a992ecb be2046c 129c901 a992ecb be2046c a992ecb be2046c e56db6a be2046c 73a84cb 3bd9d04 be2046c 40900ed be2046c 73a84cb 40900ed 73a84cb be2046c 73a84cb 40900ed 73a84cb be2046c 73a84cb 40900ed be2046c 40900ed be2046c 40900ed be2046c 40900ed 73a84cb be2046c 40900ed be2046c 2114c64 a992ecb be2046c a992ecb be2046c 3bd9d04 be2046c 3bd9d04 be2046c c87c3c8 be2046c a992ecb 4d27736 a992ecb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | """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()
|