File size: 2,209 Bytes
5db2259
 
 
15f7bcc
 
5db2259
 
15f7bcc
5db2259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15f7bcc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from dotenv import load_dotenv
load_dotenv()  # no-op if .env doesn't exist (e.g. on HF Spaces, where env vars are set directly)

import json

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel

from app.graph.build import build_graph

app = FastAPI(title="VeriScite", version="0.1.0")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # tighten to your Vercel domain before deploying
    allow_methods=["*"],
    allow_headers=["*"],
)

graph = build_graph()


class AuditRequest(BaseModel):
    text: str  # pasted paper/abstract text


@app.get("/health")
async def health():
    return {"status": "ok"}


@app.post("/audit")
async def audit(req: AuditRequest):
    initial_state = {
        "source_text": req.text,
        "claims": [],
        "audits": [],
        "current_index": 0,
        "report": None,
    }
    final_state = await graph.ainvoke(initial_state)
    return final_state["report"]


@app.post("/audit/stream")
async def audit_stream(req: AuditRequest):
    """Server-Sent Events version of /audit — emits a live event after each
    graph node (and after each individual tool call inside the escalate
    agent loop) completes, rather than only returning at the very end.
    Powers the frontend's live agent-workflow visualization.

    Event shapes:
      {"type": "progress", ...node-specific fields, see nodes.py _writer() calls}
      {"type": "final", "report": {...}}   -- sent once, at the end
    """
    initial_state = {
        "source_text": req.text,
        "claims": [],
        "audits": [],
        "current_index": 0,
        "report": None,
    }

    async def event_generator():
        async for mode, chunk in graph.astream(initial_state, stream_mode=["custom", "values"]):
            if mode == "custom":
                yield f"data: {json.dumps({'type': 'progress', **chunk})}\n\n"
            elif mode == "values" and chunk.get("report") is not None:
                yield f"data: {json.dumps({'type': 'final', 'report': chunk['report']})}\n\n"

    return StreamingResponse(event_generator(), media_type="text/event-stream")