Project

Recall-OS

RecallOS is an AI-native enterprise knowledge operating system that allows organizations to ingest, organize, search and reason over every piece of company knowledge.

โœจ What is RecallOS?

recallos

A multimodal memory architecture for persistent retrieval over heterogeneous enterprise knowledge. Upload PDFs, images, audio, and video โ€” then chat with an AI that cites its sources.

๐Ÿš€ Core Features

Feature Description
๐Ÿ“„ Multi-modality upload PDF, images, audio, video via MinIO presigned URLs
๐Ÿญ Modality-aware ingestion Per-modality parser workers dispatched by MIME type
๐Ÿงฉ Decoupled embedding Modality-agnostic dense + sparse embed, re-embeddable without reparsing
๐Ÿ” Hybrid search Dense BGE + sparse SPLADE in Qdrant, fused with RRF
๐ŸŽฏ Cross-encoder rerank Top chunks reranked before LLM context injection
๐Ÿ’ฌ Streaming chat SSE streaming with source chunk citations + optional modality filter
๐ŸŒ Web research agent /web prefix triggers LangGraph loop (Exa โ†’ reason โ†’ refine โ†’ answer)
๐Ÿ“‚ Projects Organize chats with custom system prompts
๐Ÿ“Œ Chat history Pin, delete, version (edit/resend), and rolling conversation summaries
๐Ÿ“Š Langfuse tracing Full observability for chat RAG and ingestion pipelines
๐Ÿ”„ Dead Letter Queue Failed document processing with retry and reprocessing
๐Ÿ—๏ธ Modular chat UI 14-file component architecture with custom hooks and focused modules

๐Ÿ› ๏ธ Tech Stack

LayerTechnology
๐Ÿ“ฆ MonorepoBun workspaces + Turborepo
๐Ÿ–ฅ๏ธ FrontendNext.js 16 (App Router), React 19, Tailwind CSS v4
โš™๏ธ BackendExpress 5 (JWT middleware on all routes except auth)
โšก RuntimeBun
๐Ÿ” AuthJWT + bcrypt
๐Ÿ“จ QueueRedis Streams (consumer groups, XAUTOCLAIM)
๐Ÿ—„๏ธ Object storageMinIO (S3 API)
๐Ÿ˜ MetadataPostgreSQL + Prisma 7
๐Ÿงญ VectorsQdrant (dense + sparse named vectors)
๐Ÿ“ Dense embeddingsBGE-small-en (fastembed)
๐Ÿ”ค Sparse embeddingsSPLADE++ EN v1 (fastembed)
๐ŸŽฏ RerankHugging Face cross-encoder (ms-marco-MiniLM-L6-v2)
๐Ÿ“‘ ParsingLlamaParse (LlamaCloud)
๐Ÿค– LLMOpenRouter
๐ŸŒ Web searchExa + LangGraph agent
๐Ÿ”ญ ObservabilityLangfuse (OpenTelemetry)

๐Ÿ—๏ธ Architecture

  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         presigned PUT          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚  Next.js   โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ MinIO  โ”‚
  โ”‚    web     โ”‚                                โ”‚(assets)โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜
        โ”‚                                           โ”‚
        โ”‚ REST (JWT)                                โ”‚ object key
        โ–ผ                                           โ”‚
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”    xAdd to files_stream       โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”
  โ”‚  Express   โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚  Redis   โ”‚
  โ”‚  backend   โ”‚                               โ”‚ Streams  โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                               โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚                                          โ”‚
        โ”‚ hybrid query + chat                      โ”‚ Dispatcher
        โ”‚                                          โ”‚ (routes by MIME)
        โ–ผ                                          โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚  Qdrant   โ”‚                            โ”‚ pdf_stream     โ”‚
  โ”‚ dense +   โ”‚ โ—€โ”€โ”€โ”€ embed_stream          โ”‚ image_stream   โ”‚
  โ”‚ splade    โ”‚                            โ”‚ audio_stream   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    embedding worker        โ”‚ video_stream   โ”‚
                                           โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚                                          โ”‚
        โ”‚             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚ Postgres  โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                      โ”‚  + users  โ”‚    Parser workers
                      โ”‚  + docs   โ”‚    (per modality)
                      โ”‚  + chunks โ”‚
                      โ”‚  + chats  โ”‚
                      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ”„ Ingestion Pipeline

