muralipala1504 commited on
Commit
eab7b08
·
1 Parent(s): d6e8f13

reduced the token cound and redundant chat conversation

Browse files
.gitignore CHANGED
@@ -26,3 +26,4 @@ deepshell.log
26
  docs/
27
  libretranslate.pid
28
  deepshell-backend/deepshell/logs/
 
 
26
  docs/
27
  libretranslate.pid
28
  deepshell-backend/deepshell/logs/
29
+ deepshell-backend/DEEPSHELL_ACTIONS_DESIGN.md
app.js.bak ADDED
@@ -0,0 +1,561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Plain Vanilla Chat Frontend with Copy Button, SSE Streaming, Export, and Clear
2
+ // Global mode management
3
+ window.setMode = function(mode) {
4
+ window.currentMode = mode;
5
+ document.getElementById("btn-assistant").className = "mode-btn" + (mode === "assistant" ? " active" : "");
6
+ document.getElementById("btn-trainer").className = "mode-btn" + (mode === "trainer" ? " trainer-active" : "");
7
+
8
+ const label = document.getElementById("mode-label");
9
+ if(label) label.textContent = mode === "trainer" ? "— Trainer Mode 🎓" : "";
10
+ const output = document.getElementById("chat-output");
11
+ if(output) output.innerHTML = "";
12
+ fetch("/chat/clear", { method: "POST" });
13
+ };
14
+ window.currentMode = "assistant";
15
+
16
+ window.toggleTheme = function() {
17
+ const root = document.documentElement;
18
+ const btn = document.getElementById("theme-btn");
19
+ const isLight = root.classList.toggle("light");
20
+ btn.textContent = isLight ? "🌙" : "☀️";
21
+ btn.title = isLight ? "Switch to dark theme" : "Switch to light theme";
22
+ localStorage.setItem("deepshell-theme", isLight ? "light" : "dark");
23
+ // Swap Prism theme
24
+ const darkTheme = document.getElementById("prism-theme");
25
+ const lightTheme = document.getElementById("prism-light-theme");
26
+ if (darkTheme && lightTheme) {
27
+ darkTheme.disabled = isLight;
28
+ lightTheme.disabled = !isLight;
29
+ }
30
+ };
31
+
32
+ // Apply saved Prism theme on load
33
+ document.addEventListener("DOMContentLoaded", function() {
34
+ const saved = localStorage.getItem("deepshell-theme");
35
+ if (saved === "light") {
36
+ const darkTheme = document.getElementById("prism-theme");
37
+ const lightTheme = document.getElementById("prism-light-theme");
38
+ if (darkTheme && lightTheme) {
39
+ darkTheme.disabled = true;
40
+ lightTheme.disabled = false;
41
+ }
42
+ const btn = document.getElementById("theme-btn");
43
+ if (btn) btn.textContent = "🌙";
44
+ }
45
+ });
46
+
47
+ // Apply saved theme on load
48
+ (function() {
49
+ const saved = localStorage.getItem("deepshell-theme");
50
+ if (saved === "light") {
51
+ document.documentElement.classList.add("light");
52
+ document.addEventListener("DOMContentLoaded", function() {
53
+ const btn = document.getElementById("theme-btn");
54
+ if (btn) { btn.textContent = "🌙"; }
55
+ });
56
+ }
57
+ })();
58
+ window.ttsEnabled = false;
59
+ window.lastSpokenText = "";
60
+
61
+ window.ttsLang = 'en-US';
62
+ window.setLang = function(lang) {
63
+ window.ttsLang = lang;
64
+ window.speechSynthesis.cancel();
65
+ };
66
+ window.toggleTTS = function() {
67
+ window.ttsEnabled = !window.ttsEnabled;
68
+ const btn = document.getElementById("tts-btn");
69
+ if (window.ttsEnabled) {
70
+ btn.classList.add("tts-active");
71
+ btn.title = "Voice ON — click to disable";
72
+ btn.innerHTML = "🔊 Voice ON";
73
+ } else {
74
+ btn.classList.remove("tts-active");
75
+ btn.title = "Voice OFF — click to enable";
76
+ btn.innerHTML = "🔇 Voice OFF";
77
+ window.speechSynthesis.cancel();
78
+ }
79
+ };
80
+
81
+ window.replayLast = function() {
82
+ if (window.lastSpokenText) {
83
+ window.speakText(window.lastSpokenText);
84
+ }
85
+ };
86
+
87
+ window.speakText = function(text) {
88
+ if (!window.ttsEnabled || window.currentMode !== "trainer") return;
89
+ window.lastSpokenText = text;
90
+ window.speechSynthesis.cancel();
91
+ // Strip markdown symbols for cleaner speech
92
+ const clean = text
93
+ .replace(/#{1,6}\s/g, "")
94
+ .replace(/\*\*/g, "")
95
+ .replace(/\*/g, "")
96
+ .replace(/```[\s\S]*?```/g, "")
97
+ .replace(/`([^`]+)`/g, "$1")
98
+ .replace(/🎯|💻|🔍|⚠️|🚀|💡/g, "")
99
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
100
+ .trim();
101
+ // Non-English: translate via backend then speak via Piper TTS
102
+ if (window.ttsLang !== 'en-US' && window.ttsLang !== 'en-GB') {
103
+ const langCode = window.ttsLang.split('-')[0];
104
+ const sentences = clean.match(/[^.!?]+[.!?]+/g) || [clean];
105
+ let i = 0;
106
+ function translateAndSpeakNext() {
107
+ if (i >= sentences.length || !window.ttsEnabled) return;
108
+ const sentence = sentences[i].trim();
109
+ fetch('/translate', {
110
+ method: 'POST',
111
+ headers: {'Content-Type': 'application/json'},
112
+ body: JSON.stringify({text: sentence, target: langCode})
113
+ })
114
+ .then(r => r.json())
115
+ .then(data => {
116
+ const translated = data.translatedText || sentence;
117
+ return fetch('/tts', {
118
+ method: 'POST',
119
+ headers: {'Content-Type': 'application/json'},
120
+ body: JSON.stringify({text: translated, lang: langCode})
121
+ });
122
+ })
123
+ .then(r => r.blob())
124
+ .then(blob => {
125
+ const url = URL.createObjectURL(blob);
126
+ const audio = new Audio(url);
127
+ audio.onended = () => { URL.revokeObjectURL(url); i++; translateAndSpeakNext(); };
128
+ // Small delay on first sentence to avoid audio startup cutoff
129
+ const delay = i === 0 ? 300 : 0;
130
+ setTimeout(() => audio.play(), delay);
131
+ })
132
+ .catch(() => {
133
+ const utt = new SpeechSynthesisUtterance(sentence);
134
+ utt.lang = window.ttsLang;
135
+ utt.onend = () => { i++; translateAndSpeakNext(); };
136
+ window.speechSynthesis.speak(utt);
137
+ });
138
+ }
139
+ translateAndSpeakNext();
140
+ return;
141
+ }
142
+ window._speakDirect(clean);
143
+ };
144
+ window._speakDirect = function(clean) {
145
+ const chunks = clean.match(/[^.!?]+[.!?]+/g) || [clean];
146
+ let i = 0;
147
+ function speakNext() {
148
+ if (i >= chunks.length || !window.ttsEnabled) return;
149
+ const utt = new SpeechSynthesisUtterance(chunks[i].trim());
150
+ utt.lang = window.ttsLang;
151
+ const voices = window.speechSynthesis.getVoices();
152
+ const match = voices.find(v => v.lang === window.ttsLang);
153
+ if (match) utt.voice = match;
154
+ utt.rate = 0.78;
155
+ utt.pitch = 1.0;
156
+ utt.volume = 1.0;
157
+ utt.onend = () => { i++; speakNext(); };
158
+ window.speechSynthesis.speak(utt);
159
+ }
160
+ speakNext();
161
+ };
162
+
163
+ (function () {
164
+ "use strict";
165
+
166
+
167
+ // ---------- Utilities ----------
168
+ function escapeHtml(text) {
169
+ return String(text).replace(/[&<>"']/g, (m) => ({
170
+ "&": "&amp;",
171
+ "<": "&lt;",
172
+ ">": "&gt;",
173
+ '"': "&quot;",
174
+ "'": "&#39;",
175
+ })[m]);
176
+ }
177
+
178
+ function renderMarkdown(md) {
179
+ if (window.marked) {
180
+ const renderer = new marked.Renderer();
181
+ renderer.code = function(code, lang) {
182
+ const language = lang || 'bash';
183
+ const validLang = Prism.languages[language] ? language : 'bash';
184
+ return `<pre><code class="language-${validLang}">${escapeHtml(code)}</code></pre>`;
185
+ };
186
+ return marked.parse(md, { renderer });
187
+ }
188
+ return `<pre>${escapeHtml(md)}</pre>`;
189
+ }
190
+
191
+ function createCopyButton(code) {
192
+ const btn = document.createElement("button");
193
+ btn.className = "copy-btn";
194
+ btn.textContent = "Copy";
195
+ btn.onclick = async () => {
196
+ try {
197
+ await navigator.clipboard.writeText(code);
198
+ btn.textContent = "Copied!";
199
+ setTimeout(() => { btn.textContent = "Copy"; }, 2000);
200
+ } catch (err) {
201
+ const textarea = document.createElement("textarea");
202
+ textarea.value = code;
203
+ textarea.style.position = "fixed";
204
+ textarea.style.opacity = "0";
205
+ document.body.appendChild(textarea);
206
+ textarea.select();
207
+ try {
208
+ document.execCommand("copy");
209
+ btn.textContent = "Copied!";
210
+ setTimeout(() => { btn.textContent = "Copy"; }, 2000);
211
+ } catch (e) {
212
+ btn.textContent = "Failed";
213
+ setTimeout(() => { btn.textContent = "Copy"; }, 2000);
214
+ }
215
+ document.body.removeChild(textarea);
216
+ }
217
+ };
218
+ return btn;
219
+ }
220
+
221
+ function createExecuteButton(command, output) {
222
+ const btn = document.createElement("button");
223
+ btn.className = "execute-btn";
224
+ btn.textContent = "Execute";
225
+ btn.onclick = async () => {
226
+ btn.disabled = true;
227
+ btn.textContent = "Executing...";
228
+
229
+ try {
230
+ const response = await fetch("/chat/execute", {
231
+ method: "POST",
232
+ headers: { "Content-Type": "application/json" },
233
+ body: JSON.stringify({ command }),
234
+ });
235
+
236
+ const result = await response.json();
237
+
238
+ if (result.success) {
239
+ const outputText = result.stdout || result.stderr || "(no output)";
240
+ appendMessage(output, "Command Output", `Exit Code: ${result.exit_code}\n\n${outputText}`, false);
241
+ btn.textContent = "✓ Executed";
242
+ btn.style.background = "#22c55e";
243
+ } else {
244
+ appendMessage(output, "Execution Error", result.error || "Unknown error", false);
245
+ btn.textContent = "✗ Failed";
246
+ btn.style.background = "#ef4444";
247
+ }
248
+ } catch (err) {
249
+ appendMessage(output, "Error", `Failed to execute: ${err.message}`, false);
250
+ btn.textContent = "✗ Error";
251
+ btn.style.background = "#ef4444";
252
+ }
253
+
254
+ setTimeout(() => {
255
+ btn.disabled = false;
256
+ btn.textContent = "Execute";
257
+ btn.style.background = "";
258
+ }, 3000);
259
+ };
260
+ return btn;
261
+ }
262
+
263
+ function extractExecuteTags(text) {
264
+ const regex = /<execute>(.*?)<\/execute>/gs;
265
+ const commands = [];
266
+ let match;
267
+
268
+ while ((match = regex.exec(text)) !== null) {
269
+ commands.push(match[1].trim());
270
+ }
271
+
272
+ return commands;
273
+ }
274
+
275
+ // Add copy buttons to all code blocks in a container
276
+ function addCopyButtonsToCodeBlocks(container) {
277
+ container.querySelectorAll("pre").forEach((pre) => {
278
+ if (pre.parentElement && pre.parentElement.classList.contains("code-wrapper")) {
279
+ return;
280
+ }
281
+
282
+ const codeEl = pre.querySelector("code");
283
+ const code = codeEl ? codeEl.textContent : pre.textContent;
284
+
285
+ if (!code || !code.trim()) return;
286
+
287
+ const wrapper = document.createElement("div");
288
+ wrapper.className = "code-wrapper";
289
+
290
+ const copyBtn = createCopyButton(code);
291
+
292
+ pre.parentNode.insertBefore(wrapper, pre);
293
+
294
+ wrapper.appendChild(copyBtn);
295
+ wrapper.appendChild(pre);
296
+ });
297
+ }
298
+
299
+ function appendMessage(container, sender, message, useMarkdown = false, isError = false) {
300
+ const msgDiv = document.createElement("div");
301
+ msgDiv.classList.add("message");
302
+ if (sender === "You") msgDiv.classList.add("user-msg");
303
+ if (isError) msgDiv.classList.add("error");
304
+
305
+ const commands = extractExecuteTags(message);
306
+
307
+ if (commands.length > 0) {
308
+ let displayText = message.replace(/<execute>.*?<\/execute>/gs, "");
309
+
310
+ msgDiv.innerHTML = `<strong>${sender}:</strong>${useMarkdown ? renderMarkdown(displayText) : `<pre>${escapeHtml(displayText)}</pre>`}`;
311
+
312
+ commands.forEach(cmd => {
313
+ const cmdWrapper = document.createElement("div");
314
+ cmdWrapper.className = "command-wrapper";
315
+
316
+ const cmdPre = document.createElement("pre");
317
+ cmdPre.className = "command-block";
318
+ cmdPre.textContent = cmd;
319
+
320
+ const btnContainer = document.createElement("div");
321
+ btnContainer.className = "command-buttons";
322
+
323
+ const executeBtn = createExecuteButton(cmd, container);
324
+ const copyBtn = createCopyButton(cmd);
325
+
326
+ btnContainer.appendChild(executeBtn);
327
+ btnContainer.appendChild(copyBtn);
328
+
329
+ cmdWrapper.appendChild(cmdPre);
330
+ cmdWrapper.appendChild(btnContainer);
331
+ msgDiv.appendChild(cmdWrapper);
332
+ });
333
+ } else if (useMarkdown) {
334
+ msgDiv.innerHTML = `<strong>${sender}:</strong><div class="markdown-content">${renderMarkdown(String(message))}</div>`;
335
+ addCopyButtonsToCodeBlocks(msgDiv);
336
+ } else {
337
+ msgDiv.innerHTML = `<strong>${sender}:</strong> <pre>${escapeHtml(String(message))}</pre>`;
338
+ }
339
+
340
+ container.appendChild(msgDiv);
341
+ container.scrollTop = container.scrollHeight;
342
+
343
+ if (window.Prism) {
344
+ try {
345
+ Prism.highlightAllUnder(msgDiv);
346
+ } catch (_) {}
347
+ }
348
+
349
+ return msgDiv;
350
+ }
351
+
352
+ // ---------- Streaming UI ----------
353
+ function createStreamingMessage(container, sender) {
354
+ const msgDiv = document.createElement("div");
355
+ msgDiv.classList.add("message", "loading");
356
+ msgDiv.innerHTML = `<strong>${sender}:</strong> <span class="streaming-content"></span><span class="spinner"></span>`;
357
+
358
+ const contentSpan = msgDiv.querySelector(".streaming-content");
359
+
360
+ container.appendChild(msgDiv);
361
+ container.scrollTop = container.scrollHeight;
362
+
363
+ return {
364
+ msgDiv,
365
+ contentSpan,
366
+ updateContent: (text) => {
367
+ contentSpan.textContent = text;
368
+ container.scrollTop = container.scrollHeight;
369
+ },
370
+ finalize: (text) => {
371
+ msgDiv.remove();
372
+ appendMessage(container, sender, text, true);
373
+ }
374
+ };
375
+ }
376
+
377
+ // ---------- SSE Streaming ----------
378
+ async function sendPromptStreaming(prompt, output, sendBtn, input) {
379
+ const streamUI = createStreamingMessage(output, "DeepShell");
380
+ let fullText = "";
381
+
382
+ try {
383
+ const response = await fetch("/chat/run-agent-stream", {
384
+ method: "POST",
385
+ headers: { "Content-Type": "application/json" },
386
+ body: JSON.stringify({ prompt, mode: window.currentMode || "assistant" }),
387
+ });
388
+
389
+ if (!response.ok) {
390
+ throw new Error(`Server error: ${response.status}`);
391
+ }
392
+
393
+ const reader = response.body.getReader();
394
+ const decoder = new TextDecoder("utf-8");
395
+ let buffer = "";
396
+
397
+ while (true) {
398
+ const { done, value } = await reader.read();
399
+ if (done) break;
400
+
401
+ buffer += decoder.decode(value, { stream: true });
402
+
403
+ const lines = buffer.split("\n\n");
404
+ buffer = lines.pop();
405
+
406
+ for (const line of lines) {
407
+ if (!line.trim() || !line.startsWith("data: ")) continue;
408
+
409
+ const jsonStr = line.substring(6);
410
+
411
+ try {
412
+ const data = JSON.parse(jsonStr);
413
+
414
+ if (data.type === "token") {
415
+ fullText += data.text;
416
+ streamUI.updateContent(fullText);
417
+ } else if (data.type === "done") {
418
+ streamUI.finalize(fullText);
419
+ window.speakText(fullText);
420
+ return;
421
+ } else if (data.type === "error") {
422
+ throw new Error(data.message);
423
+ }
424
+ } catch (e) {
425
+ console.warn("Failed to parse SSE data:", jsonStr, e);
426
+ }
427
+ }
428
+ }
429
+
430
+ if (fullText) {
431
+ streamUI.finalize(fullText);
432
+ }
433
+ } catch (err) {
434
+ streamUI.msgDiv.remove();
435
+ appendMessage(output, "Error", `${err.message}`, false, true);
436
+ console.error("Streaming error:", err);
437
+ } finally {
438
+ if (sendBtn) sendBtn.disabled = false;
439
+ input.focus();
440
+ }
441
+ }
442
+
443
+ // ---------- Clear Chat ----------
444
+ async function clearChat(output) {
445
+ if (!confirm("Clear conversation history? This cannot be undone.")) {
446
+ return;
447
+ }
448
+
449
+ try {
450
+ const response = await fetch("/chat/clear", {
451
+ method: "POST",
452
+ });
453
+
454
+ if (response.ok) {
455
+ output.innerHTML = "";
456
+ appendMessage(output, "System", "Chat cleared. Session history reset.", false);
457
+ } else {
458
+ throw new Error("Failed to clear chat");
459
+ }
460
+ } catch (err) {
461
+ appendMessage(output, "Error", `Failed to clear chat: ${err.message}`, false, true);
462
+ }
463
+ }
464
+
465
+ // ---------- Export Chat ----------
466
+ function exportChat(output) {
467
+ const messages = output.querySelectorAll(".message");
468
+ if (messages.length === 0) {
469
+ alert("No messages to export!");
470
+ return;
471
+ }
472
+
473
+ let markdown = "# DeepShell Chat Export\n\n";
474
+ markdown += `**Exported:** ${new Date().toLocaleString()}\n\n---\n\n`;
475
+
476
+ messages.forEach((msg) => {
477
+ const sender = msg.querySelector("strong")?.textContent.replace(":", "") || "Unknown";
478
+
479
+ // Get text content, excluding buttons
480
+ const clone = msg.cloneNode(true);
481
+ clone.querySelectorAll("button").forEach(btn => btn.remove());
482
+
483
+ const content = clone.textContent
484
+ .replace(sender + ":", "")
485
+ .trim();
486
+
487
+ markdown += `## ${sender}\n\n${content}\n\n---\n\n`;
488
+ });
489
+
490
+ // Create download
491
+ const blob = new Blob([markdown], { type: "text/markdown" });
492
+ const url = URL.createObjectURL(blob);
493
+ const a = document.createElement("a");
494
+ a.href = url;
495
+ a.download = `deepshell-chat-${Date.now()}.md`;
496
+ document.body.appendChild(a);
497
+ a.click();
498
+ document.body.removeChild(a);
499
+ URL.revokeObjectURL(url);
500
+ }
501
+
502
+ // ---------- Chat wiring ----------
503
+ document.addEventListener("DOMContentLoaded", () => {
504
+ const form = document.getElementById("chat-form");
505
+ const input = document.getElementById("chat-input");
506
+ const output = document.getElementById("chat-output");
507
+ const sendBtn = document.getElementById("chat-send");
508
+ const clearBtn = document.getElementById("clear-btn");
509
+ const exportBtn = document.getElementById("export-btn");
510
+
511
+ if (!form || !input || !output) {
512
+ console.warn("Chat elements not found (chat-form/chat-input/chat-output).");
513
+ return;
514
+ }
515
+
516
+ // Send message
517
+ form.addEventListener("submit", (e) => {
518
+ e.preventDefault();
519
+ const prompt = input.value.trim();
520
+ if (!prompt) return;
521
+
522
+ // Hide welcome message on first user input
523
+ const welcome = document.getElementById("welcome-msg");
524
+ if (welcome) welcome.style.display = "none";
525
+ appendMessage(output, "You", prompt);
526
+ input.value = "";
527
+ if (sendBtn) sendBtn.disabled = true;
528
+
529
+ sendPromptStreaming(prompt, output, sendBtn, input);
530
+ });
531
+
532
+ if (sendBtn) {
533
+ sendBtn.addEventListener("click", (e) => {
534
+ e.preventDefault();
535
+ form.requestSubmit();
536
+ });
537
+ }
538
+
539
+ // Clear chat button
540
+ if (clearBtn) {
541
+ clearBtn.addEventListener("click", () => {
542
+ clearChat(output);
543
+ });
544
+ }
545
+
546
+ // Export chat button
547
+ if (exportBtn) {
548
+ exportBtn.addEventListener("click", () => {
549
+ exportChat(output);
550
+ });
551
+ }
552
+
553
+ // Enter to send, Shift+Enter for newline
554
+ input.addEventListener("keydown", (e) => {
555
+ if (e.key === "Enter" && !e.shiftKey) {
556
+ e.preventDefault();
557
+ form.requestSubmit();
558
+ }
559
+ });
560
+ });
561
+ })();
deepshell-backend/deepshell/__main__.py CHANGED
@@ -408,7 +408,7 @@ async def _sse_generator(prompt: str, mode: str = "assistant"):
408
  try:
