themis / phase1 /scripts /statute_library.py
vg15o2's picture
Moonley backend (HF Space build)
1d9bd9b
Raw
History Blame Contribute Delete
5.77 kB
"""Exact statutory-text retrieval from a private local Chroma snapshot.
The section crosswalk decides which provisions correspond. This store only
returns the exact Act + section record requested by that mapping; it never runs
similarity search and never substitutes a neighbouring provision.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any
from statute_crosswalk import normalise_act, normalise_section
COLLECTION_NAME = "indian_statutes"
STORED_ACT_CODES = {
"IPC": ["IPC"],
"BNS": ["BNS"],
"CRPC": ["CRPC", "CrPC"],
"BNSS": ["BNSS"],
"IEA": ["IEA"],
"BSA": ["BSA"],
}
def _chroma_root(value: str | os.PathLike[str] | None) -> Path | None:
"""Accept either the Chroma root or its UUID segment directory."""
if not value:
return None
candidate = Path(value).expanduser().resolve()
if (candidate / "chroma.sqlite3").is_file():
return candidate
if candidate.is_dir() and (candidate.parent / "chroma.sqlite3").is_file():
return candidate.parent
return candidate
class ExactStatuteLibrary:
"""Provide exact bare-act records with a JSON fallback for older releases."""
def __init__(
self,
chroma_path: str | os.PathLike[str] | None = None,
*,
fallback_path: str | os.PathLike[str] | None = None,
collection_name: str = COLLECTION_NAME,
) -> None:
self._collection = None
self._provider = "unavailable"
self._count = 0
self._fallback: dict[tuple[str, str], dict[str, Any]] = {}
self._error: str | None = None
self._root = _chroma_root(chroma_path)
self._collection_name = collection_name
if self._root and (self._root / "chroma.sqlite3").is_file():
try:
import chromadb
client = chromadb.PersistentClient(path=str(self._root))
self._collection = client.get_collection(self._collection_name)
self._count = int(self._collection.count())
self._provider = "private_chroma"
return
except Exception as exc: # keep the API alive if the optional store is damaged
self._error = type(exc).__name__
self._load_fallback(fallback_path)
@classmethod
def from_env(
cls,
*,
fallback_path: str | os.PathLike[str] | None = None,
) -> "ExactStatuteLibrary":
return cls(
os.environ.get("THEMIS_STATUTE_CHROMA", "").strip() or None,
fallback_path=fallback_path,
collection_name=os.environ.get(
"THEMIS_STATUTE_COLLECTION", COLLECTION_NAME
).strip()
or COLLECTION_NAME,
)
def _load_fallback(self, path: str | os.PathLike[str] | None) -> None:
candidate = Path(path).expanduser().resolve() if path else None
if not candidate or not candidate.is_file():
return
try:
payload = json.loads(candidate.read_text(encoding="utf-8"))
for item in payload if isinstance(payload, list) else []:
metadata = item.get("metadata") if isinstance(item, dict) else None
if not isinstance(metadata, dict):
continue
act = normalise_act(metadata.get("act_short"))
section = normalise_section(metadata.get("section_number"))
if not act or not section:
continue
self._fallback[(act, section)] = {
"act": act,
"act_name": metadata.get("act_name"),
"section": section,
"title": metadata.get("title"),
"text": str(item.get("retrieval_text") or "").replace("\x00", "").strip(),
}
if self._fallback:
self._provider = "release_json"
self._count = len(self._fallback)
except Exception as exc:
self._error = self._error or type(exc).__name__
def lookup(self, act: object, section: object) -> dict[str, Any] | None:
code = normalise_act(act)
number = normalise_section(section)
if not code or not number:
return None
if self._collection is None:
value = self._fallback.get((code, number))
return dict(value) if value else None
try:
result = self._collection.get(
where={
"$and": [
{"act_short": {"$in": STORED_ACT_CODES.get(code, [code])}},
{"section_number": {"$eq": number}},
]
},
include=["documents", "metadatas"],
)
except Exception:
return None
if not result.get("ids"):
return None
metadata = (result.get("metadatas") or [{}])[0] or {}
documents = result.get("documents") or [""]
return {
"act": code,
"act_name": metadata.get("act_name"),
"section": number,
"title": metadata.get("title"),
"text": str(documents[0] or "").replace("\x00", "").strip(),
}
def status(self) -> dict[str, Any]:
return {
"configured": self._root is not None,
"ready": self._provider != "unavailable",
"provider": self._provider,
"collection": self._collection_name,
"provisions": self._count,
"lookup": "exact_act_and_section_only",
"semantic_conversion": False,
"judgment_embeddings_used": False,
"error": self._error,
}