Spaces:
Sleeping
Sleeping
File size: 5,341 Bytes
b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 732b14f b76f199 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | """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()
@router.post(
"/reports/{report_id}/agentic/generate",
summary="Generate a full RICS-style report using an agentic pipeline",
)
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))
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),
)
)
if cas_result.rowcount == 0:
raise HTTPException(
status_code=409,
detail="A generation task is already running for this report.",
)
await db.commit()
bullets_map = {
str(k): list(v) for k, v in bullets_by_section.items() if isinstance(v, list)
}
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()],
}
|