pygpt-net 2.6.10__py3-none-any.whl → 2.6.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. pygpt_net/CHANGELOG.txt +10 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +7 -1
  4. pygpt_net/config.py +11 -11
  5. pygpt_net/controller/access/access.py +49 -2
  6. pygpt_net/controller/chat/attachment.py +13 -13
  7. pygpt_net/controller/chat/command.py +4 -4
  8. pygpt_net/controller/chat/common.py +9 -14
  9. pygpt_net/controller/chat/files.py +2 -2
  10. pygpt_net/controller/chat/input.py +4 -4
  11. pygpt_net/controller/chat/output.py +4 -4
  12. pygpt_net/controller/chat/render.py +11 -6
  13. pygpt_net/controller/chat/response.py +7 -7
  14. pygpt_net/controller/chat/stream.py +11 -6
  15. pygpt_net/controller/chat/text.py +15 -10
  16. pygpt_net/controller/command/command.py +7 -7
  17. pygpt_net/controller/ctx/ctx.py +9 -5
  18. pygpt_net/controller/debug/debug.py +2 -2
  19. pygpt_net/core/ctx/bag.py +2 -1
  20. pygpt_net/core/debug/debug.py +17 -3
  21. pygpt_net/core/dispatcher/dispatcher.py +5 -3
  22. pygpt_net/core/events/render.py +3 -0
  23. pygpt_net/core/render/base.py +4 -4
  24. pygpt_net/core/render/web/body.py +83 -88
  25. pygpt_net/core/render/web/parser.py +11 -6
  26. pygpt_net/core/render/web/pid.py +19 -4
  27. pygpt_net/core/render/web/renderer.py +217 -74
  28. pygpt_net/data/config/config.json +3 -3
  29. pygpt_net/data/config/models.json +3 -3
  30. pygpt_net/data/config/presets/agent_openai.json +1 -1
  31. pygpt_net/data/config/presets/agent_openai_assistant.json +1 -1
  32. pygpt_net/data/config/presets/agent_planner.json +1 -1
  33. pygpt_net/data/config/presets/agent_react.json +1 -1
  34. pygpt_net/item/ctx.py +3 -3
  35. pygpt_net/launcher.py +2 -9
  36. pygpt_net/provider/gpt/__init__.py +13 -4
  37. pygpt_net/tools/code_interpreter/body.py +2 -3
  38. pygpt_net/ui/main.py +8 -3
  39. pygpt_net/ui/widget/textarea/html.py +2 -7
  40. pygpt_net/ui/widget/textarea/web.py +52 -28
  41. pygpt_net/utils.py +15 -8
  42. {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.12.dist-info}/METADATA +12 -2
  43. {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.12.dist-info}/RECORD +46 -46
  44. {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.12.dist-info}/LICENSE +0 -0
  45. {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.12.dist-info}/WHEEL +0 -0
  46. {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.12.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.16 00:00:00 #
9
+ # Updated Date: 2025.08.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -41,9 +41,8 @@ class Body:
41
41
  <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
42
42
  <script type="text/javascript" src="qrc:///js/highlight.min.js"></script>
43
43
  <script type="text/javascript" src="qrc:///js/katex.min.js"></script>
44
- <script type="text/javascript" src="qrc:///js/auto-render.min.js"></script>
45
44
  <script>
46
-
45
+ const DEBUG_MODE = false;
47
46
  let scrollTimeout = null;
48
47
  let prevScroll = 0;
49
48
  let bridge;
@@ -67,7 +66,23 @@ class Body:
67
66
  let pendingHighlightRoot = null;
68
67
  let pendingHighlightMath = false;
69
68
  let scrollScheduled = false;
70
- const AMP_LT_GT = /&amp;(lt|gt);/g;
69
+
70
+ // timers
71
+ let tipsTimers = [];
72
+
73
+ // clear previous references
74
+ function resetEphemeralDomRefs() {
75
+ domLastCodeBlock = null;
76
+ domLastParagraphBlock = null;
77
+ }
78
+ function dropIfDetached() {
79
+ if (domLastCodeBlock && !domLastCodeBlock.isConnected) domLastCodeBlock = null;
80
+ if (domLastParagraphBlock && !domLastParagraphBlock.isConnected) domLastParagraphBlock = null;
81
+ }
82
+ function stopTipsTimers() {
83
+ tipsTimers.forEach(clearTimeout);
84
+ tipsTimers = [];
85
+ }
71
86
 
72
87
  history.scrollRestoration = "manual";
73
88
  document.addEventListener('keydown', function(event) {
@@ -128,13 +143,16 @@ class Body:
128
143
  if (withMath) {
129
144
  renderMath(root);
130
145
  }
146
+ if (DEBUG_MODE) log("execute highlight");
131
147
  restoreCollapsedCode(root);
132
148
  }
133
149
  function highlightCode(withMath = true, root = null) {
150
+ if (DEBUG_MODE) log("queue highlight, withMath: " + withMath);
134
151
  scheduleHighlight(root || document, withMath);
135
152
  }
136
153
  function hideTips() {
137
154
  if (tips_hidden) return;
155
+ stopTipsTimers();
138
156
  const t = els.tips || document.getElementById('tips');
139
157
  if (t) t.style.display = 'none';
140
158
  tips_hidden = true;
@@ -149,24 +167,27 @@ class Body:
149
167
  function cycleTips() {
150
168
  if (tips_hidden) return;
151
169
  if (tips.length === 0) return;
152
- let tipContainer = els.tips || document.getElementById('tips');
153
170
  let currentTip = 0;
154
171
  function showNextTip() {
155
172
  if (tips_hidden) return;
173
+ const tipContainer = els.tips || document.getElementById('tips');
174
+ if (!tipContainer) return;
156
175
  tipContainer.innerHTML = tips[currentTip];
157
176
  tipContainer.classList.add('visible');
158
- setTimeout(function() {
177
+ tipsTimers.push(setTimeout(function() {
159
178
  if (tips_hidden) return;
160
179
  tipContainer.classList.remove('visible');
161
- setTimeout(function(){
180
+ tipsTimers.push(setTimeout(function(){
162
181
  currentTip = (currentTip + 1) % tips.length;
163
182
  showNextTip();
164
- }, 1000);
165
- }, 10000);
183
+ }, 1000));
184
+ }, 15000));
166
185
  }
186
+ stopTipsTimers();
167
187
  showNextTip();
168
188
  }
169
189
  function renderMath(root) {
190
+ if (DEBUG_MODE) log("execute math");
170
191
  const scope = root || document;
171
192
  const scripts = scope.querySelectorAll('script[type^="math/tex"]');
172
193
  scripts.forEach(function(script) {
@@ -212,20 +233,16 @@ class Body:
212
233
  }
213
234
  prevScroll = el.scrollHeight;
214
235
  }
215
- function sanitize(content) {
216
- if (content.indexOf('&amp;') === -1) return content;
217
- return content.replace(AMP_LT_GT, '&$1;'); // &amp;lt; -> &lt;, &amp;gt; -> &gt;
218
- }
219
236
  function appendToInput(content) {
220
237
  const element = els.appendInput || document.getElementById('_append_input_');
221
238
  if (element) {
222
- element.insertAdjacentHTML('beforeend', sanitize(content));
239
+ element.insertAdjacentHTML('beforeend', content);
223
240
  highlightCode(true, element);
224
241
  scheduleScroll();
225
242
  }
226
243
  }
227
244
  function getStreamContainer() {
228
- if (domOutputStream && document.body.contains(domOutputStream)) {
245
+ if (domOutputStream && domOutputStream.isConnected) {
229
246
  return domOutputStream;
230
247
  }
231
248
  let element = els.appendOutput || document.getElementById('_append_output_');
@@ -234,46 +251,38 @@ class Body:
234
251
  }
235
252
  return element;
236
253
  }
237
- function appendToOutput(bot_name, content) {
238
- hideTips();
239
- const element = getStreamContainer();
240
- if (element) {
241
- let box = element.querySelector('.msg-box');
242
- let msg;
243
- if (!box) {
244
- box = document.createElement('div');
245
- box.classList.add('msg-box');
246
- box.classList.add('msg-bot');
247
- var name = document.createElement('div');
248
- name.classList.add('name-header');
249
- name.classList.add('name-bot');
250
- name.textContent = bot_name;
251
- msg = document.createElement('div');
252
- msg.classList.add('msg');
253
- box.appendChild(name);
254
- box.appendChild(msg);
255
- element.appendChild(box);
256
- } else {
257
- msg = box.querySelector('.msg');
258
- }
259
- if (msg) {
260
- msg.insertAdjacentHTML('beforeend', sanitize(content));
261
- highlightCode(true, msg);
262
- scheduleScroll();
263
- }
264
- }
265
- }
266
254
  function appendNode(content) {
255
+ if (DEBUG_MODE) {
256
+ log("APPEND NODE: {" + content + "}");
257
+ }
267
258
  clearStreamBefore();
268
259
  prevScroll = 0;
269
260
  const element = els.nodes || document.getElementById('_nodes_');
270
261
  if (element) {
271
262
  element.classList.remove('empty_list');
272
- element.insertAdjacentHTML('beforeend', sanitize(content));
263
+ element.insertAdjacentHTML('beforeend', content);
273
264
  highlightCode(true, element);
274
265
  scheduleScroll();
275
266
  }
276
267
  }
268
+ function clean() {
269
+ if (DEBUG_MODE) {
270
+ log("-- CLEAN DOM --");
271
+ }
272
+ const el = els.nodes || document.getElementById('_nodes_');
273
+ if (el) {
274
+ el.replaceChildren();
275
+ }
276
+ resetEphemeralDomRefs();
277
+ els = {};
278
+ try {
279
+ if (window.gc) {
280
+ window.gc();
281
+ }
282
+ } catch (e) {
283
+ // gc not available
284
+ }
285
+ }
277
286
  function appendExtra(id, content) {
278
287
  hideTips();
279
288
  prevScroll = 0;
@@ -281,7 +290,7 @@ class Body:
281
290
  if (element) {
282
291
  const extra = element.querySelector('.msg-extra');
283
292
  if (extra) {
284
- extra.insertAdjacentHTML('beforeend', sanitize(content));
293
+ extra.insertAdjacentHTML('beforeend', content);
285
294
  highlightCode(true, extra);
286
295
  scheduleScroll();
287
296
  }
@@ -297,6 +306,7 @@ class Body:
297
306
  if (element) {
298
307
  element.remove();
299
308
  }
309
+ resetEphemeralDomRefs();
300
310
  highlightCode();
301
311
  scheduleScroll();
302
312
  }
@@ -307,19 +317,23 @@ class Body:
307
317
  const elements = container.querySelectorAll('.msg-box');
308
318
  let remove = false;
309
319
  elements.forEach(function(element) {
310
- if (element.id.endsWith('-' + id)) {
320
+ if (element.id && element.id.endsWith('-' + id)) {
311
321
  remove = true;
312
322
  }
313
323
  if (remove) {
314
324
  element.remove();
315
325
  }
316
326
  });
327
+ resetEphemeralDomRefs();
317
328
  highlightCode(true, container);
318
329
  scheduleScroll();
319
330
  }
320
331
  }
321
332
  function clearStream() {
322
333
  hideTips();
334
+ if (DEBUG_MODE) {
335
+ log("STREAM CLEAR");
336
+ }
323
337
  domLastParagraphBlock = null;
324
338
  domLastCodeBlock = null;
325
339
  domOutputStream = null;
@@ -345,10 +359,16 @@ class Body:
345
359
  }
346
360
  function beginStream() {
347
361
  hideTips();
362
+ if (DEBUG_MODE) {
363
+ log("STREAM BEGIN");
364
+ }
348
365
  clearOutput();
349
366
  scheduleScroll();
350
367
  }
351
368
  function endStream() {
369
+ if (DEBUG_MODE) {
370
+ log("STREAM END");
371
+ }
352
372
  clearOutput();
353
373
  }
354
374
  function enqueueStream(name_header, content, chunk, replace = false, is_code_block = false) {
@@ -365,7 +385,11 @@ class Body:
365
385
  }
366
386
  }
367
387
  function appendStream(name_header, content, chunk, replace = false, is_code_block = false) {
388
+ dropIfDetached(); // clear references to detached elements
368
389
  hideTips();
390
+ if (DEBUG_MODE) {
391
+ log("APPEND CHUNK: {" + chunk + "}, CONTENT: {"+content+"}, replace: " + replace + ", is_code_block: " + is_code_block);
392
+ }
369
393
  const element = getStreamContainer();
370
394
  let msg;
371
395
  if (element) {
@@ -392,7 +416,7 @@ class Body:
392
416
  if (replace) {
393
417
  msg.replaceChildren();
394
418
  if (content) {
395
- msg.insertAdjacentHTML('afterbegin', sanitize(content));
419
+ msg.insertAdjacentHTML('afterbegin', content);
396
420
  }
397
421
  let doMath = true;
398
422
  if (is_code_block) {
@@ -413,10 +437,10 @@ class Body:
413
437
  }
414
438
  }
415
439
  if (lastCodeBlock) {
416
- lastCodeBlock.insertAdjacentHTML('beforeend', sanitize(chunk));
440
+ lastCodeBlock.insertAdjacentHTML('beforeend', chunk);
417
441
  domLastCodeBlock = lastCodeBlock;
418
442
  } else {
419
- msg.insertAdjacentHTML('beforeend', sanitize(chunk));
443
+ msg.insertAdjacentHTML('beforeend', chunk);
420
444
  domLastCodeBlock = null;
421
445
  }
422
446
  } else {
@@ -427,10 +451,10 @@ class Body:
427
451
  ? msg.lastElementChild
428
452
  : null);
429
453
  if (p) {
430
- p.insertAdjacentHTML('beforeend', sanitize(chunk));
454
+ p.insertAdjacentHTML('beforeend', chunk);
431
455
  domLastParagraphBlock = p;
432
456
  } else {
433
- msg.insertAdjacentHTML('beforeend', sanitize(chunk));
457
+ msg.insertAdjacentHTML('beforeend', chunk);
434
458
  const last = msg.lastElementChild;
435
459
  domLastParagraphBlock = (last && last.tagName === 'P') ? last : null;
436
460
  }
@@ -440,38 +464,6 @@ class Body:
440
464
  }
441
465
  scheduleScroll(true);
442
466
  }
443
- function replaceOutput(name_header, content) {
444
- hideTips();
445
- const element = getStreamContainer();
446
- if (element) {
447
- let box = element.querySelector('.msg-box');
448
- let msg;
449
- if (!box) {
450
- box = document.createElement('div');
451
- box.classList.add('msg-box');
452
- box.classList.add('msg-bot');
453
- if (name_header != '') {
454
- const name = document.createElement('div');
455
- name.classList.add('name-header');
456
- name.classList.add('name-bot');
457
- name.innerHTML = name_header;
458
- box.appendChild(name);
459
- }
460
- msg = document.createElement('div');
461
- msg.classList.add('msg');
462
- box.appendChild(msg);
463
- element.appendChild(box);
464
- } else {
465
- msg = box.querySelector('.msg');
466
- }
467
- if (msg) {
468
- msg.replaceChildren();
469
- msg.insertAdjacentHTML('afterbegin', sanitize(content));
470
- highlightCode(true, msg);
471
- scheduleScroll();
472
- }
473
- }
474
- }
475
467
  function nextStream() {
476
468
  hideTips();
477
469
  const element = els.appendOutput || document.getElementById('_append_output_');
@@ -502,7 +494,7 @@ class Body:
502
494
  const last = elements[elements.length - 1];
503
495
  const contentEl = last.querySelector('.content');
504
496
  if (contentEl) {
505
- contentEl.insertAdjacentHTML('beforeend', sanitize(content));
497
+ contentEl.insertAdjacentHTML('beforeend', content);
506
498
  }
507
499
  }
508
500
  }
@@ -514,7 +506,7 @@ class Body:
514
506
  const last = elements[elements.length - 1];
515
507
  const contentEl = last.querySelector('.content');
516
508
  if (contentEl) {
517
- contentEl.innerHTML = sanitize(content);
509
+ contentEl.innerHTML = content;
518
510
  }
519
511
  }
