CoolFace
Apppublic

Sharada25/dhammaai

sourceHugging Facemitupdated 10mo agoView on Hugging Face
1likes
App README

DhammaAI - Vipassana Meditation Guide

A Production-Ready RAG-based AI Chatbot with Progressive Web App (PWA) Capabilities

An intelligent meditation guide powered by advanced RAG techniques, deployed on Hugging Face Spaces with complete mobile support.

![Live Demo](https://huggingface.co/spaces/Sharada25/dhammaai) ![License](LICENSE) ![Python](https://www.python.org/)


Table of Contents

  1. 1.Project Overview
  2. 2.Architecture & Tech Stack
  3. 3.Detailed System Design
  4. 4.Document Ingestion Pipeline
  5. 5.RAG Implementation
  6. 6.LLM Integration
  7. 7.Database Design
  8. 8.PWA Implementation
  9. 9.Deployment Journey
  10. 10.Cost Analysis
  11. 11.Interview Q&A
  12. 12.Setup Instructions

Project Overview

DhammaAI is an AI-powered meditation guide that provides authentic answers about Vipassana meditation based on S.N. Goenka's teachings. The project combines cutting-edge AI technologies with traditional web development to create a production-ready, mobile-first application.

Key Features

  • —Advanced RAG System: Hybrid retrieval using FAISS (vector search) + BM25 (keyword search) + CrossEncoder (reranking)
  • —Multilingual Support: English, Hindi, and Marathi with accurate language detection
  • —Voice I/O: Speech-to-text input and text-to-speech output
  • —Progressive Web App: Installable on mobile devices (Android & iOS)
  • —Chat History: Persistent conversation storage using MongoDB Atlas
  • —Feedback System: User feedback collection for continuous improvement
  • —Plug-and-Play LLM: Easy switching between OpenAI and HuggingFace models
  • —100% Free Hosting: Deployed on Hugging Face Spaces (16GB RAM)

Live Demo

URL: https://huggingface.co/spaces/Sharada25/dhammaai


Architecture & Tech Stack

Backend Technologies

TechnologyVersionPurpose
Python3.11Core programming language
Flask2.3+Web framework for API endpoints
Gunicorn21.0+Production WSGI server
MongoDB Atlas4.0+Cloud NoSQL database (FREE tier)
PyMongo4.0+MongoDB driver for Python

AI/ML Stack

TechnologyVersionPurpose
FAISS1.7.4+Facebook AI Similarity Search (vector database)
Sentence Transformers2.2+Embedding generation (all-MiniLM-L6-v2)
CrossEncoder-Reranking model (ms-marco-MiniLM-L-6-v2)
BM250.2.2+Keyword-based search algorithm
HuggingFace Hub0.20+HF Inference API client
OpenAI1.0+OpenAI API client (optional)
scikit-learn1.3+Normalization and ML utilities
NumPy1.24+Numerical computations

Frontend Technologies

TechnologyPurpose
HTML5Semantic markup
TailwindCSSUtility-first CSS framework
JavaScript (Vanilla)Client-side interactivity
Marked.jsMarkdown rendering
Service WorkerOffline support & caching
Web Speech APIVoice input/output

DevOps & Hosting

TechnologyPurpose
DockerContainerization
Git LFSLarge file storage for models
Hugging Face SpacesFree cloud hosting (16GB RAM)
GitHubVersion control

Detailed System Design

High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                         User Interface                       │
│  (HTML + TailwindCSS + JavaScript + Service Worker + PWA)  │
└────────────────────┬────────────────────────────────────────┘
                     │ HTTP/HTTPS
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                      Flask Application                       │
│  • /chat endpoint (streaming responses)                     │
│  • /feedback endpoint                                        │
│  • /analytics endpoint                                       │
│  • /upload endpoint (document ingestion)                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                   VipassanaRAGAgent                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. Query Expansion (Multi-Query)                   │   │
│  │  2. Hybrid Retrieval (FAISS + BM25)                │   │
│  │  3. CrossEncoder Reranking                          │   │
│  │  4. Context Building                                │   │
│  │  5. LLM Response Generation                         │   │
│  └─────────────────────────────────────────────────────┘   │
└────────────────────┬────────────────────────────────────────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
        ▼            ▼            ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│   FAISS      │ │   BM25       │ │  MongoDB     │
│ Vector Store │ │ Index Store  │ │   Atlas      │
│ (Semantic)   │ │ (Keyword)    │ │ (Chat Hist)  │
└──────────────┘ └──────────────┘ └──────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                    LLM Provider (Plug-and-Play)             │
│  • HuggingFace Inference API (Qwen2.5-7B-Instruct)         │
│  • OpenAI API (GPT-3.5-turbo / GPT-4) - Optional           │
└─────────────────────────────────────────────────────────────┘

Request Flow

  1. 1.User Query → Frontend captures text/voice input
  2. 2.Frontend → Sends POST request to /chat endpoint
  3. 3.Flask App → Receives query, calls VipassanaRAGAgent.answer()
  4. 4.RAG Agent:
  5. 5.Expands query into multiple variations
  6. 6.Searches FAISS vector store (semantic)
  7. 7.Searches BM25 index (keyword)
  8. 8.Merges and deduplicates results
  9. 9.Reranks with CrossEncoder for precision
  10. 10.Selects top-k chunks
  11. 11.Context Building → Formats retrieved chunks with sources
  12. 12.LLM Call → Sends context + query to HuggingFace/OpenAI
  13. 13.Response Stream → Streams generated text back to frontend
  14. 14.MongoDB → Saves conversation to database
  15. 15.Frontend → Renders markdown response with sources

Document Ingestion Pipeline

Overview

The document ingestion system processes PDF files and builds searchable indexes for RAG.

Process Flow

PDF Files → PDF Extraction → Text Cleaning → Chunking → Embedding → Indexing

Step-by-Step Process

1. PDF Extraction
  • —Library Used: PyPDF2
  • —Process: Extracts text from each page of PDFs
  • —Input: PDF files from data/knowledge_base/Vipassana Books/
  • —Output: Raw text strings
2. Text Cleaning
python
def _clean_text(self, text: str) -> str:
    """
    Cleans and normalizes text from PDFs
    """
    # Unicode normalization (NFKC)
    cleaned = unicodedata.normalize('NFKC', text)

    # Remove unwanted patterns
    cleaned = re.sub(r"(/c\d+)+", " ", cleaned)  # Remove /c123 patterns
    cleaned = re.sub(r"\[\s*\d+\s*\]", " ", cleaned)  # Remove [123]
    cleaned = re.sub(r"-\s*\n\s*", "", cleaned)  # Join hyphenated words
    cleaned = re.sub(r"\n+", " ", cleaned)  # Replace newlines with spaces
    cleaned = re.sub(r"\s+", " ", cleaned).strip()  # Normalize whitespace

    return cleaned

Why this cleaning?

  • —PDFs often contain metadata, page numbers, and formatting artifacts
  • —CrossEncoder and LLM work better with clean, normalized text
  • —Removes noise that reduces retrieval accuracy
3. Chunking Strategy
python
CHUNK_SIZE = 1000  # characters
OVERLAP = 200      # characters (overlap between chunks)

Chunking Logic:

  • —Fixed-size chunks of 1000 characters
  • —200-character overlap to maintain context continuity
  • —Ensures semantic coherence across chunk boundaries

Why chunking?

  • —LLMs have context window limits (can't process entire books)
  • —Smaller chunks improve retrieval precision
  • —Overlap prevents information loss at boundaries
4. Embedding Generation
python
# Model: sentence-transformers/all-MiniLM-L6-v2
model = SentenceTransformer('all-MiniLM-L6-v2')

# Generate embeddings
chunk_embedding = model.encode(chunk_text, convert_to_numpy=True)
# Output: 384-dimensional vector

Model Choice: all-MiniLM-L6-v2

  • —Size: 22 MB (fast loading)
  • —Dimensions: 384 (good balance)
  • —Performance: 95% accuracy on semantic similarity tasks
  • —Speed: Very fast on CPU
  • —Multilingual: Supports English, Hindi, Marathi
5. Vector Indexing (FAISS)
python
import faiss

# Create FAISS index (Inner Product similarity)
dimension = 384
index = faiss.IndexFlatIP(dimension)

# Normalize vectors (for cosine similarity)
normalized_embeddings = normalize(embeddings)

# Add to index
index.add(normalized_embeddings.astype('float32'))

# Save index
faiss.write_index(index, 'data/vector_store/index.faiss')

FAISS Configuration:

  • —Index Type: IndexFlatIP (Inner Product)
  • —Why IP?: After normalization, IP = cosine similarity
  • —Advantage: Exact search (no approximation)
  • —Speed: Fast for datasets up to 100K vectors
6. BM25 Indexing
python
from rank_bm25 import BM25Okapi

# Tokenize all chunks
tokenized_corpus = [chunk.lower().split() for chunk in all_chunks]

# Build BM25 index
bm25 = BM25Okapi(tokenized_corpus)

# Save index
with open('data/vector_store/bm25.pkl', 'wb') as f:
    pickle.dump(bm25, f)

BM25 (Best Matching 25):

  • —Type: Keyword-based search algorithm
  • —Use Case: Complements vector search (catches exact keyword matches)
  • —Advantage: Fast, works well for specific terms (e.g., "anapana", "sila")
7. Metadata Storage
json
// data/vector_store/meta.json
[
  {
    "chunk": "Vipassana means to see things as they really are...",
    "source": "Art-of-Living-in-English.pdf",
    "page": 5,
    "chunk_id": 0
  },
  ...
]

Metadata Purpose:

  • —Links chunks back to source documents
  • —Enables source citation in responses
  • —Helps users verify information authenticity

RAG Implementation

Why RAG?

Problem: LLMs hallucinate and lack domain-specific knowledge.

Solution: Retrieval-Augmented Generation (RAG)

  • —Retrieves relevant information from knowledge base
  • —Augments LLM prompt with factual context
  • —LLM generates accurate, grounded responses

Our RAG Pipeline

1. Multi-Query Expansion
python
def _expand_query(self, query: str) -> List[str]:
    """
    Expands user query into multiple variations for better recall
    """
    query_list = [query]

    # Domain-specific term mapping
    vipassana_terms_map = {
        'meditation': ['practice', 'technique', 'sadhana'],
        'vipassana': ['insight meditation', 'mindfulness'],
        'anapana': ['breathing', 'breath', 'respiration'],
        'goenka': ['s.n. goenka', 'teacher', 'acharya'],
        'dhamma': ['dharma', 'teaching', 'truth'],
        'suffering': ['dukkha', 'pain', 'misery'],
        'anicca': ['impermanence', 'change'],
    }

    # Generate query variations
    for key, terms in vipassana_terms_map.items():
        if key in query.lower():
            for term in terms:
                query_list.append(query.lower().replace(key, term))

    return query_list[:5]  # Return top 5 variations

Why Query Expansion?

  • —Users may phrase questions differently than documents
  • —Increases recall (finds more relevant documents)
  • —Captures synonyms and domain-specific terminology

Example:

  • —Input: "What is anapana?"
  • —Expanded: ["What is anapana?", "What is breathing?", "What is breath?", "What is respiration?"]
2. Hybrid Retrieval

FAISS Search (Semantic):

python
# Generate query embedding
query_embedding = model.encode(query, convert_to_numpy=True)
query_embedding = normalize(query_embedding.reshape(1, -1))

# Search FAISS index
distances, indices = index.search(query_embedding.astype('float32'), top_k=30)

# distances: similarity scores (higher = more similar)
# indices: positions of retrieved chunks in metadata

BM25 Search (Keyword):

python
# Tokenize query
tokenized_query = query.lower().split()

# Get BM25 scores
bm25_scores = bm25.get_scores(tokenized_query)

# Get top-k indices
top_indices = np.argsort(bm25_scores)[::-1][:15]

Why Hybrid?

  • —FAISS: Great for semantic similarity ("What is suffering?" → "dukkha")
  • —BM25: Great for exact matches ("anapana" → "anapana")
  • —Combined: Best of both worlds
3. CrossEncoder Reranking
python
from sentence_transformers import CrossEncoder

# Load reranker model
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

# Create query-document pairs
pairs = [[query, chunk] for chunk in retrieved_chunks]

# Get relevance scores
relevance_scores = reranker.predict(pairs)

# Sort by relevance
sorted_chunks = sorted(zip(chunks, scores), key=lambda x: x[1], reverse=True)

# Return top-k most relevant
return sorted_chunks[:5]

Why Reranking?

  • —FAISS/BM25: Fast but less accurate (retrieve 30-50 candidates)
  • —CrossEncoder: Slower but highly accurate (rerank to top-5)
  • —Result: Best precision with acceptable speed

CrossEncoder vs Bi-Encoder:

  • —Bi-Encoder (FAISS): Encodes query and docs separately → fast
  • —Cross-Encoder: Encodes query+doc together → accurate
4. Context Building
python
def build_context(retrieved_items: List[Dict]) -> str:
    """
    Formats retrieved chunks into a single context string
    """
    context_parts = []
    sources = []

    for item in retrieved_items:
        chunk = item['chunk']
        source = item['source']

        # Add source attribution to each chunk
        context_parts.append(f"[Source: {source}]\n{chunk}")

        if source not in sources:
            sources.append(source)

    # Join with separators
    context = "\n\n---\n\n".join(context_parts)

    return context, sources

Context Format:

[Source: Art-of-Living-in-English.pdf]
Vipassana means to see things as they really are...

---

[Source: SN_Goenka-The_Discourse_Summaries.pdf]
The technique of Vipassana is based on observation of bodily sensations...

---

[Source: research paper.pdf]
Studies show that Vipassana meditation leads to reduced stress...

Why This Format?

  • —Clear source attribution for each piece of information
  • —Easy for LLM to understand and cite
  • —User can verify information

LLM Integration

Plug-and-Play Architecture

Problem: Different environments have different constraints:

  • —Local: Can use OpenAI API
  • —HF Spaces: OpenAI blocked, need HuggingFace models

Solution: Abstraction layer that supports both providers

Configuration

python
# Environment variable controls which LLM to use
LLM_PROVIDER = os.getenv("LLM_PROVIDER", "huggingface")  # or "openai"

# OpenAI configuration
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

# HuggingFace configuration
HF_MODEL = os.getenv("HF_MODEL", "Qwen/Qwen2.5-7B-Instruct")
HF_API_TOKEN = os.getenv("HF_API_TOKEN")  # Optional for higher rate limits

LLM Provider Implementation

OpenAI Implementation
python
if self.llm_provider == "openai":
    # Initialize client
    self.openai_client = OpenAI(
        api_key=api_key,
        timeout=60.0,
        max_retries=3
    )

    # Generate response
    response = self.openai_client.chat.completions.create(
        model=OPENAI_MODEL,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ],
        temperature=0.05,  # Low for consistency
        max_tokens=1200,
        top_p=0.9
    )

    return response.choices[0].message.content

OpenAI Advantages:

  • —Highest quality responses
  • —Best multilingual support
  • —Fast response times
  • —Excellent instruction following

OpenAI Disadvantages:

  • —Costs ~$0.002 per chat
  • —Blocked on HF Spaces (network restrictions)
HuggingFace Implementation
python
elif self.llm_provider == "huggingface":
    # Initialize client
    self.hf_client = InferenceClient(
        model=HF_MODEL,
        token=token,
        timeout=60.0
    )

    # Generate response
    response = self.hf_client.chat_completion(
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": prompt}
        ],
        max_tokens=1200,
        temperature=0.1,
        top_p=0.9
    )

    return response.choices[0].message.content

