pygpt-net 2.6.11__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.
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,7 @@
1
+ 2.6.12 (2025-08-19)
2
+
3
+ - Optimized web renderer memory cleanup.
4
+
1
5
  2.6.11 (2025-08-18)
2
6
 
3
7
  - Added the ability to close the dialog window with the Esc key.
pygpt_net/__init__.py CHANGED
@@ -6,15 +6,15 @@
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.18 00:00:00 #
9
+ # Updated Date: 2025.08.19 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  __author__ = "Marcin Szczygliński"
13
13
  __copyright__ = "Copyright 2025, Marcin Szczygliński"
14
14
  __credits__ = ["Marcin Szczygliński"]
15
15
  __license__ = "MIT"
16
- __version__ = "2.6.11"
17
- __build__ = "2025-08-18"
16
+ __version__ = "2.6.12"
17
+ __build__ = "2025-08-19"
18
18
  __maintainer__ = "Marcin Szczygliński"
19
19
  __github__ = "https://github.com/szczyglis-dev/py-gpt"
20
20
  __report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
pygpt_net/app.py CHANGED
@@ -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
@@ -25,6 +25,12 @@ if platform.system() == 'Windows':
25
25
  # enable debug logging
26
26
  # os.environ["QT_LOGGING_RULES"] = "*.debug=true"
27
27
  # os.environ["QTWEBENGINE_REMOTE_DEBUGGING"] = "9222"
28
+ os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = (
29
+ "--renderer-process-limit=1 "
30
+ "--process-per-site "
31
+ "--enable-precise-memory-info "
32
+ "--js-flags=--expose-gc"
33
+ )
28
34
 
29
35
  _original_open = builtins.open
30
36
 
@@ -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.18 01:00:00 #
9
+ # Updated Date: 2025.08.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any
@@ -238,7 +238,7 @@ class Response:
238
238
  self.window.core.ctx.update_item(ctx)
239
239
 
240
240
  # update ctx meta
241
- if mode in [MODE_AGENT_LLAMA, MODE_AGENT_OPENAI] and ctx.meta is not None:
241
+ if mode in (MODE_AGENT_LLAMA, MODE_AGENT_OPENAI) and ctx.meta is not None:
242
242
  self.window.core.ctx.replace(ctx.meta)
243
243
  self.window.core.ctx.save(ctx.meta.id)
244
244
  # update preset if exists
@@ -269,7 +269,7 @@ class Response:
269
269
  self.window.dispatch(event)
270
270
 
271
271
  # if continue reasoning
272
- if global_mode not in [MODE_AGENT_LLAMA, MODE_AGENT_OPENAI]:
272
+ if global_mode not in (MODE_AGENT_LLAMA, MODE_AGENT_OPENAI):
273
273
  return # no agent mode, nothing to do
274
274
 
275
275
  if ctx.extra is None or (type(ctx.extra) == dict and "agent_finish" not in ctx.extra):
@@ -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.18 01:00:00 #
9
+ # Updated Date: 2025.08.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import base64
@@ -65,6 +65,7 @@ class StreamWorker(QRunnable):
65
65
  self.signals = WorkerSignals()
66
66
  self.ctx = ctx
67
67
  self.window = window
68
+ self.stream = None
68
69
 
69
70
  @Slot()
70
71
  def run(self):
@@ -90,8 +91,7 @@ class StreamWorker(QRunnable):
90
91
  force_func_call = False
91
92
  stopped = False
92
93
  chunk_type: ChunkType = "raw"
93
- generator = ctx.stream
94
- ctx.stream = None
94
+ generator = self.stream
95
95
 
96
96
  base_data = {
97
97
  "meta": ctx.meta,
@@ -363,6 +363,7 @@ class StreamWorker(QRunnable):
363
363
  finally:
364
364
  output = "".join(output_parts)
365
365
  output_parts.clear()
366
+ del output_parts
366
367
 
367
368
  if has_unclosed_code_tag(output):
368
369
  output += "\n```"
@@ -374,11 +375,14 @@ class StreamWorker(QRunnable):
374
375
  pass
375
376
 
376
377
  del generator
378
+ self.stream = None
377
379
 
378
380
  ctx.output = output
379
381
  ctx.set_tokens(ctx.input_tokens, output_tokens)
380
382
  core.ctx.update_item(ctx)
381
383
 
384
+ output = None
385
+
382
386
  if files and not stopped:
383
387
  core.debug.info("[chat] Container files found, downloading...")
384
388
  try:
@@ -464,11 +468,13 @@ class Stream:
464
468
  self.extra = extra if extra is not None else {}
465
469
 
466
470
  worker = StreamWorker(ctx, self.window)
467
- self.worker = worker
468
471
 
472
+ worker.stream = ctx.stream
469
473
  worker.signals.eventReady.connect(self.handleEvent)
470
474
  worker.signals.errorOccurred.connect(self.handleError)
471
475
  worker.signals.end.connect(self.handleEnd)
476
+ ctx.stream = None
477
+ self.worker = worker
472
478
 
473
479
  self.window.core.debug.info("[chat] Stream begin...")
474
480
  self.window.threadpool.start(worker)
pygpt_net/core/ctx/bag.py CHANGED
@@ -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.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import List
@@ -44,6 +44,7 @@ class Bag:
44
44
  def clear_items(self):
45
45
  """Clear items"""
46
46
  self.items.clear()
47
+ self.items = []
47
48
 
48
49
  def count_items(self) -> int:
49
50
  """
@@ -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.18 01:00:00 #
9
+ # Updated Date: 2025.08.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import gc
@@ -338,8 +338,17 @@ class Debug:
338
338
  :param label: label for memory usage
339
339
  :return: formatted memory usage string
340
340
  """
341
- mem_mb = self._process.memory_info().rss / (1024 * 1024)
342
- data = f"Memory Usage: {mem_mb:.2f} MB"
341
+ rss_mb = self._process.memory_info().rss / (1024 * 1024)
342
+ uss_mb = getattr(self._process.memory_full_info(), "uss", 0) / 1024 / 1024
343
+ data = f"RSS={rss_mb:.0f} MB USS={uss_mb:.0f} MB"
344
+
345
+ children_parts = []
346
+ for c in self._process.children(recursive=True):
347
+ children_parts.append(
348
+ f"{c.pid} {c.name()} {round(c.memory_info().rss / 1024 / 1024)} MB"
349
+ )
350
+ if children_parts:
351
+ data += "\n" + "\n".join(children_parts)
343
352
  print(f"[{label}] {data}")
344
353
  return data
345
354
 
@@ -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.18 01:00:00 #
9
+ # Updated Date: 2025.08.19 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -42,9 +42,7 @@ class Body:
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
44
  <script>
45
-
46
45
  const DEBUG_MODE = false;
47
-
48
46
  let scrollTimeout = null;
49
47
  let prevScroll = 0;
50
48
  let bridge;
@@ -69,6 +67,23 @@ class Body:
69
67
  let pendingHighlightMath = false;
70
68
  let scrollScheduled = false;
71
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
+ }
86
+
72
87
  history.scrollRestoration = "manual";
