Ragenta is a multi-tenant RAG + agents SaaS I built at NYB AI. A workspace uploads its documents, asks questions, and gets answers that point at the exact passage they came from. Every model call is paid from a credit ledger, and every ingestion job has to survive a deploy in the middle of it.
Below is the path one document takes, from upload to a [[n]] citation in a chat reply. The six stage ids are the same ones the backend uses; the diagram follows the card you are reading.
Four decisions I'd defend in a review
- Citations are frozen server-side.
- Rendering is a lookup by index, never a similarity match that can drift to the wrong paragraph. Why not re-match at render time: less code, but a similarity match can land on a neighbouring paragraph, and a wrong citation is worse than none.
- Streaming state is local, not in the query cache.
- A
setQueryData per token re-renders every subscriber. The stream writes local state; the cache is invalidated once at the end and the server's rows — with citations, model and cost — win. - Poll only while something is in flight.
- The documents list stops the moment every row is
ready or failed. Why not a fixed interval: an idle page would keep hitting the API for nothing. - Refusals happen before the stream opens.
- No credits, a model outside the plan, a missing base: a normal 4xx with a message, never an error frame after the UI has already switched to "answering".
Read the full case study — architecture, the agent flow editor, all seven decisions →