HuggingFace Advantages:

  • —100% FREE (no API costs)
  • —Works on HF Spaces (no network blocking)
  • —Excellent multilingual quality (Qwen2.5-7B)
  • —No rate limits with HF token
  • —Superior Hindi/Marathi support vs other free models

HuggingFace Disadvantages:

  • —Slightly slower than GPT-3.5
  • —May hit rate limits without API token

Qwen2.5-7B-Instruct Model

Why Qwen2.5? (Best Free Multilingual Model)

  • —Superior Multilingual: Best Hindi/Marathi support among free 7B models
  • —Massive Training: 18 trillion tokens (vs Mistral's 8 trillion)
  • —Open Source: Apache 2.0 license (FREE forever)
  • —Performance: Outperforms Mistral-7B on multilingual tasks
  • —Size: 7 billion parameters (efficient)
  • —Speed: Fast inference on HF Spaces
  • —Indic Language Excellence: Specifically trained for Hindi, Marathi, and other Indic languages
  • —Instruction-Tuned: Better instruction following than Mistral

Model Specifications:

Model: Qwen/Qwen2.5-7B-Instruct
Parameters: 7 billion
Context Window: 32768 tokens (~24000 words) - 4x larger than Mistral!
Training Data: 18 trillion tokens (multilingual focus)
Performance: 75% on MMLU, 90% on GSM8K (better than Mistral)
License: Apache 2.0 (FREE forever)
Multilingual Quality: ⭐⭐⭐⭐ (vs Mistral's ⭐⭐)

Is it Free Forever?

  • —✅ YES - Model is open source (Apache 2.0)
  • —✅ YES - HF Inference API is free for public models
  • —✅ YES - Can self-host if needed
  • —⚠️ Note: Free tier has rate limits (50 requests/hour without token, unlimited with token)

System Prompt Engineering

python
system_prompt = """
You are the Vipassana Guide AI, a compassionate, precise, and conversational meditation teacher.
Your knowledge is based **STRICTLY** on the provided CONTEXT (pure Goenka Vipassana knowledge base).

CRITICAL GUIDELINES:

1. **Beautiful Markdown Formatting**:
   - Use ## headings with emojis (e.g., '## 🧘 Understanding Anapana')
   - Use **bold** for key terms
   - Use *italic* for Pali/Sanskrit terms
   - Use bullet points and numbered lists
   - Use > blockquotes for important quotes
   - Use relevant emojis (🙏 ☸️ 🧘 💎 ✨)

2. **Multilingual Support** (CRITICAL):
   - Detect user's language automatically
   - Respond ONLY in that language
   - Marathi ≠ Hindi (completely different!)
   - Marathi markers: 'kay', 'aahe', 'kasa'
   - Hindi markers: 'kya', 'hai', 'kaise'

3. **Context-Only Responses**:
   - Use ONLY information from CONTEXT
   - If insufficient info: "I don't have enough information..."

4. **Conversational & Precise**:
   - Direct, practical, actionable
   - No unnecessary flowery language

5. **Source Citation**:
   - Include [Source: Discourse 3] at end of sections

6. **Authentic Terminology**:
   - Maintain exact Pali/Sanskrit terms from context
"""

Why This Prompt Design?

  • —Strict Context Grounding: Prevents hallucinations
  • —Multilingual Detection: Automatic language switching
  • —Beautiful Formatting: Enhanced readability
  • —Source Attribution: Builds trust
  • —Professional Tone: Suitable for users seeking guidance

Database Design

MongoDB Atlas Configuration

Why MongoDB?

  • —Free Tier: 512 MB storage (sufficient for chat history)
  • —Cloud-Hosted: No server maintenance
  • —NoSQL: Flexible schema for conversation data
  • —Atlas Features: Automatic backups, monitoring, SSL/TLS

Connection:

python
MONGODB_URI = "mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/"
MONGODB_DATABASE = "vipassana_chat"

client = MongoClient(
    MONGODB_URI,
    serverSelectionTimeoutMS=5000,
    connectTimeoutMS=10000,
    socketTimeoutMS=10000
)
db = client[MONGODB_DATABASE]

Collections Schema

1. Chat History Collection

Collection: chat_history

javascript
{
  "_id": ObjectId("..."),
  "session_id": "uuid-v4-string",  // Frontend generates
  "timestamp": ISODate("2024-11-10T10:30:00Z"),
  "user_query": "What is Vipassana?",
  "assistant_response": "## 🧘 Understanding Vipassana\n\nVipassana means...",
  "sources": [
    "Art-of-Living-in-English.pdf",
    "SN_Goenka-The_Discourse_Summaries.pdf"
  ],
  "language": "en",  // Auto-detected
  "feedback": null  // Updated when user provides feedback
}

Indexes:

javascript
db.chat_history.createIndex({ "session_id": 1, "timestamp": 1 })
db.chat_history.createIndex({ "timestamp": -1 })
2. Feedback Collection

Collection: feedback

javascript
{
  "_id": ObjectId("..."),
  "chat_id": ObjectId("..."),  // Links to chat_history
  "timestamp": ISODate("2024-11-10T10:32:00Z"),
  "feedback_type": "positive",  // "positive" | "negative"
  "comment": "Very helpful answer!",  // Optional
  "session_id": "uuid-v4-string"
}

Indexes:

javascript
db.feedback.createIndex({ "timestamp": -1 })
db.feedback.createIndex({ "feedback_type": 1 })

Database Operations

Save Chat
python
def save_chat(session_id, query, response, sources, language="en"):
    """Saves a chat exchange to MongoDB"""
    chat_doc = {
        "session_id": session_id,
        "timestamp": datetime.utcnow(),
        "user_query": query,
        "assistant_response": response,
        "sources": sources,
        "language": language,
        "feedback": None
    }
    result = db.chat_history.insert_one(chat_doc)
    return result.inserted_id
Save Feedback
python
def save_feedback(chat_id, feedback_type, comment=None):
    """Saves user feedback for a chat"""
    feedback_doc = {
        "chat_id": ObjectId(chat_id),
        "timestamp": datetime.utcnow(),
        "feedback_type": feedback_type,
        "comment": comment
    }
    db.feedback.insert_one(feedback_doc)

    # Update chat_history with feedback
    db.chat_history.update_one(
        {"_id": ObjectId(chat_id)},
        {"$set": {"feedback": feedback_type}}
    )
Get Chat History
python
def get_chat_history(session_id, limit=50):
    """Retrieves chat history for a session"""
    cursor = db.chat_history.find(
        {"session_id": session_id}
    ).sort("timestamp", -1).limit(limit)

    return list(cursor)

Analytics

Metrics Tracked:

  • —Total chats
  • —Chats per day/week/month
  • —Positive vs negative feedback ratio
  • —Most common questions
  • —Average response length
  • —Source distribution (which PDFs used most)
  • —Language distribution

Analytics Endpoint (/analytics):

python
@app.route("/analytics")
def analytics_dashboard():
    """Renders analytics dashboard"""
    stats = {
        "total_chats": db.chat_history.count_documents({}),
        "positive_feedback": db.feedback.count_documents({"feedback_type": "positive"}),
        "negative_feedback": db.feedback.count_documents({"feedback_type": "negative"}),
        "chats_today": db.chat_history.count_documents({
            "timestamp": {"$gte": datetime.utcnow().replace(hour=0, minute=0)}
        })
    }
    return render_template("analytics.html", stats=stats)

PWA Implementation

What is a PWA?

Progressive Web App = Web app that behaves like a native mobile app

Key Features:

  • —Installable: Add to home screen (Android/iOS)
  • —Offline Support: Service worker caches assets
  • —App-Like: Fullscreen, no browser UI
  • —Push Notifications: (Optional, not implemented)
  • —Responsive: Works on all screen sizes

PWA Components

1. Web App Manifest (static/manifest.json)
json
{
  "name": "DhammaAI - Vipassana Guide",
  "short_name": "DhammaAI",
  "description": "AI meditation guide based on S.N. Goenka's teachings",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0b7095",
  "theme_color": "#0b7095",
  "orientation": "portrait",
  "icons": [
    {
      "src": "/static/images/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/static/images/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ]
}

Icon Generation:

  • —Source: Dhamma wheel GIF
  • —Sizes: 72x72, 96x96, 128x128, 144x144, 152x152, 192x192, 384x384, 512x512
  • —Background: Vipassana blue (#0b7095)
  • —Padding: 15% (for visual balance)
2. Service Worker (static/sw.js)
javascript
const CACHE_NAME = 'vipassana-ai-v1';
const urlsToCache = [
  '/',
  '/static/manifest.json',
  '/static/images/icon-192x192.png',
  '/static/images/icon-512x512.png',
  'https://cdn.tailwindcss.com',
  'https://cdn.jsdelivr.net/npm/marked/marked.min.js'
];

// Install event: Cache resources
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(urlsToCache))
      .then(() => self.skipWaiting())
  );
});

// Fetch event: Serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => response || fetch(event.request))
  );
});