520
512
  }
@@ -589,7 +581,7 @@ class Body:
589
581
  element.classList.remove('hidden');
590
582
  element.classList.add('visible');
591
583
  }
592
- element.innerHTML = sanitize(content);
584
+ element.innerHTML = content;
593
585
  highlightCode(true, element);
594
586
  scheduleScroll();
595
587
  }
@@ -608,6 +600,7 @@ class Body:
608
600
  element.replaceChildren();
609
601
  element.classList.add('empty_list');
610
602
  }
603
+ resetEphemeralDomRefs();
611
604
  }
612
605
  function clearInput() {
613
606
  const element = els.appendInput || document.getElementById('_append_input_');
@@ -632,9 +625,11 @@ class Body:
632
625
  element.classList.add('hidden');
633
626
  setTimeout(function() {
634
627
  element.replaceChildren();
628
+ resetEphemeralDomRefs();
635
629
  }, 1000);
636
630
  } else {
637
631
  element.replaceChildren();
632
+ resetEphemeralDomRefs();
638
633
  }
639
634
  }
640
635
  }
@@ -869,7 +864,7 @@ class Body:
869
864
  }
870
865
  });
871
866
  });
872
- setTimeout(cycleTips, 20000);
867
+ setTimeout(cycleTips, 60000); // after 60 seconds
873
868
  </script>