73
88
  document.addEventListener('keydown', function(event) {
74
89
  if (event.ctrlKey && event.key === 'f') {
@@ -137,6 +152,7 @@ class Body:
137
152
  }
138
153
  function hideTips() {
139
154
  if (tips_hidden) return;
155
+ stopTipsTimers();
140
156
  const t = els.tips || document.getElementById('tips');
141
157
  if (t) t.style.display = 'none';
142
158
  tips_hidden = true;
@@ -151,21 +167,23 @@ class Body:
151
167
  function cycleTips() {
152
168
  if (tips_hidden) return;
153
169
  if (tips.length === 0) return;
154
- let tipContainer = els.tips || document.getElementById('tips');
155
170
  let currentTip = 0;
156
171
  function showNextTip() {
157
172
  if (tips_hidden) return;
173
+ const tipContainer = els.tips || document.getElementById('tips');
174
+ if (!tipContainer) return;
158
175
  tipContainer.innerHTML = tips[currentTip];
159
176
  tipContainer.classList.add('visible');
160
- setTimeout(function() {
177
+ tipsTimers.push(setTimeout(function() {
161
178
  if (tips_hidden) return;
162
179
  tipContainer.classList.remove('visible');
163
- setTimeout(function(){
180
+ tipsTimers.push(setTimeout(function(){
164
181
  currentTip = (currentTip + 1) % tips.length;
165
182
  showNextTip();
166
- }, 1000);
167
- }, 15000);
183
+ }, 1000));
184
+ }, 15000));
168
185
  }
186
+ stopTipsTimers();
169
187
  showNextTip();
170
188
  }
171
189
  function renderMath(root) {
@@ -224,7 +242,7 @@ class Body:
224
242
  }
225
243
  }
226
244
  function getStreamContainer() {
227
- if (domOutputStream && document.body.contains(domOutputStream)) {
245
+ if (domOutputStream && domOutputStream.isConnected) {
228
246
  return domOutputStream;
229
247
  }
230
248
  let element = els.appendOutput || document.getElementById('_append_output_');
@@ -247,6 +265,24 @@ class Body:
247
265
  scheduleScroll();
248
266
  }
249
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
+ }
250
286
  function appendExtra(id, content) {
251
287
  hideTips();
252
288
  prevScroll = 0;
@@ -270,6 +306,7 @@ class Body:
270
306
  if (element) {
271
307
  element.remove();
272
308
  }
309
+ resetEphemeralDomRefs();
273
310
  highlightCode();
274
311
  scheduleScroll();
275
312
  }
@@ -280,13 +317,14 @@ class Body:
280
317
  const elements = container.querySelectorAll('.msg-box');
281
318
  let remove = false;
282
319
  elements.forEach(function(element) {
283
- if (element.id.endsWith('-' + id)) {
320
+ if (element.id && element.id.endsWith('-' + id)) {
284
321
  remove = true;
285
322
  }
286
323
  if (remove) {
287
324
  element.remove();
288
325
  }
289
326
  });
327
+ resetEphemeralDomRefs();
290
328
  highlightCode(true, container);
291
329
  scheduleScroll();
292
330
  }
@@ -347,6 +385,7 @@ class Body:
347
385
  }
348
386
  }
349
387
  function appendStream(name_header, content, chunk, replace = false, is_code_block = false) {
388
+ dropIfDetached(); // clear references to detached elements
350
389
  hideTips();
351
390
  if (DEBUG_MODE) {
352
391
  log("APPEND CHUNK: {" + chunk + "}, CONTENT: {"+content+"}, replace: " + replace + ", is_code_block: " + is_code_block);
@@ -561,6 +600,7 @@ class Body:
561
600
  element.replaceChildren();
562
601
  element.classList.add('empty_list');
563
602
  }
603
+ resetEphemeralDomRefs();
564
604
  }
565
605
  function clearInput() {
566
606
  const element = els.appendInput || document.getElementById('_append_input_');
@@ -585,9 +625,11 @@ class Body:
585
625
  element.classList.add('hidden');
586
626
  setTimeout(function() {
587
627
  element.replaceChildren();
628
+ resetEphemeralDomRefs();
588
629
  }, 1000);
589
630
  } else {
590
631
  element.replaceChildren();
632
+ resetEphemeralDomRefs();
591
633
  }
592
634
  }
593
635
  }
@@ -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