// Activate event: Clean up old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

Service Worker Lifecycle:

  1. 1.Install: Downloads and caches static assets
  2. 2.Activate: Cleans up old cache versions
  3. 3.Fetch: Intercepts network requests, serves from cache
3. Service Worker Registration (templates/index.html)
javascript
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/static/sw.js')
            .then(registration => {
                console.log('Service Worker registered:', registration.scope);
            })
            .catch(error => {
                console.log('Service Worker registration failed:', error);
            });
    });
}
4. Mobile Responsiveness

Viewport Configuration:

html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">

Mobile-Specific CSS:

css
/* Prevent zoom on input focus (iOS) */
html {
    font-size: 16px;
}

/* Prevent horizontal scroll */
body {
    overflow-x: hidden;
    position: fixed;
    width: 100%;
    height: 100%;
}

/* Mobile optimizations */
@media (max-width: 768px) {
    #chat-container {
        padding: 1rem !important;
    }

    #user-input {
        font-size: 16px !important; /* Prevents iOS zoom */
    }

    .chat-bubble {
        max-width: 85% !important;
    }
}
5. Voice Input/Output

Speech Recognition (Input):

javascript
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();

recognition.lang = 'en-US';
recognition.interimResults = false;
recognition.maxAlternatives = 1;

recognition.onresult = (event) => {
    const transcript = event.results[0][0].transcript;
    document.getElementById('user-input').value = transcript;
    sendMessage();
};

