המשך מיגרציית 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>
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""MCP tools — aggregated legal arguments (claim de-duplication)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from uuid import UUID
|
|
|
|
from legal_mcp.services import argument_aggregator, db
|
|
from legal_mcp.tools.envelope import empty, err, ok # GAP-48: SSoT envelope
|
|
|
|
|
|
async def aggregate_claims_to_arguments(
|
|
case_number: str,
|
|
force: bool = False,
|
|
) -> str:
|
|
"""כינוס פרופוזיציות גולמיות לטיעונים משפטיים מובחנים.
|
|
|
|
Args:
|
|
case_number: מספר תיק הערר.
|
|
force: True = למחוק טיעונים קיימים ולחשב מחדש.
|
|
"""
|
|
case = await db.get_case_by_number(case_number)
|
|
if not case:
|
|
return err(f"תיק {case_number} לא נמצא.")
|
|
|
|
case_id = UUID(case["id"])
|
|
result = await argument_aggregator.aggregate_claims_to_arguments(
|
|
case_id, force=force,
|
|
)
|
|
result["case_number"] = case_number
|
|
return ok(result)
|
|
|
|
|
|
async def get_legal_arguments(
|
|
case_number: str,
|
|
party: str = "",
|
|
) -> str:
|
|
"""שליפת טיעונים משפטיים מאוגדים לתיק.
|
|
|
|
Args:
|
|
case_number: מספר תיק הערר.
|
|
party: סינון לפי צד (appellant/respondent/committee/permit_applicant).
|
|
ריק = כל הצדדים.
|
|
"""
|
|
case = await db.get_case_by_number(case_number)
|
|
if not case:
|
|
return err(f"תיק {case_number} לא נמצא.")
|
|
|
|
case_id = UUID(case["id"])
|
|
args = await argument_aggregator.get_legal_arguments(case_id, party=party)
|
|
|
|
if not args:
|
|
return empty(
|
|
"לא נמצאו טיעונים מאוגדים. הרץ aggregate_claims_to_arguments תחילה.",
|
|
data={"case_number": case_number, "arguments": []},
|
|
)
|
|
|
|
# Group by party for nicer display.
|
|
party_he = {
|
|
"appellant": "עוררים",
|
|
"respondent": "משיבים",
|
|
"committee": "ועדה מקומית",
|
|
"permit_applicant": "מבקשי היתר",
|
|
"unknown": "צד לא מזוהה",
|
|
}
|
|
by_party: dict[str, list[dict]] = {}
|
|
for a in args:
|
|
label = party_he.get(a["party"], a["party"])
|
|
by_party.setdefault(label, []).append(a)
|
|
|
|
return ok({
|
|
"case_number": case_number,
|
|
"total": len(args),
|
|
"by_party": by_party,
|
|
})
|