citationEdge / CitationEdge_Architecture.drawio
omkarkudalkar222's picture
Fresh deployment without history
0e38162
Raw
History Blame Contribute Delete
32.3 kB
<mxfile host="app.diagrams.net" version="21.0.0">
<diagram name="CitationEdge Architecture" id="arch1">
<mxGraphModel dx="1800" dy="900" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="2400" pageHeight="2200" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<!-- TITLE -->
<mxCell id="title" value="CitationEdge β€” Agentic Academic Paper Analysis System" style="text;strokeColor=none;fillColor=none;align=center;fontSize=22;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="200" y="16" width="1900" height="40" as="geometry" />
</mxCell>
<!-- ═══ LAYER 1: PRESENTATION ═══ -->
<mxCell id="L1" value="LAYER 1 β€” PRESENTATION" style="swimlane;startSize=30;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;fontStyle=1;fontColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="20" y="70" width="2160" height="225" as="geometry" />
</mxCell>
<mxCell id="user" value="User / Browser" style="rounded=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=2;fontSize=12;fontStyle=1;" vertex="1" parent="L1">
<mxGeometry x="20" y="65" width="150" height="55" as="geometry" />
</mxCell>
<mxCell id="react" value="React 18 + Vite 5&#xa;Port 5173 (dev)&#xa;Why: component model, hot-reload, ESM bundling, built-in proxy" style="rounded=1;whiteSpace=wrap;fillColor=#ffffff;strokeColor=#6c8ebf;strokeWidth=2;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="200" y="40" width="300" height="75" as="geometry" />
</mxCell>
<mxCell id="p-upload" value="Upload Page&#xa;drag-and-drop PDF" style="rounded=1;whiteSpace=wrap;fillColor=#cfe2ff;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="200" y="130" width="140" height="55" as="geometry" />
</mxCell>
<mxCell id="p-jobs" value="Jobs Page&#xa;list + delete jobs" style="rounded=1;whiteSpace=wrap;fillColor=#cfe2ff;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="350" y="130" width="140" height="55" as="geometry" />
</mxCell>
<mxCell id="p-results" value="Results Page&#xa;scores, claims, gaps" style="rounded=1;whiteSpace=wrap;fillColor=#cfe2ff;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="500" y="130" width="140" height="55" as="geometry" />
</mxCell>
<mxCell id="p-graph" value="Graph Page&#xa;D3 force-graph viz" style="rounded=1;whiteSpace=wrap;fillColor=#cfe2ff;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="650" y="130" width="140" height="55" as="geometry" />
</mxCell>
<mxCell id="p-docs" value="Docs Page&#xa;API reference" style="rounded=1;whiteSpace=wrap;fillColor=#cfe2ff;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="800" y="130" width="140" height="55" as="geometry" />
</mxCell>
<mxCell id="p-login" value="Login Page&#xa;Firebase Auth UI" style="rounded=1;whiteSpace=wrap;fillColor=#cfe2ff;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="950" y="130" width="140" height="55" as="geometry" />
</mxCell>
<mxCell id="vite-proxy" value="Vite Dev Proxy&#xa;Routes: /analyze /jobs /reports&#xa;/graph /ws β†’ localhost:8004&#xa;Why: no CORS in dev" style="rounded=1;whiteSpace=wrap;fillColor=#e8f5e9;strokeColor=#2e7d32;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="1130" y="50" width="240" height="90" as="geometry" />
</mxCell>
<mxCell id="tailwind" value="TailwindCSS + CSS Variables&#xa;Dark glassmorphism theme&#xa;Why: utility-first, zero runtime" style="rounded=1;whiteSpace=wrap;fillColor=#f3e5f5;strokeColor=#6a1b9a;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="1390" y="55" width="240" height="75" as="geometry" />
</mxCell>
<mxCell id="d3" value="D3.js (Graph Page)&#xa;Force-directed Neo4j viz&#xa;Why: best interactive graph lib" style="rounded=1;whiteSpace=wrap;fillColor=#e3f2fd;strokeColor=#1565c0;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="1650" y="55" width="240" height="75" as="geometry" />
</mxCell>
<mxCell id="firebase-fe" value="Firebase Auth&#xa;Google OAuth + Email/Password&#xa;JWT β†’ React AuthContext&#xa;Why: managed auth, free tier" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;fontSize=11;" vertex="1" parent="L1">
<mxGeometry x="1910" y="50" width="220" height="90" as="geometry" />
</mxCell>
<!-- ═══ LAYER 2: API GATEWAY ═══ -->
<mxCell id="L2" value="LAYER 2 β€” API GATEWAY" style="swimlane;startSize=30;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;fontStyle=1;fontColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="20" y="315" width="2160" height="200" as="geometry" />
</mxCell>
<mxCell id="fastapi" value="FastAPI β€” Port 8004&#xa;Why: async/await, auto OpenAPI docs,&#xa;Pydantic validation, WebSocket support" style="rounded=1;whiteSpace=wrap;fillColor=#ffffff;strokeColor=#82b366;strokeWidth=2;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="20" y="45" width="290" height="75" as="geometry" />
</mxCell>
<mxCell id="uvicorn" value="Uvicorn ASGI&#xa;Non-blocking I/O&#xa;Why: pipeline runs fully async" style="rounded=1;whiteSpace=wrap;fillColor=#e8f5e9;strokeColor=#82b366;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="330" y="45" width="220" height="75" as="geometry" />
</mxCell>
<mxCell id="pydantic" value="Pydantic Settings&#xa;config.py reads .env&#xa;lru_cache singleton" style="rounded=1;whiteSpace=wrap;fillColor=#e8f5e9;strokeColor=#82b366;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="570" y="45" width="200" height="75" as="geometry" />
</mxCell>
<mxCell id="ep1" value="POST /analyze&#xa;Upload PDF β†’ job_id" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="800" y="40" width="175" height="55" as="geometry" />
</mxCell>
<mxCell id="ep2" value="GET /jobs/{id}&#xa;status + per-agent progress" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="985" y="40" width="185" height="55" as="geometry" />
</mxCell>
<mxCell id="ep3" value="GET /reports/{id}/json,pdf&#xa;scores, claims, gaps + PDF" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="1180" y="40" width="195" height="55" as="geometry" />
</mxCell>
<mxCell id="ep4" value="GET /reports/{id}/graph&#xa;Neo4j subgraph as JSON" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="1385" y="40" width="185" height="55" as="geometry" />
</mxCell>
<mxCell id="ep5" value="WS /ws/progress/{id}&#xa;live push every ~1 second" style="rounded=1;whiteSpace=wrap;fillColor=#fff9c4;strokeColor=#f57f17;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="1580" y="40" width="185" height="55" as="geometry" />
</mxCell>
<mxCell id="ep6" value="GET /health/ready&#xa;Neo4j + MongoDB liveness" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="1775" y="40" width="185" height="55" as="geometry" />
</mxCell>
<mxCell id="ep-del" value="DELETE /jobs/{id}&#xa;soft-delete job record" style="rounded=1;whiteSpace=wrap;fillColor=#ffd7d7;strokeColor=#c0392b;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="800" y="110" width="175" height="55" as="geometry" />
</mxCell>
<mxCell id="ep-graph2" value="GET /graph/stats&#xa;POST /graph/query (Cypher)" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="985" y="110" width="210" height="55" as="geometry" />
</mxCell>
<mxCell id="ep-fig" value="GET /reports/{id}/figures&#xa;extracted figure descriptions" style="rounded=1;whiteSpace=wrap;fillColor=#c8e6c9;strokeColor=#388e3c;fontSize=11;" vertex="1" parent="L2">
<mxGeometry x="1205" y="110" width="210" height="55" as="geometry" />
</mxCell>
<!-- ═══ LAYER 3: AGENT PIPELINE ═══ -->
<mxCell id="L3" value="LAYER 3 β€” ASYNC AGENT PIPELINE" style="swimlane;startSize=30;fillColor=#ffe6cc;strokeColor=#d6b656;fontSize=13;fontStyle=1;fontColor=#d6b656;" vertex="1" parent="1">
<mxGeometry x="20" y="535" width="2160" height="930" as="geometry" />
</mxCell>
<mxCell id="orch" value="Async Orchestrator&#xa;&#xa;asyncio.gather() per wave&#xa;Critical fail β†’ abort pipeline&#xa;Optional fail β†’ partial result&#xa;Writes progress β†’ MongoDB β†’ WS&#xa;&#xa;Injects one shared&#xa;A2AProtocol into AgentContext" style="rounded=1;whiteSpace=wrap;fillColor=#ffffff;strokeColor=#d6b656;strokeWidth=2;fontSize=11;" vertex="1" parent="L3">
<mxGeometry x="20" y="55" width="230" height="145" as="geometry" />
</mxCell>
<!-- ─── Wave 1 ─── -->
<mxCell id="W1" value="WAVE 1 β€” asyncio.gather()" style="swimlane;startSize=25;fillColor=#fff3e0;strokeColor=#e65100;fontSize=11;fontStyle=1;fontColor=#e65100;" vertex="1" parent="L3">
<mxGeometry x="280" y="40" width="1020" height="155" as="geometry" />
</mxCell>
<mxCell id="a-parser" value="ParserAgent β˜… CRITICAL&#xa;&#xa;PyMuPDF: extract text,&#xa;sections, paragraphs, refs&#xa;β†’ writes Doc/Section/Para β†’ Neo4j" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;strokeWidth=2;fontSize=11;" vertex="1" parent="W1">
<mxGeometry x="20" y="35" width="290" height="100" as="geometry" />
</mxCell>
<mxCell id="a-enhanced" value="EnhancedParserAgent β˜… CRITICAL&#xa;&#xa;Layout metadata, tables,&#xa;structured section detection&#xa;β†’ SciBERT embed (768-dim)&#xa;β†’ paragraphs β†’ LanceDB" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;strokeWidth=2;fontSize=11;" vertex="1" parent="W1">
<mxGeometry x="330" y="35" width="310" height="100" as="geometry" />
</mxCell>
<mxCell id="a-visual" value="VisualParserAgent (optional)&#xa;&#xa;PyMuPDF: β‰₯8 paths OR image = figure&#xa;Rasterize at 1.8Γ— zoom β†’ base64&#xa;β†’ Groq LLaMA 4 Scout Vision API" style="rounded=1;whiteSpace=wrap;fillColor=#fff8e1;strokeColor=#f9a825;fontSize=11;" vertex="1" parent="W1">
<mxGeometry x="660" y="35" width="340" height="100" as="geometry" />
</mxCell>
<!-- ─── Wave 2 ─── -->
<mxCell id="W2" value="WAVE 2 β€” asyncio.gather() | both agents register handlers on A2A after completing" style="swimlane;startSize=25;fillColor=#fff3e0;strokeColor=#e65100;fontSize=11;fontStyle=1;fontColor=#e65100;" vertex="1" parent="L3">
<mxGeometry x="280" y="225" width="840" height="340" as="geometry" />
</mxCell>
<!-- KeywordAgent β€” full technical flow -->
<mxCell id="a-kw" value="KeywordAgent β˜… CRITICAL&#xa;&#xa;β‘  Routing: regex intent detect + cosine sim&#xa; (query_emb vs intent_embeddings β†’ argmax)&#xa;β‘‘ KBIR (ml6team/keyphrase-extraction-kbir-semeval2017)&#xa; RoBERTa BIO token classification β†’ phrase + confidence&#xa; Threshold: score β‰₯ 0.65, len &gt; 2 chars&#xa;β‘’ POS filter (spaCy): keep NOUN / PROPN / ADJ&#xa; drops verbs, adverbs, false-positive adj-only phrases&#xa;β‘£ SciBERT dedup: cosine &gt; 0.85 β†’ drop lower-scored&#xa; (allenai/scibert_scivocab_uncased, 768-dim)&#xa;β‘€ Groq LLaMA 3.3-70B β†’ type labels&#xa; (method / dataset / metric / concept)&#xa;&#xa;Output: [{term, type, score}] β†’ Keyword nodes β†’ Neo4j&#xa;β†’ registers A2A: get_keywords()&#xa;&#xa;Significance: removes ~40% duplicate near-synonyms&#xa;before CitationGapAgent runs Semantic Scholar search" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;strokeWidth=2;fontSize=11;" vertex="1" parent="W2">
<mxGeometry x="20" y="40" width="400" height="280" as="geometry" />
</mxCell>
<!-- ClaimAgent -->
<mxCell id="a-claim" value="ClaimAgent β˜… CRITICAL&#xa;&#xa;Graph-RAG: query Neo4j subgraph&#xa; (~3–6k tokens, 86% less than full paper)&#xa;β†’ Groq LLaMA 3.3-70B&#xa; extract factual assertions with&#xa; confidence + novelty scores&#xa;β†’ Claim nodes β†’ Neo4j&#xa;β†’ registers A2A: get_claims()" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;strokeWidth=2;fontSize=11;" vertex="1" parent="W2">
<mxGeometry x="440" y="40" width="370" height="160" as="geometry" />
</mxCell>
<!-- ─── A2A Protocol Registry ─── -->
<mxCell id="a2a-box" value="A2A Protocol Registry&#xa;(protocols/a2a_protocol.py)&#xa;&#xa;Shared singleton on AgentContext&#xa;In-memory dict: name β†’ handler&#xa;&#xa;Registered after Wave 2 completes:&#xa; &quot;keyword&quot; β†’ KeywordAgent._a2a_handler&#xa; &quot;claim&quot; β†’ ClaimAgent._a2a_handler&#xa;&#xa;Wave 3 agents call:&#xa; ctx.a2a.call(target, method)&#xa; β†’ direct async Python call&#xa; β†’ no DB round-trip (~1ms vs ~300ms)&#xa;&#xa;Fallback: Neo4j query if A2A&#xa;registry is unavailable" style="rounded=1;whiteSpace=wrap;fillColor=#e8f4fd;strokeColor=#1565c0;strokeWidth=2;fontSize=11;" vertex="1" parent="L3">
<mxGeometry x="1140" y="225" width="350" height="340" as="geometry" />
</mxCell>
<!-- ─── Wave 3 ─── -->
<mxCell id="W3" value="WAVE 3 β€” asyncio.gather() | uses A2A to call Wave 2 agents directly (no Neo4j re-query)" style="swimlane;startSize=25;fillColor=#fff3e0;strokeColor=#e65100;fontSize=11;fontStyle=1;fontColor=#e65100;" vertex="1" parent="L3">
<mxGeometry x="280" y="600" width="1400" height="165" as="geometry" />
</mxCell>
<mxCell id="a-cit" value="CitationGapAgent (optional)&#xa;&#xa;A2A β†’ get_keywords() [no Neo4j query]&#xa;Fallback: Neo4j if A2A unavailable&#xa;β†’ Semantic Scholar API (retry 3Γ—)&#xa;β†’ CitationGap + SuggestedPaper β†’ Neo4j" style="rounded=1;whiteSpace=wrap;fillColor=#fff8e1;strokeColor=#f9a825;strokeWidth=2;fontSize=11;" vertex="1" parent="W3">
<mxGeometry x="20" y="35" width="320" height="110" as="geometry" />
</mxCell>
<mxCell id="a-arg" value="ArgumentationAgent (optional)&#xa;&#xa;A2A β†’ get_claims() [structured dicts]&#xa;Richer than graph_memory text blob:&#xa;confidence + novelty per claim&#xa;β†’ ArgumentAnalysis β†’ Neo4j" style="rounded=1;whiteSpace=wrap;fillColor=#fff8e1;strokeColor=#f9a825;strokeWidth=2;fontSize=11;" vertex="1" parent="W3">
<mxGeometry x="360" y="35" width="320" height="110" as="geometry" />
</mxCell>
<mxCell id="a-claimverif" value="ClaimVerifierAgent (optional)&#xa;&#xa;Verify claims vs external sources&#xa;(Tavily / Semantic Scholar)&#xa;β†’ verdict + confidence β†’ Neo4j" style="rounded=1;whiteSpace=wrap;fillColor=#fff8e1;strokeColor=#f9a825;fontSize=11;" vertex="1" parent="W3">
<mxGeometry x="700" y="35" width="310" height="110" as="geometry" />
</mxCell>
<mxCell id="a-evidground" value="EvidenceGroundingAgent (optional)&#xa;&#xa;Ground claims in paper evidence&#xa;Link Claim β†’ Section spans&#xa;β†’ grounding score β†’ Neo4j" style="rounded=1;whiteSpace=wrap;fillColor=#fff8e1;strokeColor=#f9a825;fontSize=11;" vertex="1" parent="W3">
<mxGeometry x="1030" y="35" width="340" height="110" as="geometry" />
</mxCell>
<!-- ─── Wave 4 ─── -->
<mxCell id="W4" value="WAVE 4 β€” Sequential" style="swimlane;startSize=25;fillColor=#fff3e0;strokeColor=#e65100;fontSize=11;fontStyle=1;fontColor=#e65100;" vertex="1" parent="L3">
<mxGeometry x="280" y="800" width="1050" height="80" as="geometry" />
</mxCell>
<mxCell id="a-score" value="ScoringAgent β˜… CRITICAL β€” Pure math, no LLM call β€” literary = claimsΓ—0.4 + argΓ—0.4 + citationsΓ—0.2 | citation_score = max(0, 10 βˆ’ highΓ—2 βˆ’ mediumΓ—1) β†’ Score node β†’ Neo4j" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;strokeWidth=2;fontSize=11;" vertex="1" parent="W4">
<mxGeometry x="20" y="15" width="610" height="50" as="geometry" />
</mxCell>
<mxCell id="a-report" value="ReportAgent (optional) β€” ReportLab β†’ rich PDF with scores, claims, gaps, figures β†’ reports/{job_id}.pdf" style="rounded=1;whiteSpace=wrap;fillColor=#fff8e1;strokeColor=#f9a825;fontSize=11;" vertex="1" parent="W4">
<mxGeometry x="650" y="15" width="370" height="50" as="geometry" />
</mxCell>
<!-- Graph-RAG note -->
<mxCell id="graphrag" value="Graph-RAG Strategy&#xa;&#xa;Each agent queries only its own&#xa;Neo4j subgraph (~3–6k tokens)&#xa;vs 30k for full paper = 86% savings&#xa;&#xa;Retrieval methods:&#xa;β‘  Cypher (structured, primary)&#xa;β‘‘ Hybrid: 0.6Γ—keyword + 0.4Γ—ANN&#xa;β‘’ Substring fallback&#xa;&#xa;Budget cap: 5 000 tokens max" style="rounded=1;whiteSpace=wrap;fillColor=#e8eaf6;strokeColor=#3949ab;strokeWidth=2;fontSize=11;" vertex="1" parent="L3">
<mxGeometry x="1650" y="50" width="290" height="230" as="geometry" />
</mxCell>
<!-- ═══ LAYER 4: DATA STORAGE ═══ -->
<mxCell id="L4" value="LAYER 4 β€” DATA STORAGE" style="swimlane;startSize=30;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=13;fontStyle=1;fontColor=#9673a6;" vertex="1" parent="1">
<mxGeometry x="20" y="1485" width="2160" height="255" as="geometry" />
</mxCell>
<mxCell id="neo4j" value="Neo4j AuraDB (Cloud)&#xa;&#xa;URI: neo4j+ssc:// (SSL bypass β€” fixes WinError)&#xa;Why: native graph queries, relationship traversal,&#xa;shared inter-agent memory, no SQL JOINs needed&#xa;&#xa;Nodes: Document Β· Section Β· Paragraph&#xa; Claim Β· Keyword Β· CitationGap&#xa; SuggestedPaper Β· Score Β· ArgumentNode&#xa;&#xa;Rels: CONTAINS Β· HAS_CLAIM Β· SUPPORTS&#xa; CONTRADICTS Β· CITES Β· HAS_SCORE" style="rounded=1;whiteSpace=wrap;fillColor=#f3e5f5;strokeColor=#9673a6;strokeWidth=2;fontSize=11;" vertex="1" parent="L4">
<mxGeometry x="20" y="50" width="390" height="180" as="geometry" />
</mxCell>
<mxCell id="mongo" value="MongoDB (localhost:27017)&#xa;&#xa;DB: citationedge_agentic&#xa;Why: flexible schema, no migrations,&#xa;nested result documents&#xa;&#xa;Collection: jobs&#xa; status, progress, agent states&#xa;&#xa;Collection: results&#xa; scores, keywords, claims, gaps" style="rounded=1;whiteSpace=wrap;fillColor=#f3e5f5;strokeColor=#9673a6;strokeWidth=2;fontSize=11;" vertex="1" parent="L4">
<mxGeometry x="430" y="50" width="350" height="180" as="geometry" />
</mxCell>
<mxCell id="lance" value="LanceDB (./data/vectors)&#xa;&#xa;Embedded columnar vector DB&#xa;Why: no server needed, fast ANN search,&#xa;paragraph-level semantic retrieval&#xa;&#xa;Embedding model: SciBERT&#xa; allenai/scibert_scivocab_uncased&#xa; 768-dim vectors, mean pooling + L2 norm&#xa; Trained on 1.14M scientific papers&#xa; Understands domain vocab: ablation,&#xa; citation gap, transformer, baseline&#xa;&#xa;Hybrid search:&#xa; 0.6 Γ— keyword score (Cypher)&#xa;+ 0.4 Γ— ANN vector score (SciBERT)" style="rounded=1;whiteSpace=wrap;fillColor=#f3e5f5;strokeColor=#9673a6;strokeWidth=2;fontSize=11;" vertex="1" parent="L4">
<mxGeometry x="800" y="50" width="350" height="180" as="geometry" />
</mxCell>
<mxCell id="fs" value="File System&#xa;&#xa;uploads/&#xa; temp PDFs (auto-cleaned)&#xa;&#xa;reports/&#xa; generated PDF reports&#xa;&#xa;data/vectors/&#xa; LanceDB tables" style="rounded=1;whiteSpace=wrap;fillColor=#f3e5f5;strokeColor=#9673a6;fontSize=11;" vertex="1" parent="L4">
<mxGeometry x="1170" y="50" width="250" height="180" as="geometry" />
</mxCell>
<!-- ═══ LAYER 5: EXTERNAL SERVICES ═══ -->
<mxCell id="L5" value="LAYER 5 β€” EXTERNAL SERVICES" style="swimlane;startSize=30;fillColor=#f8cecc;strokeColor=#b85450;fontSize=13;fontStyle=1;fontColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="20" y="1760" width="2160" height="240" as="geometry" />
</mxCell>
<mxCell id="groq" value="Groq Cloud API&#xa;&#xa;Model 1: LLaMA 3.3-70B-Versatile&#xa; β†’ all text agents&#xa;&#xa;Model 2: LLaMA 4 Scout 17B-16E&#xa; β†’ vision (figures / flowcharts)&#xa;&#xa;Why: free tier 100k tokens/day,&#xa;fastest inference vs OpenAI/Anthropic" style="rounded=1;whiteSpace=wrap;fillColor=#ffebee;strokeColor=#b85450;strokeWidth=2;fontSize=11;" vertex="1" parent="L5">
<mxGeometry x="20" y="50" width="340" height="165" as="geometry" />
</mxCell>
<mxCell id="semscholar" value="Semantic Scholar API&#xa;&#xa;Endpoint: /graph/v1/paper/search&#xa;&#xa;Returns: title, authors, year,&#xa;abstract, citationCount, DOI&#xa;&#xa;Why: free, no API key required,&#xa;comprehensive academic coverage&#xa;&#xa;Used by: CitationGapAgent (retry 3Γ—)" style="rounded=1;whiteSpace=wrap;fillColor=#ffebee;strokeColor=#b85450;strokeWidth=2;fontSize=11;" vertex="1" parent="L5">
<mxGeometry x="380" y="50" width="320" height="165" as="geometry" />
</mxCell>
<mxCell id="firebase-ext" value="Firebase Authentication&#xa;&#xa;Google OAuth + Email/Password&#xa;JWT tokens β†’ React AuthContext&#xa;User avatar dropdown in Navbar&#xa;&#xa;Why: fully managed auth service,&#xa;free tier, no backend auth code,&#xa;built-in token refresh" style="rounded=1;whiteSpace=wrap;fillColor=#fff3e0;strokeColor=#e65100;strokeWidth=2;fontSize=11;" vertex="1" parent="L5">
<mxGeometry x="720" y="50" width="310" height="165" as="geometry" />
</mxCell>
<mxCell id="scibert-box" value="SciBERT (Local Model)&#xa;allenai/scibert_scivocab_uncased&#xa;&#xa;Loaded via HuggingFace Transformers&#xa;Runs on CPU (or CUDA if available)&#xa;Lazy-loaded, singleton instance&#xa;&#xa;768-dim embeddings via mean pooling&#xa;L2-normalised for cosine ANN search&#xa;Max 512 tokens (truncated)&#xa;&#xa;Why: domain-specific vocab from&#xa;1.14M Semantic Scholar papers β€”&#xa;outperforms MiniLM on sci text" style="rounded=1;whiteSpace=wrap;fillColor=#e8f5e9;strokeColor=#2e7d32;strokeWidth=2;fontSize=11;" vertex="1" parent="L5">
<mxGeometry x="1050" y="50" width="320" height="165" as="geometry" />
</mxCell>
<mxCell id="planned" value="PLANNED β€” Future Integrations&#xa;&#xa;Tavily Search API&#xa; web + arXiv fallback for claims&#xa;&#xa;arXiv API&#xa; CS/ML paper existence check&#xa;&#xa;DeBERTa NLI (local model)&#xa; contradiction detection&#xa;&#xa;PageIndex Retriever&#xa; LLM-reasoned ToC navigation" style="rounded=1;whiteSpace=wrap;fillColor=#fce4ec;strokeColor=#b85450;strokeDasharray=8;fontSize=11;fontStyle=2;" vertex="1" parent="L5">
<mxGeometry x="1390" y="50" width="330" height="165" as="geometry" />
</mxCell>
<!-- ═══ ARROWS ═══ -->
<!-- User β†’ React -->
<mxCell id="e1" value="HTTP" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#6c8ebf;strokeWidth=2;flowAnimation=1;animationDuration=2000;" edge="1" source="user" target="react" parent="L1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- React β†’ FastAPI -->
<mxCell id="e2" value="REST + WebSocket" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#82b366;strokeWidth=2;flowAnimation=1;animationDuration=1500;" edge="1" source="react" target="fastapi" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- FastAPI β†’ Orchestrator -->
<mxCell id="e3" value="spawn job" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#d6b656;strokeWidth=2;flowAnimation=1;animationDuration=800;" edge="1" source="fastapi" target="orch" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- FastAPI β†’ MongoDB -->
<mxCell id="e4" value="job state" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=2;flowAnimation=1;animationDuration=1000;" edge="1" source="fastapi" target="mongo" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Orchestrator β†’ Wave 1 -->
<mxCell id="e5" value="" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#e65100;strokeWidth=2;flowAnimation=1;animationDuration=600;" edge="1" source="orch" target="W1" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Wave 1 β†’ Wave 2 -->
<mxCell id="e6" value="Neo4j ready" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#e65100;strokeWidth=2;dashed=1;flowAnimation=1;animationDuration=1200;" edge="1" source="W1" target="W2" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Wave 2 β†’ Wave 3 -->
<mxCell id="e7" value="Wave 2 done&#xa;A2A registered" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#e65100;strokeWidth=2;dashed=1;flowAnimation=1;animationDuration=1200;" edge="1" source="W2" target="W3" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Wave 3 β†’ Wave 4 -->
<mxCell id="e7b" value="all done" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#e65100;strokeWidth=2;dashed=1;flowAnimation=1;animationDuration=1200;" edge="1" source="W3" target="W4" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Parser β†’ Neo4j -->
<mxCell id="e8" value="write nodes" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=2;flowAnimation=1;animationDuration=700;" edge="1" source="a-parser" target="neo4j" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Enhanced β†’ LanceDB -->
<mxCell id="e9" value="SciBERT 768-dim vectors" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=900;" edge="1" source="a-enhanced" target="lance" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Arg β†’ Neo4j -->
<mxCell id="e10" value="write analysis" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=1100;" edge="1" source="a-arg" target="neo4j" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Score β†’ Neo4j -->
<mxCell id="e11" value="Score node" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=700;" edge="1" source="a-score" target="neo4j" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Report β†’ FileSystem -->
<mxCell id="e12" value="write PDF" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=1800;" edge="1" source="a-report" target="fs" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Keyword β†’ Groq LLM -->
<mxCell id="e13" value="LLM call" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#b85450;strokeWidth=2;flowAnimation=1;animationDuration=2500;" edge="1" source="a-kw" target="groq" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Visual β†’ Groq vision -->
<mxCell id="e14" value="vision API" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#b85450;strokeWidth=1.5;flowAnimation=1;animationDuration=2500;" edge="1" source="a-visual" target="groq" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Claim β†’ Groq LLM -->
<mxCell id="e13b" value="LLM call" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#b85450;strokeWidth=1.5;flowAnimation=1;animationDuration=2500;" edge="1" source="a-claim" target="groq" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Arg β†’ Groq LLM -->
<mxCell id="e13c" value="LLM call" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#b85450;strokeWidth=1.5;flowAnimation=1;animationDuration=2500;" edge="1" source="a-arg" target="groq" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- CitationGap β†’ Semantic Scholar -->
<mxCell id="e15" value="paper search" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#b85450;strokeWidth=2;flowAnimation=1;animationDuration=2000;" edge="1" source="a-cit" target="semscholar" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Login β†’ Firebase -->
<mxCell id="e16" value="OAuth / JWT" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#e65100;strokeWidth=1.5;flowAnimation=1;animationDuration=1800;" edge="1" source="p-login" target="firebase-fe" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Claim β†’ Neo4j -->
<mxCell id="e17" value="write nodes" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=700;" edge="1" source="a-claim" target="neo4j" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Keyword β†’ Neo4j -->
<mxCell id="e18" value="write nodes" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=700;" edge="1" source="a-kw" target="neo4j" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- CitationGap β†’ Neo4j -->
<mxCell id="e19" value="write nodes" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#9673a6;strokeWidth=1.5;flowAnimation=1;animationDuration=700;" edge="1" source="a-cit" target="neo4j" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- ─── A2A ARROWS ─── -->
<!-- Keyword β†’ A2A Registry: registers get_keywords -->
<mxCell id="a2a-e1" value="register&#xa;get_keywords()" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#1565c0;strokeWidth=2;dashed=1;flowAnimation=1;animationDuration=500;fontStyle=2;fontSize=10;" edge="1" source="a-kw" target="a2a-box" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Claim β†’ A2A Registry: registers get_claims -->
<mxCell id="a2a-e2" value="register&#xa;get_claims()" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#1565c0;strokeWidth=2;dashed=1;flowAnimation=1;animationDuration=500;fontStyle=2;fontSize=10;" edge="1" source="a-claim" target="a2a-box" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- A2A Registry β†’ CitationGapAgent -->
<mxCell id="a2a-e3" value="get_keywords()&#xa;in-memory list" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#1565c0;strokeWidth=2;flowAnimation=1;animationDuration=300;fontStyle=1;fontSize=10;" edge="1" source="a2a-box" target="a-cit" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- A2A Registry β†’ ArgumentationAgent -->
<mxCell id="a2a-e4" value="get_claims()&#xa;structured dicts" style="edgeStyle=orthogonalEdgeStyle;strokeColor=#1565c0;strokeWidth=2;flowAnimation=1;animationDuration=300;fontStyle=1;fontSize=10;" edge="1" source="a2a-box" target="a-arg" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>