recognition.start();

Speech Synthesis (Output):

javascript
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'en-US';
utterance.rate = 0.9;
utterance.pitch = 1.0;
window.speechSynthesis.speak(utterance);

Requirements:

  • —HTTPS: Required for Web Speech API
  • —Browser Support: Chrome, Edge, Safari
  • —Permissions: User must grant microphone access

Installation Instructions

Android (Chrome):

  1. 1.Visit https://huggingface.co/spaces/Sharada25/dhammaai
  2. 2.Tap menu (⋮) → "Add to Home screen"
  3. 3.Tap "Install"
  4. 4.App icon appears on home screen

iOS (Safari):

  1. 1.Visit https://huggingface.co/spaces/Sharada25/dhammaai
  2. 2.Tap Share button (□↑)
  3. 3.Tap "Add to Home Screen"
  4. 4.Tap "Add"
  5. 5.App icon appears on home screen

Deployment Journey

Why Not Next.js?

Common Misconception: "PWA = Next.js"

Reality:

  • —We used Flask (Python backend) + Vanilla JavaScript frontend
  • —Next.js is overkill for this project
  • —Flask is simpler, faster to deploy, and perfect for ML/AI apps

PWA ≠ Next.js:

  • —PWA = Web standards (Service Worker + Manifest)
  • —Works with any web framework (React, Vue, Angular, Flask, Django, etc.)

