pygpt-net 2.6.10__py3-none-any.whl → 2.6.11__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 +6 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/config.py +11 -11
- pygpt_net/controller/access/access.py +49 -2
- pygpt_net/controller/chat/attachment.py +13 -13
- pygpt_net/controller/chat/command.py +4 -4
- pygpt_net/controller/chat/common.py +9 -14
- pygpt_net/controller/chat/files.py +2 -2
- pygpt_net/controller/chat/input.py +4 -4
- pygpt_net/controller/chat/output.py +4 -4
- pygpt_net/controller/chat/render.py +11 -6
- pygpt_net/controller/chat/response.py +5 -5
- pygpt_net/controller/chat/stream.py +2 -3
- pygpt_net/controller/chat/text.py +15 -10
- pygpt_net/controller/command/command.py +7 -7
- pygpt_net/controller/ctx/ctx.py +9 -5
- pygpt_net/controller/debug/debug.py +2 -2
- pygpt_net/core/debug/debug.py +6 -1
- pygpt_net/core/dispatcher/dispatcher.py +5 -3
- pygpt_net/core/events/render.py +3 -0
- pygpt_net/core/render/base.py +4 -4
- pygpt_net/core/render/web/body.py +35 -82
- pygpt_net/core/render/web/parser.py +11 -6
- pygpt_net/core/render/web/renderer.py +71 -37
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/presets/agent_openai.json +1 -1
- pygpt_net/data/config/presets/agent_openai_assistant.json +1 -1
- pygpt_net/data/config/presets/agent_planner.json +1 -1
- pygpt_net/data/config/presets/agent_react.json +1 -1
- pygpt_net/ui/main.py +4 -2
- pygpt_net/ui/widget/textarea/web.py +17 -3
- {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.11.dist-info}/METADATA +8 -2
- {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.11.dist-info}/RECORD +37 -37
- {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.11.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.11.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.10.dist-info → pygpt_net-2.6.11.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.
|
|
9
|
+
# Updated Date: 2025.08.18 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import json
|
|
@@ -31,6 +31,7 @@ from pygpt_net.core.events import RenderEvent
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class Renderer(BaseRenderer):
|
|
34
|
+
|
|
34
35
|
NODE_INPUT = 0
|
|
35
36
|
NODE_OUTPUT = 1
|
|
36
37
|
ENDINGS_CODE = (
|
|
@@ -43,6 +44,7 @@ class Renderer(BaseRenderer):
|
|
|
43
44
|
"</ol>",
|
|
44
45
|
"</li>"
|
|
45
46
|
)
|
|
47
|
+
RE_AMP_LT_GT = re.compile(r'&(lt|gt);')
|
|
46
48
|
|
|
47
49
|
def __init__(self, window=None):
|
|
48
50
|
super(Renderer, self).__init__(window)
|
|
@@ -82,7 +84,7 @@ class Renderer(BaseRenderer):
|
|
|
82
84
|
self.parser.reset()
|
|
83
85
|
try:
|
|
84
86
|
node.page().runJavaScript("if (typeof window.prepare !== 'undefined') prepare();")
|
|
85
|
-
except Exception
|
|
87
|
+
except Exception:
|
|
86
88
|
pass
|
|
87
89
|
|
|
88
90
|
def on_page_loaded(
|
|
@@ -279,7 +281,7 @@ class Renderer(BaseRenderer):
|
|
|
279
281
|
self.prev_chunk_replace = False
|
|
280
282
|
try:
|
|
281
283
|
self.get_output_node(meta).page().runJavaScript("beginStream();")
|
|
282
|
-
except Exception
|
|
284
|
+
except Exception:
|
|
283
285
|
pass
|
|
284
286
|
|
|
285
287
|
def stream_end(
|
|
@@ -304,7 +306,7 @@ class Renderer(BaseRenderer):
|
|
|
304
306
|
self.pids[pid].clear()
|
|
305
307
|
try:
|
|
306
308
|
self.get_output_node(meta).page().runJavaScript("endStream();")
|
|
307
|
-
except Exception
|
|
309
|
+
except Exception:
|
|
308
310
|
pass
|
|
309
311
|
|
|
310
312
|
def append_context(
|
|
@@ -491,6 +493,7 @@ class Renderer(BaseRenderer):
|
|
|
491
493
|
buffer_to_parse = buffer
|
|
492
494
|
|
|
493
495
|
html = self.parser.parse(buffer_to_parse)
|
|
496
|
+
del buffer_to_parse
|
|
494
497
|
is_code_block = html.endswith(self.ENDINGS_CODE)
|
|
495
498
|
is_list = html.endswith(self.ENDINGS_LIST)
|
|
496
499
|
is_newline = ("\n" in text_chunk) or buffer.endswith("\n") or is_code_block
|
|
@@ -502,14 +505,14 @@ class Renderer(BaseRenderer):
|
|
|
502
505
|
else:
|
|
503
506
|
self.prev_chunk_newline = False
|
|
504
507
|
|
|
505
|
-
|
|
508
|
+
replace = False
|
|
506
509
|
if is_newline or force_replace or is_list:
|
|
507
|
-
|
|
510
|
+
replace = True
|
|
508
511
|
if is_code_block:
|
|
509
512
|
# don't replace if it is a code block
|
|
510
513
|
if "\n" not in text_chunk:
|
|
511
514
|
# if there is no newline in raw_chunk, then don't replace
|
|
512
|
-
|
|
515
|
+
replace = False
|
|
513
516
|
|
|
514
517
|
if not is_code_block:
|
|
515
518
|
text_chunk = text_chunk.replace("\n", "<br/>")
|
|
@@ -518,25 +521,25 @@ class Renderer(BaseRenderer):
|
|
|
518
521
|
# if previous chunk was replaced and current is code block, then add \n to chunk
|
|
519
522
|
text_chunk = "".join(("\n", text_chunk)) # add newline to chunk
|
|
520
523
|
|
|
521
|
-
self.prev_chunk_replace =
|
|
524
|
+
self.prev_chunk_replace = replace
|
|
522
525
|
|
|
523
526
|
# hide loading spinner if it is the beginning of the text
|
|
524
527
|
if begin:
|
|
525
528
|
try:
|
|
526
529
|
self.get_output_node(meta).page().runJavaScript("hideLoading();")
|
|
527
|
-
except Exception
|
|
530
|
+
except Exception:
|
|
528
531
|
pass
|
|
529
532
|
|
|
530
533
|
# emit chunk to output node
|
|
531
534
|
try:
|
|
532
535
|
self.get_output_node(meta).page().bridge.chunk.emit(
|
|
533
536
|
name_header_str or "",
|
|
534
|
-
html if
|
|
535
|
-
text_chunk if not
|
|
536
|
-
bool(
|
|
537
|
+
self.sanitize_html(html) if replace else "",
|
|
538
|
+
self.sanitize_html(text_chunk) if not replace else "",
|
|
539
|
+
bool(replace),
|
|
537
540
|
bool(is_code_block),
|
|
538
541
|
)
|
|
539
|
-
except Exception
|
|
542
|
+
except Exception:
|
|
540
543
|
pass
|
|
541
544
|
|
|
542
545
|
def next_chunk(
|
|
@@ -559,7 +562,7 @@ class Renderer(BaseRenderer):
|
|
|
559
562
|
try:
|
|
560
563
|
self.get_output_node(meta).page().runJavaScript(
|
|
561
564
|
"nextStream();")
|
|
562
|
-
except Exception
|
|
565
|
+
except Exception:
|
|
563
566
|
pass
|
|
564
567
|
|
|
565
568
|
def append_chunk_input(
|
|
@@ -586,9 +589,13 @@ class Renderer(BaseRenderer):
|
|
|
586
589
|
self.clear_chunks_input(pid)
|
|
587
590
|
try:
|
|
588
591
|
self.get_output_node(meta).page().runJavaScript(
|
|
589
|
-
f"appendToInput({self.to_json(
|
|
592
|
+
f"""appendToInput({self.to_json(
|
|
593
|
+
self.sanitize_html(
|
|
594
|
+
self.helpers.format_chunk(text_chunk)
|
|
595
|
+
)
|
|
596
|
+
)});"""
|
|
590
597
|
)
|
|
591
|
-
except Exception
|
|
598
|
+
except Exception:
|
|
592
599
|
pass
|
|
593
600
|
|
|
594
601
|
def append_live(
|
|
@@ -628,11 +635,17 @@ class Renderer(BaseRenderer):
|
|
|
628
635
|
to_append = self.pids[pid].live_buffer
|
|
629
636
|
if has_unclosed_code_tag(self.pids[pid].live_buffer):
|
|
630
637
|
to_append += "\n```"
|
|
638
|
+
print(to_append)
|
|
631
639
|
try:
|
|
632
640
|
self.get_output_node(meta).page().runJavaScript(
|
|
633
|
-
f"replaceLive({self.to_json(
|
|
641
|
+
f"""replaceLive({self.to_json(
|
|
642
|
+
self.sanitize_html(
|
|
643
|
+
self.parser.parse(to_append)
|
|
644
|
+
)
|
|
645
|
+
)});"""
|
|
634
646
|
)
|
|
635
647
|
except Exception as e:
|
|
648
|
+
print(e)
|
|
636
649
|
pass
|
|
637
650
|
|
|
638
651
|
def clear_live(self, meta: CtxMeta, ctx: CtxItem):
|
|
@@ -651,7 +664,7 @@ class Renderer(BaseRenderer):
|
|
|
651
664
|
js = "clearLive();"
|
|
652
665
|
try:
|
|
653
666
|
self.get_output_node_by_pid(pid).page().runJavaScript(js)
|
|
654
|
-
except Exception
|
|
667
|
+
except Exception:
|
|
655
668
|
pass
|
|
656
669
|
|
|
657
670
|
def append_node(
|
|
@@ -824,7 +837,11 @@ class Renderer(BaseRenderer):
|
|
|
824
837
|
self.append(pid, html)
|
|
825
838
|
else:
|
|
826
839
|
try:
|
|
827
|
-
self.get_output_node(meta).page().runJavaScript(
|
|
840
|
+
self.get_output_node(meta).page().runJavaScript(
|
|
841
|
+
f"""appendExtra('{ctx.id}',{self.to_json(
|
|
842
|
+
self.sanitize_html(html)
|
|
843
|
+
)});"""
|
|
844
|
+
)
|
|
828
845
|
except Exception as e:
|
|
829
846
|
pass
|
|
830
847
|
|
|
@@ -939,7 +956,7 @@ class Renderer(BaseRenderer):
|
|
|
939
956
|
js = "clearInput();"
|
|
940
957
|
try:
|
|
941
958
|
self.get_output_node_by_pid(pid).page().runJavaScript(js)
|
|
942
|
-
except Exception
|
|
959
|
+
except Exception:
|
|
943
960
|
pass
|
|
944
961
|
|
|
945
962
|
def clear_chunks_output(
|
|
@@ -958,7 +975,7 @@ class Renderer(BaseRenderer):
|
|
|
958
975
|
js = "clearOutput();"
|
|
959
976
|
try:
|
|
960
977
|
self.get_output_node_by_pid(pid).page().runJavaScript(js)
|
|
961
|
-
except Exception
|
|
978
|
+
except Exception:
|
|
962
979
|
pass
|
|
963
980
|
|
|
964
981
|
def clear_nodes(
|
|
@@ -976,7 +993,7 @@ class Renderer(BaseRenderer):
|
|
|
976
993
|
js = "clearNodes();"
|
|
977
994
|
try:
|
|
978
995
|
self.get_output_node_by_pid(pid).page().runJavaScript(js)
|
|
979
|
-
except Exception
|
|
996
|
+
except Exception:
|
|
980
997
|
pass
|
|
981
998
|
|
|
982
999
|
def prepare_node(
|
|
@@ -1174,9 +1191,11 @@ class Renderer(BaseRenderer):
|
|
|
1174
1191
|
"""
|
|
1175
1192
|
try:
|
|
1176
1193
|
self.get_output_node_by_pid(pid).page().runJavaScript(
|
|
1177
|
-
f"if (typeof window.appendNode !== 'undefined') appendNode({self.to_json(
|
|
1194
|
+
f"""if (typeof window.appendNode !== 'undefined') appendNode({self.to_json(
|
|
1195
|
+
self.sanitize_html(html)
|
|
1196
|
+
)});"""
|
|
1178
1197
|
)
|
|
1179
|
-
except Exception
|
|
1198
|
+
except Exception:
|
|
1180
1199
|
pass
|
|
1181
1200
|
|
|
1182
1201
|
def reload(self):
|
|
@@ -1266,7 +1285,7 @@ class Renderer(BaseRenderer):
|
|
|
1266
1285
|
try:
|
|
1267
1286
|
self.get_output_node(ctx.meta).page().runJavaScript(
|
|
1268
1287
|
f"if (typeof window.removeNode !== 'undefined') removeNode({self.to_json(ctx.id)});")
|
|
1269
|
-
except Exception
|
|
1288
|
+
except Exception:
|
|
1270
1289
|
pass
|
|
1271
1290
|
|
|
1272
1291
|
def remove_items_from(self, ctx: CtxItem):
|
|
@@ -1278,7 +1297,7 @@ class Renderer(BaseRenderer):
|
|
|
1278
1297
|
try:
|
|
1279
1298
|
self.get_output_node(ctx.meta).page().runJavaScript(
|
|
1280
1299
|
f"if (typeof window.removeNodesFromId !== 'undefined') removeNodesFromId({self.to_json(ctx.id)});")
|
|
1281
|
-
except Exception
|
|
1300
|
+
except Exception:
|
|
1282
1301
|
pass
|
|
1283
1302
|
|
|
1284
1303
|
def reset_names(self, meta: CtxMeta):
|
|
@@ -1329,7 +1348,7 @@ class Renderer(BaseRenderer):
|
|
|
1329
1348
|
nodes = self.get_all_nodes()
|
|
1330
1349
|
for node in nodes:
|
|
1331
1350
|
node.page().runJavaScript("if (typeof window.enableEditIcons !== 'undefined') enableEditIcons();")
|
|
1332
|
-
except Exception
|
|
1351
|
+
except Exception:
|
|
1333
1352
|
pass
|
|
1334
1353
|
|
|
1335
1354
|
def on_disable_edit(self, live: bool = True):
|
|
@@ -1344,7 +1363,7 @@ class Renderer(BaseRenderer):
|
|
|
1344
1363
|
nodes = self.get_all_nodes()
|
|
1345
1364
|
for node in nodes:
|
|
1346
1365
|
node.page().runJavaScript("if (typeof window.disableEditIcons !== 'undefined') disableEditIcons();")
|
|
1347
|
-
except Exception
|
|
1366
|
+
except Exception:
|
|
1348
1367
|
pass
|
|
1349
1368
|
|
|
1350
1369
|
def on_enable_timestamp(self, live: bool = True):
|
|
@@ -1359,7 +1378,7 @@ class Renderer(BaseRenderer):
|
|
|
1359
1378
|
nodes = self.get_all_nodes()
|
|
1360
1379
|
for node in nodes:
|
|
1361
1380
|
node.page().runJavaScript("if (typeof window.enableTimestamp !== 'undefined') enableTimestamp();")
|
|
1362
|
-
except Exception
|
|
1381
|
+
except Exception:
|
|
1363
1382
|
pass
|
|
1364
1383
|
|
|
1365
1384
|
def on_disable_timestamp(self, live: bool = True):
|
|
@@ -1374,7 +1393,7 @@ class Renderer(BaseRenderer):
|
|
|
1374
1393
|
nodes = self.get_all_nodes()
|
|
1375
1394
|
for node in nodes:
|
|
1376
1395
|
node.page().runJavaScript("if (typeof window.disableTimestamp !== 'undefined') disableTimestamp();")
|
|
1377
|
-
except Exception
|
|
1396
|
+
except Exception:
|
|
1378
1397
|
pass
|
|
1379
1398
|
|
|
1380
1399
|
def update_names(
|
|
@@ -1469,9 +1488,11 @@ class Renderer(BaseRenderer):
|
|
|
1469
1488
|
"""
|
|
1470
1489
|
try:
|
|
1471
1490
|
self.get_output_node(meta).page().runJavaScript(
|
|
1472
|
-
f"if (typeof window.appendToolOutput !== 'undefined') appendToolOutput({self.to_json(
|
|
1491
|
+
f"""if (typeof window.appendToolOutput !== 'undefined') appendToolOutput({self.to_json(
|
|
1492
|
+
self.sanitize_html(content)
|
|
1493
|
+
)});"""
|
|
1473
1494
|
)
|
|
1474
|
-
except Exception
|
|
1495
|
+
except Exception:
|
|
1475
1496
|
pass
|
|
1476
1497
|
|
|
1477
1498
|
def tool_output_update(
|
|
@@ -1487,9 +1508,11 @@ class Renderer(BaseRenderer):
|
|
|
1487
1508
|
"""
|
|
1488
1509
|
try:
|
|
1489
1510
|
self.get_output_node(meta).page().runJavaScript(
|
|
1490
|
-
f"if (typeof window.updateToolOutput !== 'undefined') updateToolOutput({self.to_json(
|
|
1511
|
+
f"""if (typeof window.updateToolOutput !== 'undefined') updateToolOutput({self.to_json(
|
|
1512
|
+
self.sanitize_html(content)
|
|
1513
|
+
)});"""
|
|
1491
1514
|
)
|
|
1492
|
-
except Exception
|
|
1515
|
+
except Exception:
|
|
1493
1516
|
pass
|
|
1494
1517
|
|
|
1495
1518
|
def tool_output_clear(self, meta: CtxMeta):
|
|
@@ -1502,7 +1525,7 @@ class Renderer(BaseRenderer):
|
|
|
1502
1525
|
self.get_output_node(meta).page().runJavaScript(
|
|
1503
1526
|
f"if (typeof window.clearToolOutput !== 'undefined') clearToolOutput();"
|
|
1504
1527
|
)
|
|
1505
|
-
except Exception
|
|
1528
|
+
except Exception:
|
|
1506
1529
|
pass
|
|
1507
1530
|
|
|
1508
1531
|
def tool_output_begin(self, meta: CtxMeta):
|
|
@@ -1515,7 +1538,7 @@ class Renderer(BaseRenderer):
|
|
|
1515
1538
|
self.get_output_node(meta).page().runJavaScript(
|
|
1516
1539
|
f"if (typeof window.beginToolOutput !== 'undefined') beginToolOutput();"
|
|
1517
1540
|
)
|
|
1518
|
-
except Exception
|
|
1541
|
+
except Exception:
|
|
1519
1542
|
pass
|
|
1520
1543
|
|
|
1521
1544
|
def tool_output_end(self):
|
|
@@ -1524,9 +1547,20 @@ class Renderer(BaseRenderer):
|
|
|
1524
1547
|
self.get_output_node().page().runJavaScript(
|
|
1525
1548
|
f"if (typeof window.endToolOutput !== 'undefined') endToolOutput();"
|
|
1526
1549
|
)
|
|
1527
|
-
except Exception
|
|
1550
|
+
except Exception:
|
|
1528
1551
|
pass
|
|
1529
1552
|
|
|
1553
|
+
def sanitize_html(self, html: str) -> str:
|
|
1554
|
+
"""
|
|
1555
|
+
Sanitize HTML to prevent XSS attacks
|
|
1556
|
+
|
|
1557
|
+
:param html: HTML string to sanitize
|
|
1558
|
+
:return: sanitized HTML string
|
|
1559
|
+
"""
|
|
1560
|
+
if not html:
|
|
1561
|
+
return ""
|
|
1562
|
+
return self.RE_AMP_LT_GT.sub(r'&\1;', html)
|
|
1563
|
+
|
|
1530
1564
|
def append_debug(
|
|
1531
1565
|
self,
|
|
1532
1566
|
ctx: CtxItem,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"app.version": "2.6.
|
|
5
|
-
"updated_at": "2025-08-
|
|
3
|
+
"version": "2.6.11",
|
|
4
|
+
"app.version": "2.6.11",
|
|
5
|
+
"updated_at": "2025-08-18T00: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-08-
|
|
3
|
+
"version": "2.6.11",
|
|
4
|
+
"app.version": "2.6.11",
|
|
5
|
+
"updated_at": "2025-08-18T23:07:35"
|
|
6
6
|
},
|
|
7
7
|
"items": {
|
|
8
8
|
"SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
|
pygpt_net/ui/main.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.
|
|
9
|
+
# Updated Date: 2025.08.18 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -247,7 +247,9 @@ class MainWindow(QMainWindow, QtStyleTools):
|
|
|
247
247
|
|
|
248
248
|
:param message: status message
|
|
249
249
|
"""
|
|
250
|
-
|
|
250
|
+
message = message if isinstance(message, str) else str(message)
|
|
251
|
+
self.dispatch(KernelEvent(KernelEvent.STATUS, {"status": message}))
|
|
252
|
+
del message # free memory
|
|
251
253
|
|
|
252
254
|
@Slot(str)
|
|
253
255
|
def update_state(self, state: str):
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.18 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt, QObject, Signal, Slot, QEvent, QTimer
|
|
@@ -311,7 +311,7 @@ class ChatWebOutput(QWebEngineView):
|
|
|
311
311
|
|
|
312
312
|
# audio read (get text only on click, don't copy immediately)
|
|
313
313
|
action = QAction(QIcon(":/icons/volume.svg"), trans('text.context_menu.audio.read'), self)
|
|
314
|
-
action.triggered.connect(
|
|
314
|
+
action.triggered.connect(self._read_selected_text)
|
|
315
315
|
menu.addAction(action)
|
|
316
316
|
|
|
317
317
|
# copy to
|
|
@@ -320,7 +320,7 @@ class ChatWebOutput(QWebEngineView):
|
|
|
320
320
|
|
|
321
321
|
# save as (selected) - get selection at the moment of click
|
|
322
322
|
action = QAction(QIcon(":/icons/save.svg"), trans('action.save_selection_as'), self)
|
|
323
|
-
action.triggered.connect(
|
|
323
|
+
action.triggered.connect(self._save_selected_txt)
|
|
324
324
|
menu.addAction(action)
|
|
325
325
|
else:
|
|
326
326
|
# select all
|
|
@@ -344,6 +344,20 @@ class ChatWebOutput(QWebEngineView):
|
|
|
344
344
|
|
|
345
345
|
menu.exec_(self.mapToGlobal(position))
|
|
346
346
|
|
|
347
|
+
@Slot()
|
|
348
|
+
def _save_selected_txt(self):
|
|
349
|
+
"""Save selected content as text file"""
|
|
350
|
+
self.signals.save_as.emit(self.get_selected_text(), 'txt')
|
|
351
|
+
|
|
352
|
+
@Slot()
|
|
353
|
+
def _read_selected_text(self):
|
|
354
|
+
"""
|
|
355
|
+
Read selected text using text-to-speech
|
|
356
|
+
"""
|
|
357
|
+
selected_text = self.get_selected_text()
|
|
358
|
+
if selected_text:
|
|
359
|
+
self.signals.audio_read.emit(selected_text)
|
|
360
|
+
|
|
347
361
|
@Slot()
|
|
348
362
|
def _save_as_text(self):
|
|
349
363
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.11
|
|
4
4
|
Summary: Desktop AI Assistant powered by: OpenAI GPT-5, o1, o3, GPT-4, Gemini, Claude, Grok, DeepSeek, and other models supported by Llama Index, and Ollama. Chatbot, agents, completion, image generation, vision analysis, speech-to-text, plugins, internet access, file handling, command execution and more.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: py_gpt,py-gpt,pygpt,desktop,app,o1,o3,gpt-5,gpt,gpt4,gpt-4o,gpt-4v,gpt3.5,gpt-4,gpt-4-vision,gpt-3.5,llama3,mistral,gemini,grok,deepseek,bielik,claude,tts,whisper,vision,chatgpt,dall-e,chat,chatbot,assistant,text completion,image generation,ai,api,openai,api key,langchain,llama-index,ollama,presets,ui,qt,pyside
|
|
@@ -108,7 +108,7 @@ Description-Content-Type: text/markdown
|
|
|
108
108
|
|
|
109
109
|
[](https://snapcraft.io/pygpt)
|
|
110
110
|
|
|
111
|
-
Release: **2.6.
|
|
111
|
+
Release: **2.6.11** | build: **2025-08-18** | Python: **>=3.10, <3.14**
|
|
112
112
|
|
|
113
113
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
114
114
|
>
|
|
@@ -4566,6 +4566,12 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
4566
4566
|
|
|
4567
4567
|
## Recent changes:
|
|
4568
4568
|
|
|
4569
|
+
**2.6.11 (2025-08-18)**
|
|
4570
|
+
|
|
4571
|
+
- Added the ability to close the dialog window with the Esc key.
|
|
4572
|
+
- Made context item deletion without output refresh.
|
|
4573
|
+
- Optimizations.
|
|
4574
|
+
|
|
4569
4575
|
**2.6.10 (2025-08-17)**
|
|
4570
4576
|
|
|
4571
4577
|
- Enhanced the handling of the context list.
|