mhndayesh commited on
Commit
4c4f2d0
·
verified ·
1 Parent(s): fe3e538

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.js +129 -0
  2. index.html +89 -0
  3. style.css +389 -0
app.js ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const chatContainer = document.getElementById('chat-container');
2
+ const userInput = document.getElementById('user-input');
3
+ const sendBtn = document.getElementById('send-btn');
4
+ const statsDisplay = document.getElementById('pipeline-stats');
5
+
6
+ // BYOK Elements
7
+ const settingsToggle = document.getElementById('settings-toggle');
8
+ const settingsPanel = document.getElementById('settings-panel');
9
+ const apiKeyInput = document.getElementById('api-key-input');
10
+ const llmModelInput = document.getElementById('llm-model-input');
11
+ const embedModelInput = document.getElementById('embed-model-input');
12
+
13
+ // Load saved settings from localStorage
14
+ apiKeyInput.value = localStorage.getItem('byok_api_key') || '';
15
+ llmModelInput.value = localStorage.getItem('byok_llm_model') || 'openai/gpt-4o-mini';
16
+ embedModelInput.value = localStorage.getItem('byok_embed_model') || 'qwen/qwen3-embedding-8b';
17
+
18
+ // Toggle Settings Panel
19
+ settingsToggle.addEventListener('click', () => {
20
+ settingsPanel.classList.toggle('active');
21
+ });
22
+
23
+ /**
24
+ * Adds a message bubble to the chat container
25
+ */
26
+ function addMessage(text, role) {
27
+ // Remove welcome message if it exists
28
+ const welcome = document.querySelector('.welcome-message');
29
+ if (welcome) welcome.remove();
30
+
31
+ const msgDiv = document.createElement('div');
32
+ msgDiv.classList.add('message', `${role}-message`);
33
+ msgDiv.textContent = text;
34
+ chatContainer.appendChild(msgDiv);
35
+ chatContainer.scrollTop = chatContainer.scrollHeight;
36
+ return msgDiv;
37
+ }
38
+
39
+ /**
40
+ * Adds a visual typing indicator
41
+ */
42
+ function addLoadingIndicator() {
43
+ const loader = document.createElement('div');
44
+ loader.classList.add('message', 'bot-message', 'loading-msg');
45
+ loader.innerHTML = `
46
+ <div class="typing-indicator">
47
+ <span></span><span></span><span></span>
48
+ </div>
49
+ `;
50
+ chatContainer.appendChild(loader);
51
+ chatContainer.scrollTop = chatContainer.scrollHeight;
52
+ return loader;
53
+ }
54
+
55
+ /**
56
+ * Main function to send message and get BYOK response
57
+ */
58
+ async function sendMessage() {
59
+ const text = userInput.value.trim();
60
+ if (!text) return;
61
+
62
+ // Persist settings to localStorage
63
+ localStorage.setItem('byok_api_key', apiKeyInput.value);
64
+ localStorage.setItem('byok_llm_model', llmModelInput.value);
65
+ localStorage.setItem('byok_embed_model', embedModelInput.value);
66
+
67
+ // Clear input and add user message
68
+ userInput.value = '';
69
+ userInput.style.height = 'auto';
70
+ addMessage(text, 'user');
71
+
72
+ // Add loading indicator
73
+ const loader = addLoadingIndicator();
74
+
75
+ try {
76
+ const response = await fetch('/chat', {
77
+ method: 'POST',
78
+ headers: { 'Content-Type': 'application/json' },
79
+ body: JSON.stringify({
80
+ message: text,
81
+ api_key: apiKeyInput.value || null,
82
+ llm_model: llmModelInput.value || null,
83
+ embed_model: embedModelInput.value || null
84
+ })
85
+ });
86
+
87
+ const data = await response.json();
88
+
89
+ // Remove loader and add bot response
90
+ loader.remove();
91
+ if (data.answer) {
92
+ addMessage(data.answer, 'bot');
93
+
94
+ // Update performance stats
95
+ const pipeTime = data.total_time ? data.total_time.toFixed(1) : '--';
96
+ const retTime = data.timings && data.timings.retrieval ? data.timings.retrieval.toFixed(2) : '--';
97
+ statsDisplay.textContent = `Pipeline: ${pipeTime}s | Retrieval: ${retTime}s`;
98
+ } else {
99
+ addMessage('Error: No response from engine.', 'bot');
100
+ }
101
+ } catch (err) {
102
+ if (loader) loader.remove();
103
+ addMessage(`Error: ${err.message}`, 'bot');
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Helper for suggestion buttons
109
+ */
110
+ function useSuggestion(text) {
111
+ userInput.value = text;
112
+ sendMessage();
113
+ }
114
+
115
+ // Event Listeners
116
+ sendBtn.addEventListener('click', sendMessage);
117
+
118
+ userInput.addEventListener('keydown', (e) => {
119
+ if (e.key === 'Enter' && !e.shiftKey) {
120
+ e.preventDefault();
121
+ sendMessage();
122
+ }
123
+ });
124
+
125
+ // Auto-resize textarea as user types
126
+ userInput.addEventListener('input', function () {
127
+ this.style.height = 'auto';
128
+ this.style.height = (this.scrollHeight) + 'px';
129
+ });
index.html ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Agentic RAG | Intelligent Memory</title>
8
+ <link rel="stylesheet" href="style.css">
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&family=Outfit:wght@400;700&display=swap"
10
+ rel="stylesheet">
11
+ </head>
12
+
13
+ <body>
14
+ <div class="background-blobs">
15
+ <div class="blob blob-1"></div>
16
+ <div class="blob blob-2"></div>
17
+ <div class="blob blob-3"></div>
18
+ </div>
19
+
20
+ <div class="app-container">
21
+ <header>
22
+ <div class="logo">
23
+ <span class="logo-icon">🧠</span>
24
+ <h1>Agentic <span>RAG</span></h1>
25
+ </div>
26
+ <div class="settings-toggle" id="settings-toggle">
27
+ <span class="settings-icon">⚙️</span>
28
+ </div>
29
+ <div class="status">
30
+ <span class="status-dot"></span>
31
+ <span id="connection-status">Cloud Connected</span>
32
+ </div>
33
+ </header>
34
+
35
+ <div class="settings-panel" id="settings-panel">
36
+ <h3>BYOK Settings</h3>
37
+ <div class="settings-group">
38
+ <label>OpenRouter API Key</label>
39
+ <input type="password" id="api-key-input" placeholder="sk-or-v1-...">
40
+ </div>
41
+ <div class="settings-group">
42
+ <label>LLM Model</label>
43
+ <input type="text" id="llm-model-input" placeholder="openai/gpt-4o-mini" value="openai/gpt-4o-mini">
44
+ </div>
45
+ <div class="settings-group">
46
+ <label>Embedding Model</label>
47
+ <input type="text" id="embed-model-input" placeholder="qwen/qwen3-embedding-8b"
48
+ value="qwen/qwen3-embedding-8b">
49
+ </div>
50
+ <p class="settings-note">Settings are stored locally in your browser session.</p>
51
+ </div>
52
+
53
+ <main id="chat-container">
54
+ <div class="welcome-message">
55
+ <h2>Welcome to your Infinite Memory</h2>
56
+ <p>I can recall facts, names, and details from your ingested documents with 100% precision. Ask me
57
+ anything!</p>
58
+ <div class="suggestions">
59
+ <button onclick="useSuggestion('Who is the main contact for the project?')">Project
60
+ Contact?</button>
61
+ <button onclick="useSuggestion('What are the key technical specs mentioned?')">Technical
62
+ Specs?</button>
63
+ <button onclick="useSuggestion('Summarize our last discussion.')">Last Discussion?</button>
64
+ </div>
65
+ </div>
66
+ <!-- Messages will be injected here -->
67
+ </main>
68
+
69
+ <footer>
70
+ <div class="input-wrapper">
71
+ <textarea id="user-input" placeholder="Type a message..." rows="1"></textarea>
72
+ <button id="send-btn">
73
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
74
+ stroke-linejoin="round">
75
+ <line x1="22" y1="2" x2="11" y2="13"></line>
76
+ <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
77
+ </svg>
78
+ </button>
79
+ </div>
80
+ <div class="pipeline-stats" id="pipeline-stats">
81
+ Pipeline: -- | Retrieval: --
82
+ </div>
83
+ </footer>
84
+ </div>
85
+
86
+ <script src="app.js"></script>
87
+ </body>
88
+
89
+ </html>
style.css ADDED
@@ -0,0 +1,389 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg: #0f172a;
3
+ --card-bg: rgba(30, 41, 59, 0.7);
4
+ --accent: #38bdf8;
5
+ --accent-glow: rgba(56, 189, 248, 0.3);
6
+ --text: #f1f5f9;
7
+ --text-dim: #94a3b8;
8
+ }
9
+
10
+ * {
11
+ box-sizing: border-box;
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+
16
+ body {
17
+ background-color: var(--bg);
18
+ color: var(--text);
19
+ font-family: 'Inter', sans-serif;
20
+ height: 100vh;
21
+ display: flex;
22
+ justify-content: center;
23
+ align-items: center;
24
+ overflow: hidden;
25
+ }
26
+
27
+ .background-blobs {
28
+ position: fixed;
29
+ top: 0;
30
+ left: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ z-index: -1;
34
+ filter: blur(80px);
35
+ }
36
+
37
+ .blob {
38
+ position: absolute;
39
+ width: 300px;
40
+ height: 300px;
41
+ border-radius: 50%;
42
+ background: var(--accent-glow);
43
+ opacity: 0.4;
44
+ animation: move 20s infinite alternate;
45
+ }
46
+
47
+ .blob-1 {
48
+ top: -100px;
49
+ left: -100px;
50
+ background: #6366f1;
51
+ }
52
+
53
+ .blob-2 {
54
+ bottom: -100px;
55
+ right: -100px;
56
+ background: #38bdf8;
57
+ }
58
+
59
+ .blob-3 {
60
+ top: 40%;
61
+ left: 50%;
62
+ width: 500px;
63
+ height: 500px;
64
+ background: #1e1b4b;
65
+ }
66
+
67
+ @keyframes move {
68
+ from {
69
+ transform: translate(0, 0);
70
+ }
71
+
72
+ to {
73
+ transform: translate(50px, 50px);
74
+ }
75
+ }
76
+
77
+ .app-container {
78
+ width: 90%;
79
+ max-width: 900px;
80
+ height: 85vh;
81
+ background: var(--card-bg);
82
+ backdrop-filter: blur(20px);
83
+ border: 1px solid rgba(255, 255, 255, 0.1);
84
+ border-radius: 20px;
85
+ display: flex;
86
+ flex-direction: column;
87
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
88
+ position: relative;
89
+ }
90
+
91
+ header {
92
+ padding: 20px 30px;
93
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
94
+ display: flex;
95
+ justify-content: space-between;
96
+ align-items: center;
97
+ }
98
+
99
+ .logo {
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 12px;
103
+ }
104
+
105
+ .logo-icon {
106
+ font-size: 1.5rem;
107
+ }
108
+
109
+ h1 {
110
+ font-family: 'Outfit', sans-serif;
111
+ font-size: 1.4rem;
112
+ font-weight: 700;
113
+ }
114
+
115
+ h1 span {
116
+ color: var(--accent);
117
+ }
118
+
119
+ /* BYOK Settings */
120
+ .settings-toggle {
121
+ margin-left: auto;
122
+ margin-right: 15px;
123
+ cursor: pointer;
124
+ font-size: 1.2rem;
125
+ padding: 8px;
126
+ border-radius: 50%;
127
+ transition: all 0.3s;
128
+ background: rgba(255, 255, 255, 0.05);
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ }
133
+
134
+ .settings-toggle:hover {
135
+ background: rgba(255, 255, 255, 0.15);
136
+ transform: rotate(30deg);
137
+ }
138
+
139
+ .settings-panel {
140
+ background: rgba(15, 23, 42, 0.95);
141
+ backdrop-filter: blur(12px);
142
+ border: 1px solid rgba(56, 189, 248, 0.2);
143
+ border-radius: 16px;
144
+ padding: 24px;
145
+ margin: 0 20px 25px 20px;
146
+ display: none;
147
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
148
+ position: relative;
149
+ z-index: 1000;
150
+ }
151
+
152
+ .settings-panel.active {
153
+ display: block;
154
+ }
155
+
156
+ .settings-panel h3 {
157
+ margin: 0 0 20px 0;
158
+ font-size: 1.1rem;
159
+ color: var(--accent);
160
+ letter-spacing: 0.5px;
161
+ text-transform: uppercase;
162
+ }
163
+
164
+ .settings-group {
165
+ margin-bottom: 16px;
166
+ }
167
+
168
+ .settings-group label {
169
+ display: block;
170
+ font-size: 0.85rem;
171
+ margin-bottom: 8px;
172
+ color: rgba(255, 255, 255, 0.8);
173
+ font-weight: 500;
174
+ }
175
+
176
+ .settings-group input {
177
+ width: 100%;
178
+ background: rgba(0, 0, 0, 0.4);
179
+ border: 1px solid rgba(255, 255, 255, 0.15);
180
+ border-radius: 8px;
181
+ padding: 10px 14px;
182
+ color: white;
183
+ font-family: inherit;
184
+ font-size: 0.95rem;
185
+ transition: border-color 0.3s;
186
+ }
187
+
188
+ .settings-group input:focus {
189
+ outline: none;
190
+ border-color: var(--accent);
191
+ background: rgba(0, 0, 0, 0.6);
192
+ }
193
+
194
+ .settings-note {
195
+ font-size: 0.75rem;
196
+ color: rgba(255, 255, 255, 0.4);
197
+ margin-top: 15px;
198
+ font-style: italic;
199
+ }
200
+
201
+ .status {
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 8px;
205
+ font-size: 0.8rem;
206
+ color: var(--text-dim);
207
+ }
208
+
209
+ .status-dot {
210
+ width: 8px;
211
+ height: 8px;
212
+ background: #10b981;
213
+ border-radius: 50%;
214
+ box-shadow: 0 0 10px #10b981;
215
+ }
216
+
217
+ main {
218
+ flex: 1;
219
+ padding: 30px;
220
+ overflow-y: auto;
221
+ display: flex;
222
+ flex-direction: column;
223
+ gap: 20px;
224
+ }
225
+
226
+ .welcome-message {
227
+ text-align: center;
228
+ padding: 40px 20px;
229
+ max-width: 600px;
230
+ margin: auto;
231
+ }
232
+
233
+ .welcome-message h2 {
234
+ font-family: 'Outfit', sans-serif;
235
+ margin-bottom: 15px;
236
+ }
237
+
238
+ .welcome-message p {
239
+ color: var(--text-dim);
240
+ line-height: 1.6;
241
+ margin-bottom: 25px;
242
+ }
243
+
244
+ .suggestions {
245
+ display: flex;
246
+ flex-wrap: wrap;
247
+ justify-content: center;
248
+ gap: 10px;
249
+ }
250
+
251
+ .suggestions button {
252
+ background: rgba(255, 255, 255, 0.05);
253
+ border: 1px solid rgba(255, 255, 255, 0.1);
254
+ color: white;
255
+ padding: 8px 16px;
256
+ border-radius: 20px;
257
+ cursor: pointer;
258
+ font-size: 0.85rem;
259
+ transition: all 0.2s;
260
+ }
261
+
262
+ .suggestions button:hover {
263
+ background: var(--accent-glow);
264
+ border-color: var(--accent);
265
+ }
266
+
267
+ /* Chat Messages */
268
+ .message {
269
+ max-width: 80%;
270
+ padding: 14px 18px;
271
+ border-radius: 18px;
272
+ font-size: 0.95rem;
273
+ line-height: 1.5;
274
+ word-wrap: break-word;
275
+ }
276
+
277
+ .user-message {
278
+ align-self: flex-end;
279
+ background: linear-gradient(135deg, #1e3a8a, #1e40af);
280
+ border-bottom-right-radius: 4px;
281
+ }
282
+
283
+ .bot-message {
284
+ align-self: flex-start;
285
+ background: rgba(255, 255, 255, 0.05);
286
+ border: 1px solid rgba(255, 255, 255, 0.1);
287
+ border-bottom-left-radius: 4px;
288
+ }
289
+
290
+ .loading-msg {
291
+ padding: 10px 18px;
292
+ }
293
+
294
+ .typing-indicator {
295
+ display: flex;
296
+ gap: 5px;
297
+ }
298
+
299
+ .typing-indicator span {
300
+ width: 6px;
301
+ height: 6px;
302
+ background: var(--text-dim);
303
+ border-radius: 50%;
304
+ animation: bounce 1.4s infinite;
305
+ }
306
+
307
+ .typing-indicator span:nth-child(2) {
308
+ animation-delay: 0.2s;
309
+ }
310
+
311
+ .typing-indicator span:nth-child(3) {
312
+ animation-delay: 0.4s;
313
+ }
314
+
315
+ @keyframes bounce {
316
+
317
+ 0%,
318
+ 80%,
319
+ 100% {
320
+ transform: translateY(0);
321
+ }
322
+
323
+ 40% {
324
+ transform: translateY(-6px);
325
+ }
326
+ }
327
+
328
+ /* Footer & Input */
329
+ footer {
330
+ padding: 20px 30px;
331
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
332
+ }
333
+
334
+ .input-wrapper {
335
+ display: flex;
336
+ align-items: flex-end;
337
+ background: rgba(0, 0, 0, 0.2);
338
+ border: 1px solid rgba(255, 255, 255, 0.1);
339
+ border-radius: 12px;
340
+ padding: 10px;
341
+ gap: 15px;
342
+ }
343
+
344
+ textarea {
345
+ flex: 1;
346
+ background: none;
347
+ border: none;
348
+ color: white;
349
+ resize: none;
350
+ font-family: inherit;
351
+ font-size: 1rem;
352
+ max-height: 150px;
353
+ padding: 5px;
354
+ }
355
+
356
+ textarea:focus {
357
+ outline: none;
358
+ }
359
+
360
+ #send-btn {
361
+ background: var(--accent);
362
+ color: black;
363
+ border: none;
364
+ width: 40px;
365
+ height: 40px;
366
+ border-radius: 10px;
367
+ display: flex;
368
+ justify-content: center;
369
+ align-items: center;
370
+ cursor: pointer;
371
+ flex-shrink: 0;
372
+ transition: transform 0.2s;
373
+ }
374
+
375
+ #send-btn:hover {
376
+ transform: scale(1.05);
377
+ }
378
+
379
+ #send-btn svg {
380
+ width: 20px;
381
+ height: 20px;
382
+ }
383
+
384
+ .pipeline-stats {
385
+ margin-top: 15px;
386
+ text-align: center;
387
+ font-size: 0.75rem;
388
+ color: var(--text-dim);
389
+ }