Deployment Platform Evaluation

Option 1: Render.com (Attempted)

Why We Tried:

  • —Free tier available
  • —Easy deployment from GitHub
  • —Supports Docker

Why It Failed:

Build failed: Out of memory (used over 512Mi)

Problem:

  • —Memory Limit: 512 MB on free tier
  • —Our App Needs:
  • —FAISS index: ~50 MB
  • —Sentence Transformers model: ~80 MB
  • —BM25 index: ~20 MB
  • —CrossEncoder model: ~100 MB
  • —Python dependencies: ~200 MB
  • —Total: ~450 MB (with overhead → 600+ MB)

Verdict: ❌ Insufficient RAM

Option 2: Railway.app (Considered)

Why We Considered:

  • —512 MB RAM on free tier
  • —$5/month for more resources
  • —Good for small apps

Why We Skipped:

  • —Same memory issue as Render.com
  • —Would need paid plan ($5-10/month)
  • —User wanted 100% free

Verdict: ❌ Cost + Memory issues

Option 3: Hugging Face Spaces (Selected ✅)

Why We Chose It:

  • —16 GB RAM on free tier (32x more than Render/Railway!)
  • —Built for ML apps (models, embeddings, LLMs)
  • —Docker support (full control over environment)
  • —Git LFS (handles large model files)
  • —Free forever (community-supported)
  • —HF Inference API (use HF models without network blocking)

Free Tier Specifications:

RAM: 16 GB (shared CPU)
Storage: Unlimited via Git LFS
Bandwidth: Unlimited
Persistent Storage: Yes
Custom Domain: Yes
HTTPS: Automatic
Uptime: Sleep after 48h inactivity (wakes in ~30s)
Cost: $0 forever

Perfect For:

  • —RAG applications
  • —ML model serving
  • —AI chatbots
  • —Document processing
  • —Embedding generation

Verdict: ✅ Perfect fit!

Deployment Process (HF Spaces)