874
869
  </head>
875
870
  <body """
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.11 00:00:00 #
9
+ # Updated Date: 2025.08.18 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -76,10 +76,10 @@ class Parser:
76
76
  self.md.parser.blockprocessors.deregister('code')
77
77
 
78
78
  def reset(self):
79
- """
80
- Reset parser
81
- """
79
+ """Reset parser"""
82
80
  self.block_idx = 1
81
+ if self.md is not None:
82
+ self.md.reset()
83
83
 
84
84
  def get_code_blocks(self):
85
85
  """
@@ -99,11 +99,12 @@ class Parser:
99
99
  # Replace sandbox paths with file paths
100
100
  return text.replace("](sandbox:", "](file://") if "](sandbox:" in text else text
101
101
 
102
- def parse(self, text: str) -> str:
102
+ def parse(self, text: str, reset: bool = True) -> str:
103
103
  """
104
104
  Parse markdown text
105
105
 
106
106
  :param text: markdown text
107
+ :param reset: reset parser state
107
108
  :return: html formatted text
108
109
  """
109
110
  self.init()
@@ -114,7 +115,9 @@ class Parser:
114
115
 
115
116
  text = self.prepare_paths(text)
116
117
  html = self.md.convert(text)
117
- self.md.reset()
118
+
119
+ if reset:
120
+ self.md.reset()
118
121
 
