pygpt-net 2.6.46__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/CHANGELOG.txt +9 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/app.py +5 -0
- pygpt_net/app_core.py +39 -39
- pygpt_net/controller/__init__.py +72 -64
- pygpt_net/controller/audio/audio.py +2 -0
- pygpt_net/controller/chat/text.py +2 -1
- pygpt_net/controller/ctx/common.py +0 -7
- pygpt_net/controller/ctx/ctx.py +172 -6
- pygpt_net/controller/ctx/extra.py +3 -3
- pygpt_net/controller/notepad/notepad.py +0 -2
- pygpt_net/controller/settings/editor.py +3 -1
- pygpt_net/controller/theme/common.py +8 -2
- pygpt_net/controller/theme/theme.py +5 -5
- pygpt_net/controller/ui/tabs.py +9 -2
- pygpt_net/core/ctx/ctx.py +79 -26
- pygpt_net/core/render/web/renderer.py +2 -0
- pygpt_net/core/tabs/tab.py +2 -2
- pygpt_net/core/tabs/tabs.py +57 -10
- pygpt_net/data/config/config.json +2 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/data/css/web-blocks.css +256 -270
- pygpt_net/data/css/web-chatgpt.css +276 -301
- pygpt_net/data/css/web-chatgpt_wide.css +286 -294
- pygpt_net/data/js/app.js +1218 -1186
- pygpt_net/js_rc.py +14192 -14641
- pygpt_net/provider/core/config/patch.py +9 -0
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +19 -5
- pygpt_net/ui/__init__.py +9 -14
- pygpt_net/ui/layout/chat/chat.py +2 -2
- pygpt_net/ui/layout/ctx/ctx_list.py +71 -1
- pygpt_net/ui/widget/lists/base.py +32 -1
- pygpt_net/ui/widget/lists/context.py +45 -2
- pygpt_net/ui/widget/tabs/body.py +11 -3
- pygpt_net/ui/widget/textarea/notepad.py +0 -4
- {pygpt_net-2.6.46.dist-info → pygpt_net-2.6.48.dist-info}/METADATA +11 -2
- {pygpt_net-2.6.46.dist-info → pygpt_net-2.6.48.dist-info}/RECORD +40 -40
- {pygpt_net-2.6.46.dist-info → pygpt_net-2.6.48.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.46.dist-info → pygpt_net-2.6.48.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.46.dist-info → pygpt_net-2.6.48.dist-info}/entry_points.txt +0 -0
pygpt_net/controller/ui/tabs.py
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
9
|
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
|
|
11
|
+
import gc
|
|
12
12
|
from typing import Any, Optional, Tuple
|
|
13
13
|
|
|
14
|
-
from PySide6.QtCore import QTimer
|
|
14
|
+
from PySide6.QtCore import QTimer, QUrl
|
|
15
|
+
from PySide6.QtWebEngineCore import QWebEnginePage
|
|
15
16
|
|
|
16
17
|
from pygpt_net.core.events import AppEvent, RenderEvent
|
|
17
18
|
from pygpt_net.core.tabs.tab import Tab
|
|
@@ -133,9 +134,15 @@ class Tabs:
|
|
|
133
134
|
|
|
134
135
|
def reload(self):
|
|
135
136
|
"""Reload tabs"""
|
|
137
|
+
columns = self.window.ui.layout.columns
|
|
138
|
+
for col in columns:
|
|
139
|
+
col.setUpdatesEnabled(False)
|
|
140
|
+
self.window.core.tabs.remove_all()
|
|
136
141
|
self.window.core.tabs.reload()
|
|
137
142
|
self.window.dispatch(RenderEvent(RenderEvent.PREPARE))
|
|
138
143
|
self.debug()
|
|
144
|
+
for col in columns:
|
|
145
|
+
col.setUpdatesEnabled(True)
|
|
139
146
|
|
|
140
147
|
def reload_after(self):
|
|
141
148
|
"""Reload tabs after"""
|
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.
|
|
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
|
|
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
|
|
1244
|
-
|
|
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
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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=
|
|
1259
|
-
|
|
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
|
-
|
|
1264
|
-
|
|
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=
|
|
1269
|
-
|
|
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
|
-
|
|
1277
|
-
|
|
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=
|
|
1282
|
-
|
|
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
|
pygpt_net/core/tabs/tab.py
CHANGED
|
@@ -109,7 +109,7 @@ class Tab:
|
|
|
109
109
|
def cleanup(self):
|
|
110
110
|
"""Clean up on delete"""
|
|
111
111
|
try:
|
|
112
|
-
if self.on_delete:
|
|
112
|
+
if self.on_delete and callable(self.on_delete):
|
|
113
113
|
self.on_delete(self)
|
|
114
114
|
self.delete_refs()
|
|
115
115
|
except Exception as e:
|
|
@@ -131,7 +131,7 @@ class Tab:
|
|
|
131
131
|
Delete all references to widgets in this tab
|
|
132
132
|
"""
|
|
133
133
|
# cleanup children
|
|
134
|
-
if self.child:
|
|
134
|
+
if self.child and hasattr(self.child, 'cleanup'):
|
|
135
135
|
self.child.cleanup()
|
|
136
136
|
|
|
137
137
|
# cleanup parent
|
pygpt_net/core/tabs/tabs.py
CHANGED
|
@@ -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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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]
|