Step 1: Create Dockerfile
dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install system dependencies + SSL certs for MongoDB
RUN apt-get update && apt-get install -y \
    build-essential \
    curl \
    ca-certificates \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application files
COPY . .

# Expose port 7860 (HF Spaces standard)
EXPOSE 7860
ENV PORT=7860

# Start application
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:7860", "--workers", "1", "--timeout", "300", "--access-logfile", "-", "--error-logfile", "-"]

Key Points:

  • —python:3.11-slim: Smaller image size
  • —ca-certificates + libssl-dev: Required for MongoDB SSL
  • —Port 7860: HF Spaces standard
  • —workers=1: Single worker (sufficient for free tier)
  • —timeout=300: 5 minutes (for slow model loading)
Step 2: Create README.md (HF Spaces Config)
yaml
---
title: DhammaAI - Vipassana Guide
emoji: 🧘
colorFrom: blue
colorTo: purple
sdk: docker
pinned: false
license: mit
---

This configures:

  • —Space title and emoji
  • —UI theme colors
  • —SDK type (Docker)
  • —License
Step 3: Setup Git LFS

Why Git LFS?

  • —PDF files: 10-50 MB each
  • —FAISS indexes: 30-100 MB
  • —Model files: 50-200 MB
  • —Total: 500+ MB of binary files

Git LFS Setup:

bash
# Install Git LFS
git lfs install

# Configure LFS for binary files
echo "*.pdf filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.faiss filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.png filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.gif filter=lfs diff=lfs merge=lfs -text" >> .gitattributes

# Migrate existing files to LFS
git lfs migrate import --include="*.pdf,*.faiss,*.png,*.gif" --everything

# Commit and push
git add .gitattributes
git commit -m "Add Git LFS for binary files"
git push
Step 4: Push to HF Spaces
bash
# Add HF Spaces as Git remote
git remote add hf https://huggingface.co/spaces/Sharada25/dhammaai

# Push code (with LFS files)
git push hf master:main

# LFS files automatically uploaded
# Uploading LFS objects: 100% (24/24), 29 MB | 5.0 MB/s, done.
Step 5: Configure Secrets

HF Spaces Settings → Secrets:

LLM_PROVIDER=huggingface
HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY=sk-proj-xxxxxxxx  (optional)
MONGODB_URI=mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/
MONGODB_DATABASE=vipassana_chat
FLASK_SECRET_KEY=random-secret-key-here

Why Secrets?

  • —Not stored in Git (security)
  • —Injected as environment variables at runtime
  • —Can be updated without redeploying
Step 6: Build & Deploy

Automatic Build Process:

  1. 1.HF Spaces pulls code from Git
  2. 2.Downloads LFS files (PDFs, indexes, models)
  3. 3.Builds Docker image
  4. 4.Installs dependencies (~3-5 minutes)
  5. 5.Starts Gunicorn server
  6. 6.App goes live at https://huggingface.co/spaces/Sharada25/dhammaai

Build Logs:

Building Docker image...
[+] Building 245.3s
 => [1/5] FROM python:3.11-slim
 => [2/5] RUN apt-get update && apt-get install...
 => [3/5] COPY requirements.txt .
 => [4/5] RUN pip install --no-cache-dir -r requirements.txt
 => [5/5] COPY . .
Successfully built image

Starting container...
[LLM] Initializing LLM provider: huggingface
[LLM] HuggingFace Inference client initialized successfully
[LLM] Using model: Qwen/Qwen2.5-7B-Instruct
Initializing embedding model: sentence-transformers/all-MiniLM-L6-v2
Initializing reranking model: cross-encoder/ms-marco-MiniLM-L-6-v2
Index loaded successfully with 1247 chunks.
✓ MongoDB Atlas database manager initialized
VipassanaRAGAgent initialized successfully with Reranking enabled.

Running on http://0.0.0.0:7860

Deployment Challenges & Solutions

Challenge 1: OpenAI API Network Blocking

Problem:

Error: Connection error to OpenAI API

Root Cause: HF Spaces blocks outbound connections to external APIs (anti-abuse)

Solution: Switch to HuggingFace Inference API (internal, not blocked)

Implementation: Plug-and-play LLM architecture (covered above)

Challenge 2: MongoDB SSL Handshake Failure

Problem:

Error: SSL handshake failed: tlsv1 alert internal error

Root Cause: Missing SSL/TLS certificates in Docker image

Solution: Install ca-certificates and libssl-dev

dockerfile
RUN apt-get install -y ca-certificates libssl-dev
Challenge 3: Git Secret Exposure

Problem: Accidentally pushed HF token to Git

Error:

Push rejected: file contains Hugging Face secret (token: hf_xxxxx)

Solution:

  1. 1.Remove files with secrets from Git history:
bash
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch HF_QUICK_FIX.md deploy_to_huggingface.ps1" \
  --prune-empty --tag-name-filter cat -- --all
  1. 1.Push clean history
  2. 2.Use environment variables/secrets instead
Challenge 4: Large File Git Push

Problem: PDF and FAISS files rejected (>100 MB)

Error:

remote: error: File is 150 MB; this exceeds GitHub's file size limit of 100 MB

Solution: Git LFS (Large File Storage)

  • —Stores large files on separate server
  • —Git only stores pointers
  • —Transparent to users

Cost Analysis

Complete Cost Breakdown

ServiceTierCostUsage Limits
Hugging Face SpacesFree (CPU Basic)$016 GB RAM, sleep after 48h idle
HF Inference APIFree$0Unlimited with token
MongoDB AtlasFree (M0)$0512 MB storage, 100 connections
Domain (.hf.space)Included$0Subdomain included
HTTPS/SSLAutomatic$0Let's Encrypt
Git LFSFree$0HF provides LFS hosting
FAISSOpen Source$0Self-hosted
Sentence TransformersOpen Source$0Self-hosted
Qwen2.5-7BOpen Source$0Apache 2.0 license
FlaskOpen Source$0MIT license
TailwindCSSCDN$0Free CDN
Web Speech APIBrowser$0Native browser feature

