QCTW Umer5881 commited on
Commit
6dac3de
Β·
1 Parent(s): aeb8c1c

Upload 2 files (#1)

Browse files

- Upload 2 files (0493ab6e679edc09076634b2c112afe5f4c4d78d)


Co-authored-by: Muhammad UMER <Umer5881@users.noreply.huggingface.co>

Files changed (2) hide show
  1. README_HF.md +193 -0
  2. README_RAG.md +239 -0
README_HF.md ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to Push Code to a New HuggingFace Space
2
+
3
+ ## Prerequisites
4
+
5
+ - [Git](https://git-scm.com/) installed
6
+ - A [HuggingFace account](https://huggingface.co/join)
7
+ - A HuggingFace Access Token (create one at [Settings > Tokens](https://huggingface.co/settings/tokens) with **Write** permission)
8
+
9
+ ---
10
+
11
+ ## Steps
12
+
13
+ ### 1. Create a New Space on HuggingFace
14
+
15
+ 1. Go to [huggingface.co/new-space](https://huggingface.co/new-space)
16
+ 2. Choose a **Space name** (e.g., `My_App`)
17
+ 3. Select the **SDK** (Gradio, Streamlit, Docker, or Static)
18
+ 4. Choose visibility (Public or Private)
19
+ 5. Click **Create Space**
20
+
21
+ Your Space URL will be: `https://huggingface.co/spaces/<YOUR_USERNAME>/<SPACE_NAME>`
22
+
23
+ ---
24
+
25
+ ### 2. Clone the Empty Space Locally
26
+
27
+ ```bash
28
+ git clone https://huggingface.co/spaces/<YOUR_USERNAME>/<SPACE_NAME>
29
+ cd <SPACE_NAME>
30
+ ```
31
+
32
+ When prompted for credentials:
33
+ - **Username:** Your HuggingFace username
34
+ - **Password:** Your HuggingFace Access Token (NOT your account password)
35
+
36
+ ---
37
+
38
+ ### 3. Add Your Code
39
+
40
+ You have **two options** to get code into your new Space:
41
+
42
+ #### Option A: Pull from an Existing Repo into the Space
43
+
44
+ If the code you want already lives in another git repo (e.g., a teammate's HF Space or a GitHub repo), you can pull it in:
45
+
46
+ ```bash
47
+ # Inside your cloned Space folder:
48
+ cd <SPACE_NAME>
49
+
50
+ # Add the source repo as a second remote
51
+ git remote add source https://huggingface.co/spaces/<SOURCE_OWNER>/<SOURCE_REPO>
52
+ # or from GitHub:
53
+ # git remote add source https://github.com/<OWNER>/<REPO>.git
54
+
55
+ # Fetch all branches from the source
56
+ git fetch source
57
+
58
+ # Merge the source's main branch into your Space
59
+ git merge source/main --allow-unrelated-histories -m "Pull code from source repo"
60
+ ```
61
+
62
+ > If there are merge conflicts, resolve them, then `git add -A` and `git commit`.
63
+
64
+ #### Option B: Copy Files Manually
65
+
66
+ Simply copy/paste your project files into the cloned Space folder.
67
+
68
+ ---
69
+
70
+ **Either way**, make sure you have a `.gitignore` to exclude unnecessary files:
71
+
72
+ ```
73
+ .venv
74
+ __pycache__/
75
+ **/__pycache__/
76
+ *.sqlite3
77
+ chroma_db/
78
+ .env
79
+ ```
80
+
81
+ ---
82
+
83
+ ### 4. Commit and Push
84
+
85
+ ```bash
86
+ git add -A
87
+ git commit -m "Initial commit"
88
+ git push origin main
89
+ git remote remove source
90
+ ```
91
+ ---
92
+
93
+
94
+ ### Deploy to Hugging Face Spaces
95
+
96
+ 1. Once you have pushed the code, (IF NOT) Push this code to the Space repository
97
+ 2. Add `AZURE_API_KEY` as a Space Secret (Settings β†’ Secrets)
98
+ 3. The Space automatically installs dependencies and starts the app
99
+
100
+ 4. To make it work, you first need to create embeddings and push them to HuggingFace Bucket (see section 3, 4 & 5 from README.md), you can learn basics of RAG from README_RAG.md
101
+ ---
102
+
103
+ ## Alternative (OPTIONAL): Push an Existing Local Project (with Full History)
104
+
105
+ If you already have a local project with commits and want to push everything (all history) to a new HF Space:
106
+
107
+
108
+
109
+
110
+ ### 1. Add the Space as a Remote (OPTIONAL)
111
+
112
+ ```bash
113
+ cd /path/to/your/project
114
+ git remote add hfspace https://<YOUR_USERNAME>:<HF_TOKEN>@huggingface.co/spaces/<YOUR_USERNAME>/<SPACE_NAME>
115
+ ```
116
+
117
+ > **Tip:** Embedding the token in the URL avoids repeated password prompts.
118
+
119
+ ### 2. Make Sure Binary Files Are NOT Tracked
120
+
121
+ HuggingFace rejects any push containing binary files (`.sqlite3`, `.pkl`, `.bin`, etc.).
122
+ Before pushing, ensure they are in `.gitignore` **and** removed from the entire git history.
123
+
124
+ ```bash
125
+ # Add binary paths to .gitignore first, then:
126
+ git rm -r --cached path/to/binary/files
127
+ git add -A
128
+ git commit -m "Remove binary files from tracking"
129
+ ```
130
+
131
+ If binaries exist in **older commits**, you must rewrite history (see Troubleshooting below).
132
+
133
+ ### 3. Push the Current Branch with All Commits (OPTIONAL)
134
+
135
+ ```bash
136
+ git push hfspace HEAD:main --force
137
+ ```
138
+
139
+ - `HEAD` = your current branch (whatever it's called)
140
+ - `HEAD:main` = push it to the `main` branch on the Space
141
+ - `--force` = overwrite the Space's existing initial commit
142
+
143
+ This preserves your full commit history on the Space.
144
+
145
+ ---
146
+
147
+ ## Troubleshooting
148
+
149
+ ### Binary File Rejection
150
+
151
+ HuggingFace rejects pushes containing binary files (e.g., `.sqlite3`, `.pkl`, `.bin`).
152
+
153
+ **Fix:**
154
+
155
+ 1. Add the binary files to `.gitignore`
156
+ 2. Remove them from git tracking:
157
+ ```bash
158
+ git rm -r --cached path/to/binary/file
159
+ ```
160
+ 3. Squash history to purge them completely:
161
+ ```bash
162
+ git add -A
163
+ git reset --soft $(git rev-list --max-parents=0 HEAD)
164
+ git commit -m "Clean initial commit"
165
+ git push hfspace main --force
166
+ ```
167
+
168
+ ### Authentication Failed
169
+
170
+ - HuggingFace does **not** accept account passwords for git. Use an **Access Token**.
171
+ - Make sure the token has **Write** permission.
172
+ - You can embed the token in the remote URL:
173
+ ```bash
174
+ git remote set-url hfspace https://<USERNAME>:<TOKEN>@huggingface.co/spaces/<USERNAME>/<SPACE_NAME>
175
+ ```
176
+
177
+ ### Wrong Branch Name
178
+
179
+ Some repos use `master` instead of `main`. Check with:
180
+
181
+ ```bash
182
+ git branch
183
+ ```
184
+
185
+ Push to whichever branch your Space expects (usually `main`).
186
+
187
+ ---
188
+
189
+ ## Security Reminder
190
+
191
+ - **Never** commit your HF token or API keys to the repo.
192
+ - If a token is accidentally exposed, revoke it immediately at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) and generate a new one.
193
+ - Use environment variables or HuggingFace Space **Secrets** (Settings > Variables and secrets) for sensitive values.
README_RAG.md ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # πŸ—Ό RAG Chat API β€” Gustave Eiffel Hackathon 2026
2
+
3
+ A complete **Retrieval-Augmented Generation (RAG)** system deployed as a Hugging Face Space, with a `/query` API endpoint designed for the RAG evaluation system.
4
+
5
+ ---
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ This application demonstrates how to build a production-ready RAG system within the Hugging Face ecosystem. It covers:
11
+
12
+ | Requirement | Solution |
13
+ |---|---|
14
+ | LLM API calls | Azure OpenAI (`gpt-5` via REST) |
15
+ | Text β†’ Embeddings | Azure OpenAI (`text-embedding-3-small` via REST) |
16
+ | Vector Store | ChromaDB (persistent, runs in-process) |
17
+ | API Endpoint | FastAPI with `POST /query` |
18
+ | UI | Gradio Blocks (chat + document ingestion) |
19
+
20
+ ---
21
+
22
+ ## Architecture
23
+
24
+ ```
25
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
26
+ β”‚ Hugging Face Space β”‚
27
+ β”‚ β”‚
28
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
29
+ β”‚ β”‚ Gradio β”‚ β”‚ FastAPI β”‚ β”‚ ChromaDB β”‚ β”‚
30
+ β”‚ β”‚ UI │────▢│ /query │────▢│ Vector Store β”‚ β”‚
31
+ β”‚ β”‚ β”‚ β”‚ /ingest β”‚ β”‚ (persistent) β”‚ β”‚
32
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
33
+ β”‚ β”‚ β–² β”‚
34
+ β”‚ β–Ό β”‚ β”‚
35
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
36
+ β”‚ β”‚ Azure OpenAI β”‚ β”‚ Azure OpenAI β”‚ β”‚
37
+ β”‚ β”‚ GPT-5 (LLM) β”‚ β”‚ text-embedding β”‚ β”‚
38
+ β”‚ β”‚ β”‚ β”‚ -3-small β”‚ β”‚
39
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
40
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Step-by-Step Explanation
46
+
47
+ ### Step 1: Document Ingestion & Chunking
48
+
49
+ Before we can answer questions, we need to prepare our knowledge base.
50
+
51
+ 1. **Load documents** β€” Read text files from `sample_documents/` directory
52
+ 2. **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.
53
+
54
+ ```python
55
+ splitter = RecursiveCharacterTextSplitter(
56
+ chunk_size=512,
57
+ chunk_overlap=50,
58
+ separators=["\n\n", "\n", ". ", " ", ""],
59
+ )
60
+ chunks = splitter.split_text(document_text)
61
+ ```
62
+
63
+ ### Step 2: Generate Embeddings
64
+
65
+ Convert text chunks into dense vector representations that capture semantic meaning.
66
+
67
+ 1. **Call Azure OpenAI** β€” We use the `text-embedding-3-small` model via the Azure OpenAI embeddings endpoint
68
+ 2. **Encode text** β€” Each chunk is transformed into a fixed-size vector where semantically similar texts are closer together in vector space
69
+
70
+ ```python
71
+ import requests as http_requests
72
+
73
+ headers = {"api-key": AZURE_API_KEY, "Content-Type": "application/json"}
74
+ payload = {"input": ["chunk 1 text", "chunk 2 text"], "model": "text-embedding-3-small"}
75
+ resp = http_requests.post(EMBEDDING_ENDPOINT_URL, headers=headers, json=payload)
76
+ embeddings = [item["embedding"] for item in resp.json()["data"]]
77
+ ```
78
+
79
+ ### Step 3: Store in Vector Database (ChromaDB)
80
+
81
+ Persist embeddings in a vector store optimized for similarity search.
82
+
83
+ 1. **Initialize ChromaDB** β€” Create a persistent client that stores data on disk (survives Space restarts)
84
+ 2. **Create collection** β€” A named collection with cosine similarity metric
85
+ 3. **Add documents** β€” Store embeddings alongside the original text and metadata
86
+
87
+ ```python
88
+ import chromadb
89
+
90
+ client = chromadb.PersistentClient(path="./data/chroma_db")
91
+ collection = client.get_or_create_collection(
92
+ name="rag_documents",
93
+ metadata={"hnsw:space": "cosine"},
94
+ )
95
+ collection.add(
96
+ ids=["doc_0", "doc_1"],
97
+ embeddings=embeddings.tolist(),
98
+ documents=["chunk 1 text", "chunk 2 text"],
99
+ metadatas=[{"source": "file.txt"}, {"source": "file.txt"}],
100
+ )
101
+ ```
102
+
103
+ ### Step 4: Query & Retrieval
104
+
105
+ When a user asks a question, find the most relevant context.
106
+
107
+ 1. **Embed the query** β€” Use the same Azure OpenAI embedding model to convert the question to a vector
108
+ 2. **Similarity search** β€” Find the top-K nearest vectors in ChromaDB (cosine similarity)
109
+ 3. **Return context** β€” Extract the original text chunks for the closest matches
110
+
111
+ ```python
112
+ query_embedding = generate_embeddings(["What is the Eiffel Tower?"])[0]
113
+ results = collection.query(
114
+ query_embeddings=[query_embedding],
115
+ n_results=3,
116
+ )
117
+ ```
118
+
119
+ ### Step 5: LLM Generation (Augmented Response)
120
+
121
+ Combine retrieved context with the user's question and generate an answer.
122
+
123
+ 1. **Build prompt** β€” Load the template from [`prompts/rag_prompt.txt`](prompts/rag_prompt.txt), inject retrieved context and the user's question
124
+ 2. **Call Azure OpenAI** β€” Send the prompt to the Azure OpenAI chat/completions endpoint (`gpt-5`)
125
+ 3. **Return response** β€” The LLM generates an answer grounded in the provided context
126
+
127
+ The prompt template (`prompts/rag_prompt.txt`):
128
+
129
+ ```
130
+ You are a helpful assistant. Answer the user's question based ONLY on the provided context.
131
+ If the context does not contain enough information to answer, say "I don't have enough information to answer this question."
132
+ Always be concise and factual.
133
+
134
+ Context:
135
+ {context}
136
+
137
+ Question: {question}
138
+ ```
139
+
140
+ The template is loaded once at startup and sent as the user message to the chat endpoint:
141
+
142
+ ```python
143
+ RAG_PROMPT_TEMPLATE = Path("prompts/rag_prompt.txt").read_text(encoding="utf-8")
144
+
145
+ # At query time:
146
+ prompt = RAG_PROMPT_TEMPLATE.format(context=context_text, question=user_query)
147
+ headers = {"api-key": AZURE_API_KEY, "Content-Type": "application/json"}
148
+ payload = {
149
+ "model": "gpt-5",
150
+ "messages": [{"role": "user", "content": prompt}],
151
+ "max_completion_tokens": 512,
152
+ "temperature": 0.7,
153
+ "top_p": 0.95,
154
+ }
155
+ resp = requests.post(LLM_ENDPOINT_URL, headers=headers, json=payload)
156
+ answer = resp.json()["choices"][0]["message"]["content"]
157
+ ```
158
+
159
+ > **Tip:** Edit `prompts/rag_prompt.txt` to tune the model's behaviour (tone, language, output format) without touching application code.
160
+
161
+ ### Step 6: API Endpoint (`/query`)
162
+
163
+ The FastAPI endpoint ties everything together for the evaluation system.
164
+
165
+ ```python
166
+ @app.post("/query")
167
+ async def query_endpoint(request: QueryRequest):
168
+ # 1. Retrieve relevant context
169
+ # 2. Build augmented prompt
170
+ # 3. Generate LLM response
171
+ # 4. Return answer + sources
172
+ result = rag_query(request.query, top_k=request.top_k)
173
+ return JSONResponse(content=result)
174
+ ```
175
+
176
+ ---
177
+
178
+ ## API Endpoints
179
+
180
+ ### `POST /query`
181
+
182
+ The primary endpoint for the RAG evaluation system.
183
+
184
+ **Request:**
185
+ ```json
186
+ {
187
+ "query": "What materials is the Eiffel Tower made of?",
188
+ "top_k": 3
189
+ }
190
+ ```
191
+
192
+ **Response:**
193
+ ```json
194
+ {
195
+ "answer": "The Eiffel Tower is made of wrought iron (puddled iron)...",
196
+ "sources": [
197
+ {"source": "eiffel_tower.txt", "score": 0.87},
198
+ {"source": "paris_landmarks.txt", "score": 0.72}
199
+ ],
200
+ "query": "What materials is the Eiffel Tower made of?"
201
+ }
202
+ ```
203
+
204
+ ### `POST /ingest`
205
+
206
+ Add new documents to the knowledge base.
207
+
208
+ **Request:**
209
+ ```json
210
+ {
211
+ "text": "The Eiffel Tower was built in 1889...",
212
+ "source": "my_document.txt"
213
+ }
214
+ ```
215
+
216
+ **Response:**
217
+ ```json
218
+ {
219
+ "status": "success",
220
+ "chunks_added": 5,
221
+ "total_chunks": 42
222
+ }
223
+ ```
224
+
225
+ ### `GET /health`
226
+
227
+ System health check.
228
+
229
+ **Response:**
230
+ ```json
231
+ {
232
+ "status": "healthy",
233
+ "documents_in_store": 42,
234
+ "embedding_model": "text-embedding-3-small",
235
+ "llm_model": "gpt-5"
236
+ }
237
+ ```
238
+
239
+ ---