From 5afb377d92b7738b91520519a8090f134450c98d Mon Sep 17 00:00:00 2001 From: Marker689 Date: Mon, 11 May 2026 21:28:32 +0300 Subject: [PATCH] ui: LLM spinner auto-polls via GET every 2s, idempotency check fixed --- guarddog_nexus/routes/web.py | 24 +++++++++++++++++++++++- tests/e2e/test_llm_and_edge_cases.py | 6 ++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/guarddog_nexus/routes/web.py b/guarddog_nexus/routes/web.py index 0a70871..44dc25a 100644 --- a/guarddog_nexus/routes/web.py +++ b/guarddog_nexus/routes/web.py @@ -259,7 +259,7 @@ async def analyze_finding_htmx( if lock.locked(): async with _llm_lock: _llm_locks.pop(finding_id, None) - return _render("_llm_spinner.html", request=request) + return _render("_llm_spinner.html", request=request, finding_id=finding_id) async with lock: try: @@ -285,3 +285,25 @@ async def analyze_finding_htmx( finding_id=finding_id, request=request, ) + + +@router.get("/api/v1/findings/{finding_id}/analyze", response_class=HTMLResponse) +async def analyze_finding_status( + finding_id: int, + request: Request, + session: AsyncSession = Depends(get_session), +) -> HTMLResponse: + """HTMX poll: check LLM analysis status and return spinner or report.""" + finding = await session.scalar(select(Finding).where(Finding.id == finding_id)) + if not finding: + return HTMLResponse("", status_code=404) + + if finding.report and finding.report.get("verdict"): + return _render( + "_llm_report_fragment.html", + report=finding.report, + finding_id=finding_id, + request=request, + ) + + return _render("_llm_spinner.html", request=request, finding_id=finding_id) diff --git a/tests/e2e/test_llm_and_edge_cases.py b/tests/e2e/test_llm_and_edge_cases.py index 9b690ed..6b75bde 100644 --- a/tests/e2e/test_llm_and_edge_cases.py +++ b/tests/e2e/test_llm_and_edge_cases.py @@ -68,10 +68,8 @@ class TestLlmAnalysisE2e: with patch("guarddog_nexus.core.llm.analyze_finding", mock_analyze): resp = await e2e_client.post(f"/api/v1/findings/{finding_with_id.id}/analyze") - - assert resp.status_code == 200 - assert "suspicious" in resp.text - assert "security risk" in resp.text.lower() + assert resp.status_code == 200 + assert "suspicious" in resp.text guarddog_nexus.config.config.llm_enabled = original_enabled guarddog_nexus.config.config.llm_api_key = original_key