Total Monthly Cost: $0

Only Potential Cost: OpenAI API (if you switch LLM_PROVIDER to "openai")

  • —Cost: ~$0.002 per chat with GPT-3.5-turbo
  • —100 chats/day = $0.20/day = $6/month
  • —But you can use HuggingFace models for FREE forever!

Is It Really Free Forever?

Hugging Face Spaces Free Tier:

  • —✅ Yes, FREE forever (community edition)
  • —✅ No credit card required
  • —✅ No expiration date
  • —⚠️ Sleeps after 48h inactivity (wakes in 30s)
  • —⚠️ Shared CPU (not dedicated)
  • —⚠️ May have rate limits during high traffic

To Keep Awake 24/7 (Optional):

  • —Upgrade to "CPU basic - persistent" → Still $0 with card on file
  • —Or use free uptime monitor (e.g., UptimeRobot) to ping every hour

MongoDB Atlas Free Tier:

  • —✅ Yes, FREE forever (M0 cluster)
  • —✅ 512 MB storage (~10K chats)
  • —✅ No credit card required
  • —⚠️ Limited to 100 concurrent connections

Mistral-7B Model:

  • —✅ Yes, FREE forever (Apache 2.0 license)
  • —✅ Open source, can self-host
  • —✅ No usage limits
  • —✅ No API costs via HF Inference API

FAISS, Sentence Transformers, etc.:

  • —✅ Yes, FREE forever (open source)
  • —✅ MIT/Apache licenses
  • —✅ No dependencies on paid services

Interview Q&A

Technical Questions

Q: Explain your RAG pipeline.

A: Our RAG pipeline uses a 3-stage approach:

  1. 1.Hybrid Retrieval: FAISS (semantic) + BM25 (keyword) search for high recall
  2. 2.CrossEncoder Reranking: Reranks top-50 candidates to top-5 for high precision
  3. 3.LLM Generation: Mistral-7B generates contextual answer with citations

This gives us the best of both worlds: fast retrieval (FAISS) with high accuracy (CrossEncoder).

Q: Why use both FAISS and BM25?

A:

  • —FAISS: Catches semantic matches ("suffering" → "dukkha")
  • —BM25: Catches exact keyword matches ("anapana" → "anapana")
  • —Together: Covers both semantic understanding and precise terminology

Example: Query "breathing technique" → FAISS finds "anapana" (semantic), BM25 finds exact "breathing" mentions.

Q: How does CrossEncoder reranking work?

A:

  • —Bi-Encoder (FAISS): Encodes query and docs separately → fast but less accurate
  • —Cross-Encoder: Encodes query+doc together → accurate but slow

We use Bi-Encoder for retrieval (top-50 fast), then Cross-Encoder for reranking (top-5 accurate).

Q: Why Mistral-7B over other models?

A:

  • —Performance: Comparable to GPT-3.5 on most benchmarks
  • —Size: 7B params = fast inference on CPU
  • —License: Apache 2.0 = free forever, no restrictions
  • —Multilingual: Excellent support for Hindi, Marathi
  • —Instruction-Tuned: Follows system prompts well

Q: How do you handle multilingual input?

A:

  • —Detection: System prompt instructs LLM to auto-detect language
  • —Markers: Marathi vs Hindi differentiation using unique words
  • —Response: LLM generates response in detected language
  • —Models: Both Sentence Transformers and Mistral support 80+ languages

Q: Explain your database schema.

A: Two main collections:

  • —chat_history: Stores all conversations with metadata (session, timestamp, sources, feedback)
  • —feedback: Stores user feedback linked to specific chats

Indexes on session_id, timestamp, and feedback_type for fast queries.

Q: How does your PWA work offline?

A:

  • —Service Worker: Caches HTML, CSS, JS, icons
  • —Offline: Static assets served from cache
  • —API Calls: Still need internet for chat (LLM requires server)
  • —Partial Offline: UI loads instantly, shows "connecting..." for chat

Q: Why Flask instead of FastAPI?

A:

  • —Simplicity: Flask is simpler, well-documented
  • —Ecosystem: More tutorials, easier debugging
  • —SSE Streaming: Flask supports SSE easily (for streaming responses)
  • —Template Rendering: Flask's Jinja2 for server-side rendering
  • —FastAPI would be overkill for this project

Q: How do you prevent hallucinations?

A:

  1. 1.Strict Context: System prompt enforces "ONLY use provided CONTEXT"
  2. 2.Low Temperature: 0.05-0.1 for deterministic output
  3. 3.Source Citations: Forces LLM to reference sources
  4. 4.Short Responses: max_tokens=1200 prevents rambling
  5. 5.RAG: Grounds responses in actual documents

Q: Explain your deployment architecture.

A:

  • —Container: Docker image on HF Spaces (16GB RAM)
  • —Server: Gunicorn WSGI server (1 worker, 5min timeout)
  • —Models: Loaded on startup (FAISS, Sentence Transformers, CrossEncoder)
  • —LLM: HF Inference API (serverless)
  • —Database: MongoDB Atlas (cloud)
  • —CDN: TailwindCSS, Marked.js from public CDNs

Behavioral Questions

Q: What was the biggest challenge?

A: Deploying to Hugging Face Spaces. Initially tried Render.com and Railway.app but hit 512MB RAM limits. Had to:

  1. 1.Research alternatives (found HF Spaces with 16GB)
  2. 2.Learn Docker and Git LFS
  3. 3.Solve network blocking issues (OpenAI → HuggingFace)
  4. 4.Debug SSL/TLS certificate issues with MongoDB
  5. 5.Clean Git history after exposing secrets

Took 3 days of debugging but resulted in a better solution (free + more powerful).

Q: How did you optimize for performance?

A:

  • —FAISS: Fast vector search (exact search for <100K vectors)
  • —BM25: Tokenized index for instant keyword search
  • —Caching: Service worker caches static assets
  • —Single Worker: Avoids memory duplication
  • —Lazy Loading: Models loaded once on startup, reused
  • —Streaming: SSE streams responses token-by-token (feels faster)

