A newer version of the Gradio SDK is available: 6.26.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.
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 β β
β ββββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step-by-Step Explanation
Step 1: Document Ingestion & Chunking
Before we can answer questions, we need to prepare our knowledge base.
- Load documents β Read text files from
sample_documents/directory - Chunk text β Split documents into smaller overlapping chunks (512 tokens, 50 token overlap) using
RecursiveCharacterTextSplitter. This ensures each chunk fits within the embedding model's context window while maintaining semantic coherence.
splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = splitter.split_text(document_text)
Step 2: Generate Embeddings
Convert text chunks into dense vector representations that capture semantic meaning.
- Call Azure OpenAI β We use the
text-embedding-3-smallmodel via the Azure OpenAI embeddings endpoint - Encode text β Each chunk is transformed into a fixed-size vector where semantically similar texts are closer together in vector space
import requests as http_requests
headers = {"api-key": AZURE_API_KEY, "Content-Type": "application/json"}
payload = {"input": ["chunk 1 text", "chunk 2 text"], "model": "text-embedding-3-small"}
resp = http_requests.post(EMBEDDING_ENDPOINT_URL, headers=headers, json=payload)
embeddings = [item["embedding"] for item in resp.json()["data"]]
Step 3: Store in Vector Database (ChromaDB)
Persist embeddings in a vector store optimized for similarity search.
- Initialize ChromaDB β Create a persistent client that stores data on disk (survives Space restarts)
- Create collection β A named collection with cosine similarity metric
- Add documents β Store embeddings alongside the original text and metadata
import chromadb
client = chromadb.PersistentClient(path="./data/chroma_db")
collection = client.get_or_create_collection(
name="rag_documents",
metadata={"hnsw:space": "cosine"},
)
collection.add(
ids=["doc_0", "doc_1"],
embeddings=embeddings.tolist(),
documents=["chunk 1 text", "chunk 2 text"],
metadatas=[{"source": "file.txt"}, {"source": "file.txt"}],
)
Step 4: Query & Retrieval
When a user asks a question, find the most relevant context.
- Embed the query β Use the same Azure OpenAI embedding model to convert the question to a vector
- Similarity search β Find the top-K nearest vectors in ChromaDB (cosine similarity)
- Return context β Extract the original text chunks for the closest matches
query_embedding = generate_embeddings(["What is the Eiffel Tower?"])[0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=3,
)
Step 5: LLM Generation (Augmented Response)
Combine retrieved context with the user's question and generate an answer.
- Build prompt β Load the template from
prompts/rag_prompt.txt, inject retrieved context and the user's question - Call Azure OpenAI β Send the prompt to the Azure OpenAI chat/completions endpoint (
gpt-5) - Return response β The LLM generates an answer grounded in the provided context
The prompt template (prompts/rag_prompt.txt):
You are a helpful assistant. Answer the user's question based ONLY on the provided context.
If the context does not contain enough information to answer, say "I don't have enough information to answer this question."
Always be concise and factual.
Context:
{context}
Question: {question}
The template is loaded once at startup and sent as the user message to the chat endpoint:
RAG_PROMPT_TEMPLATE = Path("prompts/rag_prompt.txt").read_text(encoding="utf-8")
# At query time:
prompt = RAG_PROMPT_TEMPLATE.format(context=context_text, question=user_query)
headers = {"api-key": AZURE_API_KEY, "Content-Type": "application/json"}
payload = {
"model": "gpt-5",
"messages": [{"role": "user", "content": prompt}],
"max_completion_tokens": 512,
"temperature": 0.7,
"top_p": 0.95,
}
resp = requests.post(LLM_ENDPOINT_URL, headers=headers, json=payload)
answer = resp.json()["choices"][0]["message"]["content"]
Tip: Edit
prompts/rag_prompt.txtto tune the model's behaviour (tone, language, output format) without touching application code.
Step 6: API Endpoint (/query)
The FastAPI endpoint ties everything together for the evaluation system.
@app.post("/query")
async def query_endpoint(request: QueryRequest):
# 1. Retrieve relevant context
# 2. Build augmented prompt
# 3. Generate LLM response
# 4. Return answer + sources
result = rag_query(request.query, top_k=request.top_k)
return JSONResponse(content=result)
API Endpoints
POST /query
The primary endpoint for the RAG evaluation system.
Request:
{
"query": "What materials is the Eiffel Tower made of?",
"top_k": 3
}
Response:
{
"answer": "The Eiffel Tower is made of wrought iron (puddled iron)...",
"sources": [
{"source": "eiffel_tower.txt", "score": 0.87},
{"source": "paris_landmarks.txt", "score": 0.72}
],
"query": "What materials is the Eiffel Tower made of?"
}
POST /ingest
Add new documents to the knowledge base.
Request:
{
"text": "The Eiffel Tower was built in 1889...",
"source": "my_document.txt"
}
Response:
{
"status": "success",
"chunks_added": 5,
"total_chunks": 42
}
GET /health
System health check.
Response:
{
"status": "healthy",
"documents_in_store": 42,
"embedding_model": "text-embedding-3-small",
"llm_model": "gpt-5"
}