pygpt-net 2.6.47__py3-none-any.whl → 2.6.48__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/core/ctx/ctx.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.24 02:00:00 #
9
+ # Updated Date: 2025.09.15 22:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -1003,7 +1003,7 @@ class Ctx:
1003
1003
  for item in reversed(history_items):
1004
1004
  num = from_ctx(item, mode, model)
1005
1005
  new_total = tokens + num
1006
- if max_tokens > 0 and new_total > max_tokens:
1006
+ if 0 < max_tokens < new_total:
1007
1007
  break
1008
1008
  tokens = new_total
1009
1009
  context_tokens += num
@@ -1240,49 +1240,102 @@ class Ctx:
1240
1240
  return len(self.filters_labels) < num_all
1241
1241
 
1242
1242
  def load_meta(self):
1243
- """Load ctx list from provider"""
1244
- limit = 0
1243
+ """Load ctx list: pinned and grouped unlimited; ungrouped not pinned paginated directly in SQL."""
1244
+ # base package size (per page)
1245
+ base_limit = 0
1245
1246
  if self.window.core.config.has('ctx.records.limit'):
1246
- limit = int(self.window.core.config.get('ctx.records.limit') or 0)
1247
-
1248
- if "is_important" not in self.filters and "indexed_ts" not in self.filters:
1249
- filters_pinned = self.get_parsed_filters()
1250
- filters_pinned['is_important'] = {
1251
- "mode": "=",
1252
- "value": 1,
1253
- }
1254
- meta_pinned = self.provider.get_meta(
1247
+ try:
1248
+ base_limit = int(self.window.core.config.get('ctx.records.limit') or 0)
1249
+ except Exception:
1250
+ base_limit = 0
1251
+
1252
+ # total loaded (persisted); fallback to base when missing
1253
+ try:
1254
+ loaded_total = int(self.window.core.config.get('ctx.records.limit.total') or 0)
1255
+ except Exception:
1256
+ loaded_total = 0
1257
+ if loaded_total <= 0:
1258
+ loaded_total = base_limit
1259
+
1260
+ # Common filters (labels etc.)
1261
+ common_filters = self.get_parsed_filters()
1262
+
1263
+ # If explicit filters target a narrow subset (legacy path), keep old behavior
1264
+ if "is_important" in self.filters or "indexed_ts" in self.filters:
1265
+ limit = 0 if base_limit == 0 else loaded_total
1266
+ self.meta = self.provider.get_meta(
1255
1267
  search_string=self.search_string,
1256
1268
  order_by='updated_ts',
1257
1269
  order_direction='DESC',
1258
- limit=0,
1259
- filters=filters_pinned,
1270
+ limit=limit,
1271
+ offset=0,
1272
+ filters=common_filters,
1260
1273
  search_content=self.is_search_content(),
1261
1274
  )
1275
+ return
1262
1276
 
1263
- filters = self.get_parsed_filters()
1264
- meta_unpinned = self.provider.get_meta(
1277
+ # 1) Pinned (important) – unlimited
1278
+ filters_pinned = self.get_parsed_filters()
1279
+ filters_pinned['is_important'] = {"mode": "=", "value": 1}
1280
+ meta_pinned = self.provider.get_meta(
1281
+ search_string=self.search_string,
1282
+ order_by='updated_ts',
1283
+ order_direction='DESC',
1284
+ limit=0,
1285
+ offset=0,
1286
+ filters=filters_pinned,
1287
+ search_content=self.is_search_content(),
1288
+ )
1289
+
1290
+ # 2) Grouped – unlimited
1291
+ filters_grouped = self.get_parsed_filters()
1292
+ filters_grouped['group_id'] = {"mode": ">", "value": 0}
1293
+ meta_grouped = self.provider.get_meta(
1294
+ search_string=self.search_string,
1295
+ order_by='updated_ts',
1296
+ order_direction='DESC',
1297
+ limit=0,
1298
+ offset=0,
1299
+ filters=filters_grouped,
1300
+ search_content=self.is_search_content(),
1301
+ )
1302
+
1303
+ # 3) Ungrouped & not pinned – paginate directly in SQL
1304
+ # If base_limit == 0 -> unlimited (no paging)
1305
+ filters_ungrp = self.get_parsed_filters()
1306
+ filters_ungrp['is_important'] = {"mode": "=", "value": 0}
1307
+ filters_ungrp['group_id'] = {"mode": "NULL_OR_ZERO", "value": 0} # special mode handled in Storage
1308
+
1309
+ if base_limit <= 0:
1310
+ meta_ungrouped = self.provider.get_meta(
1265
1311
  search_string=self.search_string,
1266
1312
  order_by='updated_ts',
1267
1313
  order_direction='DESC',
1268
- limit=limit,
1269
- filters=filters,
1314
+ limit=0, # unlimited
1315
+ offset=0,
1316
+ filters=filters_ungrp,
1270
1317
  search_content=self.is_search_content(),
1271
1318
  )
1272
-
1273
- self.meta = {**meta_pinned, **meta_unpinned}
1274
-
1275
1319
  else:
1276
- filters = self.get_parsed_filters()
1277
- self.meta = self.provider.get_meta(
1320
+ # Always take the top-N ungrouped newest items directly from DB
1321
+ take = max(0, int(loaded_total or 0))
1322
+ meta_ungrouped = self.provider.get_meta(
1278
1323
  search_string=self.search_string,
1279
1324
  order_by='updated_ts',
1280
1325
  order_direction='DESC',
1281
- limit=limit,
1282
- filters=filters,
1326
+ limit=take,
1327
+ offset=0,
1328
+ filters=filters_ungrp,
1283
1329
  search_content=self.is_search_content(),
1284
1330
  )
1285
1331
 
1332
+ # Compose final dict with deterministic order: pinned -> grouped -> ungrouped
1333
+ combined = {}
1334
+ combined.update(meta_pinned)
1335
+ combined.update(meta_grouped)
1336
+ combined.update(meta_ungrouped)
1337
+ self.meta = combined
1338
+
1286
1339
  def load_tmp_meta(self, meta_id: int):
1287
1340
  """
1288
1341
  Load tmp meta
@@ -13,7 +13,9 @@ import uuid
13
13
  from datetime import datetime
14
14
  from typing import Optional, Any, Dict, Tuple, Union
15
15
 
16
+ from PySide6.QtCore import QUrl
16
17
  from PySide6.QtGui import QIcon
18
+ from PySide6.QtWebEngineCore import QWebEnginePage
17
19
  from PySide6.QtWidgets import QVBoxLayout, QWidget, QLayout
18
20
 
19
21
  from pygpt_net.ui.widget.tabs.body import TabBody
@@ -234,20 +236,38 @@ class Tabs:
234
236
  tab.icon = self.icons[tab.type]
235
237
 
236
238
  if tab.type == Tab.TAB_CHAT: # chat
237
- self.add_chat(tab)
238
- self.window.core.ctx.output.mapping[tab.pid] = tab.data_id # restore pid => meta.id mapping
239
- self.window.core.ctx.output.last_pids[tab.data_id] = tab.pid
240
- self.window.core.ctx.output.last_pid = tab.pid
239
+ try:
240
+ self.add_chat(tab)
241
+ self.window.core.ctx.output.mapping[tab.pid] = tab.data_id # restore pid => meta.id mapping
242
+ self.window.core.ctx.output.last_pids[tab.data_id] = tab.pid
243
+ self.window.core.ctx.output.last_pid = tab.pid
244
+ except Exception as e:
245
+ print("Error restoring chat tab:", e)
241
246
  elif tab.type == Tab.TAB_NOTEPAD: # notepad
242
- self.add_notepad(tab)
247
+ try:
248
+ self.add_notepad(tab)
249
+ except Exception as e:
250
+ print("Error restoring notepad tab:", e)
243
251
  elif tab.type == Tab.TAB_FILES: # files
244
- self.add_tool_explorer(tab)
252
+ try:
253
+ self.add_tool_explorer(tab)
254
+ except Exception as e:
255
+ print("Error restoring explorer tab:", e)
245
256
  elif tab.type == Tab.TAB_TOOL_PAINTER: # painter
246
- self.add_tool_painter(tab)
257
+ try:
258
+ self.add_tool_painter(tab)
259
+ except Exception as e:
260
+ print("Error restoring painter tab:", e)
247
261
  elif tab.type == Tab.TAB_TOOL_CALENDAR: # calendar
248
- self.add_tool_calendar(tab)
262
+ try:
263
+ self.add_tool_calendar(tab)
264
+ except Exception as e:
265
+ print("Error restoring calendar tab:", e)
249
266
  elif tab.type == Tab.TAB_TOOL: # custom tools, id 100+
250
- self.add_tool(tab)
267
+ try:
268
+ self.add_tool(tab)
269
+ except Exception as e:
270
+ print("Error restoring tool tab:", e)
251
271
 
252
272
  self.pids[tab.pid] = tab
253
273
  self.last_pid = self.get_max_pid()
@@ -277,8 +297,35 @@ class Tabs:
277
297
  tab = self.get_tab_by_pid(pid)
278
298
  if tab is None:
279
299
  return
300
+ try:
301
+ if tab.type == Tab.TAB_CHAT:
302
+ node = self.window.ui.nodes['output'].get(tab.pid)
303
+ if node:
304
+ node.hide()
305
+ p = node.page()
306
+ p.triggerAction(QWebEnginePage.Stop)
307
+ p.setUrl(QUrl("about:blank"))
308
+ p.history().clear()
309
+ p.setLifecycleState(QWebEnginePage.LifecycleState.Discarded)
310
+ tab.delete_ref(node)
311
+ layout = tab.child.layout()
312
+ layout.removeWidget(node)
313
+ self.window.ui.nodes['output'].pop(pid, None)
314
+ node.on_delete()
315
+ node_plain = self.window.ui.nodes['output_plain'].get(tab.pid)
316
+ if node_plain:
317
+ tab.delete_ref(node_plain)
318
+ layout = tab.child.layout()
319
+ layout.removeWidget(node_plain)
320
+ self.window.ui.nodes['output_plain'].pop(pid, None)
321
+ node_plain.on_delete()
322
+
323
+ if tab.type in (Tab.TAB_CHAT, Tab.TAB_NOTEPAD, Tab.TAB_TOOL):
324
+ tab.cleanup() # unload assigned data from memory
325
+
326
+ except Exception as e:
327
+ print(f"Error unloading tab {pid}: {e}")
280
328
 
281
- tab.cleanup() # unload assigned data from memory
282
329
  column_idx = tab.column_idx
283
330
  self.window.ui.layout.get_tabs_by_idx(column_idx).removeTab(tab.idx)
284
331
  del self.pids[pid]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.47",
4
- "app.version": "2.6.47",
3
+ "version": "2.6.48",
4
+ "app.version": "2.6.48",
5
5
  "updated_at": "2025-09-15T00:00:00"
6
6
  },
7
7
  "access.audio.event.speech": false,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.47",
4
- "app.version": "2.6.47",
3
+ "version": "2.6.48",
4
+ "app.version": "2.6.48",
5
5
  "updated_at": "2025-09-15T08:03:34"
6
6
  },
7
7
  "items": {