Add chair feedback system and content checklists for block-yod
Backend changes cherry-picked from ui-rewrite branch to enable feedback API endpoints for the Next.js staging UI. - chair_feedback DB table + API endpoints (GET/POST/PATCH) - Content checklists by appeal subtype injected into block-yod prompt - MCP tools for recording and listing chair feedback - Corpus analysis documentation (24 decisions) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
127
web/app.py
127
web/app.py
@@ -2436,6 +2436,133 @@ async def api_reprocess_document(case_number: str, doc_id: str):
|
||||
return {"status": "reprocessing"}
|
||||
|
||||
|
||||
# ── Chair feedback endpoints ──────────────────────────────────────
|
||||
|
||||
|
||||
@app.get("/api/feedback")
|
||||
async def api_list_feedback(
|
||||
case_number: str = "",
|
||||
category: str = "",
|
||||
unresolved_only: bool = False,
|
||||
):
|
||||
"""List chair feedback, optionally filtered by case/category."""
|
||||
case_id = None
|
||||
if case_number:
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if case:
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
feedbacks = await db.list_chair_feedback(
|
||||
case_id=case_id,
|
||||
category=category or None,
|
||||
unresolved_only=unresolved_only,
|
||||
)
|
||||
|
||||
items = []
|
||||
# Build case_number lookup
|
||||
case_numbers: dict[str, str] = {}
|
||||
pool = await db.get_pool()
|
||||
for fb in feedbacks:
|
||||
cid = fb.get("case_id")
|
||||
cn = ""
|
||||
if cid and str(cid) not in case_numbers:
|
||||
async with pool.acquire() as conn:
|
||||
row = await conn.fetchrow(
|
||||
"SELECT case_number, title FROM cases WHERE id = $1", cid,
|
||||
)
|
||||
if row:
|
||||
case_numbers[str(cid)] = row["case_number"]
|
||||
if cid:
|
||||
cn = case_numbers.get(str(cid), "")
|
||||
|
||||
items.append({
|
||||
"id": str(fb["id"]),
|
||||
"case_id": str(fb["case_id"]) if fb["case_id"] else None,
|
||||
"case_number": cn,
|
||||
"block_id": fb["block_id"],
|
||||
"category": fb["category"],
|
||||
"feedback_text": fb["feedback_text"],
|
||||
"lesson_extracted": fb["lesson_extracted"],
|
||||
"resolved": fb["resolved"],
|
||||
"applied_to": fb.get("applied_to", []),
|
||||
"created_at": fb["created_at"].isoformat() if fb.get("created_at") else None,
|
||||
})
|
||||
|
||||
return items
|
||||
|
||||
|
||||
@app.post("/api/feedback")
|
||||
async def api_create_feedback(
|
||||
case_number: str = Form(""),
|
||||
block_id: str = Form("block-yod"),
|
||||
feedback_text: str = Form(...),
|
||||
category: str = Form("missing_content"),
|
||||
lesson_extracted: str = Form(""),
|
||||
):
|
||||
"""Record a new chair feedback entry."""
|
||||
case_id = None
|
||||
if case_number:
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if case:
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
valid_categories = [
|
||||
"missing_content", "wrong_tone", "wrong_structure",
|
||||
"factual_error", "style", "other",
|
||||
]
|
||||
if category not in valid_categories:
|
||||
raise HTTPException(400, f"קטגוריה לא חוקית. אפשרויות: {', '.join(valid_categories)}")
|
||||
|
||||
feedback_id = await db.record_chair_feedback(
|
||||
case_id=case_id,
|
||||
block_id=block_id,
|
||||
feedback_text=feedback_text,
|
||||
category=category,
|
||||
lesson_extracted=lesson_extracted,
|
||||
)
|
||||
|
||||
return {"id": str(feedback_id), "status": "created"}
|
||||
|
||||
|
||||
@app.post("/api/feedback/json")
|
||||
async def api_create_feedback_json(body: dict):
|
||||
"""Record a new chair feedback entry (JSON body)."""
|
||||
case_number = body.get("case_number", "")
|
||||
case_id = None
|
||||
if case_number:
|
||||
case = await db.get_case_by_number(case_number)
|
||||
if case:
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
valid_categories = [
|
||||
"missing_content", "wrong_tone", "wrong_structure",
|
||||
"factual_error", "style", "other",
|
||||
]
|
||||
category = body.get("category", "missing_content")
|
||||
if category not in valid_categories:
|
||||
raise HTTPException(400, f"קטגוריה לא חוקית. אפשרויות: {', '.join(valid_categories)}")
|
||||
|
||||
feedback_id = await db.record_chair_feedback(
|
||||
case_id=case_id,
|
||||
block_id=body.get("block_id", "block-yod"),
|
||||
feedback_text=body.get("feedback_text", ""),
|
||||
category=category,
|
||||
lesson_extracted=body.get("lesson_extracted", ""),
|
||||
)
|
||||
|
||||
return {"id": str(feedback_id), "status": "created"}
|
||||
|
||||
|
||||
@app.patch("/api/feedback/{feedback_id}/resolve")
|
||||
async def api_resolve_feedback(feedback_id: str, body: dict):
|
||||
"""Mark feedback as resolved."""
|
||||
await db.resolve_chair_feedback(
|
||||
feedback_id=UUID(feedback_id),
|
||||
applied_to=body.get("applied_to", []),
|
||||
)
|
||||
return {"status": "resolved"}
|
||||
|
||||
|
||||
# ── Background Processing ─────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user