A newer version of the Gradio SDK is available: 6.20.0
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
- Overview
- Architecture
- Setup & Deployment
- Adding Binary Files to the HF Space
- Configuration
- 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
# 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.
# 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:
# 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) orconda deactivate(conda).
Testing the API
# 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 syncworkflow 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 athttps://huggingface.co/spaces/<org>/<space-name>
gets a default storage bucket athttps://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
# 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
- Open your Space on huggingface.co
- Go to Settings β Persistent Storage
- Under "Attach storage", select the existing bucket
millimanfrance/Example-App-Hackathon-Gustave-Eiffel-2026-storage(or create a new one) - Save β Hugging Face will mount the bucket at
/datainside 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):
# 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"
--deleteflag: Without it,hf synconly uploads new/changed files and never removes anything from the remote. Add--deleteto 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):
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):
# 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:
Hackathon2026TrainDatais 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:
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:
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 |
# 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:
./data/config.jsonβ the live runtime config (read at startup)./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:
cp config.json data/config.json
Then edit data/config.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_urlfor embeddings ends in/embeddingsand 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:
# 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:
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.jsonto Git β it contains endpoint URLs tied to your Azure resource. Adddata/config.jsonto.gitignore. The rootconfig.jsonis 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-smallproduces 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
answerfor correctness evaluation - Source
documentsfor faithfulness/groundedness checks - Similarity
scoresfor 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:
- Verify
AZURE_API_KEYis set correctly as an environment variable or Space Secret - Check that the endpoint URLs in
config.jsonmatch your Azure OpenAI deployment - Ensure your Azure OpenAI deployment is active and the model name matches the deployment name
- The app calls the Azure OpenAI-compatible
chat/completionsandembeddingsendpoints 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:
# 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/.pemfile, then pointREQUESTS_CA_BUNDLEto 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:
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:
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:
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:
# 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:
# ~/.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:
# 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