119
122
  soup = self._make_soup(html)
120
123
  self.strip_whitespace_lists(soup)
@@ -124,6 +127,8 @@ class Parser:
124
127
 
125
128
  result = str(soup)
126
129
  soup.decompose()
130
+ del soup
131
+
127
132
  return result
128
133
  except Exception:
129
134
  return text
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.12 19:00:00 #
9
+ # Updated Date: 2025.08.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import io
@@ -43,6 +43,9 @@ class PidData:
43
43
 
44
44
  @buffer.setter
45
45
  def buffer(self, value: str):
46
+ if value is None or value == "":
47
+ self._buffer.close()
48
+ self._buffer = io.StringIO()
46
49
  self._buffer.seek(0)
47
50
  self._buffer.truncate(0)
48
51
  if value:
@@ -57,6 +60,9 @@ class PidData:
57
60
 
58
61
  @live_buffer.setter
59
62
  def live_buffer(self, value: str):
63
+ if value is None or value == "":
64
+ self._live_buffer.close()
65
+ self._live_buffer = io.StringIO()
60
66
  self._live_buffer.seek(0)
61
67
  self._live_buffer.truncate(0)
62
68
  if value:
@@ -71,6 +77,9 @@ class PidData:
71
77
 
72
78
  @html.setter
73
79
  def html(self, value: str):
80
+ if value is None or value == "":
81
+ self._html.close()
82
+ self._html = io.StringIO()
74
83
  self._html.seek(0)
75
84
  self._html.truncate(0)
76
85
  if value:
@@ -85,6 +94,9 @@ class PidData:
85
94
 
86
95
  @document.setter
87
96
  def document(self, value: str):
97
+ if value is None or value == "":
98
+ self._document.close()
99
+ self._document = io.StringIO()
88
100
  self._document.seek(0)
89
101
  self._document.truncate(0)
90
102
  if value:
@@ -99,9 +111,12 @@ class PidData:
99
111
 
100
112
  :param all: If True, clear all data, otherwise only buffers
101
113
  """
102
- for buf in (self._html, self._document, self._buffer, self._live_buffer):
103
- buf.seek(0)
104
- buf.truncate(0)
114
+ for name in ("_buffer", "_live_buffer", "_html", "_document"):
115
+ try:
116
+ getattr(self, name).close()
117
+ except Exception:
118
+ pass
119
+ setattr(self, name, io.StringIO())
105
120
 
106
121
  if all:
107
122
  self.item = None