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:
@@ -140,6 +140,36 @@ async def document_list(case_number: str) -> str:
|
||||
return await documents.document_list(case_number)
|
||||
|
||||
|
||||
# Claims extraction
|
||||
@mcp.tool()
|
||||
async def extract_claims(
|
||||
case_number: str,
|
||||
doc_title: str = "",
|
||||
party_hint: str = "",
|
||||
) -> str:
|
||||
"""חילוץ טענות מכתב טענות בתיק. מחלץ טענות לפי צד ושומר ב-DB."""
|
||||
return await documents.extract_claims(case_number, doc_title, party_hint)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_claims(
|
||||
case_number: str,
|
||||
party_role: str = "",
|
||||
) -> str:
|
||||
"""שליפת טענות שחולצו לתיק. party_role: appellant/respondent/committee/permit_applicant (ריק=הכל)."""
|
||||
return await documents.get_claims(case_number, party_role)
|
||||
|
||||
|
||||
# References
|
||||
@mcp.tool()
|
||||
async def extract_references(
|
||||
case_number: str,
|
||||
doc_title: str = "",
|
||||
) -> str:
|
||||
"""זיהוי תכניות, פסיקה וחקיקה מתוך מסמכי תיק. מזהה ומקשר להפניות קיימות ב-DB."""
|
||||
return await documents.extract_references(case_number, doc_title)
|
||||
|
||||
|
||||
# Search
|
||||
@mcp.tool()
|
||||
async def search_decisions(
|
||||
@@ -193,12 +223,44 @@ async def get_decision_template(case_number: str) -> str:
|
||||
return await drafting.get_decision_template(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def validate_decision(case_number: str) -> str:
|
||||
"""בדיקת QA — 6 בדיקות איכות על ההחלטה. אם בדיקה קריטית נכשלת — ייצוא חסום."""
|
||||
return await drafting.validate_decision(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def export_docx(case_number: str, output_path: str = "") -> str:
|
||||
"""ייצוא החלטה לקובץ DOCX מעוצב — גופן David, RTL, כותרות, מספור סעיפים."""
|
||||
return await drafting.export_docx(case_number, output_path)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def analyze_style() -> str:
|
||||
"""ניתוח סגנון על קורפוס ההחלטות של דפנה. מחלץ ושומר דפוסי כתיבה."""
|
||||
return await drafting.analyze_style()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def write_block(
|
||||
case_number: str,
|
||||
block_id: str,
|
||||
instructions: str = "",
|
||||
) -> str:
|
||||
"""כתיבת בלוק יחיד בהחלטה: block-alef עד block-yod-bet. שומר ב-DB."""
|
||||
return await drafting.write_block(case_number, block_id, instructions)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def write_all_blocks(
|
||||
case_number: str,
|
||||
start_from: str = "block-alef",
|
||||
instructions: str = "",
|
||||
) -> str:
|
||||
"""כתיבת כל הבלוקים בהחלטה, בלוק אחרי בלוק. שומר כל בלוק מיד."""
|
||||
return await drafting.write_all_blocks(case_number, start_from, instructions)
|
||||
|
||||
|
||||
# Workflow
|
||||
@mcp.tool()
|
||||
async def workflow_status(case_number: str) -> str:
|
||||
@@ -206,12 +268,55 @@ async def workflow_status(case_number: str) -> str:
|
||||
return await workflow.workflow_status(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_metrics(case_number: str = "") -> str:
|
||||
"""מדדי הצלחה — KPIs לתיק ספציפי או דשבורד כולל. ריק = דשבורד."""
|
||||
return await workflow.get_metrics(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def processing_status() -> str:
|
||||
"""סטטוס כללי - מספר תיקים, מסמכים, chunks."""
|
||||
return await workflow.processing_status()
|
||||
|
||||
|
||||
# Outcome & Brainstorming
|
||||
@mcp.tool()
|
||||
async def set_outcome(
|
||||
case_number: str,
|
||||
outcome: str,
|
||||
reasoning: str = "",
|
||||
) -> str:
|
||||
"""הזנת תוצאה לתיק: rejected (דחייה), accepted (קבלה), partial (קבלה חלקית). אם אין נימוק — מפעיל סיעור מוחות."""
|
||||
return await workflow.set_outcome(case_number, outcome, reasoning)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def brainstorm_directions(case_number: str) -> str:
|
||||
"""סיעור מוחות — הצגת טענות מרכזיות והצעת 2-3 כיוונים אפשריים לנימוק ההחלטה."""
|
||||
return await workflow.brainstorm_directions(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def approve_direction(
|
||||
case_number: str,
|
||||
direction_index: int = 0,
|
||||
additional_notes: str = "",
|
||||
) -> str:
|
||||
"""אישור כיוון — יוצר מסמך כיוון מאושר. חובה לפני כתיבת דיון (בלוק י)."""
|
||||
return await workflow.approve_direction(case_number, direction_index, additional_notes)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def ingest_final_version(
|
||||
case_number: str,
|
||||
file_path: str = "",
|
||||
final_text: str = "",
|
||||
) -> str:
|
||||
"""קליטת גרסה סופית שדפנה חתמה — השוואה לטיוטה וחילוץ לקחים לשיפור עתידי."""
|
||||
return await workflow.ingest_final_version(case_number, file_path, final_text)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
mcp.run(transport="stdio")
|
||||
|
||||
Reference in New Issue
Block a user