From ac0eb1e5f62b284950555cb3044784d3851c1b5d Mon Sep 17 00:00:00 2001 From: jon Date: Thu, 23 Jul 2026 13:48:32 -0500 Subject: [PATCH] feat(rag): PDF/docx ingest + chat citations - Upload endpoint (base64 JSON, no multipart dep): extracts text from pdf/docx/txt/md via pypdf + python-docx, then runs the existing chunk/embed pipeline. Documents page uploads files straight through. - Citations: the chat stream emits an SSE `sources` event listing the documents that fed the answer; the UI shows them as chips under the reply. - Deps: pypdf, python-docx (both pure-Python, Windows-safe). Co-Authored-By: Claude Opus 4.8 --- interface/web/src/Chatbot.jsx | 22 ++++++++++++++++ interface/web/src/Documents.jsx | 40 +++++++++++++++++++++------- requirements-base.txt | 4 +++ requirements-windows.txt | 4 +++ synapse/main.py | 46 +++++++++++++++++++++++++++++++++ tests/test_documents.py | 16 ++++++++++++ 6 files changed, 123 insertions(+), 9 deletions(-) diff --git a/interface/web/src/Chatbot.jsx b/interface/web/src/Chatbot.jsx index ceb4070..b332753 100644 --- a/interface/web/src/Chatbot.jsx +++ b/interface/web/src/Chatbot.jsx @@ -215,6 +215,18 @@ export function Chatbot({ conversationId, setConversationId, onConversationChang pendingEventType = null; continue; } + if (pendingEventType === "sources") { + try { + const src = JSON.parse(payload).sources; + setMessages(prev => { + const updated = [...prev]; + updated[assistantIndex] = { ...updated[assistantIndex], sources: src }; + return updated; + }); + } catch { /* ignore */ } + pendingEventType = null; + continue; + } if (pendingEventType === "done") { // Answer is complete; re-enable input while the backend finishes // slow post-processing (title, memory) on the still-open stream. @@ -526,6 +538,16 @@ export function Chatbot({ conversationId, setConversationId, onConversationChang : Thinking... } + {msg.sources && msg.sources.length > 0 && ( +
+ Sources: + {msg.sources.map((s, i) => ( + + 📄 {s} + + ))} +
+ )} {msg.content && editingIdx !== idx && (