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:
76
mcp-server/src/legal_mcp/services/audit.py
Normal file
76
mcp-server/src/legal_mcp/services/audit.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""Audit log — תיעוד כל פעולה במערכת.
|
||||
|
||||
כל פעולה מתועדת: מי, מתי, מה, על איזה תיק.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from legal_mcp.services import db
|
||||
|
||||
logger = logging.getLogger("audit")
|
||||
|
||||
|
||||
async def log_action(
|
||||
action: str,
|
||||
case_id: UUID | None = None,
|
||||
document_id: UUID | None = None,
|
||||
details: dict | None = None,
|
||||
user: str = "system",
|
||||
) -> None:
|
||||
"""רישום פעולה ב-audit log.
|
||||
|
||||
Args:
|
||||
action: סוג הפעולה (upload, classify, extract_claims, set_outcome, write_block, export, etc.)
|
||||
case_id: מזהה תיק (אם רלוונטי)
|
||||
document_id: מזהה מסמך (אם רלוונטי)
|
||||
details: פרטים נוספים
|
||||
user: מזהה המשתמש
|
||||
"""
|
||||
pool = await db.get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
await conn.execute(
|
||||
"""INSERT INTO audit_log (id, action, case_id, document_id, details, actor, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)""",
|
||||
uuid4(), action, case_id, document_id,
|
||||
json.dumps(details or {}, ensure_ascii=False, default=str),
|
||||
user, datetime.utcnow(),
|
||||
)
|
||||
logger.info("AUDIT: %s | case=%s | user=%s | %s", action, case_id, user,
|
||||
json.dumps(details or {}, ensure_ascii=False)[:200])
|
||||
|
||||
|
||||
async def get_audit_log(
|
||||
case_id: UUID | None = None,
|
||||
action: str | None = None,
|
||||
limit: int = 50,
|
||||
) -> list[dict]:
|
||||
"""שליפת audit log."""
|
||||
pool = await db.get_pool()
|
||||
conditions = []
|
||||
params: list = []
|
||||
idx = 1
|
||||
|
||||
if case_id:
|
||||
conditions.append(f"case_id = ${idx}")
|
||||
params.append(case_id)
|
||||
idx += 1
|
||||
if action:
|
||||
conditions.append(f"action = ${idx}")
|
||||
params.append(action)
|
||||
idx += 1
|
||||
|
||||
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
||||
params.append(limit)
|
||||
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
f"SELECT * FROM audit_log {where} ORDER BY created_at DESC LIMIT ${idx}",
|
||||
*params,
|
||||
)
|
||||
|
||||
return [dict(r) for r in rows]
|
||||
Reference in New Issue
Block a user