fastapi_hf / README.md
looh2's picture
Add detailed Product Requirements Document for FastAPI Multi-Agent + RAG Service
0fb7b50
|
Raw
History Blame Contribute Delete
10.7 kB
metadata
title: FastAPI ML Model on Hugging Face Spaces
emoji: 🚀
colorFrom: green
colorTo: indigo
sdk: docker
pinned: false

FastAPI Multi-Agent + RAG Service

A FastAPI backend that routes a user's question to the right specialist agent — web search, RAG over uploaded documents, SQL over a database, or image generation — with caching, conversation memory, and observability built in.

Product requirements: see PRD.md

HF Spaces config reference: https://huggingface.co/docs/hub/spaces-config-reference

Contents


Quick Start

# 1. Create & activate a virtual environment
python -m venv .venv
.venv\Scripts\activate          # Windows
source .venv/bin/activate       # macOS / Linux

# 2. Install dependencies
pip install -r requirements.txt

# 3. Set environment variables (see below), then run
uvicorn app:app --reload --host 127.0.0.1 --port 7860

Server runs at http://localhost:7860.


Environment Variables

Required

Variable Purpose
DEEPSEEK_API_KEY LLM used by the RAG, SQL, and image-prompt agents
SUPABASE_URL Supabase project URL (or NEXT_PUBLIC_SUPABASE_URL)
SUPABASE_SERVICE_ROLE_KEY Supabase key (or SUPABASE_ANON_KEY / NEXT_PUBLIC_SUPABASE_ANON_KEY)

Optional

Variable Default Purpose
DEEPSEEK_BASE_URL https://api.deepseek.com LLM endpoint
DEEPSEEK_MODEL deepseek-chat Chat model
EMBEDDING_MODEL BAAI/bge-base-en-v1.5 HuggingFace embedding model
REDIS_URL redis://localhost:6379 Cache + session memory
TAVILY_API_KEY Enables the web-search agent (falls back to RAG if unset)
TOP_K / FETCH_K 7 / 20 Chunks returned / candidates fetched (fewer candidates → faster reranking)
MATCH_THRESHOLD 0.3 Min cosine similarity for a vector match
RERANK_ENABLED true Toggle cross-encoder reranking
RAG_CONDENSE_QUERY true Rewrite conversational follow-ups into standalone retrieval queries

SQL route only — set one database URL: SUPABASE_DB_POOLER_URL, SQL_DATABASE_URL, SUPABASE_DB_URL, DATABASE_URL, or SUPABASE_DATABASE_URL.


Docker

Setting Value
Python 3.11.9
Port 7860
User user (uid 1000)
Workdir /app
Entrypoint uvicorn app:app --host 0.0.0.0 --port 7860

Authentication & Admin Approval

