Murali Chandran commited on
Commit
9dfc96d
·
1 Parent(s): 9d70c37

hugging face implementation CPU/gguf

Browse files
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
Dockerfile ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # 1. Start with an OS that already has CUDA and PyTorch installed
2
+ # FROM pytorch/pytorch:2.1.2-cuda12.1-cudnn8-runtime
3
+
4
+ # # 2. Set the working directory inside the container
5
+ # WORKDIR /app
6
+
7
+ # # 3. Copy your requirements file and install dependencies
8
+ # # (You will need to create a requirements.txt with fastapi, uvicorn, transformers, bitsandbytes, faiss-cpu, sentence-transformers, etc.)
9
+ # COPY requirements.txt .
10
+ # RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+ # # 4. Copy your actual code and knowledge base into the container
13
+ # COPY . .
14
+
15
+ # # 5. Open port 8000 so the outside world can talk to the API
16
+ # EXPOSE 8000
17
+
18
+ # # 6. Start the FastAPI server when the container boots
19
+ # CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
20
+
21
+
22
+
23
+
24
+ # 1. Use a lightweight Python base
25
+ FROM python:3.11-slim
26
+
27
+ # 2. Install system dependencies for building llama-cpp on a CPU
28
+ RUN apt-get update && apt-get install -y \
29
+ build-essential \
30
+ cmake \
31
+ python3-dev \
32
+ && rm -rf /var/lib/apt/lists/*
33
+
34
+ # 3. Set up a non-root user (Strict Hugging Face Security Requirement)
35
+ RUN useradd -m -u 1000 user
36
+ USER user
37
+ ENV PATH="/home/user/.local/bin:$PATH"
38
+
39
+ WORKDIR /app
40
+
41
+ # 4. Install your Python dependencies
42
+ COPY --chown=user requirements.txt .
43
+ RUN pip install --no-cache-dir --upgrade pip && \
44
+ pip install --no-cache-dir -r requirements.txt
45
+
46
+ # 5. Copy your actual code and knowledge base into the container
47
+ COPY --chown=user . .
48
+
49
+ # 6. Run the app using Python so it triggers the dynamic port logic in app.py
50
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.responses import FileResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+
5
+ import uvicorn
6
+ import os
7
+
8
+ # Import our custom modules
9
+ from schemas import BotRequest
10
+ from llm_cpu import load_llm, generate_answer
11
+ from rag import initialize_vector_store, retrieve_context
12
+
13
+ app = FastAPI(title="AskJulia-Modular-v1")
14
+
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=["*"],
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ # 1. Initialize Systems on Startup
23
+ llm = load_llm()
24
+ faiss_index = initialize_vector_store()
25
+ print("--- Server Ready ---")
26
+
27
+ # 2. Serve the HTML Interface
28
+ @app.get("/")
29
+ async def get_ui():
30
+ # Instead of a massive string in Python, we just serve the raw HTML file
31
+ return FileResponse("index.html")
32
+
33
+ # 3. Handle the Ask Julia API
34
+ @app.post("/explain")
35
+ async def explain(request: BotRequest):
36
+ clean_query = request.user_query.lower().replace("@bot", "").strip()
37
+
38
+ # Step A: Find relevant notes from the database
39
+ retrieved_context = retrieve_context(faiss_index, clean_query)
40
+
41
+ # Step B: Ask the LLM to generate the final answer
42
+ reply = generate_answer(llm, retrieved_context, clean_query)
43
+
44
+ return {"reply": reply}
45
+
46
+ if __name__ == "__main__":
47
+ # uvicorn.run(app, host="0.0.0.0", port=8000)
48
+
49
+ # Hugging Face provides the PORT environment variable.
50
+ # If it's not found (like when you run it locally), it defaults to 7860.
51
+ port = int(os.environ.get("PORT", 7860))
52
+
53
+ # "0.0.0.0" is critical. It tells the server to accept connections
54
+ # from outside the Docker container (which is how Hugging Face routes traffic).
55
+ uvicorn.run(app, host="0.0.0.0", port=port)
faiss_index.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1b75585cf78a809f3d975954231d8b2aaef18958eebb03ac1c80430098cdc27a
3
+ size 6189
faiss_metadata.json ADDED
@@ -0,0 +1 @@
 
 
1
+ ["# Internal Notes: Flow Matching Vectors\nIn our specific implementation for diffusion.fyi, we define the probability path $p_t(x)$ as a linear interpolation between noise $p_0$ and data $p_1$.", "The \"Ask Julia\" velocity field $v_t(x)$ is specifically designed to minimize the \"curviness\" of the flow. We use a unique constant called the **Julia-Constant ($J_c$)** set at $0.42$ for all our Euler integrations to ensure stability in high-dimensions.", "# Integration Techniques for Flow\nWhile standard ODE solvers work, we recommend the **Hyper-Step Method**. This method uses a variable step size where $dt$ is inversely proportional to the local gradient of the vector field.", "Warning: If the velocity $v_t$ exceeds a magnitude of $10.0$, the Hyper-Step will automatically halve the integration time to prevent divergence."]
index.html ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Ask Julia Assistant</title>
7
+
8
+ <script>
9
+ window.MathJax = { tex: { inlineMath: [['$', '$'], ['\\(', '\\)']] } };
10
+ </script>
11
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
12
+ <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
13
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
14
+
15
+ <style>
16
+ :root { --sidebar-width: 450px; }
17
+ body { font-family: 'Inter', sans-serif; background: #0f172a; color: #f8fafc; display: flex; height: 100vh; margin: 0; overflow: hidden; }
18
+
19
+ #content-area {
20
+ flex: 1; padding: 40px; overflow-y: auto;
21
+ transition: padding-right 0.3s ease;
22
+ }
23
+ body.sidebar-active #content-area {
24
+ padding-right: calc(var(--sidebar-width) + 40px);
25
+ }
26
+
27
+ #sidebar {
28
+ position: fixed; right: 0; top: 0; height: 100%;
29
+ width: var(--sidebar-width); background: #1e293b;
30
+ display: flex; flex-direction: column;
31
+ box-shadow: -5px 0 15px rgba(0,0,0,0.3);
32
+ transform: translateX(100%); transition: transform 0.3s ease;
33
+ z-index: 100; padding: 20px; box-sizing: border-box;
34
+ }
35
+ #sidebar.open { transform: translateX(0); }
36
+
37
+ #sidebar-tab {
38
+ position: absolute; left: -40px; top: 50%; transform: translateY(-50%);
39
+ width: 40px; height: 70px; background: #1e293b; color: #38bdf8;
40
+ border: none; border-radius: 8px 0 0 8px; cursor: pointer;
41
+ box-shadow: -5px 0 10px rgba(0,0,0,0.1); display: flex;
42
+ align-items: center; justify-content: center; font-size: 1.2rem;
43
+ border-left: 1px solid #334155; border-top: 1px solid #334155; border-bottom: 1px solid #334155;
44
+ padding: 0; transition: background 0.2s;
45
+ }
46
+ #sidebar-tab:hover { background: #334155; }
47
+
48
+ h2 { color: #38bdf8; }
49
+ .research-text { line-height: 1.8; font-size: 1.1rem; color: #cbd5e1; }
50
+
51
+ #chat-output { flex: 1; overflow-y: auto; margin-bottom: 10px; font-size: 0.95rem; line-height: 1.6; border-bottom: 1px solid #334155; padding-bottom: 10px; }
52
+
53
+ .ai-msg { background: #0f172a; padding: 15px; border-radius: 8px; margin-top: 10px; border-left: 4px solid #38bdf8; }
54
+
55
+ ::selection { background: #38bdf8; color: #0f172a; }
56
+ .cursor-blink { animation: blink 1s step-end infinite; color: #38bdf8; }
57
+ @keyframes blink { 50% { opacity: 0; } }
58
+
59
+ /* FOOLPROOF FLEXBOX INPUT LAYOUT */
60
+ .input-container {
61
+ display: flex;
62
+ align-items: flex-end; /* Keeps button at the bottom when typing long messages */
63
+ gap: 10px;
64
+ margin-top: 10px;
65
+ width: 100%;
66
+ }
67
+
68
+ #chat-input {
69
+ flex: 1;
70
+ background: #0f172a; border: 1px solid #334155; color: white;
71
+ padding: 12px;
72
+ border-radius: 8px; box-sizing: border-box; outline: none;
73
+ resize: none; overflow-y: hidden; min-height: 45px; max-height: 150px;
74
+ font-family: inherit; font-size: 0.95rem;
75
+ }
76
+ #chat-input:focus { border-color: #38bdf8; }
77
+
78
+ #send-btn {
79
+ flex-shrink: 0; /* Prevents button from shrinking */
80
+ background: #38bdf8;
81
+ color: #0f172a;
82
+ border: none;
83
+ border-radius: 8px;
84
+ width: 45px;
85
+ height: 45px; /* Exactly matches the minimum height of the text area */
86
+ cursor: pointer;
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ transition: transform 0.1s, background 0.2s, opacity 0.2s;
91
+ }
92
+
93
+ #send-btn:hover { background: #7dd3fc; }
94
+ #send-btn:active { transform: scale(0.9); }
95
+
96
+ /* Mobile Adjustment */
97
+ @media (max-width: 600px) {
98
+ :root { --sidebar-width: 100%; }
99
+ #sidebar-tab { width: 50px; height: 50px; left: -50px; }
100
+ }
101
+ </style>
102
+ </head>
103
+ <body>
104
+ <div id="content-area">
105
+ <h2>Research Material</h2>
106
+ <div class="research-text">
107
+ <p>In Flow Matching, the transition is defined by the ODE: $dx = v_t(x)dt$.
108
+ This vector field $v_t$ defines the velocity at each point in space-time to push samples from $p_0$ to $p_1$.</p>
109
+
110
+ <p>The forward diffusion process adds noise $q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{1-\beta_t}x_{t-1}, \beta_t I)$.
111
+ As $t$ increases, the original structure of the image is lost to Gaussian randomness.</p>
112
+
113
+ <p>Copy any text above and paste it into the sidebar with your question!</p>
114
+ </div>
115
+ </div>
116
+
117
+ <div id="sidebar">
118
+ <button id="sidebar-tab" onclick="toggleSidebar()" title="Toggle Ask Julia">
119
+ <span id="tab-icon">◀</span>
120
+ </button>
121
+
122
+ <h3>Ask Julia</h3>
123
+ <div id="chat-output"> </div>
124
+
125
+ <div class="input-container">
126
+ <textarea id="chat-input" rows="1" placeholder="@bot explain..."></textarea>
127
+ <button id="send-btn" onclick="askBot()" title="Send">
128
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
129
+ </button>
130
+ </div>
131
+
132
+ </div>
133
+
134
+ <script>
135
+ const sidebar = document.getElementById('sidebar');
136
+ const output = document.getElementById('chat-output');
137
+ const input = document.getElementById('chat-input');
138
+ const sendBtn = document.getElementById('send-btn');
139
+ const tabIcon = document.getElementById('tab-icon');
140
+
141
+ function toggleSidebar() {
142
+ sidebar.classList.toggle('open');
143
+ document.body.classList.toggle('sidebar-active');
144
+
145
+ if (sidebar.classList.contains('open')) {
146
+ tabIcon.innerText = "▶";
147
+ } else {
148
+ tabIcon.innerText = "◀";
149
+ }
150
+ }
151
+
152
+ input.addEventListener('input', function() {
153
+ this.style.height = 'auto';
154
+ this.style.height = this.scrollHeight + 'px';
155
+ this.style.overflowY = this.scrollHeight > 150 ? 'auto' : 'hidden';
156
+ });
157
+
158
+ function renderTextWithMath(text) {
159
+ const mathBlocks = [];
160
+ const mathRegex = /(\\$\\$[\\s\\S]*?\\$\\$|\\$[^\\$]*?\\$|\\\\\\[[\\s\\S]*?\\\\\\]|\\\\\\([\\s\\S]*?\\\\\\))/g;
161
+
162
+ let placeholderText = text.replace(mathRegex, (match) => {
163
+ mathBlocks.push(match);
164
+ return `@@MATH_BLOCK_${mathBlocks.length - 1}@@`;
165
+ });
166
+
167
+ let html = marked.parse(placeholderText);
168
+
169
+ mathBlocks.forEach((math, index) => {
170
+ html = html.replace(`@@MATH_BLOCK_${index}@@`, math);
171
+ });
172
+
173
+ return html;
174
+ }
175
+
176
+ function typeARResponse(text, element) {
177
+ return new Promise((resolve) => {
178
+ let i = 0;
179
+ let currentText = "";
180
+ const speed = 10;
181
+ const chunkSize = 2;
182
+
183
+ const interval = setInterval(() => {
184
+ currentText += text.substring(i, i + chunkSize);
185
+ i += chunkSize;
186
+
187
+ element.innerHTML = renderTextWithMath(currentText) + '<span class="cursor-blink">▌</span>';
188
+ output.scrollTop = output.scrollHeight;
189
+
190
+ if (i >= text.length) {
191
+ clearInterval(interval);
192
+ element.innerHTML = renderTextWithMath(text);
193
+ resolve();
194
+ }
195
+ }, speed);
196
+ });
197
+ }
198
+
199
+ async function askBot() {
200
+ const query = input.value.trim();
201
+ if(!query.includes('@bot')) return;
202
+
203
+ // Disable UI while waiting for response
204
+ sendBtn.style.opacity = "0.5";
205
+ sendBtn.disabled = true;
206
+ input.disabled = true;
207
+
208
+ output.innerHTML += `<div><b>You:</b> ${query}</div><div class="ai-msg" id="last-response">Thinking...</div>`;
209
+
210
+ input.value = "";
211
+ input.style.height = 'auto';
212
+ output.scrollTop = output.scrollHeight;
213
+
214
+ try {
215
+ const res = await fetch('/explain', {
216
+ method: 'POST',
217
+ headers: { 'Content-Type': 'application/json' },
218
+ body: JSON.stringify({ page_text: "Context provided in query.", user_query: query })
219
+ });
220
+ const data = await res.json();
221
+
222
+ const lastResponse = document.getElementById('last-response');
223
+ lastResponse.removeAttribute('id');
224
+
225
+ await typeARResponse(data.reply, lastResponse);
226
+
227
+ if (window.MathJax) MathJax.typesetPromise([lastResponse]);
228
+
229
+ } catch (err) {
230
+ document.getElementById('last-response').innerHTML = "<span style='color: #f87171;'>Error: Could not connect to backend.</span>";
231
+ }
232
+
233
+ // Re-enable UI
234
+ sendBtn.style.opacity = "1";
235
+ sendBtn.disabled = false;
236
+ input.disabled = false;
237
+ input.focus();
238
+ output.scrollTop = output.scrollHeight;
239
+ }
240
+
241
+ input.addEventListener('keydown', (e) => {
242
+ if(e.key === 'Enter' && !e.shiftKey) {
243
+ e.preventDefault();
244
+ askBot();
245
+ }
246
+ });
247
+ </script>
248
+ </body>
249
+ </html>
knowledge_base/flow_matching_logic.md ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Internal Notes: Flow Matching Vectors
2
+ In our specific implementation for diffusion.fyi, we define the probability path $p_t(x)$ as a linear interpolation between noise $p_0$ and data $p_1$.
3
+
4
+ The "Ask Julia" velocity field $v_t(x)$ is specifically designed to minimize the "curviness" of the flow. We use a unique constant called the **Julia-Constant ($J_c$)** set at $0.42$ for all our Euler integrations to ensure stability in high-dimensions.
knowledge_base/integration_methods.md ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Integration Techniques for Flow
2
+ While standard ODE solvers work, we recommend the **Hyper-Step Method**. This method uses a variable step size where $dt$ is inversely proportional to the local gradient of the vector field.
3
+
4
+ Warning: If the velocity $v_t$ exceeds a magnitude of $10.0$, the Hyper-Step will automatically halve the integration time to prevent divergence.
llm_cpu.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from huggingface_hub import hf_hub_download
2
+ from llama_cpp import Llama
3
+ import os
4
+
5
+ def load_llm():
6
+ print("--- Downloading/Loading Qwen2.5 1.5B GGUF (4-bit Quantized) ---")
7
+
8
+ # This downloads the heavily compressed CPU version of Qwen
9
+ # It caches it locally, so it only downloads once!
10
+ model_path = hf_hub_download(
11
+ repo_id="Qwen/Qwen2.5-1.5B-Instruct-GGUF",
12
+ filename="qwen2.5-1.5b-instruct-q4_k_m.gguf"
13
+ )
14
+
15
+ # Load into standard CPU RAM
16
+ # n_threads should match your physical CPU cores (e.g., 4 or 8)
17
+ llm = Llama(
18
+ model_path=model_path,
19
+ n_ctx=2048, # How much text it can remember at once
20
+ n_threads=4, # Number of CPU cores to use
21
+ verbose=False # Hides the messy C++ logs
22
+ )
23
+ print("--- CPU Model Loaded Successfully ---")
24
+ return llm
25
+
26
+ def generate_answer(llm, retrieved_context, query):
27
+ # Qwen-specific ChatML format for the CPU engine
28
+ # This guarantees the model stays in 'Julia Assistant' mode
29
+ prompt = (
30
+ f"<|im_start|>system\n"
31
+ f"You are the 'Ask Julia' assistant for diffusion.fyi. "
32
+ f"Your tone is encouraging and academic. "
33
+ f"You MUST use LaTeX for math (e.g., $x$ or $$y=x$$). "
34
+ f"CRITICAL INSTRUCTION: You may ONLY answer based on the provided CONTEXT. "
35
+ f"If the exact name or concept asked by the user is NOT in the CONTEXT, you MUST reply with EXACTLY: 'I don't have that specific information in my notes.' "
36
+ f"DO NOT guess, DO NOT talk about other constants instead, and DO NOT hallucinate.<|im_end|>\n"
37
+ f"<|im_start|>user\n"
38
+ f"CONTEXT:\n{retrieved_context}\n\n"
39
+ f"USER QUESTION: {query}<|im_end|>\n"
40
+ f"<|im_start|>assistant\n"
41
+ )
42
+
43
+ # Generation parameters tuned for CPU
44
+ output = llm(
45
+ prompt,
46
+ max_tokens=2048,
47
+ temperature=0.1,
48
+ stop=["<|im_end|>", "<|im_start|>"],
49
+ echo=False # Prevents the prompt from being repeated in the answer
50
+ )
51
+
52
+ response = output['choices'][0]['text'].strip()
53
+
54
+ # Delimiter Cleanup (Safety net for the frontend)
55
+ response = response.replace('\\(', '$').replace('\\)', '$')
56
+ response = response.replace('\\[', '$$').replace('\\]', '$$')
57
+
58
+ return response
main.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ def main():
2
+ print("Hello from ask-julia-backend!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ pydantic
4
+
5
+ faiss-cpu
6
+ sentence-transformers
7
+ numpy
8
+
9
+ # CPU Related -- helped for quantisation
10
+ #llama-cpp-python
11
+ #huggingface-hub
12
+
13
+ # GPU Related
14
+ transformers
15
+ accelerate
16
+ bitsandbytes