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

@@ -1,4 +1,7 @@
"""Configuration loaded from central .env file."""
"""Configuration loaded from Infisical or central .env file.
Priority: Infisical → environment variables → .env file
"""
import os
from pathlib import Path
@@ -9,6 +12,23 @@ from dotenv import load_dotenv
dotenv_path = os.environ.get("DOTENV_PATH", str(Path.home() / ".env"))
load_dotenv(dotenv_path)
# Try loading from Infisical if configured
INFISICAL_TOKEN = os.environ.get("INFISICAL_TOKEN", "")
if INFISICAL_TOKEN:
try:
from infisical_sdk import InfisicalSDKClient
_client = InfisicalSDKClient(token=INFISICAL_TOKEN)
_secrets = _client.get_all_secrets(
environment=os.environ.get("INFISICAL_ENV", "production"),
project_id=os.environ.get("INFISICAL_PROJECT_ID", ""),
)
for s in _secrets:
os.environ.setdefault(s.secret_key, s.secret_value)
except ImportError:
pass # Infisical SDK not installed — use .env
except Exception:
pass # Infisical unreachable — fall back to .env
# PostgreSQL
POSTGRES_URL = os.environ.get(
"POSTGRES_URL",
@@ -38,3 +58,12 @@ TRAINING_DIR = DATA_DIR / "training"
# Chunking parameters
CHUNK_SIZE_TOKENS = 600
CHUNK_OVERLAP_TOKENS = 100
# External service allowlist — case materials may ONLY be sent to these domains
ALLOWED_EXTERNAL_SERVICES = {
"api.anthropic.com", # Claude API (text generation, OCR)
"api.voyageai.com", # Voyage AI (embeddings)
}
# Audit
AUDIT_ENABLED = os.environ.get("AUDIT_ENABLED", "true").lower() == "true"