המשך מיגרציית INV-TOOL1 מעבר למשפחת-החיפוש (#71). הומרו ל-{status,data,message}: precedent_library, citations, internal_decisions, missing_precedents, training_enrichment, precedents, legal_arguments, cases, documents, workflow (~55 כלים). בוטלו 5 עותקי _ok/_err משוכפלים (alias ל-tools/envelope.py — SSoT, G2). עיקרון: envelope-status = הצלחת-הקריאה-לכלי; תוצאה-עסקית (idempotent_existing, noop, completed...) נשמרת בתוך data. err רק לכשל אמיתי (not-found/invalid/exception). תאימות-API: צרכני web/app.py של cases/workflow/precedents חוּוטו דרך envelope_unwrap + בדיקת status=="error"→4xx — תשובת ה-HTTP זהה, web-ui לא מושפע. (documents/legal_arguments/citations/... אינם נצרכים מ-app.py — agent-only.) בדיקות: 182/182 עוברים (test_corpus_constraints עודכן לחוזה החדש). נותר: משפחת drafting (מסלול הפקת-ההחלטה) בפרוסה נפרדת עם שער טסט-ייצוא. Invariants: מקדם INV-TOOL1 + G2 (SSoT, ביטול כפילות). מתועד ב-X9 + gap-audit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
108 lines
4.5 KiB
Python
108 lines
4.5 KiB
Python
"""MCP tools for the Internal Decisions corpus.
|
||
|
||
Decisions of appeals committees (ועדות ערר) live in the same physical
|
||
``case_law`` table as court rulings but are distinguished by
|
||
``source_kind='internal_committee'`` and must carry ``chair_name`` +
|
||
``district``.
|
||
|
||
The existing ``precedent_library_upload`` MCP tool always stores
|
||
``source_kind='external_upload'`` and does not accept chair/district —
|
||
which is why **44+ existing appeals-committee decisions were tagged
|
||
wrong**. This wrapper is the authoritative ingestion path for committee
|
||
decisions and enforces the required metadata at the tool boundary.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from legal_mcp.services import internal_decisions as int_svc
|
||
from legal_mcp.tools.envelope import err as _err, ok as _ok # GAP-48: SSoT envelope
|
||
|
||
# Valid Hebrew district names (matches _COURT_TO_DISTRICT in service)
|
||
VALID_DISTRICTS = {"ירושלים", "מרכז", "תל אביב", "תל-אביב", "צפון", "דרום", "חיפה", "ארצי"}
|
||
|
||
# proceeding_type — ערר vs בל"מ. The service can derive it from
|
||
# appeal_subtype/subject if left empty, so this stays optional at the API.
|
||
VALID_PROCEEDING_TYPES = {"ערר", 'בל"מ'}
|
||
|
||
|
||
async def internal_decision_upload(
|
||
file_path: str,
|
||
case_number: str,
|
||
chair_name: str,
|
||
district: str,
|
||
case_name: str = "",
|
||
court: str = "",
|
||
decision_date: str = "",
|
||
practice_area: str = "",
|
||
appeal_subtype: str = "",
|
||
subject_tags: list[str] | None = None,
|
||
summary: str = "",
|
||
is_binding: bool = False,
|
||
proceeding_type: str = "",
|
||
) -> str:
|
||
"""העלאת החלטה של ועדת ערר (internal_committee) לקורפוס הסמכותי.
|
||
|
||
Required: file_path, case_number, chair_name, district.
|
||
The tool enforces chair_name+district so the record cannot be saved
|
||
in the broken legacy mode (external_upload with empty chair/district).
|
||
|
||
Args:
|
||
file_path: נתיב מלא לקובץ PDF/DOCX/RTF/TXT/MD.
|
||
case_number: מספר הערר ("ערר (ועדות ערר - תכנון ובנייה ירושלים) 1110/20 ...").
|
||
chair_name: שם יו"ר הוועדה (חובה).
|
||
district: מחוז (ירושלים/מרכז/תל אביב/צפון/דרום/חיפה/ארצי) — חובה.
|
||
case_name: שם קצר.
|
||
court: ערכאה ("ועדת הערר לתכנון ובנייה — מחוז ירושלים").
|
||
decision_date: ISO date (YYYY-MM-DD), אופציונלי.
|
||
practice_area: rishuy_uvniya / betterment_levy / compensation_197.
|
||
appeal_subtype: building_permit / וכו'.
|
||
subject_tags: תגיות נושא.
|
||
is_binding: בד"כ False (ועדת ערר לא מחייבת ועדה אחרת — שכנוע אופקי).
|
||
proceeding_type: 'ערר' או 'בל"מ'. אם ריק — נגזר מ-appeal_subtype/case_name.
|
||
|
||
Returns: JSON עם case_law_id, מספר chunks, halachot_pending.
|
||
"""
|
||
if not file_path.strip():
|
||
return _err("file_path חובה")
|
||
if not case_number.strip():
|
||
return _err("case_number חובה")
|
||
if not chair_name.strip():
|
||
return _err(
|
||
"chair_name חובה. החלטות ועדת ערר חייבות שם יו\"ר — "
|
||
"בלעדיו ההחלטה לא ניתנת לחיפוש סלקטיבי לפי הרכב."
|
||
)
|
||
if not district.strip():
|
||
return _err(
|
||
"district חובה. ערכים תקפים: " + ", ".join(sorted(VALID_DISTRICTS))
|
||
)
|
||
if district.strip() not in VALID_DISTRICTS:
|
||
return _err(
|
||
f"district לא תקין: {district!r}. ערכים תקפים: "
|
||
+ ", ".join(sorted(VALID_DISTRICTS))
|
||
)
|
||
if proceeding_type.strip() and proceeding_type.strip() not in VALID_PROCEEDING_TYPES:
|
||
return _err(
|
||
f"proceeding_type לא תקין: {proceeding_type!r}. ערכים תקפים: "
|
||
+ ", ".join(sorted(VALID_PROCEEDING_TYPES))
|
||
)
|
||
|
||
try:
|
||
result = await int_svc.ingest_internal_decision(
|
||
case_number=case_number,
|
||
case_name=case_name,
|
||
court=court,
|
||
decision_date=decision_date or None,
|
||
chair_name=chair_name,
|
||
district=district,
|
||
practice_area=practice_area,
|
||
appeal_subtype=appeal_subtype,
|
||
subject_tags=subject_tags or [],
|
||
summary=summary,
|
||
is_binding=is_binding,
|
||
file_path=file_path,
|
||
proceeding_type=proceeding_type,
|
||
)
|
||
except Exception as e:
|
||
return _err(str(e))
|
||
return _ok(result)
|