Skip to main content

The UI's role in RAG Me Up

It is tempting to treat the chat UI as cosmetic. In RAG Me Up it is part of the retrieval contract. The Python RAG server is intentionally stateless: every request must carry the conversation memory the pipeline needs. If the client drops history, documents, or dataset filters, follow-ups, provenance display, and scoped search all break — no matter how good the backend is.

The shipping front-end is a React SPA plus a Node.js API under ui/node-react, talking to Flask (/chat, /chat_stream, document and config routes). You can replace the UI, but you must honor the same responsibilities.

1. State keeping

The server does not remember your chat. On each turn the client sends:

FieldWhy the server needs it
prompt / queryThe latest user message
historyPrior roles/contents for fetch check, summarization, and answer generation
docsPreviously retrieved chunks — reused when fetch check says "no new retrieval"
datasetsOptional metadata filter for hybrid search

After the response, the client must persist and round-trip:

  • Updated history from the server (including system messages with injected documents when applicable).
  • The latest documents array (with optional provenance scores).
  • UI-only state: selected datasets, message offsets, chat id.

In the React app this lives in component state (history, documents, selectedDatasets, messages) and is also stored via the Node API so chats survive reloads. Optimistic user messages appear immediately; the assistant message is finalized from the streaming done payload (or a token fallback if done never arrives).

Mental model: Python owns the RAG algorithm. The UI owns the session.

2. Streaming via Server-Sent Events (SSE)

Interactive RAG is slow enough that users need progress, not a spinner. RAG Me Up streams the pipeline over SSE.

Python (/chat_stream) yields events:

EventPayloadMeaning
step{ step: "..." }Human-readable pipeline stage (summarizing, HyDE, retrieving, reranking, rewriting, generating, provenance, …)
documents{ documents: [...] }Retrieved chunks as soon as they are ready (before the full answer)
token{ token: "..." }Next piece of the LLM answer
doneFull metadataFinal reply, history, documents, rewritten query, flags, provenance merged in
error{ error: "..." }Failure path

Node proxies that stream to the browser (/chats/:id/message/stream), optionally persisting the finished turn to the DB when done arrives.

React (sendMessageStream in api.js) parses event: / data: frames and calls onStep, onToken, onDocuments, onDone, onError. ChatView shows a live step list and a growing markdown bubble, then commits the final assistant message (and any "your question was rewritten" notice).

Non-streaming /chat still exists for simple clients; production UX should prefer SSE.

3. What else the UI owns

Beyond state and streaming, a complete RAG Me Up client typically handles:

Dataset scoping

Subfolders under data_directory become dataset metadata at index time. The UI lets users select which datasets to search and passes that list on every turn. Empty selection = search all.

Citation and provenance display

Retrieved chunks are shown as document cards (filename, snippet, scores). When provenance runs, each card can show the attribution score next to retrieval rank — making "retrieved" vs "actually used" visible. That is the user-facing payoff of the provenance stage.

Rewritten-query transparency

If the rewrite loop fires, the response includes rewritten. Surfacing it ("Your message has been rewritten: …") builds trust and helps users learn how to ask better questions.

Document lifecycle

Upload / list / delete / download go through Node + Python (add_document, get_documents, delete, …). The UI is how operators grow the corpus without SSH.

Configuration surface

A config page can read/update RAG .env keys (prompts, toggles, models) and trigger reinitialization (reload_llm) so prompt iteration does not require redeploying containers.

Auth and chat persistence

The Node layer adds login, chat titles (/create_title on Python), and stored transcripts. None of that belongs in the stateless RAG worker — by design.

First-token and failure UX

Show pipeline steps during long HyDE+retrieve+rerank sequences; fall back to accumulated tokens if the stream drops before done; never clear documents on a follow-up that did not fetch anew.

Checklist for a custom UI

If you build your own client against the Python API:

  1. Round-trip history and docs on every message.
  2. Prefer /chat_stream and render step / token / documents / done.
  3. Pass datasets when you support corpus scoping.
  4. Display provenance and rewritten when present.
  5. Keep secrets and user accounts out of the Python process — put them in your BFF (as the Node server does).

The UI is not a thin skin over an LLM. In RAG Me Up it is the memory, the progress channel, and the place where retrieval evidence becomes something a human can audit.