feat(Day 49 phase 1.5): GET /v1/doctrines/{id} for dossier expand
Browse filesAdds per-doctrine details endpoint for the dossier click-to-expand
UX. Returns name_he, elements, exceptions, statute_refs (full),
leading_cases (full), and pending_review flag. Keywords stay
internal — they're a tuning surface, not user-facing.
CRITICAL: registered AFTER /v1/doctrines/catalog so the static
path matches first. FastAPI routes match in registration order,
so /v1/doctrines/{doctrine_id} placed earlier would shadow catalog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- tau_rag/api/fastapi_app.py +38 -0
tau_rag/api/fastapi_app.py
CHANGED
|
@@ -22620,3 +22620,41 @@ def doctrines_catalog_public(): # type: ignore
|
|
| 22620 |
return JSONResponse(status_code=500, content={
|
| 22621 |
"ok": False, "error": f"{type(e).__name__}: {e}"
|
| 22622 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22620 |
return JSONResponse(status_code=500, content={
|
| 22621 |
"ok": False, "error": f"{type(e).__name__}: {e}"
|
| 22622 |
})
|
| 22623 |
+
|
| 22624 |
+
|
| 22625 |
+
@app.get("/v1/doctrines/{doctrine_id}")
|
| 22626 |
+
def doctrines_get_one(doctrine_id: str): # type: ignore
|
| 22627 |
+
"""Day 49 — full per-doctrine details for the dossier expand-on-click.
|
| 22628 |
+
|
| 22629 |
+
Returns name_he, elements, exceptions, statute_refs, all leading_cases,
|
| 22630 |
+
and the `pending_lawyer_review` flag. Keywords stay internal —
|
| 22631 |
+
they're a tuning surface, not user-facing.
|
| 22632 |
+
|
| 22633 |
+
NOTE: must be registered AFTER /v1/doctrines/catalog so the
|
| 22634 |
+
static path wins over the dynamic match.
|
| 22635 |
+
"""
|
| 22636 |
+
try:
|
| 22637 |
+
from ..intelligence.doctrine_classifier import load_doctrine_catalog
|
| 22638 |
+
cat = load_doctrine_catalog()
|
| 22639 |
+
idx = {d["id"]: d for d in cat.get("doctrines", [])}
|
| 22640 |
+
doc = idx.get(doctrine_id)
|
| 22641 |
+
if not doc:
|
| 22642 |
+
return JSONResponse(status_code=404, content={
|
| 22643 |
+
"ok": False, "reason": "doctrine_not_found",
|
| 22644 |
+
})
|
| 22645 |
+
return {
|
| 22646 |
+
"ok": True,
|
| 22647 |
+
"id": doc.get("id"),
|
| 22648 |
+
"name_he": doc.get("name_he"),
|
| 22649 |
+
"name_en": doc.get("name_en"),
|
| 22650 |
+
"domain": doc.get("domain"),
|
| 22651 |
+
"elements": doc.get("elements") or [],
|
| 22652 |
+
"exceptions": doc.get("exceptions") or [],
|
| 22653 |
+
"statute_refs": doc.get("statute_refs") or [],
|
| 22654 |
+
"leading_cases": doc.get("leading_cases") or [],
|
| 22655 |
+
"pending_review": bool(doc.get("_pending_lawyer_review")),
|
| 22656 |
+
}
|
| 22657 |
+
except Exception as e:
|
| 22658 |
+
return JSONResponse(status_code=500, content={
|
| 22659 |
+
"ok": False, "error": f"{type(e).__name__}: {e}"
|
| 22660 |
+
})
|