mhndayesh commited on
Commit
7abca39
·
verified ·
1 Parent(s): 8349b37

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.js +125 -0
  2. index.html +127 -0
  3. style.css +183 -0
app.js ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const form = document.getElementById('chat-form');
2
+ const input = document.getElementById('user-input');
3
+ const sendBtn = document.getElementById('send-btn');
4
+ const chatHistory = document.getElementById('chat-history');
5
+ const apiKeyInput = document.getElementById('api-key');
6
+ const dot = document.getElementById('connection-dot');
7
+ const statusText = document.getElementById('connection-text');
8
+
9
+ let conversation = [];
10
+
11
+ // Auto-adjust textarea height
12
+ input.addEventListener('input', function () {
13
+ this.style.height = 'auto';
14
+ this.style.height = (this.scrollHeight) + 'px';
15
+ if (this.value.trim() !== '' && apiKeyInput.value.trim() !== '') {
16
+ sendBtn.disabled = false;
17
+ } else {
18
+ sendBtn.disabled = true;
19
+ }
20
+ });
21
+
22
+ input.addEventListener('keydown', function (e) {
23
+ if (e.key === 'Enter' && !e.shiftKey) {
24
+ e.preventDefault();
25
+ if (!sendBtn.disabled) form.dispatchEvent(new Event('submit'));
26
+ }
27
+ });
28
+
29
+ apiKeyInput.addEventListener('input', () => {
30
+ if (apiKeyInput.value.trim().length > 10) {
31
+ dot.className = 'dot online';
32
+ statusText.textContent = 'API Key Ready';
33
+ if (input.value.trim() !== '') sendBtn.disabled = false;
34
+ } else {
35
+ dot.className = 'dot offline';
36
+ statusText.textContent = 'Waiting for key...';
37
+ sendBtn.disabled = true;
38
+ }
39
+ });
40
+
41
+ function appendBubble(role, htmlContent) {
42
+ const bubble = document.createElement('div');
43
+ bubble.className = `bubble ${role}`;
44
+
45
+ const avatar = document.createElement('div');
46
+ avatar.className = 'avatar';
47
+ avatar.innerHTML = role === 'assistant' ? '<i class="fa-solid fa-robot"></i>' : '<i class="fa-solid fa-user"></i>';
48
+
49
+ const msg = document.createElement('div');
50
+ msg.className = 'message';
51
+ msg.innerHTML = htmlContent;
52
+
53
+ bubble.appendChild(avatar);
54
+ bubble.appendChild(msg);
55
+ chatHistory.appendChild(bubble);
56
+ chatHistory.scrollTop = chatHistory.scrollHeight;
57
+
58
+ return msg;
59
+ }
60
+
61
+ form.addEventListener('submit', async (e) => {
62
+ e.preventDefault();
63
+ const userText = input.value.trim();
64
+ if (!userText) return;
65
+
66
+ const apiKey = apiKeyInput.value.trim();
67
+ const llmModel = document.getElementById('llm-model').value.trim();
68
+ const embedModel = document.getElementById('embed-model').value.trim();
69
+ const endpoint = document.getElementById('api-endpoint').value.trim();
70
+
71
+ if (!apiKey) {
72
+ alert("Please enter your OpenRouter API Key.");
73
+ return;
74
+ }
75
+
76
+ // UI Update
77
+ input.value = '';
78
+ input.style.height = 'auto';
79
+ sendBtn.disabled = true;
80
+ appendBubble('user', `<p>${userText}</p>`);
81
+
82
+ conversation.push({ role: "user", content: userText });
83
+
84
+ // Show typing
85
+ const typingMsg = appendBubble('assistant', '<div class="typing-indicator">Connecting to Backend...<br><small><i>Injecting context and pinging OpenRouter</i></small></div>');
86
+
87
+ try {
88
+ const payload = {
89
+ model: llmModel,
90
+ embed_model: embedModel,
91
+ messages: conversation
92
+ };
93
+
94
+ const response = await fetch(endpoint, {
95
+ method: 'POST',
96
+ headers: {
97
+ 'Content-Type': 'application/json',
98
+ 'Authorization': `Bearer ${apiKey}`
99
+ },
100
+ body: JSON.stringify(payload)
101
+ });
102
+
103
+ if (!response.ok) {
104
+ let errText = await response.text();
105
+ throw new Error(`Server Error (${response.status}): ${errText}`);
106
+ }
107
+
108
+ const data = await response.json();
109
+ const aiAnswer = data.choices[0].message.content;
110
+
111
+ // Update conversation history
112
+ conversation.push({ role: "assistant", content: aiAnswer });
113
+
114
+ // Parse markdown and show
115
+ typingMsg.innerHTML = marked.parse(aiAnswer);
116
+
117
+ } catch (err) {
118
+ console.error(err);
119
+ typingMsg.innerHTML = `<span style="color: #ef4444;">Connection failed: ${err.message}</span>`;
120
+ // Remove the failed user message from internal state
121
+ conversation.pop();
122
+ }
123
+
124
+ if (input.value.trim() !== '') sendBtn.disabled = false;
125
+ });
index.html ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Infinite Memory | Cloud-Hybrid AI</title>
7
+ <!-- Fonts: Inter for modern clean UI -->
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="style.css">
10
+ <!-- FontAwesome for Icons -->
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
+ </head>
13
+ <body>
14
+
15
+ <!-- Background Elements -->
16
+ <div class="glow-orb orb-1"></div>
17
+ <div class="glow-orb orb-2"></div>
18
+ <div class="glow-orb orb-3"></div>
19
+
20
+ <div class="container">
21
+
22
+ <!-- Left Panel: Claims & Info -->
23
+ <aside class="info-panel">
24
+ <div class="logo-area">
25
+ <i class="fa-solid fa-brain-circuit"></i>
26
+ <h2>InfiniteMemory<span class="beta-badge">DEMO</span></h2>
27
+ </div>
28
+
29
+ <div class="hero-claims">
30
+ <h1>Scale Context <i>Infinitely.</i></h1>
31
+ <p class="sub-hero">A revolutionary Cloud-Hybrid memory architecture that offloads context storage.</p>
32
+
33
+ <ul class="claims-list">
34
+ <li>
35
+ <div class="claim-icon"><i class="fa-solid fa-bolt"></i></div>
36
+ <div class="claim-text">
37
+ <strong>Save 90% on Token Costs</strong>
38
+ <span>Dramatically cuts expensive prompt sizes. Only the absolute best context is sent to the LLM.</span>
39
+ </div>
40
+ </li>
41
+ <li>
42
+ <div class="claim-icon"><i class="fa-solid fa-infinity"></i></div>
43
+ <div class="claim-text">
44
+ <strong>10M+ Context Window</strong>
45
+ <span>Store a lifetime of data without constantly passing it back and forth every API call.</span>
46
+ </div>
47
+ </li>
48
+ <li>
49
+ <div class="claim-icon"><i class="fa-solid fa-microchip"></i></div>
50
+ <div class="claim-text">
51
+ <strong>Permanent Agent Memory</strong>
52
+ <span>Your agent remembers everything forever for a fraction of standard vector database costs.</span>
53
+ </div>
54
+ </li>
55
+ </ul>
56
+ </div>
57
+
58
+ <!-- Configuration / Security settings -->
59
+ <div class="config-card">
60
+ <h3><i class="fa-solid fa-shield-halved"></i> Bring Your Own Key (BYOK)</h3>
61
+ <div class="alert-box warning">
62
+ <i class="fa-solid fa-triangle-exclamation"></i>
63
+ <div>
64
+ <strong>DEMO ONLY:</strong> We do not store keys. Communication is HTTPS encrypted. However, you are strictly responsible for your API Key. Please use <b>revokable</b> keys and we highly advise testing with <i>Free</i> OpenRouter models!
65
+ </div>
66
+ </div>
67
+
68
+ <div class="input-group">
69
+ <label>OpenRouter API Key <span class="req">*</span></label>
70
+ <input type="password" id="api-key" placeholder="sk-or-v1-..." autocomplete="off">
71
+ </div>
72
+
73
+ <div class="model-selectors">
74
+ <div class="input-group">
75
+ <label>LLM Reasoning Model</label>
76
+ <input type="text" id="llm-model" value="openai/gpt-oss-120b:free">
77
+ </div>
78
+ <div class="input-group">
79
+ <label>Embedding Model</label>
80
+ <input type="text" id="embed-model" value="nvidia/llama-nemotron-embed-vl-1b-v2:free">
81
+ </div>
82
+ </div>
83
+
84
+ <div class="input-group" style="margin-top: 10px;">
85
+ <label>Backend Endpoint (Railway URL)</label>
86
+ <!-- Provide the internal name user gave, but note it might need to change to public URL depending on deployment -->
87
+ <input type="text" id="api-endpoint" value="https://prod-infinit-memory.up.railway.app/v1/chat/completions">
88
+ </div>
89
+ </div>
90
+ </aside>
91
+
92
+ <!-- Right Panel: Chat Interface -->
93
+ <main class="chat-container">
94
+ <header class="chat-header">
95
+ <div class="header-title">
96
+ <i class="fa-brands fa-rocketchat"></i> Live Agent Output
97
+ </div>
98
+ <div class="status-indicator">
99
+ <span class="dot offline" id="connection-dot"></span>
100
+ <span id="connection-text">Waiting for key...</span>
101
+ </div>
102
+ </header>
103
+
104
+ <div class="chat-bubbles" id="chat-history">
105
+ <div class="bubble assistant">
106
+ <div class="avatar"><i class="fa-solid fa-robot"></i></div>
107
+ <div class="message">
108
+ Hello! I am your Infinite Memory agent. Provide your API key on the left, and try asking me something complex. I instantly query millions of tokens without maxing out your cloud budget.
109
+ </div>
110
+ </div>
111
+ </div>
112
+
113
+ <div class="chat-input-area">
114
+ <form id="chat-form">
115
+ <textarea id="user-input" placeholder="Type a message... (Press Enter to send)" rows="1"></textarea>
116
+ <button type="submit" id="send-btn" disabled>
117
+ <i class="fa-solid fa-paper-plane"></i>
118
+ </button>
119
+ </form>
120
+ </div>
121
+ </main>
122
+ </div>
123
+
124
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
125
+ <script src="app.js"></script>
126
+ </body>
127
+ </html>
style.css ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg-dark: #0f172a;
3
+ --surface: rgba(30, 41, 59, 0.7);
4
+ --surface-border: rgba(255, 255, 255, 0.1);
5
+ --primary: #3b82f6;
6
+ --primary-glow: rgba(59, 130, 246, 0.5);
7
+ --text-main: #f8fafc;
8
+ --text-muted: #94a3b8;
9
+ --danger: #ef4444;
10
+ --danger-bg: rgba(239, 68, 68, 0.1);
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ margin: 0;
16
+ padding: 0;
17
+ }
18
+
19
+ body {
20
+ font-family: 'Inter', sans-serif;
21
+ background-color: var(--bg-dark);
22
+ color: var(--text-main);
23
+ overflow: hidden;
24
+ height: 100vh;
25
+ display: flex;
26
+ justify-content: center;
27
+ align-items: center;
28
+ }
29
+
30
+ /* Background elements */
31
+ .glow-orb {
32
+ position: absolute;
33
+ border-radius: 50%;
34
+ filter: blur(80px);
35
+ z-index: -1;
36
+ opacity: 0.5;
37
+ }
38
+ .orb-1 { top: -10%; left: -10%; width: 40vw; height: 40vw; background: rgba(59, 130, 246, 0.3); }
39
+ .orb-2 { bottom: -20%; right: -10%; width: 50vw; height: 50vw; background: rgba(139, 92, 246, 0.2); }
40
+ .orb-3 { top: 40%; left: 40%; width: 30vw; height: 30vw; background: rgba(16, 185, 129, 0.1); }
41
+
42
+ /* Main Container Layout */
43
+ .container {
44
+ display: flex;
45
+ width: 95vw;
46
+ max-width: 1400px;
47
+ height: 90vh;
48
+ background: var(--surface);
49
+ backdrop-filter: blur(16px);
50
+ -webkit-backdrop-filter: blur(16px);
51
+ border: 1px solid var(--surface-border);
52
+ border-radius: 20px;
53
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
54
+ overflow: hidden;
55
+ }
56
+
57
+ /* LEFT PANEL */
58
+ .info-panel {
59
+ flex: 0 0 400px;
60
+ border-right: 1px solid var(--surface-border);
61
+ padding: 2rem;
62
+ display: flex;
63
+ flex-direction: column;
64
+ overflow-y: auto;
65
+ background: rgba(15, 23, 42, 0.4);
66
+ }
67
+
68
+ .logo-area {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 12px;
72
+ margin-bottom: 2rem;
73
+ }
74
+ .logo-area i { font-size: 2rem; color: var(--primary); }
75
+ .logo-area h2 { font-weight: 800; font-size: 1.5rem; letter-spacing: -0.5px; position: relative;}
76
+ .beta-badge {
77
+ position: absolute; top: -10px; right: -40px;
78
+ background: var(--danger); font-size: 0.6rem; padding: 2px 6px; border-radius: 4px; font-weight: bold;
79
+ }
80
+
81
+ .hero-claims h1 {
82
+ font-size: 2.2rem;
83
+ line-height: 1.1;
84
+ margin-bottom: 0.5rem;
85
+ background: linear-gradient(to right, #60a5fa, #a78bfa);
86
+ -webkit-background-clip: text;
87
+ -webkit-text-fill-color: transparent;
88
+ }
89
+ .hero-claims i { font-style: italic; }
90
+ .sub-hero { font-size: 0.95rem; color: var(--text-muted); margin-bottom: 2rem; line-height: 1.5; }
91
+
92
+ .claims-list { list-style: none; margin-bottom: 2rem; }
93
+ .claims-list li {
94
+ display: flex; gap: 1rem; margin-bottom: 1.5rem;
95
+ }
96
+ .claim-icon {
97
+ width: 40px; height: 40px; border-radius: 10px; background: rgba(255,255,255,0.05);
98
+ display: flex; align-items: center; justify-content: center; font-size: 1.2rem; color: #60a5fa; flex-shrink: 0;
99
+ }
100
+ .claim-text strong { display: block; font-size: 0.95rem; margin-bottom: 0.2rem; color: #e2e8f0; }
101
+ .claim-text span { font-size: 0.8rem; color: var(--text-muted); line-height: 1.4; display: block; }
102
+
103
+ /* Config Section */
104
+ .config-card {
105
+ background: rgba(0,0,0,0.2); border: 1px solid var(--surface-border); border-radius: 12px; padding: 1.5rem; margin-top: auto;
106
+ }
107
+ .config-card h3 { font-size: 0.9rem; margin-bottom: 1rem; color: #cbd5e1; display: flex; align-items: center; gap: 8px;}
108
+
109
+ .alert-box {
110
+ display: flex; gap: 10px; padding: 12px; border-radius: 8px; font-size: 0.75rem; line-height: 1.4; margin-bottom: 1rem;
111
+ }
112
+ .alert-box.warning { background: var(--danger-bg); border: 1px solid rgba(239, 68, 68, 0.3); color: #fca5a5; }
113
+ .alert-box i { font-size: 1rem; }
114
+
115
+ .input-group { margin-bottom: 1rem; }
116
+ .input-group label { display: block; font-size: 0.75rem; color: var(--text-muted); margin-bottom: 6px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;}
117
+ .input-group label .req { color: var(--danger); }
118
+ .input-group input {
119
+ width: 100%; background: rgba(0,0,0,0.3); border: 1px solid var(--surface-border); border-radius: 6px;
120
+ padding: 10px 12px; color: var(--text-main); font-family: monospace; font-size: 0.85rem; outline: none; transition: 0.2s;
121
+ }
122
+ .input-group input:focus { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-glow); }
123
+
124
+ /* RIGHT PANEL: CHAT */
125
+ .chat-container { flex: 1; display: flex; flex-direction: column; position: relative; }
126
+
127
+ .chat-header {
128
+ height: 70px; border-bottom: 1px solid var(--surface-border); display: flex; justify-content: space-between; align-items: center; padding: 0 2rem;
129
+ background: rgba(255,255,255,0.02);
130
+ }
131
+ .header-title { font-weight: 600; font-size: 1.1rem; display: flex; align-items: center; gap: 10px;}
132
+ .header-title i { color: #a78bfa; }
133
+
134
+ .status-indicator { display: flex; align-items: center; gap: 8px; font-size: 0.8rem; color: var(--text-muted); }
135
+ .dot { width: 8px; height: 8px; border-radius: 50%; }
136
+ .dot.offline { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
137
+ .dot.online { background: #10b981; box-shadow: 0 0 8px #10b981; }
138
+
139
+ .chat-bubbles { flex: 1; padding: 2rem; overflow-y: auto; display: flex; flex-direction: column; gap: 1.5rem; }
140
+
141
+ .bubble { display: flex; gap: 1rem; max-width: 85%; }
142
+ .bubble.assistant { align-self: flex-start; }
143
+ .bubble.user { align-self: flex-end; flex-direction: row-reverse; }
144
+
145
+ .avatar { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
146
+ .assistant .avatar { background: linear-gradient(135deg, #3b82f6, #8b5cf6); color: white; }
147
+ .user .avatar { background: rgba(255,255,255,0.1); color: #cbd5e1; }
148
+
149
+ .message {
150
+ padding: 1rem 1.2rem; border-radius: 12px; font-size: 0.95rem; line-height: 1.6;
151
+ }
152
+ .assistant .message { background: rgba(255,255,255,0.03); border: 1px solid var(--surface-border); border-top-left-radius: 2px;}
153
+ .user .message { background: var(--primary); color: white; border-top-right-radius: 2px;}
154
+
155
+ .message p { margin-bottom: 0.5rem; }
156
+ .message p:last-child { margin-bottom: 0; }
157
+ .message pre { background: rgba(0,0,0,0.3); padding: 1rem; border-radius: 8px; overflow-x: auto; margin: 10px 0; border: 1px solid rgba(255,255,255,0.1); }
158
+ .message code { font-family: monospace; font-size: 0.85em; }
159
+
160
+ /* Input Area */
161
+ .chat-input-area {
162
+ padding: 1.5rem 2rem; border-top: 1px solid var(--surface-border); background: rgba(15, 23, 42, 0.6);
163
+ }
164
+ #chat-form {
165
+ display: flex; gap: 1rem; background: rgba(0,0,0,0.2); border: 1px solid var(--surface-border); padding: 0.5rem; border-radius: 12px; align-items: flex-end;
166
+ }
167
+ #chat-form:focus-within { border-color: rgba(96, 165, 250, 0.5); }
168
+
169
+ textarea {
170
+ flex: 1; background: transparent; border: none; color: white; font-family: inherit; font-size: 0.95rem; padding: 0.8rem; resize: none; outline: none; max-height: 150px; line-height: 1.5;
171
+ }
172
+ #send-btn {
173
+ width: 44px; height: 44px; border-radius: 10px; border: none; background: var(--primary); color: white; font-size: 1.1rem; cursor: pointer; transition: 0.2s; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
174
+ }
175
+ #send-btn:hover:not(:disabled) { background: #2563eb; transform: translateY(-1px); }
176
+ #send-btn:disabled { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.3); cursor: not-allowed; }
177
+
178
+ /* Responsive */
179
+ @media (max-width: 900px) {
180
+ .container { flex-direction: column; }
181
+ .info-panel { flex: none; height: 40vh; border-right: none; border-bottom: 1px solid var(--surface-border); padding: 1.5rem;}
182
+ .hero-claims { display: none; } /* Hide claims on small mobile space */
183
+ }