"""MCP tools for the Digests radar (X12). A digest ("כל יום" daily one-pager, Ofer Toister) is a SECONDARY, discovery- layer source that POINTS at a ruling. It is distinct from the three citation corpora: - ``search_precedent_library`` — authoritative external court rulings. - ``search_internal_decisions`` — appeals-committee decisions. - ``search_decisions`` — Dafna's prior decisions (style corpus). A digest is NEVER cited in a decision (INV-DIG1) and NEVER enters the halacha pipeline (INV-DIG2). ``search_digests`` is a research compass: it surfaces the relevant digest + the UNDERLYING ruling's citation, which is then ingested into the precedent library and cited from there. """ from __future__ import annotations import time from uuid import UUID from legal_mcp.services import db, digest_library, telemetry from legal_mcp.tools.envelope import empty, err as _err, ok as _ok async def digest_upload( file_path: str, yomon_number: str = "", digest_date: str = "", practice_area: str = "", appeal_subtype: str = "", subject_tags: list[str] | None = None, ) -> str: """העלאת יומון ("כל יום") לקורפוס-הגילוי + חילוץ מטא-דאטה אוטומטי. היומון הוא מקור-משני המצביע על פסק הדין המקורי — אינו מצוטט בהחלטה. Args: file_path: נתיב מלא לקובץ PDF/DOCX של היומון. yomon_number: מספר היומון (אופציונלי — יחולץ מהטקסט אם ריק). digest_date: ISO date של גיליון היומון (אופציונלי). practice_area: rishuy_uvniya / betterment_levy / compensation_197. subject_tags: תגיות נושא (אופציונלי — יחולצו אם ריק). Returns: JSON עם digest_id, מספר היומון, מראה-המקום, וקישור-אוטומטי אם נמצא. """ try: result = await digest_library.ingest_digest( file_path=file_path, yomon_number=yomon_number, digest_date=digest_date or None, practice_area=practice_area, appeal_subtype=appeal_subtype, subject_tags=subject_tags or None, ) except Exception as e: return _err(str(e)) return _ok(result) async def digest_list( practice_area: str = "", concept_tag: str = "", linked: bool | None = None, search: str = "", limit: int = 100, ) -> str: """רשימת יומונים בקורפוס-הגילוי, עם פילטרים. linked=false → יומונים שהפסק המקורי שלהם עוד לא נקלט לספריית הפסיקה (פער-ידע גלוי).""" rows = await digest_library.list_digests( practice_area=practice_area, concept_tag=concept_tag, linked=linked, search=search, limit=limit, ) return _ok(rows) async def digest_get(digest_id: str) -> str: """יומון ספציפי לפי מזהה.""" try: cid = UUID(digest_id) except ValueError: return _err("digest_id לא תקין") record = await digest_library.get_digest(cid) if not record: return _err("יומון לא נמצא") return _ok(record) async def digest_link(digest_id: str, case_law_id: str) -> str: """קישור ידני של יומון לפסק הדין המקורי בספריית הפסיקה (INV-DIG3).""" try: UUID(digest_id) UUID(case_law_id) except ValueError: return _err("מזהה לא תקין") try: result = await digest_library.link_digest(digest_id, case_law_id) except Exception as e: return _err(str(e)) return _ok(result) async def digest_relink(digest_id: str) -> str: """ניסיון-קישור מחדש: בודק אם פסק הדין המקורי של היומון כבר בספרייה ומקשר.""" try: UUID(digest_id) except ValueError: return _err("digest_id לא תקין") try: result = await digest_library.relink_digest(digest_id) except Exception as e: return _err(str(e)) return _ok(result) async def digest_delete(digest_id: str) -> str: """מחיקת יומון מקורפוס-הגילוי.""" try: cid = UUID(digest_id) except ValueError: return _err("digest_id לא תקין") ok_ = await digest_library.delete_digest(cid) if not ok_: return _err("יומון לא נמצא") return _ok({"deleted": True, "digest_id": digest_id}) async def search_digests( query: str, practice_area: str = "", subject_tag: str = "", concept_tag: str = "", limit: int = 10, ) -> str: """חיפוש סמנטי בקורפוס-הגילוי (יומוני "כל יום"). מצפן-מחקר בלבד — מחזיר את היומון הרלוונטי + מראה-המקום של הפסק המקורי (radar). היומון אינו מצוטט בהחלטה (INV-DIG1); הצטט מהפסק המקורי דרך search_precedent_library.""" if not query or len(query.strip()) < 2: return empty("שאילתה קצרה מדי (פחות מ-2 תווים).") q = query.strip() t0 = time.perf_counter() results = await digest_library.search_digests( query=q, practice_area=practice_area, subject_tag=subject_tag, concept_tag=concept_tag, limit=limit, ) elapsed_ms = int((time.perf_counter() - t0) * 1000) telemetry.log_search_bg( search_type="digests", query=q, results=results, duration_ms=elapsed_ms, practice_area=practice_area or None, user_agent="unknown", ) if not results: return empty("לא נמצאו יומונים תואמים.") return _ok(results) async def digest_process_pending(limit: int = 20) -> str: """ריקון תור היומונים שהועלו מה-UI וממתינים לעיבוד-LLM מקומי. מריץ חילוץ- מטא-דאטה + embedding + autolink על כל יומון בסטטוס 'pending', מקומית עם ה-CLI (claude_session local-only). מנקה לסטטוס 'completed'.""" try: result = await digest_library.process_pending_digests(limit=limit) except Exception as e: return _err(str(e)) return _ok(result)