Spaces:
Runtime error
Runtime error
| """Agentic RICS inspection endpoints.""" | |
| from __future__ import annotations | |
| import logging | |
| from datetime import UTC, datetime | |
| from fastapi import APIRouter, Depends, HTTPException, Request | |
| from sqlalchemy import update | |
| from sqlalchemy.ext.asyncio import AsyncSession | |
| from app.api import rate_limit | |
| from app.db.database import get_db | |
| from app.db.models import Report, ReportStatus | |
| from app.jobs.models import GenerationJob, JobType | |
| from app.jobs.queue import dispatch_or_enqueue | |
| from app.services.generation import mark_report_generation_failed, run_agentic_full_report_job | |
| from app.agentic.runtime_status import is_openai_inspector_live | |
| from app.agentic.tools import list_tool_specs | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter() | |
| async def agentic_generate_report( | |
| report_id: str, | |
| request: Request, | |
| body: dict, | |
| db: AsyncSession = Depends(get_db), | |
| _rl: None = Depends(rate_limit.check_generate), | |
| ) -> dict: | |
| tenant_id: str = request.state.tenant_id | |
| report = await db.get(Report, report_id) | |
| if report is None or report.tenant_id != tenant_id: | |
| raise HTTPException(status_code=404, detail="Report not found") | |
| bullets_by_section = body.get("bullets_by_section") or {} | |
| if not isinstance(bullets_by_section, dict) or not bullets_by_section: | |
| raise HTTPException(status_code=422, detail="bullets_by_section must be a non-empty object") | |
| ai_percent = body.get("ai_percent", 50) | |
| retrieval_level = body.get("retrieval_level", "paragraph") | |
| reference_document_ids = body.get("reference_document_ids") or [] | |
| similarity_scan = bool(body.get("similarity_scan", False)) | |
| peer_sections = body.get("peer_sections") if isinstance(body.get("peer_sections"), dict) else {} | |
| sim_excl = body.get("similarity_exclude_document_ids") | |
| similarity_exclude_document_ids = ( | |
| [str(x) for x in sim_excl] if isinstance(sim_excl, list) else [] | |
| ) | |
| interference_level = body.get("interference_level") | |
| il_arg = str(interference_level).strip() if interference_level is not None else None | |
| try: | |
| ai_percent_i = int(ai_percent) | |
| except Exception: # noqa: BLE001 | |
| ai_percent_i = 50 | |
| ai_percent_i = max(0, min(100, ai_percent_i)) | |
| bullets_map = { | |
| str(k): list(v) for k, v in bullets_by_section.items() if isinstance(v, list) | |
| } | |
| cas_result = await db.execute( | |
| update(Report) | |
| .where( | |
| Report.id == report_id, | |
| Report.tenant_id == tenant_id, | |
| Report.status != ReportStatus.generating, | |
| ) | |
| .values( | |
| status=ReportStatus.generating, | |
| generation_started_at=datetime.now(UTC), | |
| generation_section_total=len(bullets_map) if bullets_map else None, | |
| ) | |
| ) | |
| if cas_result.rowcount == 0: | |
| raise HTTPException( | |
| status_code=409, | |
| detail="A generation task is already running for this report.", | |
| ) | |
| await db.commit() | |
| ref_ids = ( | |
| [str(x) for x in reference_document_ids] | |
| if isinstance(reference_document_ids, list) | |
| else None | |
| ) | |
| peer_map = {str(k): str(v) for k, v in (peer_sections or {}).items()} | |
| agentic_job = GenerationJob( | |
| job_type=JobType.agentic_full, | |
| report_id=report_id, | |
| tenant_id=tenant_id, | |
| payload={ | |
| "bullets_by_section": bullets_map, | |
| "ai_percent": ai_percent_i, | |
| "retrieval_level": str(retrieval_level), | |
| "reference_document_ids": ref_ids, | |
| "similarity_scan": similarity_scan, | |
| "peer_sections": peer_map, | |
| "similarity_exclude_document_ids": similarity_exclude_document_ids or None, | |
| "interference_level": il_arg, | |
| }, | |
| ) | |
| async def _run_agentic_task() -> None: | |
| try: | |
| await run_agentic_full_report_job( | |
| report_id, | |
| tenant_id, | |
| bullets_by_section=bullets_map, | |
| ai_percent=ai_percent_i, | |
| retrieval_level=str(retrieval_level), | |
| reference_document_ids=ref_ids, | |
| similarity_scan=similarity_scan, | |
| peer_sections=peer_map, | |
| similarity_exclude_document_ids=similarity_exclude_document_ids or None, | |
| interference_level=il_arg, | |
| ) | |
| except Exception as exc: # noqa: BLE001 | |
| logger.exception("Agentic background task failed report=%s", report_id) | |
| await mark_report_generation_failed(report_id, tenant_id, str(exc)) | |
| queue_mode = await dispatch_or_enqueue(job=agentic_job, inline_factory=_run_agentic_task) | |
| inspector_mode = "openai_tool_agent" if is_openai_inspector_live() else "legacy_pipeline" | |
| queue_hint = " (Redis worker)" if queue_mode == "redis" else "" | |
| return { | |
| "report_id": report_id, | |
| "status": "generating", | |
| "message": ( | |
| f"Agentic generation queued{queue_hint}. " | |
| f"Poll GET /reports/{report_id}/status or " | |
| f"GET /reports/{report_id}/sections for results." | |
| ), | |
| "inspector_mode": inspector_mode, | |
| "ai_percent": ai_percent_i, | |
| "retrieval_level": str(retrieval_level), | |
| "agent_tools": [s.name for s in list_tool_specs()], | |
| } | |