gebsunamy commited on
Commit
54c1955
·
verified ·
1 Parent(s): dc6e1e9

Upload 2 files

Browse files
Files changed (2) hide show
  1. static/script.js +62 -83
  2. static/style.css +15 -14
static/script.js CHANGED
@@ -2,9 +2,7 @@ document.addEventListener("DOMContentLoaded", () => {
2
  const chatContainer = document.getElementById("chat-container");
3
  const chatForm = document.getElementById("chat-form");
4
  const userInput = document.getElementById("user-input");
5
- const sendBtn = document.getElementById("send-btn");
6
  const usageStatsDiv = document.getElementById("usage-stats");
7
- const modelStatsDiv = document.getElementById("model-stats");
8
  const themeToggle = document.getElementById("theme-toggle");
9
  const showSaveFormBtn = document.getElementById("show-save-form-btn");
10
  const saveModalOverlay = document.getElementById("save-modal-overlay");
@@ -16,14 +14,28 @@ document.addEventListener("DOMContentLoaded", () => {
16
  const progressBar = document.getElementById("training-progress-bar");
17
  const progressStep = document.getElementById("training-step");
18
  const progressPercentage = document.getElementById("training-percentage");
19
- const progressDetails = document.getElementById("training-details");
20
 
21
  let eventSource = null;
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  function startTrainingListener() {
24
- if (eventSource) eventSource.close();
25
  eventSource = new EventSource("/training-progress");
26
-
27
  eventSource.onmessage = (event) => {
28
  const data = JSON.parse(event.data);
29
  if (data.is_training) {
@@ -31,56 +43,21 @@ document.addEventListener("DOMContentLoaded", () => {
31
  progressBar.style.width = `${data.percentage}%`;
32
  progressStep.textContent = data.current_step;
33
  progressPercentage.textContent = `${data.percentage}%`;
34
- progressDetails.textContent = data.details;
35
  } else {
36
  trainingOverlay.classList.add("hidden");
37
- updateStats();
38
- if (eventSource) {
39
- eventSource.close();
40
- eventSource = null;
41
- }
42
  }
43
  };
44
-
45
- eventSource.onerror = () => {
46
- if (eventSource) eventSource.close();
47
- eventSource = null;
48
- // Retry after delay if needed
49
- setTimeout(checkTrainingStatus, 3000);
50
- };
51
- }
52
-
53
- async function checkTrainingStatus() {
54
- const res = await fetch("/status");
55
- const data = await res.json();
56
- if (data.is_training) {
57
- startTrainingListener();
58
- } else {
59
- trainingOverlay.classList.add("hidden");
60
- }
61
- }
62
-
63
- async function updateStats() {
64
- try {
65
- const [statusRes, statsRes] = await Promise.all([fetch("/status"), fetch("/stats")]);
66
- if (statusRes.ok) {
67
- const s = await statusRes.json();
68
- usageStatsDiv.innerHTML = `📊 ${s.tokens_used}/${s.max_tokens} رمز اليوم`;
69
- if (s.is_training) startTrainingListener();
70
- }
71
- if (statsRes.ok) {
72
- const s = await statsRes.json();
73
- modelStatsDiv.innerHTML = `🧠 ${s.question_count} مدخلات في القاعدة`;
74
- }
75
- } catch (e) {}
76
  }
77
 
78
  function addMessage(text, className) {
79
  const div = document.createElement("div");
80
  div.className = `message ${className}`;
81
- div.textContent = text;
82
  chatContainer.appendChild(div);
83
  chatContainer.scrollTop = chatContainer.scrollHeight;
 
84
  }
85
 
86
  chatForm.addEventListener("submit", async (e) => {
@@ -93,53 +70,59 @@ document.addEventListener("DOMContentLoaded", () => {
93
 
94
  const botMsgDiv = document.createElement("div");
95
  botMsgDiv.className = "message bot-message";
96
- botMsgDiv.textContent = "...";
 
 
 
97
  chatContainer.appendChild(botMsgDiv);
98
- chatContainer.scrollTop = chatContainer.scrollHeight;
99
 
100
  try {
101
- const res = await fetch("/chat", {
102
  method: "POST",
103
  headers: { "Content-Type": "application/json" },
104
  body: JSON.stringify({ question: q })
105
  });
106
- const data = await res.json();
 
 
 
 
 
 
 
 
107
 
108
- if (data.error) {
109
- botMsgDiv.textContent = `❌ ${data.error}`;
110
- if (res.status === 503) startTrainingListener();
111
- } else if (data.ambiguous_questions) {
112
- botMsgDiv.textContent = "أي من هذه الأسئلة تقصد؟";
113
- const btnContainer = document.createElement("div");
114
- btnContainer.style.display = "flex";
115
- btnContainer.style.flexDirection = "column";
116
- btnContainer.style.gap = "5px";
117
- btnContainer.style.marginTop = "10px";
118
 
119
- data.ambiguous_questions.forEach(aq => {
120
- const btn = document.createElement("button");
121
- btn.textContent = aq;
122
- btn.style.padding = "8px";
123
- btn.style.borderRadius = "10px";
124
- btn.style.border = "1px solid var(--primary)";
125
- btn.style.background = "transparent";
126
- btn.style.color = "var(--primary)";
127
- btn.style.cursor = "pointer";
128
- btn.onclick = () => {
129
- userInput.value = aq;
130
- chatForm.dispatchEvent(new Event('submit'));
131
- btnContainer.remove();
132
- };
133
- btnContainer.appendChild(btn);
134
- });
135
- botMsgDiv.appendChild(btnContainer);
136
- } else {
137
- botMsgDiv.textContent = data.response_text;
 
 
 
 
138
  }
139
  } catch (err) {
140
  botMsgDiv.textContent = "❌ حدث خطأ في الاتصال.";
141
  }
142
- chatContainer.scrollTop = chatContainer.scrollHeight;
143
  updateStats();
144
  });
145
 
@@ -158,11 +141,8 @@ document.addEventListener("DOMContentLoaded", () => {
158
  e.preventDefault();
159
  const question = document.getElementById("new-question").value.trim();
160
  const answers = Array.from(document.querySelectorAll(".answer-input")).map(i => i.value.trim()).filter(Boolean);
161
-
162
  if (!question || !answers.length) return;
163
-
164
  const data = answers.map(a => ({ question, answer: a }));
165
-
166
  try {
167
  const res = await fetch("/save", {
168
  method: "POST",
@@ -172,7 +152,7 @@ document.addEventListener("DOMContentLoaded", () => {
172
  if (res.ok) {
173
  saveModalOverlay.classList.add("hidden");
174
  saveForm.reset();
175
- startTrainingListener();
176
  }
177
  } catch (err) {}
178
  };
@@ -182,6 +162,5 @@ document.addEventListener("DOMContentLoaded", () => {
182
  themeToggle.textContent = isDark ? "☀️" : "🌙";
183
  };
184
 
185
- checkTrainingStatus();
186
  updateStats();
187
  });
 
2
  const chatContainer = document.getElementById("chat-container");
3
  const chatForm = document.getElementById("chat-form");
4
  const userInput = document.getElementById("user-input");
 
5
  const usageStatsDiv = document.getElementById("usage-stats");
 
6
  const themeToggle = document.getElementById("theme-toggle");
7
  const showSaveFormBtn = document.getElementById("show-save-form-btn");
8
  const saveModalOverlay = document.getElementById("save-modal-overlay");
 
14
  const progressBar = document.getElementById("training-progress-bar");
15
  const progressStep = document.getElementById("training-step");
16
  const progressPercentage = document.getElementById("training-percentage");
 
17
 
18
  let eventSource = null;
19
 
20
+ async function updateStats() {
21
+ try {
22
+ const res = await fetch("/status");
23
+ const s = await res.json();
24
+ usageStatsDiv.innerHTML = `
25
+ <div class="sys-stats">
26
+ <span>📊 ${s.tokens_used}/${s.max_tokens} Token</span>
27
+ <span>💻 CPU: ${s.cpu}%</span>
28
+ <span>🧠 RAM: ${s.ram}%</span>
29
+ </div>
30
+ `;
31
+ if (s.is_training) startTrainingListener();
32
+ } catch (e) {}
33
+ }
34
+ setInterval(updateStats, 3000);
35
+
36
  function startTrainingListener() {
37
+ if (eventSource) return;
38
  eventSource = new EventSource("/training-progress");
 
39
  eventSource.onmessage = (event) => {
40
  const data = JSON.parse(event.data);
41
  if (data.is_training) {
 
43
  progressBar.style.width = `${data.percentage}%`;
44
  progressStep.textContent = data.current_step;
45
  progressPercentage.textContent = `${data.percentage}%`;
 
46
  } else {
47
  trainingOverlay.classList.add("hidden");
48
+ eventSource.close();
49
+ eventSource = null;
 
 
 
50
  }
51
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }
53
 
54
  function addMessage(text, className) {
55
  const div = document.createElement("div");
56
  div.className = `message ${className}`;
57
+ div.innerHTML = `<div class="msg-content">${text}</div>`;
58
  chatContainer.appendChild(div);
59
  chatContainer.scrollTop = chatContainer.scrollHeight;
60
+ return div;
61
  }
62
 
63
  chatForm.addEventListener("submit", async (e) => {
 
70
 
71
  const botMsgDiv = document.createElement("div");
72
  botMsgDiv.className = "message bot-message";
73
+ const statusEl = document.createElement("div");
74
+ statusEl.className = "generating-status";
75
+ statusEl.textContent = "جاري البدء ... 0%";
76
+ botMsgDiv.appendChild(statusEl);
77
  chatContainer.appendChild(botMsgDiv);
 
78
 
79
  try {
80
+ const response = await fetch("/chat", {
81
  method: "POST",
82
  headers: { "Content-Type": "application/json" },
83
  body: JSON.stringify({ question: q })
84
  });
85
+
86
+ if (!response.ok) {
87
+ const errData = await response.json();
88
+ botMsgDiv.textContent = `❌ ${errData.error || "Error"}`;
89
+ return;
90
+ }
91
+
92
+ const reader = response.body.getReader();
93
+ const decoder = new TextDecoder();
94
 
95
+ while (true) {
96
+ const { value, done } = await reader.read();
97
+ if (done) break;
 
 
 
 
 
 
 
98
 
99
+ const chunk = decoder.decode(value);
100
+ const lines = chunk.split("\n");
101
+
102
+ for (const line of lines) {
103
+ if (!line.trim()) continue;
104
+ try {
105
+ const data = JSON.parse(line);
106
+
107
+ if (data.status === "Generating") {
108
+ statusEl.textContent = `${data.step} ... ${data.percentage}%`;
109
+ } else if (data.status === "Complete" || data.response_text) {
110
+ botMsgDiv.innerHTML = `
111
+ <div class="msg-content">${data.response_text}</div>
112
+ <div class="msg-footer">
113
+ ⏱️ ${data.duration.toFixed(3)}s | 🔤 ${data.token_count} Token | 💻 ${data.cpu}% CPU
114
+ </div>
115
+ `;
116
+ } else if (data.error) {
117
+ botMsgDiv.textContent = `❌ ${data.error}`;
118
+ }
119
+ } catch (e) { console.error("Error parsing chunk", e); }
120
+ }
121
+ chatContainer.scrollTop = chatContainer.scrollHeight;
122
  }
123
  } catch (err) {
124
  botMsgDiv.textContent = "❌ حدث خطأ في الاتصال.";
125
  }
 
126
  updateStats();
127
  });
128
 
 
141
  e.preventDefault();
142
  const question = document.getElementById("new-question").value.trim();
143
  const answers = Array.from(document.querySelectorAll(".answer-input")).map(i => i.value.trim()).filter(Boolean);
 
144
  if (!question || !answers.length) return;
 
145
  const data = answers.map(a => ({ question, answer: a }));
 
146
  try {
147
  const res = await fetch("/save", {
148
  method: "POST",
 
152
  if (res.ok) {
153
  saveModalOverlay.classList.add("hidden");
154
  saveForm.reset();
155
+ updateStats();
156
  }
157
  } catch (err) {}
158
  };
 
162
  themeToggle.textContent = isDark ? "☀️" : "🌙";
163
  };
164
 
 
165
  updateStats();
166
  });
static/style.css CHANGED
@@ -26,19 +26,24 @@ body { background: var(--bg); color: var(--text); direction: rtl; height: 100vh;
26
 
27
  #app-container { width: 100%; max-width: 500px; height: 95vh; background: var(--card); border-radius: 20px; display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1); border: 1px solid var(--border); }
28
 
29
- header { padding: 20px; background: var(--card); border-bottom: 1px solid var(--border); }
30
  .header-main { display: flex; justify-content: space-between; align-items: center; }
31
- h1 { font-size: 1.2rem; font-weight: 700; color: var(--primary); }
32
- .header-actions { display: flex; gap: 10px; }
33
- .icon-btn { background: var(--bot-msg); border: none; width: 40px; height: 40px; border-radius: 12px; cursor: pointer; color: var(--text); font-size: 1.1rem; }
34
 
35
- #usage-stats { font-size: 0.8rem; opacity: 0.7; margin-top: 5px; }
 
 
36
 
37
  #chat-container { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 15px; }
38
- .message { max-width: 85%; padding: 12px 16px; border-radius: 18px; font-size: 0.95rem; line-height: 1.5; }
39
  .bot-message { background: var(--bot-msg); align-self: flex-start; border-bottom-right-radius: 4px; }
40
  .user-message { background: var(--user-msg); color: var(--user-text); align-self: flex-end; border-bottom-left-radius: 4px; }
41
 
 
 
 
42
  #chat-form-container { padding: 15px; background: var(--card); border-top: 1px solid var(--border); }
43
  #chat-form { display: flex; gap: 10px; }
44
  #user-input { flex: 1; padding: 12px 20px; border-radius: 25px; border: 1px solid var(--border); background: var(--bg); color: var(--text); outline: none; }
@@ -47,14 +52,12 @@ h1 { font-size: 1.2rem; font-weight: 700; color: var(--primary); }
47
  /* Training Overlay */
48
  #training-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.85); backdrop-filter: blur(8px); z-index: 9999; display: flex; justify-content: center; align-items: center; color: white; }
49
  .training-card { background: #1c1c1e; padding: 40px; border-radius: 24px; text-align: center; width: 90%; max-width: 400px; border: 1px solid #333; }
50
- .loader { width: 60px; height: 60px; border: 5px solid #333; border-top: 5px solid var(--primary); border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px; }
51
  @keyframes spin { 100% { transform: rotate(360deg); } }
52
 
53
- .progress-container { background: #333; height: 10px; border-radius: 5px; margin: 20px 0; overflow: hidden; }
54
- .progress-bar { background: var(--primary); height: 100%; width: 0%; transition: width 0.3s ease; box-shadow: 0 0 15px var(--primary-glow); }
55
- #training-details { font-size: 0.85rem; opacity: 0.6; margin-top: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
56
 
57
- /* Modal */
58
  #save-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; justify-content: center; align-items: center; z-index: 1000; }
59
  #save-modal { background: var(--card); padding: 25px; border-radius: 20px; width: 90%; max-width: 450px; }
60
  #save-form { display: flex; flex-direction: column; gap: 15px; }
@@ -62,9 +65,7 @@ h1 { font-size: 1.2rem; font-weight: 700; color: var(--primary); }
62
  .modal-actions { display: flex; gap: 10px; }
63
  .modal-actions button { flex: 1; padding: 12px; border-radius: 10px; border: none; cursor: pointer; font-weight: 700; }
64
  #cancel-save-btn { background: var(--bot-msg); color: var(--text); }
65
- button[type="submit"] { background: var(--primary); color: white; }
66
 
67
  .hidden { display: none !important; }
68
-
69
- ::-webkit-scrollbar { width: 6px; }
70
  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; }
 
26
 
27
  #app-container { width: 100%; max-width: 500px; height: 95vh; background: var(--card); border-radius: 20px; display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1); border: 1px solid var(--border); }
28
 
29
+ header { padding: 15px 20px; background: var(--card); border-bottom: 1px solid var(--border); }
30
  .header-main { display: flex; justify-content: space-between; align-items: center; }
31
+ h1 { font-size: 1.1rem; font-weight: 700; color: var(--primary); }
32
+ .header-actions { display: flex; gap: 8px; }
33
+ .icon-btn { background: var(--bot-msg); border: none; width: 35px; height: 35px; border-radius: 10px; cursor: pointer; color: var(--text); font-size: 1rem; }
34
 
35
+ #usage-stats { margin-top: 10px; }
36
+ .sys-stats { display: flex; gap: 10px; flex-wrap: wrap; font-size: 0.75rem; opacity: 0.8; }
37
+ .sys-stats span { background: var(--bot-msg); padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border); }
38
 
39
  #chat-container { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 15px; }
40
+ .message { max-width: 85%; padding: 12px 16px; border-radius: 18px; font-size: 0.95rem; position: relative; }
41
  .bot-message { background: var(--bot-msg); align-self: flex-start; border-bottom-right-radius: 4px; }
42
  .user-message { background: var(--user-msg); color: var(--user-text); align-self: flex-end; border-bottom-left-radius: 4px; }
43
 
44
+ .msg-footer { font-size: 0.65rem; opacity: 0.6; margin-top: 8px; border-top: 1px solid rgba(128,128,128,0.2); padding-top: 4px; }
45
+ .generating-status { font-style: italic; font-size: 0.85rem; opacity: 0.7; color: var(--primary); }
46
+
47
  #chat-form-container { padding: 15px; background: var(--card); border-top: 1px solid var(--border); }
48
  #chat-form { display: flex; gap: 10px; }
49
  #user-input { flex: 1; padding: 12px 20px; border-radius: 25px; border: 1px solid var(--border); background: var(--bg); color: var(--text); outline: none; }
 
52
  /* Training Overlay */
53
  #training-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.85); backdrop-filter: blur(8px); z-index: 9999; display: flex; justify-content: center; align-items: center; color: white; }
54
  .training-card { background: #1c1c1e; padding: 40px; border-radius: 24px; text-align: center; width: 90%; max-width: 400px; border: 1px solid #333; }
55
+ .loader { width: 50px; height: 50px; border: 4px solid #333; border-top: 4px solid var(--primary); border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px; }
56
  @keyframes spin { 100% { transform: rotate(360deg); } }
57
 
58
+ .progress-container { background: #333; height: 8px; border-radius: 4px; margin: 15px 0; overflow: hidden; }
59
+ .progress-bar { background: var(--primary); height: 100%; width: 0%; transition: width 0.3s ease; }
 
60
 
 
61
  #save-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; justify-content: center; align-items: center; z-index: 1000; }
62
  #save-modal { background: var(--card); padding: 25px; border-radius: 20px; width: 90%; max-width: 450px; }
63
  #save-form { display: flex; flex-direction: column; gap: 15px; }
 
65
  .modal-actions { display: flex; gap: 10px; }
66
  .modal-actions button { flex: 1; padding: 12px; border-radius: 10px; border: none; cursor: pointer; font-weight: 700; }
67
  #cancel-save-btn { background: var(--bot-msg); color: var(--text); }
 
68
 
69
  .hidden { display: none !important; }
70
+ ::-webkit-scrollbar { width: 5px; }
 
71
  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; }