""" rfi.py - Stage 11: RFI / QAQC comment generation (LLM, text-only). Drafts professional, evidence-based RFI / QAQC comments from the prioritized issue list. Uses TEXT_MODEL (defaults to MODEL). Returns [] on failure. """ from typing import Dict, List from backend import config from backend.pipeline._serialize import dumps from backend.pipeline._stage import call_stage, collect_list from backend.prompts import RFI_SYSTEM_PROMPT, RFI_USER_INSTRUCTION def _valid_rfi(r: Dict) -> Dict: """Keep an RFI only if it has a question or title; coerce list fields.""" if not isinstance(r, dict): return None if not (r.get("question") or r.get("title")): return None return { "rfi_id": r.get("rfi_id") or "", "issue_id": r.get("issue_id") or "", "title": (r.get("title") or "").strip(), "question": (r.get("question") or "").strip(), "background": (r.get("background") or "").strip(), "sheets_referenced": r.get("sheets_referenced") or [], "disciplines_to_respond": r.get("disciplines_to_respond") or [], "suggested_response_needed": (r.get("suggested_response_needed") or "").strip(), "priority": (r.get("priority") or "medium").strip().lower(), } def generate_rfis(prioritized: List[Dict]) -> List[Dict]: if not prioritized: return [] parsed = call_stage( RFI_SYSTEM_PROMPT, RFI_USER_INSTRUCTION, subs={"prioritized_issues": dumps(prioritized)}, max_tokens=config.RFI_MAX_TOKENS, ) rfis = collect_list(parsed, "rfi_comments", _valid_rfi) print(f"[RFI] drafted {len(rfis)} RFI/QAQC comment(s)") return rfis