WebashalarForML commited on
Commit
4d9fce0
·
verified ·
1 Parent(s): 2d33bc7

Update static/frontend.html

Browse files
Files changed (1) hide show
  1. static/frontend.html +195 -111
static/frontend.html CHANGED
@@ -1,148 +1,232 @@
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>Patient Assistant Chat</title>
7
  <style>
8
- /* your existing styles here */
9
- :root { --bg:#0f172a; --panel:#111827; --soft:#1f2937; --text:#e5e7eb; --muted:#9ca3af; --accent:#22c55e; }
10
- * { box-sizing: border-box; }
11
- body { margin:0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background: var(--bg); color: var(--text); }
12
- .app { max-width: 900px; margin: 0 auto; padding: 20px; }
13
- .card { background: var(--panel); border: 1px solid #242b3a; border-radius: 16px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,.25); }
14
- .header { padding: 16px 20px; border-bottom: 1px solid #242b3a; display: flex; align-items: center; gap: 12px; }
15
- .badge { background: #0b3b26; color: #8bf2c0; padding: 6px 10px; border-radius: 999px; font-weight: 600; font-size: 12px; letter-spacing: .3px; }
16
- .row { display:flex; gap:12px; align-items:center; flex-wrap: wrap; }
17
- label { font-size: 13px; color: var(--muted); }
18
- input[type="text"] { background: var(--soft); color: var(--text); border: 1px solid #2b3346; padding: 10px 12px; border-radius: 10px; min-width: 200px; outline: none; }
19
- input[type="text"]:focus { border-color: #374462; box-shadow: 0 0 0 3px rgba(59,130,246,.15); }
20
- button { background: var(--accent); color: #052e19; border: none; padding: 10px 14px; border-radius: 10px; font-weight: 700; cursor: pointer; }
21
- button.secondary { background: #334155; color: #dbeafe; }
22
- button:disabled { opacity: .6; cursor: not-allowed; }
23
-
24
- .chat { height: 420px; overflow-y: auto; padding: 18px; display: flex; flex-direction: column; gap: 10px; background: #0b1220; }
25
- .msg { max-width: 80%; padding: 10px 12px; border-radius: 12px; border: 1px solid #1f2937; white-space: pre-wrap; word-break: break-word; }
26
- .user   { align-self: flex-end; background: #0b3b26; border-color: #14532d; }
27
- .assist { align-self: flex-start; background: #111827; border-color: #1f2937; }
28
-
29
- .composer { display: grid; grid-template-columns: 1fr auto; gap: 10px; padding: 14px; border-top: 1px solid #242b3a; background: #0b1220; }
30
- .composer input { width: 100%; }
31
-
32
- .state { padding: 12px 16px; border-top: 1px dashed #243044; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 12px; color: #cbd5e1; background: #0b1220; }
33
- details > summary { cursor: pointer; }
34
- .small { font-size: 12px; color: var(--muted); }
35
  </style>
36
  </head>
37
  <body>
38
- <div class="app">
39
- <div class="card">
40
- <div class="header">
41
- <span class="badge">Patient Assistant</span>
42
- </div>
43
 
44
- <div id="chat" class="chat"></div>
 
 
 
45
 
46
- <div class="composer">
47
- <input id="message" type="text" placeholder="Type your message…" />
48
- <button id="send">Send</button>
49
  </div>
50
 
51
- <div class="state">
52
- <details>
53
- <summary>Agent State (from backend)</summary>
54
- <pre id="stateView">{}</pre>
55
- </details>
56
- </div>
57
  </div>
58
 
59
- <p class="small" style="margin-top:10px; opacity:.8">
60
- This version allows the agent to ask for your Patient ID (PID) during the conversation.
61
- </p>
 
62
  </div>
63
 
64
  <script>
65
- const NODE_CHAT_ENDPOINT = "/chat";
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- const chatEl = document.getElementById("chat");
68
- const msgEl = document.getElementById("message");
69
- const sendBtn = document.getElementById("send");
70
- const stateView = document.getElementById("stateView");
71
 
72
- let chatHistory = JSON.parse(localStorage.getItem("chatHistory")) || [];
73
- let patientState = JSON.parse(localStorage.getItem("patientState")) || {};
 
74
 
75
- sendBtn.addEventListener("click", onSend);
76
- msgEl.addEventListener("keydown", (e) => {
77
- if (e.key === "Enter") onSend();
78
- });
79
 
80
- function saveState() {
81
- localStorage.setItem("chatHistory", JSON.stringify(chatHistory));
82
- localStorage.setItem("patientState", JSON.stringify(patientState));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
84
 
85
- function renderChat() {
86
- chatEl.innerHTML = "";
87
- for (const m of chatHistory) {
88
- const div = document.createElement("div");
89
- div.className = `msg ${m.role === "user" ? "user" : "assist"}`;
90
- div.textContent = `${m.role === "user" ? "You" : "Assistant"}: ${m.content}`;
91
- chatEl.appendChild(div);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  }
93
- chatEl.scrollTop = chatEl.scrollHeight;
 
 
94
  }
95
 
96
- async function onSend() {
97
- const message = (msgEl.value || "").trim();
98
- if (!message) return;
 
 
99
 
100
- // Clear the input and disable the button while we wait for the response
101
- msgEl.value = "";
102
- sendBtn.disabled = true;
103
 
104
- // Add the user's message to the chat history
105
- chatHistory.push({ role: "user", content: message });
106
- renderChat();
107
- saveState();
 
 
 
108
 
109
  try {
110
- // Send the chat history and patient state to the backend
111
- const res = await fetch(NODE_CHAT_ENDPOINT, {
112
- method: "POST",
113
- headers: { "Content-Type": "application/json" },
114
- body: JSON.stringify({ chat_history: chatHistory, patient_state: patientState })
 
 
 
115
  });
116
 
117
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
118
- const data = await res.json();
119
-
120
- const reply = data.assistant_reply || "(no reply)";
121
- patientState = data.updated_state || patientState;
122
-
123
- // Add the assistant's reply to the chat history
124
- chatHistory.push({ role: "assistant", content: reply });
125
- renderChat();
126
- saveState();
127
-
128
- // Display the raw agent state for debugging
129
- stateView.textContent = JSON.stringify(patientState, null, 2);
130
- } catch (err) {
131
- // Handle any errors and display them in the chat
132
- chatHistory.push({ role: "assistant", content: `Error: ${err.message}` });
133
- renderChat();
134
- saveState();
135
- } finally {
136
- sendBtn.disabled = false;
137
  }
138
  }
139
 
140
- // Initial greeting
141
- if (chatHistory.length === 0) {
142
- chatHistory.push({ role: "assistant", content: "Hello! I am your patient assistant. Please tell me your Patient ID to get started." });
143
- saveState();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  }
145
- renderChat();
146
  </script>
147
  </body>
148
- </html>
 
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>Code Assistant Agent</title>
7
  <style>
8
+ body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f9; }
9
+ .container { max-width: 800px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }
10
+ h1 { color: #333; text-align: center; margin-bottom: 20px; }
11
+ #chat-window { height: 400px; overflow-y: scroll; border: 1px solid #ddd; padding: 10px; border-radius: 4px; margin-bottom: 20px; background-color: #e9eff4; }
12
+ .message { margin-bottom: 10px; padding: 8px 12px; border-radius: 15px; max-width: 85%; }
13
+ .user { background-color: #dcf8c6; margin-left: auto; text-align: right; }
14
+ .assistant { background-color: #fff; border: 1px solid #ccc; text-align: left; }
15
+ #input-container { display: flex; gap: 10px; margin-bottom: 20px; }
16
+ #user-input { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
17
+ #send-button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
18
+ #send-button:hover { background-color: #0056b3; }
19
+ #state-info { padding: 10px; background-color: #eee; border-radius: 4px; font-size: 0.9em; margin-bottom: 20px; }
20
+ pre { background-color: #272822; color: #f8f8f2; padding: 10px; border-radius: 4px; overflow-x: auto; }
21
+ .tag-area { margin-top: 5px; padding: 5px; border-top: 1px dashed #ccc; }
22
+ .tag-chip { display: inline-block; background-color: #007bff; color: white; padding: 2px 8px; border-radius: 12px; font-size: 0.8em; margin-right: 5px; cursor: pointer; }
23
+ .bookmark-btn { background-color: #28a745; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; margin-left: 10px; }
24
+ .bookmark-btn:hover { background-color: #1e7e34; }
25
+ .tagged-replies-list { list-style: none; padding: 0; }
26
+ .tagged-replies-list li { border-bottom: 1px solid #eee; padding: 10px 0; }
 
 
 
 
 
 
 
 
27
  </style>
28
  </head>
29
  <body>
30
+ <div class="container">
31
+ <h1>Code Assistant Agent 🤖</h1>
 
 
 
32
 
33
+ <div id="state-info">
34
+ Current Language: <span id="current-language">Python</span>
35
+ | Total Bookmarked Replies: <span id="tagged-count">0</span>
36
+ </div>
37
 
38
+ <div id="chat-window">
 
 
39
  </div>
40
 
41
+ <div id="input-container">
42
+ <input type="text" id="user-input" placeholder="Ask about code, concepts, or bugs..." onkeypress="if(event.key === 'Enter') sendMessage()">
43
+ <button id="send-button" onclick="sendMessage()">Send</button>
 
 
 
44
  </div>
45
 
46
+ <h2>Bookmarked Replies</h2>
47
+ <input type="text" id="tag-search" placeholder="Search by Tag (e.g., Python, Debugging)" oninput="filterReplies()">
48
+ <ul id="tagged-replies-display" class="tagged-replies-list">
49
+ </ul>
50
  </div>
51
 
52
  <script>
53
+ // Initial state structure matching the backend
54
+ let assistantState = {
55
+ conversationSummary: "",
56
+ language: "Python",
57
+ taggedReplies: []
58
+ };
59
+ const apiUrl = '/chat';
60
+ const tagUrl = '/tag_reply';
61
+
62
+ document.addEventListener('DOMContentLoaded', () => {
63
+ updateStateInfo();
64
+ // Initial greeting
65
+ appendMessage({ role: 'assistant', content: "Hello! I'm your Code Assistant. What programming language are you working in today?" });
66
+ });
67
 
68
+ // --- Core Chat Logic ---
 
 
 
69
 
70
+ async function sendMessage() {
71
+ const inputElement = document.getElementById('user-input');
72
+ const userMessage = inputElement.value.trim();
73
 
74
+ if (!userMessage) return;
 
 
 
75
 
76
+ appendMessage({ role: 'user', content: userMessage });
77
+ inputElement.value = '';
78
+
79
+ try {
80
+ const response = await fetch(apiUrl, {
81
+ method: 'POST',
82
+ headers: { 'Content-Type': 'application/json' },
83
+ body: JSON.stringify({
84
+ chat_history: [
85
+ { role: 'user', content: userMessage } // Only need the latest message for this setup
86
+ ],
87
+ assistant_state: assistantState
88
+ })
89
+ });
90
+
91
+ if (!response.ok) {
92
+ throw new Error(`HTTP error! status: ${response.status}`);
93
+ }
94
+
95
+ const data = await response.json();
96
+
97
+ // Update state and display reply
98
+ assistantState = data.updated_state;
99
+
100
+ appendMessage({
101
+ role: 'assistant',
102
+ content: data.assistant_reply,
103
+ suggestedTags: data.suggested_tags
104
+ });
105
+
106
+ updateStateInfo();
107
+
108
+ } catch (error) {
109
+ console.error('Chat failed:', error);
110
+ appendMessage({ role: 'assistant', content: `Error: Could not connect to the assistant. (${error.message})` });
111
+ }
112
  }
113
 
114
+ // --- Display and State Management ---
115
+
116
+ function appendMessage(msg) {
117
+ const chatWindow = document.getElementById('chat-window');
118
+ const msgDiv = document.createElement('div');
119
+ msgDiv.className = `message ${msg.role}`;
120
+
121
+ let content = msg.content;
122
+
123
+ // Basic Markdown for code blocks in the frontend
124
+ content = content.replace(/```(\w+)?\n([\s\S]*?)\n```/g, (match, lang, code) => {
125
+ const language = lang || 'code';
126
+ return `<pre><code class="language-${language}">${code.trim()}</code></pre>`;
127
+ });
128
+
129
+ msgDiv.innerHTML = content;
130
+
131
+ // Add Tagging UI for assistant replies
132
+ if (msg.role === 'assistant') {
133
+ const tagArea = document.createElement('div');
134
+ tagArea.className = 'tag-area';
135
+ tagArea.innerHTML = 'Tags: ';
136
+
137
+ const tagInput = document.createElement('input');
138
+ tagInput.type = 'text';
139
+ tagInput.placeholder = 'Add your tags (comma-separated)';
140
+ tagInput.id = 'tag-input-' + Date.now();
141
+ tagInput.style.marginRight = '10px';
142
+ tagInput.value = (msg.suggestedTags || []).join(', '); // Pre-populate with suggested tags
143
+
144
+ const bookmarkBtn = document.createElement('button');
145
+ bookmarkBtn.className = 'bookmark-btn';
146
+ bookmarkBtn.textContent = 'Save/Bookmark';
147
+ bookmarkBtn.onclick = () => saveReply(msg.content, tagInput.value, bookmarkBtn);
148
+
149
+ tagArea.appendChild(tagInput);
150
+ tagArea.appendChild(bookmarkBtn);
151
+ msgDiv.appendChild(tagArea);
152
  }
153
+
154
+ chatWindow.appendChild(msgDiv);
155
+ chatWindow.scrollTop = chatWindow.scrollHeight;
156
  }
157
 
158
+ function updateStateInfo() {
159
+ document.getElementById('current-language').textContent = assistantState.language;
160
+ document.getElementById('tagged-count').textContent = assistantState.taggedReplies.length;
161
+ filterReplies(); // Refresh the bookmark list
162
+ }
163
 
164
+ // --- Bookmarking and Tagging Logic ---
 
 
165
 
166
+ async function saveReply(replyContent, tagsString, buttonElement) {
167
+ const tags = tagsString.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0);
168
+
169
+ if (tags.length === 0) {
170
+ alert("Please enter at least one tag.");
171
+ return;
172
+ }
173
 
174
  try {
175
+ const response = await fetch(tagUrl, {
176
+ method: 'POST',
177
+ headers: { 'Content-Type': 'application/json' },
178
+ body: JSON.stringify({
179
+ reply: replyContent,
180
+ tags: tags,
181
+ assistant_state: assistantState
182
+ })
183
  });
184
 
185
+ if (!response.ok) {
186
+ throw new Error(`HTTP error! status: ${response.status}`);
187
+ }
188
+
189
+ const data = await response.json();
190
+ assistantState = data.updated_state; // Update state with the new tagged reply
191
+
192
+ alert(data.message);
193
+ buttonElement.textContent = 'Saved!';
194
+ buttonElement.disabled = true;
195
+ updateStateInfo();
196
+
197
+ } catch (error) {
198
+ console.error('Tagging failed:', error);
199
+ alert(`Failed to save reply: ${error.message}`);
 
 
 
 
 
200
  }
201
  }
202
 
203
+ function filterReplies() {
204
+ const query = document.getElementById('tag-search').value.toLowerCase();
205
+ const displayList = document.getElementById('tagged-replies-display');
206
+ displayList.innerHTML = '';
207
+
208
+ const filtered = assistantState.taggedReplies.filter(item => {
209
+ if (!query) return true;
210
+ return item.tags.some(tag => tag.toLowerCase().includes(query));
211
+ });
212
+
213
+ if (filtered.length === 0 && query) {
214
+ displayList.innerHTML = '<li>No bookmarked replies match your search tag.</li>';
215
+ return;
216
+ }
217
+
218
+ filtered.forEach(item => {
219
+ const li = document.createElement('li');
220
+ let tagChips = item.tags.map(tag => `<span class="tag-chip">${tag}</span>`).join('');
221
+
222
+ // Show a truncated reply and tags
223
+ li.innerHTML = `
224
+ <p style="font-weight: bold;">${item.reply.substring(0, 150)}...</p>
225
+ <p style="font-size: 0.9em;">${tagChips}</p>
226
+ `;
227
+ displayList.appendChild(li);
228
+ });
229
  }
 
230
  </script>
231
  </body>
232
+ </html>