All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
Same case_number can exist as both a regular appeal (ערר) and an extension-of-time request (בל"מ), and we were inferring the difference from appeal_subtype prefixes — fragile, and case-number lookups weren't disambiguated. Now stored as a first-class field on both case_law (corpus) and cases (live cases), with partial unique indexes on (case_number, proceeding_type). - SCHEMA_V15: column + CHECK constraints + backfill from appeal_subtype LIKE 'extension_request_%' + partial unique indexes replace the old global UNIQUE(case_number). - derive_proceeding_type() centralizes the inference rule (extension_request_* → בל"מ; subject regex fallback; default ערר). - Metadata extractor prompt asks Claude to populate the new field explicitly; apply_to_record writes it for internal_committee rows. - internal_decision_upload, case_create, case_update accept an optional proceeding_type; FastAPI request models expose it. - Wizard + edit dialog get a sided Select; case header renders the resolved label (ערר / בל"מ). - Uploaded the 2 staged בל"מ decisions on betterment levy: 8126/24 (סופר נוח, 13 chunks), 8047/23 (הרנון, 48 chunks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
117 lines
4.6 KiB
Python
117 lines
4.6 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
|
||
|
||
import json
|
||
|
||
from legal_mcp.services import internal_decisions as int_svc
|
||
|
||
# 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 = {"ערר", 'בל"מ'}
|
||
|
||
|
||
def _ok(payload) -> str:
|
||
return json.dumps(payload, ensure_ascii=False, indent=2, default=str)
|
||
|
||
|
||
def _err(msg: str) -> str:
|
||
return json.dumps({"error": msg}, ensure_ascii=False)
|
||
|
||
|
||
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)
|