feat(X13): auto-fetch court verdicts from נט המשפט → corpus (Tier 0 + scaffold)
תת-מערכת אחזור-פסיקה אוטומטי: כשיומון מצביע על פס"ד בית-משפט, מסווגים את הערכאה, מורידים מהמקור הציבורי המתאים, וקולטים דרך צינור-הקליטה הקנוני. - spec-first: docs/spec/X13-court-fetch.md (INV-CF1..CF7) + אינדקס - מסווג court_citation.py (supreme/admin/skip) + 10 בדיקות (עת"מ 46111-12-22 → admin) - Tier 0: court_fetch_supreme.py — supremedecisions API (reverse-engineered), httpx + browser-headers (אומת 200) + politeness - תור court_fetch_jobs (SCHEMA_V30) + DB helpers + court_fetch_orchestrator.py - Tier 1 scaffold: legal-court-fetch-service (aiohttp+Bearer, מראת legal-chat-service) + camofox_client (Camoufox open-source) + recaptcha_audio (Whisper מקומי) + pm2 - Tier 2 fallback חינני: manual + missing_precedent (INV-CF2/CF3 — אין drop שקט) - כלי-MCP court_verdict_fetch / court_fetch_status; SCRIPTS.md Invariants: מקיים G2 (מסלול-קליטה יחיד, INV-CF1) · G3/G1 (idempotent+נרמול, INV-CF5) · G4/§6 (אין בליעה שקטה, INV-CF2) · G10 (שער-אנושי, INV-CF3) · G5 (source_type, INV-CF6) · G9 (provenance+audit, INV-CF7). מקורות INV-CF4: RFC 9309 · Google crawler · OWASP OAT. Follow-ups (טרם אומתו חי): live Tier-0 validation · התקנת camofox-browser+whisper · כיול selectors Tier-1 · COURT_FETCH_SHARED_SECRET (Infisical+Coolify) · טריגר מ-digest try_autolink (worktree-digests-radar). V30 עלול להתנגש עם digests-radar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
56
mcp-server/src/legal_mcp/tools/court_fetch.py
Normal file
56
mcp-server/src/legal_mcp/tools/court_fetch.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""MCP tools for the X13 court-verdict auto-fetch subsystem.
|
||||
|
||||
- ``court_verdict_fetch`` — classify a citation, fetch the verdict from the
|
||||
matching public source (Supreme portal / נט המשפט), and ingest it into the
|
||||
precedent library via the canonical pipeline. The standalone entry point
|
||||
(also driven automatically from digest auto-link, see X12/X13).
|
||||
- ``court_fetch_status`` — inspect the fetch-job queue (pending/failed/manual).
|
||||
|
||||
Local-only: ``court_verdict_fetch`` runs the ingest pipeline, which drives
|
||||
halacha extraction via the local ``claude`` CLI — same constraint as
|
||||
``precedent_process_pending``. Invoking it from the container will fail.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from legal_mcp.services import court_fetch_orchestrator as orch
|
||||
from legal_mcp.services import db
|
||||
from legal_mcp.tools.envelope import err as _err, ok as _ok
|
||||
|
||||
|
||||
async def court_verdict_fetch(citation: str) -> str:
|
||||
"""אחזור אוטומטי של פסק-דין בית-משפט וקליטה לקורפוס.
|
||||
|
||||
מקבל ציטוט (למשל 'עת"מ 46111-12-22' או 'עע"מ 1234/22'), מסווג את הערכאה,
|
||||
מוריד את הפסק מהמקור הציבורי המתאים, וקולט אותו דרך צינור-הקליטה הקנוני.
|
||||
ערר/בל"מ (ועדת-ערר) אינם ניתנים לאחזור ציבורי ויסומנו כפער.
|
||||
"""
|
||||
if not (citation or "").strip():
|
||||
return _err("citation is required")
|
||||
try:
|
||||
result = await orch.fetch_and_ingest(citation.strip())
|
||||
except Exception as e: # noqa: BLE001 — surfaced, not swallowed (INV-CF2)
|
||||
return _err(f"אחזור נכשל: {e}")
|
||||
|
||||
status = result.get("status")
|
||||
if status in ("done", "already_done"):
|
||||
return _ok(result, message="הפסק נקלט לקורפוס")
|
||||
if status == "skipped":
|
||||
return _ok(result, message="ועדת-ערר — לא ניתן לאחזור ציבורי (סומן כפער)")
|
||||
if status in ("manual", "awaiting_manual"):
|
||||
return _ok(result, message="האחזור האוטונומי נכשל — הוסלם להורדה ידנית")
|
||||
if status == "unrecognized":
|
||||
return _err("הציטוט לא זוהה כמספר-תיק תקין")
|
||||
return _ok(result, message=f"סטטוס: {status}")
|
||||
|
||||
|
||||
async def court_fetch_status(case_number: str = "", status_filter: str = "") -> str:
|
||||
"""סטטוס תור-האחזור. case_number לפריט יחיד, או status_filter לסינון רשימה."""
|
||||
if case_number.strip():
|
||||
from legal_mcp.services.court_citation import normalize_case_number
|
||||
job = await db.court_fetch_job_get(normalize_case_number(case_number))
|
||||
if not job:
|
||||
return _ok({"job": None}, message="אין job עבור תיק זה")
|
||||
return _ok({"job": job})
|
||||
jobs = await db.court_fetch_job_list(status=status_filter.strip() or None)
|
||||
return _ok({"jobs": jobs, "count": len(jobs)})
|
||||
Reference in New Issue
Block a user