mockspace / README.md
Muhammad Umer
README.md is also correct as we have verified all remaining sections
909bec0
|
Raw
History Blame Contribute Delete
20.3 kB
---
title: Example App Hackathon Gustave Eiffel 2026
emoji: πŸ€–
colorFrom: blue
colorTo: purple
sdk: gradio
sdk_version: 4.44.1
python_version: '3.11'
app_file: app.py
pinned: false
license: apache-2.0
---
# πŸ—Ό RAG Chat API β€” Gustave Eiffel Hackathon 2026
A complete **Retrieval-Augmented Generation (RAG)** system deployed as a Hugging Face Space, with a `/query` API endpoint designed for the RAG evaluation system.
---
## Table of Contents
1. [Overview](#overview)
2. [Architecture](#architecture)
3. [Setup & Deployment](#setup--deployment)
4. [Adding Binary Files to the HF Space](#adding-binary-files-to-the-hf-space)
5. [Configuration](#configuration)
6. [How It Works (Detailed)](#how-it-works-detailed)
---
## Overview
This application demonstrates how to build a production-ready RAG system within the Hugging Face ecosystem. It covers:
| Requirement | Solution |
|---|---|
| LLM API calls | Azure OpenAI (`gpt-5` via REST) |
| Text β†’ Embeddings | Azure OpenAI (`text-embedding-3-small` via REST) |
| Vector Store | ChromaDB (persistent, runs in-process) |
| API Endpoint | FastAPI with `POST /query` |
| UI | Gradio Blocks (chat + document ingestion) |
---
## Architecture
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Hugging Face Space β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Gradio β”‚ β”‚ FastAPI β”‚ β”‚ ChromaDB β”‚ β”‚
β”‚ β”‚ UI │────▢│ /query │────▢│ Vector Store β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ /ingest β”‚ β”‚ (persistent) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β–² β”‚
β”‚ β–Ό β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Azure OpenAI β”‚ β”‚ Azure OpenAI β”‚ β”‚
β”‚ β”‚ GPT-5 (LLM) β”‚ β”‚ text-embedding β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ -3-small β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
---
## Setup & Deployment
### Prerequisites
- Python 3.11+
- A Hugging Face account with an API token
### Local Development
```bash
# Clone the repository
git clone https://huggingface.co/spaces/YOUR_USERNAME/Example-App-Hackathon-Gustave-Eiffel-2026
cd Example-App-Hackathon-Gustave-Eiffel-2026
```
#### Set Up a Python Virtual Environment
Using a virtual environment isolates project dependencies from your global Python installation.
```bash
# Create the virtual environment (Python 3.11+ required)
python -m venv ~/.venv/hackathon-eiffel
# Activate it
# macOS / Linux
source ~/.venv/hackathon-eiffel/bin/activate
# Windows (PowerShell)
~\.venv\hackathon-eiffel\Scripts\Activate.ps1
# Windows (Command Prompt)
~\.venv\hackathon-eiffel\Scripts\activate.bat
```
Once your virtual environment is active, install dependencies and run the app:
```bash
# Install dependencies
pip install -r requirements.txt
# Set your Azure API key
# macOS / Linux
export AZURE_API_KEY="your_azure_api_key_here"
# Windows (PowerShell)
$env:AZURE_API_KEY = "your_azure_api_key_here"
# Run the application
python app.py
# Server starts at http://localhost:7860
# To make it work, you first need to create embeddings , you can learn about it from README_RAG.md
```
> **Tip:** To deactivate the virtual environment when you are done, run `deactivate` (venv) or `conda deactivate` (conda).
### Testing the API
```bash
# Health check
curl http://localhost:7860/health
# Query the RAG system
curl -X POST http://localhost:7860/query \
-H "Content-Type: application/json" \
-d '{"query": "What is the Eiffel Tower?", "top_k": 3}'
# Ingest a new document
curl -X POST http://localhost:7860/ingest \
-H "Content-Type: application/json" \
-d '{"text": "Your document text here...", "source": "new_doc.txt"}'
```
---
## Adding Binary Files to the HF Space
Large or binary files (PDFs, pre-built ChromaDB databases, datasets, model weights) are stored in a **Hugging Face bucket** and mounted into the Space container. The `hf sync` command keeps your local folder in sync with the bucket.
> **Note:** Git LFS is not supported for Hugging Face Spaces persistent storage. Use the bucket + `hf sync` workflow described here instead.
### The `/data` Shared Folder
Each Hugging Face Space has a **persistent storage bucket** that is automatically mounted at `/data` inside the running container. This folder is the single shared location where the application reads and writes all persistent files β€” the ChromaDB vector store, ingested documents, and any binary assets.
| Path in container | Purpose |
|---|---|
| `/data/chroma_db/` | ChromaDB vector store (survives Space restarts) |
| `/data/sample_documents/` | Text/PDF files auto-ingested on startup |
| `/data/DataSet/` | Training and evaluation datasets |
**Default bucket naming convention:** A Space at
`https://huggingface.co/spaces/<org>/<space-name>`
gets a default storage bucket at
`https://huggingface.co/buckets/<org>/<space-name>-storage`
For this project:
- Space: `https://huggingface.co/spaces/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026`
- Bucket: `https://huggingface.co/buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage`
The `hf://` URI for use with the CLI is:
`hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage`
### Prerequisites
```bash
# Install the hf CLI (requires uv)
uv tool install "huggingface_hub[cli]"
# Authenticate (needs Write access to the bucket)
export HF_TOKEN="hf_your_token_here"
# or interactively:
hf auth login
```
### Step 1 β€” Attach the Storage Bucket to Your Space
1. Open your Space on huggingface.co
2. Go to **Settings β†’ Persistent Storage**
3. Under **"Attach storage"**, select the existing bucket **`millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage`** (or create a new one)
4. Save β€” Hugging Face will mount the bucket at `/data` inside the Space container
### Step 2 β€” Sync Your Local `./data` with the Space Bucket
Place all persistent files under a local `./data` folder. The expected structure is:
```
./data/
β”œβ”€β”€ chroma_db/ # Binary files and ChromaDB vector store
β”‚ └── chroma.sqlite3
./train_data/ # Training/test datasets (PDFs, CSVs, etc.)
β”œβ”€β”€ Automobile - Train/
β”œβ”€β”€ Climatique-Train/
└── ...
```
**Upload (local β†’ bucket):**
```bash
# Mirror ./data to the bucket β€” removes remote files deleted locally
hf sync ./data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage --delete
# Sync only a specific sub-folder (e.g., the vector store)
hf sync ./data/chroma_db hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage/chroma_db
# Sync only specific file types
hf sync ./data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage \
--include "*.pdf" --include "*.sqlite3"
```
> **`--delete` flag:** Without it, `hf sync` only uploads new/changed files and never removes anything from the remote. Add `--delete` to make the bucket an exact mirror of your local `./data`.
`hf sync` is **incremental** β€” it computes checksums and only transfers files that have changed.
### Step 3 β€” Download the Bucket to Another Machine
To pull the latest bucket contents back to a local `./data` folder (e.g., on a new dev machine):
```bash
hf sync hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage ./data
```
### Step 4 β€” Sync Hackathon Training Data
The shared training dataset for this hackathon is stored in a separate read-only bucket. Sync it to a local `./train_data` folder (it will be mounted at `/train_data` inside the Space):
```bash
# Download all training data locally
hf sync hf://buckets/millimanfrance/Hackathon2026TrainData ./train_data
# Download only a specific category
hf sync hf://buckets/millimanfrance/Hackathon2026TrainData/Automobile ./train_data/Automobile
```
> **Note:** `Hackathon2026TrainData` is a shared read-only bucket. Do not attempt to push to it.
To make this data available inside your Space, push it to your Space's own bucket under a `train_data/` sub-folder:
```bash
hf sync ./train_data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage/train_data
```
It will then be accessible inside the container at `/data/train_data/`.
### Accessing Files in the Space Application
Once the bucket is mounted, files appear under `/data` inside the Space container.
The application resolves the data root automatically via a single `DATA_DIR` constant in `app.py`:
```python
from pathlib import Path
# /data when running in HF Spaces (bucket mount), ./data for local dev
DATA_DIR = Path("/data") if Path("/data").is_dir() else Path("./data")
CHROMA_PERSIST_DIR = str(DATA_DIR / "chroma_db")
SAMPLE_DOCS_DIR = DATA_DIR / "sample_documents"
```
All persistent data β€” the vector store, sample documents, datasets β€” lives under `DATA_DIR` so a single `hf sync ./data ...` covers everything.
### Useful `hf sync` Flags
| Flag | Description |
|---|---|
| `--include "*.pdf"` | Only sync files matching the pattern |
| `--exclude "*.tmp"` | Skip files matching the pattern |
| `--delete` | Remove files in the destination that no longer exist in the source |
| `--dry-run` | Preview what would be transferred without actually doing it |
```bash
# Preview before committing
hf sync ./data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage --dry-run
```
---
## Configuration
The application loads model configuration with the following priority:
1. **`./data/config.json`** β€” the live runtime config (read at startup)
2. **`./config.json`** (project root) β€” example/template file used as fallback when no runtime config exists; **never put real credentials here**
### Step 1 β€” Create `data/config.json`
Copy the root template to `./data/` and fill in your Azure OpenAI resource details:
```bash
cp config.json data/config.json
```
Then edit `data/config.json`:
```json
{
"embedding": {
"endpoint_url": "https://<your-resource>.openai.azure.com/openai/deployments/<your-embedding-deployment>/embeddings?api-version=2024-12-01-preview",
"model": "text-embedding-3-small"
},
"llm": {
"endpoint_url": "https://<your-resource>.openai.azure.com/openai/deployments/<your-deployment>/chat/completions?api-version=2024-12-01-preview",
"model": "gpt-5",
"max_completion_tokens": 512,
"temperature": 0.7,
"top_p": 0.95
}
}
```
| Field | What to put |
|---|---|
| `<your-resource>` | Your Azure OpenAI resource name (from the Azure Portal) |
| `<your-embedding-deployment>` | The deployment name for your embedding model |
| `<your-deployment>` | The deployment name for your LLM |
| `model` | Must match the model name used when creating the deployment |
> **Note:** The `endpoint_url` for embeddings ends in `/embeddings` and the one for the LLM ends in `/chat/completions`. Keep those suffixes intact.
### Step 2 β€” Set the API Key
The API key is **not** stored in `config.json`. Set it as an environment variable:
```bash
# macOS / Linux
export AZURE_API_KEY="your_azure_api_key_here"
# Windows (PowerShell)
$env:AZURE_API_KEY = "your_azure_api_key_here"
```
When deploying to Hugging Face Spaces, add it as a **Space Secret** (Settings β†’ Secrets β†’ New secret β†’ name it `AZURE_API_KEY`).
### Step 3 β€” Push `data/config.json` to the HF Bucket
`data/config.json` lives under `./data` so a normal bucket sync includes it automatically:
```bash
hf sync ./data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage --delete
```
The Space container will then find it at `/data/config.json` on next restart.
> **Do not commit `data/config.json` to Git** β€” it contains endpoint URLs tied to your Azure resource. Add `data/config.json` to `.gitignore`. The root `config.json` is safe to commit as it only contains placeholder values.
### Tunable Parameters
The following constants in `app.py` can also be adjusted directly:
| Variable | Default | Description |
|---|---|---|
| `AZURE_API_KEY` | (env var / Space Secret) | Azure OpenAI API key (shared by LLM and embedding endpoints) |
| `EMBEDDING_MODEL_NAME` | `text-embedding-3-small` | Azure OpenAI embedding model |
| `LLM_MODEL_NAME` | `gpt-5` | Azure OpenAI LLM for answer generation |
| `LLM_MAX_TOKENS` | `512` | Maximum tokens in the LLM response |
| `LLM_TEMPERATURE` | `0.7` | Sampling temperature for the LLM |
| `LLM_TOP_P` | `0.95` | Top-p (nucleus) sampling for the LLM |
| `DATA_DIR` | `/data` (HF Spaces) or `./data` (local) | Root directory for all persistent data |
| `CHROMA_PERSIST_DIR` | `DATA_DIR/chroma_db` | ChromaDB storage path |
| `SAMPLE_DOCS_DIR` | `DATA_DIR/sample_documents` | Documents ingested on startup |
| `CHUNK_SIZE` | `512` | Text chunk size in characters |
| `CHUNK_OVERLAP` | `50` | Overlap between chunks |
| `TOP_K_RESULTS` | `3` | Number of context chunks to retrieve |
---
## How It Works (Detailed)
### Why RAG?
Large Language Models have a knowledge cutoff and can hallucinate. RAG solves this by:
- **Grounding** responses in actual documents
- **Updating** knowledge without retraining
- **Providing** source attribution for answers
### Why ChromaDB in HF Spaces?
ChromaDB is ideal for Hugging Face Spaces because:
- Runs **in-process** (no external database needed)
- Supports **persistent storage** (survives Space restarts)
- Uses **HNSW index** for fast approximate nearest neighbor search
- Zero configuration required
### Why Azure OpenAI Embeddings?
- **High-quality embeddings** β€” `text-embedding-3-small` produces state-of-the-art embeddings with excellent semantic understanding
- **No local model loading** β€” Eliminates GPU/memory requirements on the Space
- **Scalable** β€” Handles large batch embedding requests via the API
- **Consistent** β€” Same model used across environments (dev, staging, production)
### Evaluation Integration
The `/query` endpoint is designed to be called by external RAG evaluation frameworks. It returns:
- The generated `answer` for correctness evaluation
- Source `documents` for faithfulness/groundedness checks
- Similarity `scores` for retrieval quality assessment
---
## Troubleshooting
### ChromaDB Telemetry Error
If you see `capture() takes 1 positional argument but 3 were given` in the logs, it is a version incompatibility between ChromaDB and the `posthog` library. Telemetry is disabled at startup (`anonymized_telemetry=False`) so this error should no longer appear. If it persists after updating dependencies, pin `posthog<3.0` in `requirements.txt`.
### LLM / Embedding API Errors
If the `/query` endpoint logs a `503 Service Unavailable` or `401 Unauthorized`:
1. Verify `AZURE_API_KEY` is set correctly as an environment variable or Space Secret
2. Check that the endpoint URLs in `config.json` match your Azure OpenAI deployment
3. Ensure your Azure OpenAI deployment is active and the model name matches the deployment name
4. The app calls the Azure OpenAI-compatible `chat/completions` and `embeddings` endpoints directly via REST
### SSL Certificate Verification Error
If you see an error like the one below when running `python app.py`, your machine is most likely behind a **corporate proxy that performs SSL inspection** and has its own root CA that Python does not trust by default.
```
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify
failed: unable to get local issuer certificate
```
**Fix β€” point `requests` to your corporate CA bundle:**
```bash
# macOS / Linux β€” use the system bundle (adjust path for your distro)
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # Debian/Ubuntu
export REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt # RHEL/CentOS
# Or point to a specific corporate certificate file
export REQUESTS_CA_BUNDLE=/path/to/corporate-ca.crt
# Windows (PowerShell)
$env:REQUESTS_CA_BUNDLE = "C:\path\to\corporate-ca.crt"
```
Set the variable **before** running `python app.py`. The `requests` library (used for Azure OpenAI API calls) reads `REQUESTS_CA_BUNDLE` automatically β€” no code change is needed.
> **How to export your corporate CA certificate:**
> Open the failing URL (`https://huggingface.co`) in your browser, click the padlock icon β†’ View Certificate β†’ export the root CA as a `.crt` / `.pem` file, then point `REQUESTS_CA_BUNDLE` to that file.
### `hf sync` 401 Unauthorized Error
If `hf sync` fails with:
```
Error: Client error '401 Unauthorized' for url
'https://huggingface.co/api/buckets/.../tree?recursive=true'
Invalid username or password.
```
the `hf` CLI has not been authenticated. Log in with your Hugging Face token:
```bash
hf auth login
# Paste your token when prompted (needs read + write access to the bucket)
```
Or set the token as an environment variable so the CLI picks it up automatically without an interactive prompt:
```bash
export HF_TOKEN="hf_your_token_here"
hf sync ./data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage
```
> **Generate a token:** Go to huggingface.co β†’ Settings β†’ Access Tokens β†’ New token. Select **Write** role so the CLI can both read and upload to the bucket.
> **Check current login state:**
> ```bash
> hf auth whoami
> ```
---
### `hf sync` SSL Certificate Verification Error
The `hf` CLI (installed via `uv tool install huggingface_hub`) uses `httpx` internally instead of `requests`, so it ignores `REQUESTS_CA_BUNDLE`. If `hf sync` fails with:
```
httpcore.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate
```
you must set the certificate bundle through the variables that `httpx` (and the underlying `ssl` module) respects:
```bash
# Option A β€” point to your corporate CA bundle file (recommended)
export SSL_CERT_FILE=/path/to/corporate-ca.crt
export REQUESTS_CA_BUNDLE=/path/to/corporate-ca.crt # keep this too for other tools
hf sync ./data hf://buckets/millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage
# Option B β€” append your CA cert to the system bundle and point there
cat /path/to/corporate-ca.crt >> /etc/ssl/certs/ca-certificates.crt
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
```
`SSL_CERT_FILE` overrides the default CA store for Python's `ssl` module, which `httpx` / `httpcore` uses directly.
**If you need to set these variables permanently** (e.g., in a shared dev environment), add them to your shell profile:
```bash
# ~/.bashrc or ~/.zshrc
export SSL_CERT_FILE=/path/to/corporate-ca.crt
export REQUESTS_CA_BUNDLE=/path/to/corporate-ca.crt
```
> **Finding your corporate CA cert on Linux:**
> ```bash
> # List all trusted CAs and look for your company's entry
> awk -v cmd='openssl x509 -noout -subject' '/BEGIN CERT/{close(cmd)}; {print | cmd}' \
> /etc/ssl/certs/ca-certificates.crt | grep -i "your-company-name"
>
> # Or export the cert directly from the proxy
> echo | openssl s_client -connect huggingface.co:443 -showcerts 2>/dev/null \
> | openssl x509 -outform PEM > /tmp/hf-chain.pem
> export SSL_CERT_FILE=/tmp/hf-chain.pem
> ```
---
## License
Apache 2.0