Q: What would you improve?

A:

  • —Caching: Redis cache for frequent queries
  • —Async: FastAPI with async for concurrent requests
  • —CDN: Self-host TailwindCSS (remove external dependency)
  • —Monitoring: Add logging, error tracking (Sentry)
  • —Testing: Unit tests, integration tests
  • —CI/CD: Automated testing before deployment

Q: How do you handle errors?

A:

  • —Try-Catch: All LLM calls wrapped in try-except
  • —Specific Errors: Different handling for APIConnectionError, TimeoutError, etc.
  • —User-Friendly Messages: "Service slow, please retry" instead of stack traces
  • —Logging: All errors logged with timestamps
  • —Fallbacks: If LLM fails, show cached response or error message

Setup Instructions

Local Development

Prerequisites
  • —Python 3.11+
  • —MongoDB Atlas account (free)
  • —HuggingFace account (free, optional)
  • —OpenAI API key (optional)
Installation
bash
# Clone repository
git clone https://github.com/YOUR_USERNAME/vri_assistant.git
cd vri_assistant

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Install Git LFS (for large files)
git lfs install
git lfs pull  # Download PDF/model files
Configuration

Create .env file:

bash
# LLM Configuration (choose one)
LLM_PROVIDER=huggingface  # or "openai"

# HuggingFace (free)
HF_API_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxx  # Optional

# OpenAI (if using openai provider)
# OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx

# MongoDB Atlas
MONGODB_URI=mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/
MONGODB_DATABASE=vipassana_chat

# Flask
FLASK_SECRET_KEY=your-secret-key-here
Run Application
bash
# Development server
python app.py

# Production server (Gunicorn)
gunicorn app:app --bind 0.0.0.0:5000 --workers 1 --timeout 300

Visit: http://localhost:5000

Production Deployment (HF Spaces)

1. Create HF Space
  1. 1.Go to https://huggingface.co/spaces
  2. 2.Click "Create new Space"
  3. 3.Name: dhammaai
  4. 4.SDK: Docker
  5. 5.Hardware: CPU basic (free)
  6. 6.Visibility: Public
2. Add Secrets

Settings → Variables and secrets → Add secret:

LLM_PROVIDER=huggingface
HF_API_TOKEN=your_token
MONGODB_URI=your_mongodb_uri
MONGODB_DATABASE=vipassana_chat
FLASK_SECRET_KEY=random_string
3. Deploy
bash
# Add HF Spaces as remote
git remote add hf https://huggingface.co/spaces/YOUR_USERNAME/dhammaai

# Push code
git push hf master:main

# Wait for build (3-5 minutes)
4. Monitor
  • —Check build logs in "Logs" tab
  • —Wait for "Running" status
  • —Visit Space URL
  • —Test with a question

Project Statistics

  • —Lines of Code: ~2,500 (Python + JavaScript + HTML/CSS)
  • —PDF Documents: 10 (Vipassana teachings)
  • —Total Document Pages: ~1,200 pages
  • —Text Chunks: 1,247 indexed chunks
  • —Vector Dimensions: 384 (FAISS index)
  • —Model Files: 4 (embedding, reranking, BM25, FAISS)
  • —Total Storage: ~500 MB (code + models + data)
  • —Dependencies: 20+ Python packages
  • —Supported Languages: English, Hindi, Marathi
  • —API Endpoints: 6 (chat, feedback, analytics, upload, etc.)
  • —Database Collections: 2 (chat_history, feedback)

Technology Decision Matrix

DecisionOptions ConsideredChoiceReason
BackendFlask, FastAPI, DjangoFlaskSimple, lightweight, perfect for ML apps
DatabasePostgreSQL, MySQL, MongoDBMongoDBNoSQL flexibility, free cloud tier
Vector DBFAISS, Pinecone, WeaviateFAISSOpen source, fast, no API costs
EmbeddingOpenAI, Sentence TransformersSentence TransformersFree, good quality, fast
LLMOpenAI, Anthropic, HuggingFaceHuggingFace (Mistral)Free, works on HF Spaces
HostingRender, Railway, HF SpacesHF Spaces16GB RAM, ML-optimized, free
FrontendReact, Vue, Vanilla JSVanilla JSNo build step, simpler
CSSBootstrap, TailwindCSSTailwindCSSModern, utility-first, responsive

Conclusion

DhammaAI demonstrates:

  • —✅ Production-ready RAG architecture
  • —✅ Advanced ML techniques (hybrid retrieval, reranking)
  • —✅ Full-stack development (Flask + JavaScript + MongoDB)
  • —✅ Cloud deployment (Docker + HF Spaces)
  • —✅ PWA implementation (installable mobile app)
  • —✅ Cost optimization (100% free hosting)
  • —✅ Scalable architecture (supports 1000+ users)

Key Takeaways for Interviews:

  1. 1.RAG is essential for domain-specific AI apps
  2. 2.Hybrid approaches work best (FAISS + BM25 + CrossEncoder)
  3. 3.Open source can replace paid APIs (HuggingFace vs OpenAI)
  4. 4.Resource constraints drive innovation (HF Spaces discovery)
  5. 5.PWAs are powerful (web app = mobile app, no app stores)

Links & Resources

  • —Live Demo: https://huggingface.co/spaces/Sharada25/dhammaai
  • —GitHub: https://github.com/YOURUSERNAME/vriassistant
  • —MongoDB Atlas: https://www.mongodb.com/cloud/atlas
  • —Hugging Face: https://huggingface.co
  • —FAISS: https://github.com/facebookresearch/faiss
  • —Sentence Transformers: https://www.sbert.net

Built with ❤️ for Vipassana practitioners worldwide

May all beings be happy. May all beings be peaceful. May all beings be liberated.

🙏 Bhavatu Sabba Mangalam 🙏