Every endpoint (except / and /admin/*) requires a valid Supabase JWT and an admin-approved account. New signups start as pending in public.profiles.status and cannot call the API until approved.

Endpoint Who What it does
GET /admin/users?status= Admin (profiles.role = 'admin') List users, optionally filtered by pending / approved / rejected
POST /admin/users/{user_id}/status Admin Set a user's status to approved or rejected (admins cannot change their own)

Status lookups are cached and invalidated on change. CORS is restricted to the known frontend origins; SlowAPI rate limiting applies to all routes.


API Overview

Area Endpoints Notes
Multi-agent chat POST /multi_agent_chat, POST /feedback, POST /log_login See Multi-Agent Chat
RAG ingestion POST /chunk_pdf, POST /chunk_url Streams NDJSON progress; see RAG Pipeline
Document CRUD GET/PUT/DELETE /documents…, GET /documents/{id}/public_url Always scoped to the calling user
Form filling POST /fill_pdf_form Extracts fields from an AI response and renders a pre-fill PDF (Great Eastern PSF02)
ML/DL catalog POST /models/*, GET /predict/stock-lstm ~28 demo models: regression, classification, clustering, PCA/ICA, association rules (Apriori/FP-Growth/ECLAT), CNN digits, LSTM stock, sentiment, book/movie recommenders — notebooks in ml/, datasets in public/data/

Multi-Agent Chat

Endpoint: POST /multi_agent_chat

In one line: authenticate → pick an agent by keywords → serve from cache if possible → run the agent → return the answer, logging in the background.

Routing

Keywords in the query Agent What it does
draw, image, picture, logo… 🖼️ Image Generates an image
memory, document, pdf, recall… 📚 RAG Answers from uploaded docs
sales, orders, inventory, stock… 🗄️ SQL Queries the database
latest, news, today, trending… 🔎 Tavily Searches the web
no clear match 🔎 Tavily Default fallback

Request flow

flowchart LR
    A([Question]) --> B[1. Check login]
    B --> C[2. Pick agent<br/>by keywords]
    C --> D{3. Seen this<br/>before?}
    D -->|Yes, cached| Z([Answer])
    D -->|No| E[4. Run the agent]
    E --> F[5. Score the answer<br/>+ save to cache]
    F --> Z
    F -.background.-> G[(Save chat history<br/>& logs)]

Fallbacks: the Tavily agent falls back to RAG when no API key is set, and the SQL agent falls back to RAG on error.

Memory: with a session_id, the last 10 turns are loaded from Redis for context, and the cache is skipped to keep the answer personal.


RAG Pipeline

The RAG agent (routes/agents/agent_rag.py) combines semantic chunking, hybrid retrieval, and cross-encoder reranking. Ingestion lives in routes/Memory_Upload.py; shared chunking/embedding utilities in routes/rag_chunking.py.

How it works, in five steps

  1. Ingest — pull clean text out of PDFs (text + tables) and web pages, with an SSRF guard blocking requests to private/internal addresses.
  2. Chunk — split prose by meaning (semantic chunking) and tables by row windows, prepending a Document: <name> | Page: N header so every chunk carries its source context.
  3. Embed & store — turn each chunk into a vector and store it in Supabase/pgvector, keyed per user so users can never see each other's documents.
  4. Search — rewrite follow-up questions into standalone queries, run keyword + vector search in parallel, fuse the results (RRF), then let a stricter cross-encoder rerank the top candidates and drop near-duplicates.
  5. Answer — the LLM must answer only from the retrieved chunks; a final groundedness check scores the answer against its sources and floors hallucinated output.

The table below maps each step to the exact libraries, models, and settings.

Techniques

Stage Technique Notes
Parsing Text + table extraction pdfplumber text, camelot tables (lattice → stream), PyMuPDFLoader fallback
Parsing URL scraping BeautifulSoup text with an SSRF guard (private/loopback IPs blocked)
Enrichment Header prepending Adds Document: <name> | Page: N so chunks carry source context
Chunking Semantic (prose) LangChain SemanticChunker, embedding-based splits by meaning
Chunking Row-window (tables) 16 rows/chunk, 3-row overlap — keeps tables intact
Chunking Recursive fallback 400 chars / 80 overlap; re-splits semantic chunks > 1200 chars
Embedding HuggingFace BGE BAAI/bge-base-en-v1.5, L2-normalized, batched (32)
Storage pgvector + Chroma Supabase rag_user_documents, keyed per user with source metadata
Query History-aware rewriting Follow-ups condensed into a standalone retrieval query (RAG_CONDENSE_QUERY); a lexical guard skips the extra LLM rewrite for already-standalone questions, and the original phrasing is kept for generation
Retrieval Hybrid + RRF pgvector cosine + full-text keyword search fused via Reciprocal Rank Fusion (RPC match_rag_documents)
Retrieval Metadata filtering By user_id, source_type (pdf/url), url, created_after
Reranking Cross-encoder ms-marco-MiniLM-L-6-v2 rescores query–passage pairs; lexical token-overlap fallback
Diversity Near-duplicate dedup Drops repeated chunks (e.g. overlapping table row-windows) before the final top_k
Generation Grounded synthesis DeepSeek over numbered context + last 3 conversation turns
Eval Groundedness scoring Post-hook scores answer's lexical coverage by retrieved context (floors ungrounded answers)

Ingestion sequence — POST /chunk_pdf, POST /chunk_url

sequenceDiagram
    participant U as Client
    participant API as Upload route
    participant S as Supabase (storage + DB)
    participant E as BGE embedder

    U->>API: PDF / URL (+ user_id, JWT)
    API->>API: Validate (MIME, size, %PDF magic / SSRF guard)
    API->>S: Upload file + insert documents row
    API->>API: Parse (text + tables) & enrich headers
    API->>API: Chunk (semantic prose, row-window tables)
    loop batches of 32
        API->>E: embed_documents(chunk_texts)
        API->>S: insert rows into rag_user_documents
        API-->>U: NDJSON progress event (55→90%)
    end
    API-->>U: done (inserted_rows, 100%)

Retrieval + generation sequence — RAG route of POST /multi_agent_chat

sequenceDiagram
    participant U as Client
    participant R as RAGAgent
    participant DB as Supabase RPC
    participant X as Cross-encoder
    participant LLM as DeepSeek

    U->>R: query (+ top_k, fetch_k, filters)
    R->>R: Condense follow-up → standalone query (if history)
    R->>R: Normalize query, embed with BGE
    R->>DB: match_rag_documents (vector + keyword, RRF, filters)
    DB-->>R: candidate chunks (ordered, up to fetch_k)
    Note over R,DB: Falls back to Chroma similarity/MMR if the RPC errors
    R->>R: Dedup near-duplicate chunks
    R->>X: rerank query–passage pairs → top_k
    Note over R,X: Falls back to token-overlap reranking if unavailable
    R->>R: build_context (numbered sources)
    R->>LLM: system prompt + history + context + original question
    LLM-->>R: answer + token usage
    R->>R: Score groundedness (answer vs. context)
    R-->>U: answer + sources + groundedness

The RAG and SQL agents run in a threadpool (run_in_threadpool) so their CPU and blocking-network work don't stall the async event loop under concurrency.