Source code for lalandre_rag.summaries.models
"""Data models, constants, and helpers for canonical act summaries."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, Optional
CANONICAL_SUMMARY_KIND = "canonical"
CANONICAL_SUMMARY_PROMPT_VERSION = "canonical_summary_v1"
SUMMARY_STATUS_PENDING = "pending"
SUMMARY_STATUS_READY = "ready"
SUMMARY_STATUS_FAILED = "failed"
[docs]
@dataclass(frozen=True)
class CanonicalSummarySnapshot:
"""Canonical summary state returned by the summary service."""
act_id: int
celex: str
language: str
status: str
is_stale: bool
summary: Optional[str]
generated_at: Optional[datetime]
prompt_version: Optional[str]
model_id: Optional[str]
source_version_id: Optional[int]
error_text: Optional[str]
trace: Dict[str, Any]
@property
def available(self) -> bool:
"""Return whether the snapshot contains a ready-to-use summary."""
return bool(self.summary and self.status == SUMMARY_STATUS_READY)
[docs]
class SummaryTraceRecorder:
"""Build structured trace payloads for summary generation and lookup."""
[docs]
@staticmethod
def lookup(*, status: str, is_stale: bool, reason: Optional[str] = None) -> Dict[str, Any]:
"""Build trace metadata for summary lookup operations."""
trace: Dict[str, Any] = {
"lookup_status": status,
"is_stale": bool(is_stale),
}
if reason:
trace["reason"] = reason
return trace
[docs]
@staticmethod
def generation(
*,
mode: str,
context_chars: int,
subdivisions_used: int,
model_id: str,
prompt_version: str,
extra: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""Build trace metadata for summary generation operations."""
trace: Dict[str, Any] = {
"mode": mode,
"context_chars": int(context_chars),
"subdivisions_used": int(subdivisions_used),
"model_id": model_id,
"prompt_version": prompt_version,
}
if extra:
trace.update(extra)
return trace
[docs]
def build_default_summary_questions(celex: str) -> set[str]:
"""Return the default summary prompts recognized for a CELEX identifier."""
normalized = celex.strip()
if not normalized:
return set()
return {
f"résumé {normalized}".casefold(),
f"résumé de {normalized}".casefold(),
f"resume {normalized}".casefold(),
f"resume de {normalized}".casefold(),
}
[docs]
def is_default_summary_question(question: str, celex: str) -> bool:
"""Return whether *question* matches the default summary prompt family."""
normalized_question = question.strip()
if not normalized_question:
return True
return normalized_question.casefold() in build_default_summary_questions(celex)