pygpt-net 2.6.47__py3-none-any.whl → 2.6.49__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 +8 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app_core.py +39 -39
- pygpt_net/controller/__init__.py +72 -62
- pygpt_net/controller/ctx/common.py +0 -7
- pygpt_net/controller/ctx/ctx.py +176 -8
- pygpt_net/controller/ctx/extra.py +3 -3
- pygpt_net/controller/settings/editor.py +3 -1
- pygpt_net/controller/theme/common.py +8 -2
- pygpt_net/controller/ui/tabs.py +10 -43
- pygpt_net/core/ctx/ctx.py +79 -26
- pygpt_net/core/render/web/renderer.py +4 -10
- pygpt_net/core/tabs/tabs.py +50 -11
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- 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/provider/core/config/patch.py +9 -0
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +19 -5
- pygpt_net/tools/code_interpreter/ui/html.py +176 -31
- pygpt_net/tools/code_interpreter/ui/widgets.py +1 -4
- pygpt_net/tools/html_canvas/ui/widgets.py +2 -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 +23 -1
- pygpt_net/ui/widget/textarea/web.py +85 -45
- {pygpt_net-2.6.47.dist-info → pygpt_net-2.6.49.dist-info}/METADATA +10 -2
- {pygpt_net-2.6.47.dist-info → pygpt_net-2.6.49.dist-info}/RECORD +35 -35
- {pygpt_net-2.6.47.dist-info → pygpt_net-2.6.49.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.47.dist-info → pygpt_net-2.6.49.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.47.dist-info → pygpt_net-2.6.49.dist-info}/entry_points.txt +0 -0
pygpt_net/controller/ui/tabs.py
CHANGED
|
@@ -6,14 +6,12 @@
|
|
|
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.16 02:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
import gc
|
|
12
|
-
from typing import Any, Optional, Tuple
|
|
13
11
|
|
|
14
|
-
from
|
|
15
|
-
from PySide6.QtWebEngineCore import QWebEnginePage
|
|
12
|
+
from typing import Any, Optional, Tuple
|
|
16
13
|
|
|
14
|
+
from PySide6.QtCore import QTimer
|
|
17
15
|
from pygpt_net.core.events import AppEvent, RenderEvent
|
|
18
16
|
from pygpt_net.core.tabs.tab import Tab
|
|
19
17
|
from pygpt_net.item.ctx import CtxMeta
|
|
@@ -134,46 +132,15 @@ class Tabs:
|
|
|
134
132
|
|
|
135
133
|
def reload(self):
|
|
136
134
|
"""Reload tabs"""
|
|
137
|
-
self.
|
|
135
|
+
columns = self.window.ui.layout.columns
|
|
136
|
+
for col in columns:
|
|
137
|
+
col.setUpdatesEnabled(False)
|
|
138
|
+
self.window.core.tabs.remove_all()
|
|
138
139
|
self.window.core.tabs.reload()
|
|
139
140
|
self.window.dispatch(RenderEvent(RenderEvent.PREPARE))
|
|
140
141
|
self.debug()
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"""Unload current tabs from memory"""
|
|
144
|
-
tabs = self.window.core.tabs.pids
|
|
145
|
-
for pid in tabs:
|
|
146
|
-
tab = self.window.core.tabs.get_tab_by_pid(pid)
|
|
147
|
-
if tab is not None:
|
|
148
|
-
try:
|
|
149
|
-
if tab.type == Tab.TAB_CHAT:
|
|
150
|
-
node = self.window.ui.nodes['output'].get(tab.pid)
|
|
151
|
-
if node:
|
|
152
|
-
node.hide()
|
|
153
|
-
p = node.page()
|
|
154
|
-
p.triggerAction(QWebEnginePage.Stop)
|
|
155
|
-
p.setUrl(QUrl("about:blank"))
|
|
156
|
-
p.history().clear()
|
|
157
|
-
p.setLifecycleState(QWebEnginePage.LifecycleState.Discarded)
|
|
158
|
-
tab.delete_ref(node)
|
|
159
|
-
layout = tab.child.layout()
|
|
160
|
-
layout.removeWidget(node)
|
|
161
|
-
self.window.ui.nodes['output'].pop(pid, None)
|
|
162
|
-
node.on_delete()
|
|
163
|
-
node_plain = self.window.ui.nodes['output_plain'].get(tab.pid)
|
|
164
|
-
if node_plain:
|
|
165
|
-
tab.delete_ref(node_plain)
|
|
166
|
-
layout = tab.child.layout()
|
|
167
|
-
layout.removeWidget(node_plain)
|
|
168
|
-
self.window.ui.nodes['output_plain'].pop(pid, None)
|
|
169
|
-
node_plain.on_delete()
|
|
170
|
-
tab.cleanup()
|
|
171
|
-
except Exception as e:
|
|
172
|
-
print(f"Error unloading tab {pid}: {e}")
|
|
173
|
-
try:
|
|
174
|
-
gc.collect()
|
|
175
|
-
except Exception:
|
|
176
|
-
pass
|
|
142
|
+
for col in columns:
|
|
143
|
+
col.setUpdatesEnabled(True)
|
|
177
144
|
|
|
178
145
|
def reload_after(self):
|
|
179
146
|
"""Reload tabs after"""
|
|
@@ -394,7 +361,7 @@ class Tabs:
|
|
|
394
361
|
if tab.type == Tab.TAB_CHAT and self.column_idx == 1 and not getattr(tab, "loaded", False):
|
|
395
362
|
meta = self.window.core.ctx.get_meta_by_id(tab.data_id)
|
|
396
363
|
if meta is not None:
|
|
397
|
-
self.window.controller.ctx.load(meta.id)
|
|
364
|
+
self.window.controller.ctx.load(meta.id, no_fresh=True)
|
|
398
365
|
tab.loaded = True
|
|
399
366
|
|
|
400
367
|
current_ctx = self.window.core.ctx.get_current()
|
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
|
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.16 02:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import gc
|
|
@@ -1274,12 +1274,7 @@ class Renderer(BaseRenderer):
|
|
|
1274
1274
|
pass
|
|
1275
1275
|
self._bridge_ready[pid] = False
|
|
1276
1276
|
self._pending_nodes[pid] = []
|
|
1277
|
-
node.
|
|
1278
|
-
p = node.page()
|
|
1279
|
-
p.triggerAction(QWebEnginePage.Stop)
|
|
1280
|
-
p.setUrl(QUrl("about:blank"))
|
|
1281
|
-
p.history().clear()
|
|
1282
|
-
p.setLifecycleState(QWebEnginePage.LifecycleState.Discarded)
|
|
1277
|
+
node.unload() # unload web page
|
|
1283
1278
|
self._stream_reset(pid)
|
|
1284
1279
|
self.pids[pid].clear(all=True)
|
|
1285
1280
|
self.pids[pid].loaded = False
|
|
@@ -1294,9 +1289,8 @@ class Renderer(BaseRenderer):
|
|
|
1294
1289
|
:param meta: context meta
|
|
1295
1290
|
"""
|
|
1296
1291
|
tab = node.get_tab()
|
|
1297
|
-
tab.delete_ref(node)
|
|
1298
1292
|
layout = tab.child.layout()
|
|
1299
|
-
|
|
1293
|
+
tab.child.remove_widget(node)
|
|
1300
1294
|
self.window.ui.nodes['output'].pop(tab.pid, None)
|
|
1301
1295
|
|
|
1302
1296
|
node.on_delete()
|
|
@@ -1307,7 +1301,7 @@ class Renderer(BaseRenderer):
|
|
|
1307
1301
|
view.signals.save_as.connect(self.window.controller.chat.render.handle_save_as)
|
|
1308
1302
|
view.signals.audio_read.connect(self.window.controller.chat.render.handle_audio_read)
|
|
1309
1303
|
|
|
1310
|
-
layout.addWidget(view)
|
|
1304
|
+
layout.addWidget(view) # tab body layout
|
|
1311
1305
|
view.setVisible(True)
|
|
1312
1306
|
self.window.ui.nodes['output'][tab.pid] = view
|
|
1313
1307
|
try:
|
pygpt_net/core/tabs/tabs.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.16 02:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import uuid
|
|
@@ -234,20 +234,38 @@ class Tabs:
|
|
|
234
234
|
tab.icon = self.icons[tab.type]
|
|
235
235
|
|
|
236
236
|
if tab.type == Tab.TAB_CHAT: # chat
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
try:
|
|
238
|
+
self.add_chat(tab)
|
|
239
|
+
self.window.core.ctx.output.mapping[tab.pid] = tab.data_id # restore pid => meta.id mapping
|
|
240
|
+
self.window.core.ctx.output.last_pids[tab.data_id] = tab.pid
|
|
241
|
+
self.window.core.ctx.output.last_pid = tab.pid
|
|
242
|
+
except Exception as e:
|
|
243
|
+
print("Error restoring chat tab:", e)
|
|
241
244
|
elif tab.type == Tab.TAB_NOTEPAD: # notepad
|
|
242
|
-
|
|
245
|
+
try:
|
|
246
|
+
self.add_notepad(tab)
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print("Error restoring notepad tab:", e)
|
|
243
249
|
elif tab.type == Tab.TAB_FILES: # files
|
|
244
|
-
|
|
250
|
+
try:
|
|
251
|
+
self.add_tool_explorer(tab)
|
|
252
|
+
except Exception as e:
|
|
253
|
+
print("Error restoring explorer tab:", e)
|
|
245
254
|
elif tab.type == Tab.TAB_TOOL_PAINTER: # painter
|
|
246
|
-
|
|
255
|
+
try:
|
|
256
|
+
self.add_tool_painter(tab)
|
|
257
|
+
except Exception as e:
|
|
258
|
+
print("Error restoring painter tab:", e)
|
|
247
259
|
elif tab.type == Tab.TAB_TOOL_CALENDAR: # calendar
|
|
248
|
-
|
|
260
|
+
try:
|
|
261
|
+
self.add_tool_calendar(tab)
|
|
262
|
+
except Exception as e:
|
|
263
|
+
print("Error restoring calendar tab:", e)
|
|
249
264
|
elif tab.type == Tab.TAB_TOOL: # custom tools, id 100+
|
|
250
|
-
|
|
265
|
+
try:
|
|
266
|
+
self.add_tool(tab)
|
|
267
|
+
except Exception as e:
|
|
268
|
+
print("Error restoring tool tab:", e)
|
|
251
269
|
|
|
252
270
|
self.pids[tab.pid] = tab
|
|
253
271
|
self.last_pid = self.get_max_pid()
|
|
@@ -277,8 +295,29 @@ class Tabs:
|
|
|
277
295
|
tab = self.get_tab_by_pid(pid)
|
|
278
296
|
if tab is None:
|
|
279
297
|
return
|
|
298
|
+
try:
|
|
299
|
+
if tab.type == Tab.TAB_CHAT:
|
|
300
|
+
node = self.window.ui.nodes['output'].get(tab.pid)
|
|
301
|
+
if node:
|
|
302
|
+
node.unload() # unload web page
|
|
303
|
+
tab.child.remove_widget(node)
|
|
304
|
+
self.window.ui.nodes['output'].pop(pid, None)
|
|
305
|
+
node.on_delete()
|
|
306
|
+
node_plain = self.window.ui.nodes['output_plain'].get(tab.pid)
|
|
307
|
+
if node_plain:
|
|
308
|
+
tab.child.remove_widget(node_plain)
|
|
309
|
+
self.window.ui.nodes['output_plain'].pop(pid, None)
|
|
310
|
+
node_plain.on_delete()
|
|
311
|
+
|
|
312
|
+
if tab.type in (Tab.TAB_CHAT, Tab.TAB_NOTEPAD, Tab.TAB_TOOL):
|
|
313
|
+
tab.cleanup() # unload assigned data from memory
|
|
314
|
+
|
|
315
|
+
# tab.delete_refs()
|
|
316
|
+
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print(f"Error unloading tab {pid}: {e}")
|
|
319
|
+
self.window.core.debug.log(e)
|
|
280
320
|
|
|
281
|
-
tab.cleanup() # unload assigned data from memory
|
|
282
321
|
column_idx = tab.column_idx
|
|
283
322
|
self.window.ui.layout.get_tabs_by_idx(column_idx).removeTab(tab.idx)
|
|
284
323
|
del self.pids[pid]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"app.version": "2.6.
|
|
5
|
-
"updated_at": "2025-09-
|
|
3
|
+
"version": "2.6.49",
|
|
4
|
+
"app.version": "2.6.49",
|
|
5
|
+
"updated_at": "2025-09-16T00:00:00"
|
|
6
6
|
},
|
|
7
7
|
"access.audio.event.speech": false,
|
|
8
8
|
"access.audio.event.speech.disabled": [],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"app.version": "2.6.
|
|
5
|
-
"updated_at": "2025-09-
|
|
3
|
+
"version": "2.6.49",
|
|
4
|
+
"app.version": "2.6.49",
|
|
5
|
+
"updated_at": "2025-09-16T08:03:34"
|
|
6
6
|
},
|
|
7
7
|
"items": {
|
|
8
8
|
"SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
|