Documents of any modality are accepted (PDF, images, audio, video).

 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”   presigned URL    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    bytes      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚Clientโ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚Expressโ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ MinIO โ”‚
 โ””โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜                    โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚                            โ”‚
    โ”‚ POST /confirm              โ”‚
    โ–ผ                            โ–ผ
 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  xAdd  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚ Document โ”‚ โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ files_stream โ”‚
 โ”‚ UPLOADED โ”‚        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚ Dispatcher
                            โ”‚ (MIME detection)
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ–ผ                 โ–ผ                 โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ pdf_stream โ”‚   โ”‚image_streamโ”‚ ... โ”‚video_streamโ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                 โ”‚                 โ”‚
         โ–ผ                 โ–ผ                 โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  QUEUED   โ”‚    โ”‚  PARSING  โ”‚      โ”‚   PARSED  โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                โ”‚                  โ”‚
         โ–ผ                โ–ผ                  โ–ผ
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚   ParsedChunkSet + Chunks    โ”‚โ”€โ”€โ–ถ  โ”‚ embed_stream โ”‚
   โ”‚   (Postgres)                 โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
                                              โ–ผ
                                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                        โ”‚ EMBEDDING โ”‚
                                        โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
                                              โ”‚
                                   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                   โ–ผ                       โ–ผ
                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                         โ”‚  Qdrant   โ”‚             โ”‚   READY   โ”‚
                         โ”‚  vectors  โ”‚             โ”‚ (or FAIL) โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Recovery: Each stream has its own consumer group. Workers run a stale-job reclaimer (XAUTOCLAIM). After MAX_RETRIES, jobs move to a Dead Letter Queue (dlq_stream).


