Query & data flows
The backend exposes two main retrieval paths. They use different SQL and merge strategies—skim the
diagrams before you test live calls below.
Streaming chat (POST /api/ai-chat/sessions/{sessionId}/messages/stream)
User JSON: user_id, query, top_k
│
▼
┌───────────────────┐ block ┌────────────────────────────┐
│ Input safety │─────────────▶│ Stream guidance (NDJSON) │
│ (input_checker) │ └────────────────────────────┘
└─────────┬─────────┘
│ pass
▼
┌───────────────────┐ Azure ┌──────────────────┐
│ Embed query │──────────────▶│ query vector │
└─────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ PostgreSQL — unified retrieval (per user + global wellness) │
│ • document_chunks (vector similarity, filtered by user_id) │
│ • wellness_chunks (vector similarity — shared corpus) │
│ ORDER BY similarity DESC → top_k │
└─────────────────────────────┬───────────────────────────────┘
│
▼
┌───────────────────┐ Azure ┌────────────────────────────┐
│ Build context │──────────────▶│ Chat completion + stream │
│ string from rows │ │ (NDJSON token chunks) │
└─────────┬─────────┘ └─────────────┬──────────────┘
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ Output safety │ │ Client shows │
│ (output_checker) │ │ streamed answer │
└───────────────────┘ └───────────────────┘
Hybrid search (POST /search/hybrid)
Query text + optional category_id + top_k
│
▼
┌───────────────────┐ Azure ┌──────────────────┐
│ Embed query │──────────────▶│ query vector │
└─────────┬─────────┘ └────────┬─────────┘
│ │
├──────────────────────────────────┤
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ BM25 / full-text │ │ Vector search │
│ (wellness chunks) │ │ (same chunk set) │
└──────────┬──────────┘ └──────────┬──────────┘
│ │
└────────────┬───────────────────┘
▼
┌────────────────────────┐
│ RRF merge (k=60) │
│ ranks → rrf_score │
│ source: bm25_only / │
│ vector_only / both │
└────────────┬───────────┘
▼
JSON: chunk_id, content, topic_title,
category_id, ranks, rrf_score, source
Unified chat retrieval
One vector similarity query across user documents and shared wellness chunks; feeds the LLM context.
Hybrid search API
BM25 + vector in parallel, fused with reciprocal rank fusion—useful for debugging relevance and overlap.
What’s built & what’s next
| Area |
Status |
Notes |
| FastAPI surface |
Live |
/api/ai-chat/health, /api/ai-chat/sessions/{sessionId}/messages/stream, /search/hybrid; OpenAPI at /docs |
| Streaming chat + safety |
Live |
Input/output checkers around RAG + generation; NDJSON stream |
| Unified vector retrieval (chat) |
Live |
document_chunks (per user) ∪ wellness_chunks; cosine similarity ordering |
| Hybrid retrieval |
API |
BM25 + vector + RRF in app/core/retrieval/hybrid_search.py; optional category filter |
| Full-text / BM25 |
Core |
app/db/migrations/001_consolidated_platform_schema.sql (wellness FTS); hybrid path |
| Reranker & context filter |
Modular |
Implemented with tests; wire into chat/hybrid when you want stricter ordering or diet-aware filtering |
| Pipeline & ingestion |
Core |
Blob upload, triggers, chunking—run via project scripts / Azure pipeline (see repo) |
Try the API
Start the server from the project root, e.g. uvicorn app.main:app --reload --host 127.0.0.1 --port 8000, then use the forms below (same origin).
Chat prompt debugger (POST /chat/debug)
Dry-run the same path as /chat/stream: safety, intent, retrieval, rerank, context filter, and the
exact messages[] sent to Azure. Nothing is written to Redis or PostgreSQL. Disable in production with
ENABLE_CHAT_DEBUG=false.
Pipeline debugger (BM25 + vector + rerank + context filter)
BM25 results
| score | topic | chunk_id | content |
|---|
Vector search results
| score | topic | chunk_id | content |
|---|
Hybrid (RRF) results
| rrf | source | bm25# | vec# | topic | chunk_id | content |
|---|
Reranked results
| rerank | source | topic | chunk_id | content |
|---|
Context-filtered results
| rerank | source | topic | chunk_id | content |
|---|