409
  # Try streaming with stream=True
410
  try:
411
- response = client.chat(full_prompt, max_tokens=4000, temperature=0.2, stream=True, system_prompt=system_prompt)
412
 
413
  # Handle streaming response
414
  if hasattr(response, "__iter__"):
@@ -427,7 +427,7 @@ async def _sse_generator(prompt: str, mode: str = "assistant"):
427
 
428
  except TypeError:
429
  # stream=True not supported, fallback to non-streaming
430
- response = client.chat(full_prompt, max_tokens=4000, temperature=0.2, system_prompt=system_prompt)
431
  try:
432
  text = response.choices[0].message.content
433
  except Exception:
 
408
  try:
409
  # Try streaming with stream=True
410
  try:
411
+ response = client.chat(full_prompt, max_tokens=2200, temperature=0.2, stream=True, system_prompt=system_prompt)
412
 
413
  # Handle streaming response
414
  if hasattr(response, "__iter__"):
 
427
 
428
  except TypeError:
429
  # stream=True not supported, fallback to non-streaming
430
+ response = client.chat(full_prompt, max_tokens=2200, temperature=0.2, system_prompt=system_prompt)
431
  try:
432
  text = response.choices[0].message.content
433
  except Exception:
deepshell-backend/deepshell/llm.py CHANGED
@@ -92,8 +92,10 @@ DIFFICULTY AWARENESS:
92
  When refusing off-topic questions:
93
  "I'm DeepShell Trainer, focused exclusively on Linux and DevOps education. For [topic], please use a general-purpose AI assistant. Ready to teach you something DevOps-related?"
94
  CONTENT LENGTH:
95
- Generate exactly 1300 words of spoken tutorial content.
96
- No more, no less. Cover the topic completely within that word count.
 
 
97
  """
98
 
99
 
 
92
  When refusing off-topic questions:
93
  "I'm DeepShell Trainer, focused exclusively on Linux and DevOps education. For [topic], please use a general-purpose AI assistant. Ready to teach you something DevOps-related?"
94
  CONTENT LENGTH:
95
+ Aim for roughly 1300-1600 words of spoken tutorial content, covering the
96
+ topic completely. Stop naturally once you've given the pro tip for the
97
+ last concept — do NOT add a summary, recap, or motivational closing
98
+ section of any kind.
99
  """
100
 
101
 
favicon-files.zip ADDED
Binary file (4.09 kB). View file