Add full decision writing pipeline: classify, extract, brainstorm, write, QA, export
New services (11 files): - classifier.py: auto doc-type classification + party identification (Claude Haiku) - claims_extractor.py: claim extraction from pleadings (Claude Sonnet + regex) - references_extractor.py: plan/case-law/legislation detection (regex) - brainstorm.py: direction generation with 2-3 options (Claude Sonnet) - block_writer.py: 12-block decision writer (template + Claude Sonnet/Opus) - docx_exporter.py: DOCX export with David font, RTL, headings - qa_validator.py: 6 QA checks with export blocking on critical failure - learning_loop.py: draft vs final comparison + lesson extraction - metrics.py: KPIs dashboard per case and global - audit.py: action audit log - cli.py: standalone CLI with 11 commands Updated pipeline: extract → classify → chunk → embed → store → extract_references New MCP tools: 29 total (was 16) New DB tables: audit_log, decisions CRUD, claims CRUD Config: Infisical support, external service allowlist Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ from legal_mcp.services import db, processor
|
||||
async def document_upload(
|
||||
case_number: str,
|
||||
file_path: str,
|
||||
doc_type: str = "appeal",
|
||||
doc_type: str = "auto",
|
||||
title: str = "",
|
||||
) -> str:
|
||||
"""העלאה ועיבוד מסמך לתיק ערר. מחלץ טקסט, יוצר chunks ו-embeddings.
|
||||
@@ -23,7 +23,7 @@ async def document_upload(
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
file_path: נתיב מלא לקובץ (PDF, DOCX, RTF, TXT)
|
||||
doc_type: סוג מסמך (appeal=כתב ערר, response=תשובה, decision=החלטה, reference=מסמך עזר, exhibit=נספח)
|
||||
doc_type: סוג מסמך (auto=סיווג אוטומטי, appeal=כתב ערר, response=תשובה, protocol=פרוטוקול, plan=תכנית, permit=היתר, court_decision=פסק דין, decision=החלטת ועדה, appraisal=שומה, objection=התנגדות, exhibit=נספח, reference=מסמך עזר)
|
||||
title: שם המסמך (אם ריק, ייקח משם הקובץ)
|
||||
"""
|
||||
case = await db.get_case_by_number(case_number)
|
||||
@@ -44,17 +44,29 @@ async def document_upload(
|
||||
dest = case_dir / source.name
|
||||
shutil.copy2(str(source), str(dest))
|
||||
|
||||
# For auto classification, start with "reference" — will be updated after processing
|
||||
initial_doc_type = doc_type if doc_type != "auto" else "reference"
|
||||
|
||||
# Create document record
|
||||
doc = await db.create_document(
|
||||
case_id=case_id,
|
||||
doc_type=doc_type,
|
||||
doc_type=initial_doc_type,
|
||||
title=title,
|
||||
file_path=str(dest),
|
||||
)
|
||||
|
||||
# Process document (extract → chunk → embed → store)
|
||||
# Process document (extract → classify → chunk → embed → store)
|
||||
result = await processor.process_document(UUID(doc["id"]), case_id)
|
||||
|
||||
# If auto-classification, update doc_type from classification result
|
||||
actual_doc_type = initial_doc_type
|
||||
if doc_type == "auto" and result.get("classification"):
|
||||
classified_type = result["classification"].get("classification", {}).get("doc_type", "")
|
||||
if classified_type:
|
||||
actual_doc_type = classified_type
|
||||
await db.update_document(UUID(doc["id"]), doc_type=classified_type)
|
||||
doc["doc_type"] = classified_type
|
||||
|
||||
# Git commit
|
||||
repo_dir = config.CASES_DIR / case_number
|
||||
if repo_dir.exists():
|
||||
@@ -62,10 +74,16 @@ async def document_upload(
|
||||
doc_type_hebrew = {
|
||||
"appeal": "כתב ערר",
|
||||
"response": "תשובה",
|
||||
"protocol": "פרוטוקול",
|
||||
"plan": "תכנית",
|
||||
"permit": "היתר",
|
||||
"court_decision": "פסק דין",
|
||||
"decision": "החלטה",
|
||||
"reference": "מסמך עזר",
|
||||
"appraisal": "שומה",
|
||||
"objection": "התנגדות",
|
||||
"exhibit": "נספח",
|
||||
}.get(doc_type, doc_type)
|
||||
"reference": "מסמך עזר",
|
||||
}.get(actual_doc_type, actual_doc_type)
|
||||
subprocess.run(
|
||||
["git", "commit", "-m", f"הוספת {doc_type_hebrew}: {title}"],
|
||||
cwd=repo_dir,
|
||||
@@ -216,3 +234,135 @@ async def document_list(case_number: str) -> str:
|
||||
return f"אין מסמכים בתיק {case_number}."
|
||||
|
||||
return json.dumps(docs, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def extract_references(
|
||||
case_number: str,
|
||||
doc_title: str = "",
|
||||
) -> str:
|
||||
"""זיהוי תכניות, פסיקה וחקיקה מתוך מסמכי תיק.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
doc_title: שם מסמך ספציפי (אם ריק, מזהה בכל המסמכים)
|
||||
"""
|
||||
from legal_mcp.services import references_extractor
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
docs = await db.list_documents(case_id)
|
||||
if not docs:
|
||||
return f"אין מסמכים בתיק {case_number}."
|
||||
|
||||
if doc_title:
|
||||
docs = [d for d in docs if doc_title.lower() in d["title"].lower()]
|
||||
|
||||
results = []
|
||||
for doc in docs:
|
||||
text = await db.get_document_text(UUID(doc["id"]))
|
||||
if not text:
|
||||
continue
|
||||
|
||||
refs = await references_extractor.extract_and_link_references(
|
||||
UUID(doc["id"]), case_id, text,
|
||||
)
|
||||
results.append({
|
||||
"document": doc["title"],
|
||||
"plans": refs["plans"],
|
||||
"case_law": refs["case_law"],
|
||||
"case_law_linked": refs["case_law_linked"],
|
||||
"legislation": refs["legislation"],
|
||||
})
|
||||
|
||||
return json.dumps(results, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def extract_claims(
|
||||
case_number: str,
|
||||
doc_title: str = "",
|
||||
party_hint: str = "",
|
||||
) -> str:
|
||||
"""חילוץ טענות מכתב טענות בתיק ושמירה ב-DB.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
doc_title: שם מסמך ספציפי (אם ריק, מחלץ מכל כתבי הטענות)
|
||||
party_hint: שם הצד המגיש (אם ידוע)
|
||||
"""
|
||||
from legal_mcp.services import claims_extractor
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
docs = await db.list_documents(case_id)
|
||||
if not docs:
|
||||
return f"אין מסמכים בתיק {case_number}."
|
||||
|
||||
# Filter to claims documents (appeal, response) or specific doc
|
||||
if doc_title:
|
||||
docs = [d for d in docs if doc_title.lower() in d["title"].lower()]
|
||||
else:
|
||||
docs = [d for d in docs if d["doc_type"] in ("appeal", "response", "objection")]
|
||||
|
||||
if not docs:
|
||||
return "לא נמצאו כתבי טענות בתיק."
|
||||
|
||||
results = []
|
||||
for doc in docs:
|
||||
text = await db.get_document_text(UUID(doc["id"]))
|
||||
if not text:
|
||||
continue
|
||||
|
||||
result = await claims_extractor.extract_and_store_claims(
|
||||
case_id=case_id,
|
||||
document_id=UUID(doc["id"]),
|
||||
text=text,
|
||||
doc_type=doc["doc_type"],
|
||||
party_hint=party_hint,
|
||||
)
|
||||
results.append(result)
|
||||
|
||||
return json.dumps(results, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def get_claims(case_number: str, party_role: str = "") -> str:
|
||||
"""שליפת טענות שחולצו לתיק.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
party_role: סינון לפי צד (appellant/respondent/committee/permit_applicant). ריק = הכל.
|
||||
"""
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
claims = await db.get_claims(
|
||||
UUID(case["id"]),
|
||||
party_role=party_role if party_role else None,
|
||||
)
|
||||
|
||||
if not claims:
|
||||
return f"אין טענות בתיק {case_number}."
|
||||
|
||||
# Format for display
|
||||
role_hebrew = {
|
||||
"appellant": "עוררים",
|
||||
"respondent": "משיבים",
|
||||
"committee": "ועדה מקומית",
|
||||
"permit_applicant": "מבקשי היתר",
|
||||
"appraiser": "שמאי",
|
||||
}
|
||||
formatted = []
|
||||
for c in claims:
|
||||
formatted.append({
|
||||
"party": role_hebrew.get(c["party_role"], c["party_role"]),
|
||||
"claim": c["claim_text"],
|
||||
"source": c.get("source_document", ""),
|
||||
})
|
||||
|
||||
return json.dumps(formatted, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
@@ -332,9 +332,144 @@ async def get_decision_template(case_number: str) -> str:
|
||||
return template
|
||||
|
||||
|
||||
async def validate_decision(case_number: str) -> str:
|
||||
"""בדיקת QA אוטומטית על ההחלטה — 6 בדיקות. אם נכשלת בדיקה קריטית — ייצוא חסום.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
"""
|
||||
from legal_mcp.services import qa_validator
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
try:
|
||||
result = await qa_validator.validate_decision(case_id)
|
||||
return json.dumps(result, default=str, ensure_ascii=False, indent=2)
|
||||
except ValueError as e:
|
||||
return json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def export_docx(case_number: str, output_path: str = "") -> str:
|
||||
"""ייצוא החלטה לקובץ DOCX מעוצב — גופן David, RTL, כותרות, מספור סעיפים.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
output_path: נתיב לשמירה (אופציונלי — ברירת מחדל: תיקיית התיק)
|
||||
"""
|
||||
from legal_mcp.services import docx_exporter
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
try:
|
||||
path = await docx_exporter.export_decision(case_id, output_path or None)
|
||||
return json.dumps({
|
||||
"status": "completed",
|
||||
"path": path,
|
||||
"message": f"DOCX נוצר: {path}",
|
||||
}, ensure_ascii=False, indent=2)
|
||||
except ValueError as e:
|
||||
return json.dumps({
|
||||
"status": "error",
|
||||
"message": str(e),
|
||||
}, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def analyze_style() -> str:
|
||||
"""הרצת ניתוח סגנון על קורפוס ההחלטות של דפנה. מחלץ דפוסי כתיבה ושומר אותם."""
|
||||
from legal_mcp.services.style_analyzer import analyze_corpus
|
||||
|
||||
result = await analyze_corpus()
|
||||
return json.dumps(result, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def write_block(
|
||||
case_number: str,
|
||||
block_id: str,
|
||||
instructions: str = "",
|
||||
) -> str:
|
||||
"""כתיבת בלוק יחיד בהחלטה. כותב ושומר ב-DB.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
block_id: מזהה הבלוק: block-alef, block-bet, block-gimel, block-dalet, block-he, block-vav, block-zayin, block-chet, block-tet, block-yod, block-yod-alef, block-yod-bet
|
||||
instructions: הנחיות נוספות לניסוח
|
||||
"""
|
||||
from legal_mcp.services import block_writer
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
try:
|
||||
result = await block_writer.write_and_store_block(case_id, block_id, instructions)
|
||||
return json.dumps(result, default=str, ensure_ascii=False, indent=2)
|
||||
except ValueError as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
async def write_all_blocks(
|
||||
case_number: str,
|
||||
start_from: str = "block-alef",
|
||||
instructions: str = "",
|
||||
) -> str:
|
||||
"""כתיבת כל הבלוקים בהחלטה, בלוק אחרי בלוק. שומר כל בלוק מיד אחרי כתיבה.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
start_from: מאיזה בלוק להתחיל (ברירת מחדל: block-alef)
|
||||
instructions: הנחיות כלליות
|
||||
"""
|
||||
from legal_mcp.services import block_writer
|
||||
from legal_mcp.services.block_writer import BLOCK_CONFIG
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
# Determine start index
|
||||
start_idx = BLOCK_CONFIG.get(start_from, {}).get("index", 1)
|
||||
|
||||
results = []
|
||||
block_order = sorted(BLOCK_CONFIG.items(), key=lambda x: x[1]["index"])
|
||||
|
||||
for bid, cfg in block_order:
|
||||
if cfg["index"] < start_idx:
|
||||
continue
|
||||
|
||||
try:
|
||||
result = await block_writer.write_and_store_block(case_id, bid, instructions)
|
||||
results.append({
|
||||
"block_id": bid,
|
||||
"title": result["title"],
|
||||
"word_count": result["word_count"],
|
||||
"status": "completed",
|
||||
})
|
||||
except ValueError as e:
|
||||
results.append({
|
||||
"block_id": bid,
|
||||
"title": cfg["title"],
|
||||
"status": "error",
|
||||
"error": str(e),
|
||||
})
|
||||
# Stop on critical error (e.g., missing direction for block-yod)
|
||||
if "כיוון מאושר" in str(e):
|
||||
break
|
||||
|
||||
total_words = sum(r.get("word_count", 0) for r in results)
|
||||
return json.dumps({
|
||||
"blocks": results,
|
||||
"total_words": total_words,
|
||||
"completed": sum(1 for r in results if r["status"] == "completed"),
|
||||
}, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""MCP tools for workflow status tracking."""
|
||||
"""MCP tools for workflow: status, outcome, brainstorming, direction."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -95,6 +95,25 @@ def _suggest_next_steps(case: dict, docs: list, has_draft: bool) -> list[str]:
|
||||
return steps
|
||||
|
||||
|
||||
async def get_metrics(case_number: str = "") -> str:
|
||||
"""מדדי הצלחה — KPIs לתיק ספציפי או דשבורד כולל.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק (אם ריק — דשבורד כולל)
|
||||
"""
|
||||
from legal_mcp.services import metrics
|
||||
|
||||
if case_number:
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
result = await metrics.get_case_metrics(UUID(case["id"]))
|
||||
else:
|
||||
result = await metrics.get_dashboard()
|
||||
|
||||
return json.dumps(result, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def processing_status() -> str:
|
||||
"""סטטוס כללי - מספר תיקים, מסמכים ממתינים לעיבוד."""
|
||||
pool = await db.get_pool()
|
||||
@@ -116,3 +135,186 @@ async def processing_status() -> str:
|
||||
"style_corpus_entries": corpus_count,
|
||||
"style_patterns": pattern_count,
|
||||
}, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
# ── Outcome & Brainstorming ───────────────────────────────────────
|
||||
|
||||
async def set_outcome(
|
||||
case_number: str,
|
||||
outcome: str,
|
||||
reasoning: str = "",
|
||||
) -> str:
|
||||
"""הזנת תוצאה לתיק ערר. יוצר רשומת החלטה ומפעיל סיעור מוחות אם אין נימוק.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
outcome: תוצאה — rejected (דחייה), accepted (קבלה), partial (קבלה חלקית)
|
||||
reasoning: נימוק (אופציונלי). אם ריק — מפעיל סיעור מוחות.
|
||||
"""
|
||||
from legal_mcp.services import brainstorm
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
valid_outcomes = ("rejected", "accepted", "partial")
|
||||
if outcome not in valid_outcomes:
|
||||
return f"תוצאה לא תקינה. אפשרויות: {', '.join(valid_outcomes)}"
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
# Create or update decision
|
||||
existing = await db.get_decision_by_case(case_id)
|
||||
if existing:
|
||||
await db.update_decision(
|
||||
UUID(existing["id"]),
|
||||
outcome=outcome,
|
||||
outcome_summary=reasoning[:200] if reasoning else "",
|
||||
outcome_reasoning=reasoning,
|
||||
)
|
||||
decision = await db.get_decision(UUID(existing["id"]))
|
||||
else:
|
||||
decision = await db.create_decision(
|
||||
case_id=case_id,
|
||||
outcome=outcome,
|
||||
outcome_summary=reasoning[:200] if reasoning else "",
|
||||
outcome_reasoning=reasoning,
|
||||
)
|
||||
|
||||
# Update case status
|
||||
await db.update_case(case_id, status="in_progress", expected_outcome=outcome)
|
||||
|
||||
outcome_hebrew = {"rejected": "דחייה", "accepted": "קבלה", "partial": "קבלה חלקית"}.get(outcome, outcome)
|
||||
|
||||
result = {
|
||||
"decision_id": decision["id"],
|
||||
"outcome": outcome,
|
||||
"outcome_hebrew": outcome_hebrew,
|
||||
"reasoning": reasoning,
|
||||
"has_reasoning": bool(reasoning),
|
||||
}
|
||||
|
||||
if not reasoning:
|
||||
result["message"] = "לא סופק נימוק. הפעל /brainstorm כדי לקבל כיוונים אפשריים."
|
||||
result["next_step"] = "brainstorm"
|
||||
else:
|
||||
result["message"] = f"תוצאה נשמרה: {outcome_hebrew}. ניתן להתחיל כתיבת טיוטה."
|
||||
result["next_step"] = "draft"
|
||||
|
||||
return json.dumps(result, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def brainstorm_directions(
|
||||
case_number: str,
|
||||
) -> str:
|
||||
"""סיעור מוחות — הצגת טענות מרכזיות והצעת 2-3 כיוונים אפשריים לנימוק.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
"""
|
||||
from legal_mcp.services import brainstorm
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
# Get existing decision for outcome
|
||||
decision = await db.get_decision_by_case(case_id)
|
||||
if not decision:
|
||||
return "לא הוזנה תוצאה לתיק. הפעל set_outcome קודם."
|
||||
|
||||
outcome = decision.get("outcome", "")
|
||||
reasoning = decision.get("outcome_reasoning", "")
|
||||
|
||||
directions = await brainstorm.generate_directions(case_id, outcome, reasoning)
|
||||
|
||||
# Save brainstorm results to decision
|
||||
await db.update_decision(
|
||||
UUID(decision["id"]),
|
||||
direction_doc={"brainstorm": directions, "approved": False},
|
||||
)
|
||||
|
||||
return json.dumps(directions, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def approve_direction(
|
||||
case_number: str,
|
||||
direction_index: int = 0,
|
||||
additional_notes: str = "",
|
||||
) -> str:
|
||||
"""אישור כיוון — יוצר מסמך כיוון מאושר. לא ניתן להתחיל כתיבת דיון בלי כיוון מאושר.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
direction_index: מספר הכיוון שנבחר (0 = ראשון)
|
||||
additional_notes: הערות נוספות
|
||||
"""
|
||||
from legal_mcp.services import brainstorm
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
decision = await db.get_decision_by_case(case_id)
|
||||
if not decision:
|
||||
return "לא הוזנה תוצאה לתיק."
|
||||
|
||||
direction_data = decision.get("direction_doc") or {}
|
||||
brainstorm_result = direction_data.get("brainstorm", {})
|
||||
|
||||
if not brainstorm_result.get("directions"):
|
||||
return "לא בוצע סיעור מוחות. הפעל brainstorm_directions קודם."
|
||||
|
||||
direction_doc = brainstorm.build_direction_doc(
|
||||
outcome=decision.get("outcome", ""),
|
||||
reasoning=decision.get("outcome_reasoning", ""),
|
||||
directions_result=brainstorm_result,
|
||||
selected_direction=direction_index,
|
||||
additional_notes=additional_notes,
|
||||
)
|
||||
|
||||
await db.update_decision(UUID(decision["id"]), direction_doc=direction_doc)
|
||||
|
||||
return json.dumps({
|
||||
"status": "approved",
|
||||
"message": "כיוון אושר. ניתן להתחיל כתיבת טיוטה.",
|
||||
"direction": direction_doc,
|
||||
}, default=str, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
async def ingest_final_version(
|
||||
case_number: str,
|
||||
file_path: str = "",
|
||||
final_text: str = "",
|
||||
) -> str:
|
||||
"""קליטת גרסה סופית (שדפנה חתמה). משווה לטיוטה ומחלצת לקחים.
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
file_path: נתיב לקובץ הגרסה הסופית (PDF/DOCX)
|
||||
final_text: טקסט ישיר (אם אין קובץ)
|
||||
"""
|
||||
from legal_mcp.services import learning_loop
|
||||
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
# Extract text from file if provided
|
||||
if file_path and not final_text:
|
||||
from legal_mcp.services import extractor
|
||||
final_text, _ = await extractor.extract_text(file_path)
|
||||
|
||||
if not final_text:
|
||||
return "לא סופק טקסט — יש לספק file_path או final_text."
|
||||
|
||||
try:
|
||||
result = await learning_loop.process_final_version(case_id, final_text)
|
||||
return json.dumps(result, default=str, ensure_ascii=False, indent=2)
|
||||
except ValueError as e:
|
||||
return json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False, indent=2)
|
||||
|
||||
Reference in New Issue
Block a user