feat(mcp): FU-14 GAP-48 פרוסה 2 — envelope אחיד ל-11 משפחות-כלים
המשך מיגרציית 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>
This commit is contained in:
@@ -7,11 +7,10 @@ free-text citations the chair attaches during the compose phase.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
from legal_mcp.services import db
|
||||
from legal_mcp.tools.envelope import empty, err, ok # GAP-48: SSoT envelope
|
||||
|
||||
|
||||
async def precedent_attach(
|
||||
@@ -34,14 +33,14 @@ async def precedent_attach(
|
||||
"""
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return json.dumps({"error": f"תיק {case_number} לא נמצא."}, ensure_ascii=False)
|
||||
return err(f"תיק {case_number} לא נמצא.")
|
||||
|
||||
pdf_uuid: UUID | None = None
|
||||
if pdf_document_id:
|
||||
try:
|
||||
pdf_uuid = UUID(pdf_document_id)
|
||||
except ValueError:
|
||||
return json.dumps({"error": "pdf_document_id לא תקין"}, ensure_ascii=False)
|
||||
return err("pdf_document_id לא תקין")
|
||||
|
||||
# INV-TOOL3 / GAP-52: idempotent on (case_id, section_id, citation, quote).
|
||||
# Re-attaching the same quote to the same section returns the existing row.
|
||||
@@ -49,7 +48,7 @@ async def precedent_attach(
|
||||
if (_p.get("citation") == citation and _p.get("quote") == quote
|
||||
and (_p.get("section_id") or None) == (section_id or None)):
|
||||
_p["idempotent_existing"] = True
|
||||
return json.dumps(_p, ensure_ascii=False, indent=2, default=str)
|
||||
return ok(_p)
|
||||
|
||||
row = await db.create_case_precedent(
|
||||
case_id=UUID(case["id"]),
|
||||
@@ -60,17 +59,17 @@ async def precedent_attach(
|
||||
pdf_document_id=pdf_uuid,
|
||||
practice_area=case.get("practice_area"),
|
||||
)
|
||||
return json.dumps(row, ensure_ascii=False, indent=2, default=str)
|
||||
return ok(row)
|
||||
|
||||
|
||||
async def precedent_list(case_number: str) -> str:
|
||||
"""רשימת כל הפסיקות שצורפו לתיק, ממוינות לפי סעיף ואז לפי זמן יצירה."""
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return json.dumps({"error": f"תיק {case_number} לא נמצא."}, ensure_ascii=False)
|
||||
return err(f"תיק {case_number} לא נמצא.")
|
||||
|
||||
rows = await db.list_case_precedents(UUID(case["id"]))
|
||||
return json.dumps(rows, ensure_ascii=False, indent=2, default=str)
|
||||
return ok(rows)
|
||||
|
||||
|
||||
async def precedent_remove(precedent_id: str) -> str:
|
||||
@@ -78,12 +77,10 @@ async def precedent_remove(precedent_id: str) -> str:
|
||||
try:
|
||||
pid = UUID(precedent_id)
|
||||
except ValueError:
|
||||
return json.dumps({"error": "precedent_id לא תקין"}, ensure_ascii=False)
|
||||
return err("precedent_id לא תקין")
|
||||
|
||||
ok = await db.delete_case_precedent(pid)
|
||||
return json.dumps(
|
||||
{"deleted": ok, "precedent_id": precedent_id}, ensure_ascii=False,
|
||||
)
|
||||
deleted = await db.delete_case_precedent(pid)
|
||||
return ok({"deleted": deleted, "precedent_id": precedent_id})
|
||||
|
||||
|
||||
async def precedent_search_library(
|
||||
@@ -97,7 +94,7 @@ async def precedent_search_library(
|
||||
limit: מספר תוצאות מקסימלי
|
||||
"""
|
||||
if not query or len(query.strip()) < 2:
|
||||
return json.dumps([], ensure_ascii=False)
|
||||
return empty("שאילתה קצרה מדי (פחות מ-2 תווים).")
|
||||
|
||||
rows = await db.search_precedent_library(query.strip(), practice_area, limit)
|
||||
return json.dumps(rows, ensure_ascii=False, indent=2, default=str)
|
||||
return ok(rows)
|
||||
|
||||
Reference in New Issue
Block a user