Fixes the case where the H1 chip, the pipeline stepper and the manual-changer dropdown disagreed (e.g. 1043-02-26 on analyst_verified): the status sat between the canonical 10 and a legacy bucket, so each surface read a different list. Root fix — ONE authoritative status model + canonicalize the real intermediate states (chair-approved): - New mcp-server/.../case_status_model.py — the single registry: ordered StatusDef list (key/label/description/phase/selectable/terminal/on_enter), the 5 phases, STATUS_ORDER, phase_of/label_of, and a drift assertion that the CaseStatus enum matches it. `on_enter` is the seam for a status to *do* something on entry, declared next to its definition (no dispatcher wired yet). - models.CaseStatus enum: analyst_verified + research_complete promoted to first-class canonical statuses (the agents set them — they belong in the model, not a legacy fallback). - tools/cases.py: the forward-only STATUS_ORDER guard now derives from the registry instead of an inline list. - GET /api/status-model exposes the model so the frontend mirror can be generated against it. - web-ui case-status.ts mirrors it: the two intermediates moved from the legacy map into CASE_STATUSES / PHASES(thinking) / STATUS_LABELS / STATUS_DESCRIPTIONS; status-badge icons+tones extended. So the chip (status), stepper (its phase) and dropdown (now includes it) all agree. Invariants: G2 — collapses the scattered status definitions (enum, inline STATUS_ORDER, two frontend label maps) onto one backend authority + a documented frontend mirror; no parallel status list remains. Agent-prompt alignment + backfill follow in a separate change. py_compile + tsc + eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
117 lines
3.7 KiB
Python
117 lines
3.7 KiB
Python
"""Pydantic models for cases, documents, and related entities."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import enum
|
|
from datetime import date, datetime
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# Core case lifecycle. The canonical key set, order, labels, phases and (future)
|
|
# per-status actions all live in ONE place — legal_mcp/case_status_model.py —
|
|
# which asserts this enum matches it. The frontend mirror is
|
|
# web-ui/src/lib/api/case-status.ts (generated against GET /api/status-model).
|
|
# The analyst/research intermediate states are first-class canonical statuses
|
|
# (the agents set them) — not legacy.
|
|
class CaseStatus(str, enum.Enum):
|
|
NEW = "new"
|
|
PROCESSING = "processing"
|
|
DOCUMENTS_READY = "documents_ready"
|
|
ANALYST_VERIFIED = "analyst_verified"
|
|
RESEARCH_COMPLETE = "research_complete"
|
|
OUTCOME_SET = "outcome_set"
|
|
DIRECTION_APPROVED = "direction_approved"
|
|
QA_REVIEW = "qa_review"
|
|
DRAFTED = "drafted"
|
|
EXPORTED = "exported"
|
|
REVIEWED = "reviewed"
|
|
FINAL = "final"
|
|
|
|
|
|
class DocType(str, enum.Enum):
|
|
APPEAL = "appeal" # כתב ערר
|
|
RESPONSE = "response" # תשובה / כתב תשובה
|
|
PROTOCOL = "protocol" # פרוטוקול דיון
|
|
PLAN = "plan" # תכנית (תב"ע)
|
|
PERMIT = "permit" # היתר בנייה
|
|
COURT_DECISION = "court_decision" # פסק דין / החלטת בית משפט
|
|
DECISION = "decision" # החלטת ועדה
|
|
APPRAISAL = "appraisal" # שומה / חוות דעת שמאית
|
|
OBJECTION = "objection" # התנגדות
|
|
EXHIBIT = "exhibit" # נספח / מסמך תומך
|
|
REFERENCE = "reference" # מסמך עזר אחר
|
|
|
|
|
|
class SectionType(str, enum.Enum):
|
|
INTRO = "intro"
|
|
FACTS = "facts"
|
|
APPELLANT_CLAIMS = "appellant_claims"
|
|
RESPONDENT_CLAIMS = "respondent_claims"
|
|
LEGAL_ANALYSIS = "legal_analysis"
|
|
CONCLUSION = "conclusion"
|
|
RULING = "ruling"
|
|
OTHER = "other"
|
|
|
|
|
|
class ExtractionStatus(str, enum.Enum):
|
|
PENDING = "pending"
|
|
PROCESSING = "processing"
|
|
COMPLETED = "completed"
|
|
FAILED = "failed"
|
|
|
|
|
|
class CaseCreate(BaseModel):
|
|
case_number: str = Field(description="מספר תיק הערר (לדוגמה: 123-24)")
|
|
title: str = Field(description="כותרת קצרה של הערר")
|
|
appellants: list[str] = Field(default_factory=list, description="שמות העוררים")
|
|
respondents: list[str] = Field(default_factory=list, description="שמות המשיבים")
|
|
subject: str = Field(default="", description="נושא הערר")
|
|
property_address: str = Field(default="", description="כתובת הנכס")
|
|
permit_number: str = Field(default="", description="מספר היתר")
|
|
committee_type: str = Field(default="ועדה מקומית", description="סוג הוועדה")
|
|
hearing_date: date | None = Field(default=None, description="תאריך דיון")
|
|
notes: str = Field(default="", description="הערות")
|
|
|
|
|
|
class CaseInfo(BaseModel):
|
|
id: UUID
|
|
case_number: str
|
|
title: str
|
|
appellants: list[str]
|
|
respondents: list[str]
|
|
subject: str
|
|
property_address: str
|
|
permit_number: str
|
|
committee_type: str
|
|
status: CaseStatus
|
|
hearing_date: date | None
|
|
decision_date: date | None
|
|
tags: list[str]
|
|
notes: str
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class DocumentInfo(BaseModel):
|
|
id: UUID
|
|
case_id: UUID
|
|
doc_type: DocType
|
|
title: str
|
|
file_path: str
|
|
extraction_status: ExtractionStatus
|
|
page_count: int | None
|
|
created_at: datetime
|
|
|
|
|
|
class SearchResult(BaseModel):
|
|
chunk_content: str
|
|
score: float
|
|
case_number: str
|
|
document_title: str
|
|
section_type: str
|
|
page_number: int | None
|
|
document_id: UUID
|
|
case_id: UUID
|