๐Ÿ” Retrieval & Chat

  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚   User message   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ Embed query      โ”‚
  โ”‚ dense BGE +      โ”‚
  โ”‚ sparse SPLADE    โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ Qdrant hybrid    โ”‚  prefetch dense top-50
  โ”‚ query            โ”‚  prefetch sparse top-50
  โ”‚                  โ”‚  fuse with RRF โ†’ top 50
  โ”‚                  โ”‚  (filtered by user's docs)
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ Cross-encoder    โ”‚
  โ”‚ rerank โ†’ top 5   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ System prompt +  โ”‚
  โ”‚ recent history + โ”‚
  โ”‚ project prompt   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ OpenRouter SSE   โ”‚
  โ”‚ stream           โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ Answer + sources โ”‚
  โ”‚ stored on msg    โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ฌ Chat Features

Feature Details
๐Ÿ”„ Streaming replies Real-time SSE streaming from OpenRouter
๐Ÿ“Ž Source citations Each answer carries ranked chunk references
๐Ÿ“‚ Projects Organize chats with custom system prompts
๐Ÿ“Œ Pin / delete Manage chat history
โœ๏ธ Edit & resend Create version branches (1/2, 2/2)
๐ŸŒ Web mode /web prefix triggers LangGraph research agent
๐Ÿ“Š Live agent steps Watch the web agent search, reason, and refine in real-time
๐Ÿ“ Conversation summaries Rolling summaries injected into later prompts

๐Ÿ”Œ API Surface

Base path: /api/v1 (JWT middleware on all routes except /auth/*).

Area Methods Description
๐Ÿ” Auth POST /auth/signup, POST /auth/signin User registration & login
๐Ÿ“ค Upload POST /upload/post-file-url, POST /upload/confirm Presigned URL flow
๐Ÿ“„ Documents GET /download/list, POST /download/get-download-url, DELETE /download/:id File management
๐Ÿ’ฌ Chat GET /chat, GET /chat/:id, PATCH /chat/:id, DELETE /chat/:id, POST /chat/message Chat CRUD + SSE streaming
๐Ÿ“‚ Projects GET/POST /projects, PATCH/DELETE /projects/:id Project management

๐Ÿ–ฅ๏ธ Frontend Routes

Route Purpose
/ Landing page (redirects to chat if signed in)
/signin, /signup Authentication
/dashboard Upload documents, view status, download / delete
/chat Full chat UI with history, projects, sources, web agent

๐Ÿ—ƒ๏ธ Data Model (Postgres)

Model Role
User Username + hashed password
Document Title, object key, mimeType, modality, status (UPLOADED โ†’ READY / FAILED)
ParsedChunkSet Group of parsed chunks per modality, status (PARSED / INDEXED)
ParsedChunk Individual text chunk with JSON metadata (page, timestamp, caption, OCR, etc.)
Project Named workspace + optional system prompt
Chat Title, pin, optional project, summary fields
Message role, content, sourceChunks JSON
Memory Schema for durable facts (not wired into chat yet)

Chunk vectors live in Qdrant with payload including documentId, chunkId, modality, page, timestamps, caption.


๐Ÿ’พ Storage Roles

Store What it holds
๐Ÿ—„๏ธ MinIO Original asset files (PDF, images, audio, video)
๐Ÿ˜ PostgreSQL Users, docs, chunk sets, chunks, chats, messages, projects
๐Ÿ“จ Redis Streams Multi-stream job queue per modality + consumer group PEL + DLQ
๐Ÿงญ Qdrant Per-chunk dense + SPLADE vectors and text payload

There is no OpenSearch โ€” lexical signal comes from SPLADE sparse vectors inside Qdrant, fused with dense cosine via RRF.


๐Ÿ”ญ Observability

@repo/langfuse instruments:

Pipeline Traced steps
๐Ÿ’ฌ Chat RAG hybrid-retrieve โ†’ cross-encode-rerank โ†’ generate-response
๐ŸŒ Web agent LangGraph nodes (search โ†’ reason โ†’ refine โ†’ answer)
๐Ÿ“„ Ingest process-document and nested per-modality steps

If Langfuse keys are missing, tracing no-ops silently.


Key design decisions:

  • ๐Ÿช Custom hook (useChatState) encapsulates ~50 state variables and all SSE streaming logic
  • ๐Ÿงฉ Presentational components are pure โ€” they receive props and render
  • ๐Ÿ“ฆ Types & helpers are shared across all modules via local imports
  • ๐Ÿ”Œ Zero changes to the route import (import ChatPage from "@/components/chat-app" resolves to index.tsx)

๐Ÿƒ Local Development

๐Ÿ“‹ Prerequisites

๐Ÿš€ Quick Start

# ๐Ÿ“ฆ Install dependencies
bun install

# โš™๏ธ Configure environment (root and/or apps/*)
# Typical keys: DATABASE_URL, MinIO, JWT_SECRET, PORT,
# STREAM_NAME / GROUP_NAME, COLLECTION, DENSE_DIM,
# OPENROUTER_API_KEY, LLAMA_CLOUD_API_KEY, etc.

# ๐Ÿ—ƒ๏ธ Apply Prisma migrations
cd packages/db && bunx prisma migrate dev

# ๐Ÿƒ Start everything (web + backend + workers)
bun run dev

๐ŸŽฏ Run Individually

bun run --filter web dev        # ๐Ÿ–ฅ๏ธ  Next.js on :3001
bun run --filter backend dev    # โš™๏ธ  Express on :3000
bun run --filter workers dev    # ๐Ÿญ All workers
bun run --filter workers dev:pdf       # ๐Ÿ“„ PDF worker only
bun run --filter workers dev:embedder  # ๐Ÿงฎ Embedder only
bun run --filter workers dev:dlq       # ๐Ÿ”„ DLQ worker only

๐Ÿ”ง Development Commands

Command Purpose
bun install Install dependencies
bun run dev Start all apps via Turborepo
bun run build Production build
bun run lint ESLint (web: --max-warnings 0)
bun run check-types TypeScript check
bun run format Prettier (--write)
cd packages/db && bunx prisma migrate dev Apply migrations

๐Ÿงญ Design Principles

Principle Description
โšก Async ingest Uploads never block on parse/embed
๐Ÿ” Hybrid retrieval Dense meaning + sparse terms, fused with RRF
๐Ÿ“Ž Source grounded Answers carry chunk citations
๐Ÿ”’ User scoped Retrieval and deletes are filtered by ownership
๐Ÿงฉ Modular monorepo Shared clients in packages/*
๐Ÿ”ญ Observable Optional Langfuse traces end to end
๐Ÿ”Œ Decoupled parsing & embedding Re-embed without reparsing via ParsedChunkSet
โž• Extensible modalities New types need only a new parser worker

๐Ÿง  RecallOS

Search your knowledge. Cite your sources.