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:
2026-04-03 10:21:47 +00:00
parent df7cc4f5a5
commit d9e5ef0f46
21 changed files with 3957 additions and 14 deletions

View File

@@ -0,0 +1,166 @@
"""עוזר משפטי — CLI לניהול תהליך כתיבת החלטות.
Usage: python -m legal_mcp.cli <command>
"""
from __future__ import annotations
import asyncio
import json
import sys
from pathlib import Path
def _run(coro):
"""Run async function."""
return asyncio.run(coro)
def _init():
"""Initialize DB."""
from legal_mcp.services.db import init_schema
_run(init_schema())
def cmd_status(case_number: str = ""):
"""סטטוס — תיק ספציפי או כללי."""
_init()
if case_number:
from legal_mcp.tools.workflow import workflow_status
print(_run(workflow_status(case_number)))
else:
from legal_mcp.tools.workflow import processing_status
print(_run(processing_status()))
def cmd_upload(case_number: str, file_path: str, doc_type: str = "auto", title: str = ""):
"""העלאת מסמך לתיק."""
_init()
from legal_mcp.tools.documents import document_upload
result = _run(document_upload(case_number, file_path, doc_type, title))
print(result)
def cmd_outcome(case_number: str, outcome: str, reasoning: str = ""):
"""הזנת תוצאה: rejected / accepted / partial."""
_init()
from legal_mcp.tools.workflow import set_outcome
result = _run(set_outcome(case_number, outcome, reasoning))
print(result)
def cmd_brainstorm(case_number: str):
"""סיעור מוחות — הצעת כיוונים לנימוק."""
_init()
from legal_mcp.tools.workflow import brainstorm_directions
result = _run(brainstorm_directions(case_number))
print(result)
def cmd_approve(case_number: str, direction: int = 0, notes: str = ""):
"""אישור כיוון."""
_init()
from legal_mcp.tools.workflow import approve_direction
result = _run(approve_direction(case_number, direction, notes))
print(result)
def cmd_write(case_number: str, block_id: str = "", instructions: str = ""):
"""כתיבת בלוק או כל הבלוקים."""
_init()
from legal_mcp.tools.drafting import write_block, write_all_blocks
if block_id:
result = _run(write_block(case_number, block_id, instructions))
else:
result = _run(write_all_blocks(case_number, instructions=instructions))
print(result)
def cmd_validate(case_number: str):
"""בדיקת QA."""
_init()
from legal_mcp.tools.drafting import validate_decision
result = _run(validate_decision(case_number))
print(result)
def cmd_export(case_number: str, output: str = ""):
"""ייצוא DOCX."""
_init()
from legal_mcp.tools.drafting import export_docx
result = _run(export_docx(case_number, output))
print(result)
def cmd_claims(case_number: str, role: str = ""):
"""שליפת טענות."""
_init()
from legal_mcp.tools.documents import get_claims
result = _run(get_claims(case_number, role))
print(result)
def cmd_metrics(case_number: str = ""):
"""מדדי הצלחה."""
_init()
from legal_mcp.tools.workflow import get_metrics
result = _run(get_metrics(case_number))
print(result)
def cmd_ingest_final(case_number: str, file_path: str):
"""קליטת גרסה סופית."""
_init()
from legal_mcp.tools.workflow import ingest_final_version
result = _run(ingest_final_version(case_number, file_path=file_path))
print(result)
COMMANDS = {
"status": (cmd_status, "סטטוס תיק או כללי", "[case_number]"),
"upload": (cmd_upload, "העלאת מסמך לתיק", "<case_number> <file_path> [doc_type] [title]"),
"outcome": (cmd_outcome, "הזנת תוצאה", "<case_number> <rejected|accepted|partial> [reasoning]"),
"brainstorm": (cmd_brainstorm, "סיעור מוחות", "<case_number>"),
"approve": (cmd_approve, "אישור כיוון", "<case_number> [direction_index] [notes]"),
"write": (cmd_write, "כתיבת בלוק/ים", "<case_number> [block_id] [instructions]"),
"validate": (cmd_validate, "בדיקת QA", "<case_number>"),
"export": (cmd_export, "ייצוא DOCX", "<case_number> [output_path]"),
"claims": (cmd_claims, "שליפת טענות", "<case_number> [party_role]"),
"metrics": (cmd_metrics, "מדדי הצלחה", "[case_number]"),
"ingest-final": (cmd_ingest_final, "קליטת גרסה סופית", "<case_number> <file_path>"),
}
def main():
if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help", "help"):
print("עוזר משפטי — CLI\n")
print("שימוש: python -m legal_mcp.cli <command> [args]\n")
print("פקודות:")
for name, (_, desc, usage) in COMMANDS.items():
print(f" {name:15s} {desc:25s} {usage}")
print()
sys.exit(0)
cmd_name = sys.argv[1]
args = sys.argv[2:]
if cmd_name not in COMMANDS:
print(f"פקודה לא ידועה: {cmd_name}")
print(f"הפקודות הזמינות: {', '.join(COMMANDS.keys())}")
sys.exit(1)
func, _, _ = COMMANDS[cmd_name]
try:
func(*args)
except TypeError as e:
print(f"שגיאה בפרמטרים: {e}")
_, _, usage = COMMANDS[cmd_name]
print(f"שימוש: python -m legal_mcp.cli {cmd_name} {usage}")
sys.exit(1)
except Exception as e:
print(f"שגיאה: {e}")
sys.exit(1)
if __name__ == "__main__":
main()