pygpt-net 2.4.39__py3-none-any.whl → 2.4.40__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.
- CHANGELOG.md +13 -0
- README.md +19 -2
- pygpt_net/CHANGELOG.txt +13 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/__init__.py +5 -3
- pygpt_net/controller/audio/__init__.py +9 -1
- pygpt_net/controller/chat/input.py +2 -1
- pygpt_net/controller/chat/render.py +2 -2
- pygpt_net/controller/ctx/__init__.py +2 -2
- pygpt_net/controller/debug/__init__.py +13 -2
- pygpt_net/controller/kernel/__init__.py +2 -1
- pygpt_net/controller/notepad.py +7 -6
- pygpt_net/controller/theme/nodes.py +2 -5
- pygpt_net/controller/tools/__init__.py +37 -1
- pygpt_net/controller/ui/__init__.py +1 -5
- pygpt_net/controller/ui/tabs.py +104 -12
- pygpt_net/core/command.py +3 -1
- pygpt_net/core/ctx/__init__.py +6 -2
- pygpt_net/core/ctx/container.py +5 -5
- pygpt_net/core/debug/tabs.py +3 -1
- pygpt_net/core/render/base.py +2 -2
- pygpt_net/core/render/web/body.py +1 -1
- pygpt_net/core/render/web/renderer.py +208 -38
- pygpt_net/core/tabs/__init__.py +104 -43
- pygpt_net/core/tabs/tab.py +4 -1
- pygpt_net/core/web.py +127 -1
- pygpt_net/data/config/config.json +4 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/modes.json +3 -3
- pygpt_net/data/css/web-blocks.css +18 -0
- pygpt_net/data/css/web-blocks.light.css +7 -0
- pygpt_net/data/css/web-chatgpt.css +8 -0
- pygpt_net/data/css/web-chatgpt_wide.css +8 -0
- pygpt_net/data/icons/split_screen.svg +1 -0
- pygpt_net/data/locale/locale.de.ini +1 -1
- pygpt_net/data/locale/locale.en.ini +4 -2
- pygpt_net/data/locale/locale.es.ini +1 -1
- pygpt_net/data/locale/locale.fr.ini +1 -1
- pygpt_net/data/locale/locale.it.ini +1 -1
- pygpt_net/data/locale/locale.pl.ini +2 -2
- pygpt_net/data/locale/locale.uk.ini +1 -1
- pygpt_net/data/locale/locale.zh.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
- pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
- pygpt_net/icons.qrc +1 -0
- pygpt_net/icons_rc.py +165 -136
- pygpt_net/item/ctx.py +46 -24
- pygpt_net/plugin/audio_output/__init__.py +4 -1
- pygpt_net/plugin/base/plugin.py +18 -4
- pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
- pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
- pygpt_net/plugin/cmd_web/__init__.py +46 -6
- pygpt_net/plugin/cmd_web/config.py +74 -48
- pygpt_net/plugin/cmd_web/websearch.py +61 -28
- pygpt_net/plugin/cmd_web/worker.py +79 -13
- pygpt_net/provider/core/config/patch.py +22 -1
- pygpt_net/tools/__init__.py +9 -1
- pygpt_net/tools/base.py +15 -1
- pygpt_net/tools/code_interpreter/__init__.py +174 -75
- pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
- pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
- pygpt_net/tools/html_canvas/__init__.py +78 -23
- pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
- pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
- pygpt_net/ui/base/context_menu.py +2 -2
- pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
- pygpt_net/ui/layout/toolbox/footer.py +1 -1
- pygpt_net/ui/main.py +2 -2
- pygpt_net/ui/menu/debug.py +11 -1
- pygpt_net/ui/widget/filesystem/explorer.py +2 -2
- pygpt_net/ui/widget/lists/context.py +26 -5
- pygpt_net/ui/widget/tabs/Input.py +2 -2
- pygpt_net/ui/widget/tabs/body.py +2 -1
- pygpt_net/ui/widget/tabs/output.py +126 -61
- {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/METADATA +20 -3
- {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/RECORD +85 -84
- {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/LICENSE +0 -0
- {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/WHEEL +0 -0
- {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/entry_points.txt +0 -0
pygpt_net/core/tabs/__init__.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: 2024.12.
|
9
|
+
# Updated Date: 2024.12.12 01:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import uuid
|
@@ -15,24 +15,17 @@ from datetime import datetime
|
|
15
15
|
from PySide6.QtGui import QIcon
|
16
16
|
from PySide6.QtWidgets import QVBoxLayout, QWidget, QLayout
|
17
17
|
|
18
|
-
from .tab import Tab
|
19
18
|
from pygpt_net.ui.widget.tabs.body import TabBody
|
20
19
|
from pygpt_net.utils import trans
|
21
20
|
|
21
|
+
from .tab import Tab
|
22
|
+
|
22
23
|
|
23
24
|
class Tabs:
|
24
25
|
|
25
26
|
# number of columns
|
26
27
|
NUM_COLS = 2
|
27
28
|
|
28
|
-
# types
|
29
|
-
TAB_ADD = -1
|
30
|
-
TAB_CHAT = 0
|
31
|
-
TAB_NOTEPAD = 1
|
32
|
-
TAB_FILES = 2
|
33
|
-
TAB_TOOL_PAINTER = 3
|
34
|
-
TAB_TOOL_CALENDAR = 4
|
35
|
-
|
36
29
|
def __init__(self, window=None):
|
37
30
|
"""
|
38
31
|
Tabs core
|
@@ -43,18 +36,20 @@ class Tabs:
|
|
43
36
|
self.last_pid = -1
|
44
37
|
self.pids = {} # pid: Tab data
|
45
38
|
self.icons = {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
39
|
+
Tab.TAB_CHAT: ":/icons/chat.svg",
|
40
|
+
Tab.TAB_NOTEPAD: ":/icons/paste.svg",
|
41
|
+
Tab.TAB_FILES: ":/icons/folder_filled.svg",
|
42
|
+
Tab.TAB_TOOL_PAINTER: ":/icons/brush.svg",
|
43
|
+
Tab.TAB_TOOL_CALENDAR: ":/icons/calendar.svg",
|
44
|
+
Tab.TAB_TOOL: ":/icons/build.svg",
|
51
45
|
}
|
52
46
|
self.titles = {
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
47
|
+
Tab.TAB_CHAT: "output.tab.chat",
|
48
|
+
Tab.TAB_NOTEPAD: "output.tab.notepad",
|
49
|
+
Tab.TAB_FILES: "output.tab.files",
|
50
|
+
Tab.TAB_TOOL_PAINTER: "output.tab.painter",
|
51
|
+
Tab.TAB_TOOL_CALENDAR: "output.tab.calendar",
|
52
|
+
Tab.TAB_TOOL: "output.tab.tool",
|
58
53
|
}
|
59
54
|
|
60
55
|
def get_tab_by_index(self, idx: int, column_idx: int = 0) -> Tab or None:
|
@@ -113,7 +108,8 @@ class Tabs:
|
|
113
108
|
title: str,
|
114
109
|
icon=None,
|
115
110
|
child=None,
|
116
|
-
data_id=None
|
111
|
+
data_id=None,
|
112
|
+
tool_id=None
|
117
113
|
) -> Tab:
|
118
114
|
"""
|
119
115
|
Add tab
|
@@ -123,6 +119,7 @@ class Tabs:
|
|
123
119
|
:param icon: Tab icon
|
124
120
|
:param child: Tab child
|
125
121
|
:param data_id: Tab data ID
|
122
|
+
:param tool_id: Tool ID
|
126
123
|
:return: Tab
|
127
124
|
"""
|
128
125
|
self.last_pid += 1 # PID++, start from 0
|
@@ -135,6 +132,7 @@ class Tabs:
|
|
135
132
|
tab.icon = icon
|
136
133
|
tab.child = child
|
137
134
|
tab.data_id = data_id
|
135
|
+
tab.tool_id = tool_id
|
138
136
|
|
139
137
|
if type == Tab.TAB_CHAT:
|
140
138
|
self.add_chat(tab)
|
@@ -146,6 +144,8 @@ class Tabs:
|
|
146
144
|
self.add_tool_painter(tab)
|
147
145
|
elif type == Tab.TAB_TOOL_CALENDAR:
|
148
146
|
self.add_tool_calendar(tab)
|
147
|
+
elif type == Tab.TAB_TOOL:
|
148
|
+
self.add_tool(tab)
|
149
149
|
|
150
150
|
self.pids[tab.pid] = tab
|
151
151
|
return tab
|
@@ -153,6 +153,7 @@ class Tabs:
|
|
153
153
|
def append(
|
154
154
|
self,
|
155
155
|
type: int,
|
156
|
+
tool_id: str,
|
156
157
|
idx: int,
|
157
158
|
column_idx: int = 0
|
158
159
|
) -> Tab:
|
@@ -160,6 +161,7 @@ class Tabs:
|
|
160
161
|
Append tab to the right side of the tab with the specified index
|
161
162
|
|
162
163
|
:param type: tab type
|
164
|
+
:param tool_id: tool ID
|
163
165
|
:param idx: index of the tab to the right of which the new tab will be added
|
164
166
|
:param column_idx: index of the column in which the tab will be added
|
165
167
|
:return: Tab
|
@@ -167,9 +169,9 @@ class Tabs:
|
|
167
169
|
self.last_pid += 1 # PID++, start from 0
|
168
170
|
title = ""
|
169
171
|
icon = self.icons[type]
|
170
|
-
if type ==
|
172
|
+
if type == Tab.TAB_CHAT:
|
171
173
|
title = trans('output.tab.chat') + " {}".format(self.count_by_type(type) + 1)
|
172
|
-
elif type ==
|
174
|
+
elif type == Tab.TAB_NOTEPAD:
|
173
175
|
title = trans('output.tab.notepad') + " {}".format(self.count_by_type(type) + 1)
|
174
176
|
|
175
177
|
tab = Tab()
|
@@ -180,11 +182,14 @@ class Tabs:
|
|
180
182
|
tab.icon = icon
|
181
183
|
tab.new_idx = idx + 1 # place on right side
|
182
184
|
tab.column_idx = column_idx
|
185
|
+
tab.tool_id = tool_id
|
183
186
|
|
184
187
|
if type == Tab.TAB_CHAT:
|
185
188
|
self.add_chat(tab)
|
186
189
|
elif type == Tab.TAB_NOTEPAD:
|
187
190
|
self.add_notepad(tab)
|
191
|
+
elif type == Tab.TAB_TOOL:
|
192
|
+
self.add_tool(tab)
|
188
193
|
|
189
194
|
self.pids[tab.pid] = tab
|
190
195
|
self.update()
|
@@ -217,6 +222,9 @@ class Tabs:
|
|
217
222
|
if 'column_idx' in data and data['column_idx'] is not None:
|
218
223
|
tab.column_idx = data['column_idx']
|
219
224
|
|
225
|
+
if 'tool_id' in data and data['tool_id'] is not None:
|
226
|
+
tab.tool_id = data['tool_id']
|
227
|
+
|
220
228
|
if tab.type in self.icons:
|
221
229
|
tab.icon = self.icons[tab.type]
|
222
230
|
|
@@ -233,6 +241,8 @@ class Tabs:
|
|
233
241
|
self.add_tool_painter(tab)
|
234
242
|
elif tab.type == Tab.TAB_TOOL_CALENDAR: # calendar
|
235
243
|
self.add_tool_calendar(tab)
|
244
|
+
elif tab.type == Tab.TAB_TOOL: # custom tools, id 100+
|
245
|
+
self.add_tool(tab)
|
236
246
|
|
237
247
|
self.pids[tab.pid] = tab
|
238
248
|
self.last_pid = self.get_max_pid()
|
@@ -279,7 +289,7 @@ class Tabs:
|
|
279
289
|
for pid in list(self.pids):
|
280
290
|
tab = self.pids[pid]
|
281
291
|
if tab.type == type and tab.column_idx == column_idx:
|
282
|
-
if type ==
|
292
|
+
if type == Tab.TAB_CHAT:
|
283
293
|
if self.count_by_type(type) == 1:
|
284
294
|
continue # do not remove last chat tab
|
285
295
|
self.remove(pid)
|
@@ -341,6 +351,20 @@ class Tabs:
|
|
341
351
|
count += 1
|
342
352
|
return count
|
343
353
|
|
354
|
+
def get_max_data_id_by_type(self, type: int) -> int:
|
355
|
+
"""
|
356
|
+
Get max data ID by type
|
357
|
+
|
358
|
+
:param type: Tab type
|
359
|
+
:return: Max data ID
|
360
|
+
"""
|
361
|
+
max = 0
|
362
|
+
for pid in self.pids:
|
363
|
+
tab = self.pids[pid]
|
364
|
+
if tab.type == type and tab.data_id > max:
|
365
|
+
max = tab.data_id
|
366
|
+
return max
|
367
|
+
|
344
368
|
def get_order_by_idx_and_type(
|
345
369
|
self,
|
346
370
|
idx: int,
|
@@ -430,8 +454,8 @@ class Tabs:
|
|
430
454
|
tab.parent = tabs.get_column()
|
431
455
|
if tab.data_id is not None:
|
432
456
|
idx = tab.data_id # restore prev idx
|
433
|
-
tab.child, idx = self.window.controller.notepad.create(idx)
|
434
|
-
tab.data_id =
|
457
|
+
tab.child, idx, data_id = self.window.controller.notepad.create(idx)
|
458
|
+
tab.data_id = data_id # notepad idx in db, enumerated from 1
|
435
459
|
if tab.new_idx is not None:
|
436
460
|
tab.idx = tabs.insertTab(tab.new_idx, tab.child, tab.title)
|
437
461
|
else:
|
@@ -489,6 +513,30 @@ class Tabs:
|
|
489
513
|
if tab.tooltip is not None:
|
490
514
|
tabs.setTabToolTip(tab.idx, tab.tooltip)
|
491
515
|
|
516
|
+
def add_tool(self, tab: Tab):
|
517
|
+
"""
|
518
|
+
Add custom tool tab
|
519
|
+
|
520
|
+
:param tab: Tab instance
|
521
|
+
"""
|
522
|
+
column = self.window.ui.layout.get_column_by_idx(tab.column_idx)
|
523
|
+
tabs = column.get_tabs()
|
524
|
+
tool = self.window.tools.get(tab.tool_id)
|
525
|
+
if tool is None:
|
526
|
+
raise Exception("Tool not found: {}".format(tab.tool_id))
|
527
|
+
widget = tool.as_tab(tab)
|
528
|
+
if widget is None:
|
529
|
+
raise Exception("Tool widget not found: {}".format(tab.tool_id))
|
530
|
+
tab.icon = tool.tab_icon
|
531
|
+
tab.title = trans(tool.tab_title)
|
532
|
+
tab.parent = column
|
533
|
+
tab.child = self.from_widget(widget)
|
534
|
+
tab.idx = tabs.addTab(tab.child, tab.title)
|
535
|
+
tab.child.setOwner(tab)
|
536
|
+
tabs.setTabIcon(tab.idx, QIcon(tab.icon))
|
537
|
+
if tab.tooltip is not None:
|
538
|
+
tabs.setTabToolTip(tab.idx, tab.tooltip)
|
539
|
+
|
492
540
|
def move_tab(self, tab: Tab, column_idx: int):
|
493
541
|
"""
|
494
542
|
Move tab to column
|
@@ -500,16 +548,16 @@ class Tabs:
|
|
500
548
|
return
|
501
549
|
if tab.column_idx == column_idx:
|
502
550
|
return
|
503
|
-
|
551
|
+
|
504
552
|
old_column = self.window.ui.layout.get_column_by_idx(tab.column_idx)
|
505
553
|
old_tabs = old_column.get_tabs()
|
506
554
|
old_tabs.removeTab(tab.idx)
|
507
555
|
new_column = self.window.ui.layout.get_column_by_idx(column_idx)
|
508
556
|
new_tabs = new_column.get_tabs()
|
509
|
-
tab.idx = new_tabs.addTab(tab.child, tab.title)
|
557
|
+
tab.idx = new_tabs.addTab(tab.child, QIcon(tab.icon), tab.title)
|
510
558
|
tab.parent = new_column
|
559
|
+
tab.column_idx = column_idx
|
511
560
|
self.update()
|
512
|
-
self.window.core.tabs.reload_titles()
|
513
561
|
|
514
562
|
def get_first_by_type(self, type: int) -> Tab or None:
|
515
563
|
"""
|
@@ -535,7 +583,7 @@ class Tabs:
|
|
535
583
|
"uuid": uuid.uuid4(),
|
536
584
|
"pid": 0,
|
537
585
|
"idx": 0,
|
538
|
-
"type":
|
586
|
+
"type": Tab.TAB_CHAT,
|
539
587
|
"data_id": None,
|
540
588
|
"title": "Chat",
|
541
589
|
"tooltip": "Chat",
|
@@ -545,31 +593,34 @@ class Tabs:
|
|
545
593
|
"uuid": uuid.uuid4(),
|
546
594
|
"pid": 1,
|
547
595
|
"idx": 1,
|
548
|
-
"type":
|
596
|
+
"type": Tab.TAB_FILES,
|
549
597
|
"data_id": None,
|
550
598
|
"title": "Files",
|
551
599
|
"tooltip": "Files",
|
552
600
|
"column_idx": 0,
|
601
|
+
"tool_id": "explorer",
|
553
602
|
}
|
554
603
|
data[2] = {
|
555
604
|
"uuid": uuid.uuid4(),
|
556
605
|
"pid": 2,
|
557
606
|
"idx": 2,
|
558
|
-
"type":
|
607
|
+
"type": Tab.TAB_TOOL_CALENDAR,
|
559
608
|
"data_id": None,
|
560
609
|
"title": "Calendar",
|
561
610
|
"tooltip": "Calendar",
|
562
611
|
"column_idx": 0,
|
612
|
+
"tool_id": "calendar",
|
563
613
|
}
|
564
614
|
data[3] = {
|
565
615
|
"uuid": uuid.uuid4(),
|
566
616
|
"pid": 3,
|
567
617
|
"idx": 3,
|
568
|
-
"type":
|
618
|
+
"type": Tab.TAB_TOOL_PAINTER,
|
569
619
|
"data_id": None,
|
570
620
|
"title": "Painter",
|
571
621
|
"tooltip": "Painter",
|
572
622
|
"column_idx": 0,
|
623
|
+
"tool_id": "painter",
|
573
624
|
}
|
574
625
|
"""
|
575
626
|
data[4] = {
|
@@ -581,6 +632,7 @@ class Tabs:
|
|
581
632
|
"title": "Notepad",
|
582
633
|
"tooltip": "Notepad",
|
583
634
|
"column_idx": 0,
|
635
|
+
"tool_id": "notepad",
|
584
636
|
}
|
585
637
|
"""
|
586
638
|
# load notepads from db
|
@@ -593,11 +645,12 @@ class Tabs:
|
|
593
645
|
"uuid": uuid.uuid4(),
|
594
646
|
"pid": next_idx,
|
595
647
|
"idx": next_idx,
|
596
|
-
"type":
|
648
|
+
"type": Tab.TAB_NOTEPAD,
|
597
649
|
"data_id": item['data_id'],
|
598
650
|
"title": item['title'],
|
599
651
|
"tooltip": item['title'],
|
600
652
|
"column_idx": 0,
|
653
|
+
"tool_id": "notepad",
|
601
654
|
}
|
602
655
|
next_idx += 1
|
603
656
|
return data
|
@@ -628,6 +681,7 @@ class Tabs:
|
|
628
681
|
"tooltip": trans(self.titles[type]),
|
629
682
|
"custom_name": False,
|
630
683
|
"column_idx": 0,
|
684
|
+
"tool_id": None,
|
631
685
|
}
|
632
686
|
tmp_pid += 1
|
633
687
|
|
@@ -657,6 +711,7 @@ class Tabs:
|
|
657
711
|
"tooltip": tab.tooltip,
|
658
712
|
"custom_name": tab.custom_name,
|
659
713
|
"column_idx": tab.column_idx,
|
714
|
+
"tool_id": tab.tool_id,
|
660
715
|
}
|
661
716
|
opened_tabs = {}
|
662
717
|
for column_idx in range(0, self.NUM_COLS):
|
@@ -694,28 +749,34 @@ class Tabs:
|
|
694
749
|
|
695
750
|
def reload_titles(self):
|
696
751
|
"""Reload default tab titles"""
|
752
|
+
return
|
753
|
+
processed = []
|
697
754
|
for column_idx in range(0, self.NUM_COLS):
|
698
755
|
tabs = self.window.ui.layout.get_tabs_by_idx(column_idx)
|
699
756
|
counters = {
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
757
|
+
Tab.TAB_CHAT: 1,
|
758
|
+
Tab.TAB_NOTEPAD: 1,
|
759
|
+
Tab.TAB_FILES: 1,
|
760
|
+
Tab.TAB_TOOL_PAINTER: 1,
|
761
|
+
Tab.TAB_TOOL_CALENDAR: 1,
|
705
762
|
}
|
706
763
|
for pid in self.pids:
|
707
764
|
tab = self.pids[pid]
|
708
|
-
if tab.
|
765
|
+
if tab.pid in processed:
|
709
766
|
continue
|
710
|
-
if tab.custom_name:
|
711
|
-
continue # leave custom names
|
767
|
+
if tab.custom_name or tab.type == Tab.TAB_TOOL:
|
768
|
+
continue # leave custom names and tools
|
712
769
|
tab.title = trans(self.titles[tab.type])
|
713
770
|
num_tabs = self.count_by_type(tab.type)
|
714
771
|
if num_tabs > 1:
|
715
|
-
tab.
|
772
|
+
if tab.type in counters:
|
773
|
+
tab.title += " {}".format(counters[tab.type])
|
774
|
+
else:
|
775
|
+
counters[tab.type] = 1
|
716
776
|
counters[tab.type] += 1
|
717
777
|
tabs.setTabText(tab.idx, tab.title)
|
718
778
|
tabs.setTabToolTip(tab.idx, tab.title)
|
779
|
+
processed.append(pid)
|
719
780
|
|
720
781
|
def from_widget(self, widget: QWidget) -> TabBody:
|
721
782
|
"""
|
pygpt_net/core/tabs/tab.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: 2024.12.09
|
9
|
+
# Updated Date: 2024.12.09 23:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from datetime import datetime
|
@@ -21,6 +21,7 @@ class Tab:
|
|
21
21
|
TAB_FILES = 2
|
22
22
|
TAB_TOOL_PAINTER = 3
|
23
23
|
TAB_TOOL_CALENDAR = 4
|
24
|
+
TAB_TOOL = 100
|
24
25
|
|
25
26
|
def __init__(self):
|
26
27
|
"""
|
@@ -39,6 +40,7 @@ class Tab:
|
|
39
40
|
self.child = None
|
40
41
|
self.parent = None
|
41
42
|
self.column_idx = 0
|
43
|
+
self.tool_id = None
|
42
44
|
|
43
45
|
dt = datetime.now()
|
44
46
|
self.created_at = dt
|
@@ -66,4 +68,5 @@ class Tab:
|
|
66
68
|
"created_at": str(self.created_at),
|
67
69
|
"updated_at": str(self.updated_at),
|
68
70
|
"column_idx": self.column_idx,
|
71
|
+
"tool_id": self.tool_id,
|
69
72
|
}
|
pygpt_net/core/web.py
CHANGED
@@ -6,8 +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: 2024.
|
9
|
+
# Updated Date: 2024.12.13 00:00:00 #
|
10
10
|
# ================================================== #
|
11
|
+
import os
|
12
|
+
import uuid
|
13
|
+
|
14
|
+
import requests
|
15
|
+
|
16
|
+
from bs4 import BeautifulSoup
|
17
|
+
from urllib.parse import urljoin
|
11
18
|
|
12
19
|
from pygpt_net.provider.web.base import BaseProvider
|
13
20
|
|
@@ -81,3 +88,122 @@ class Web:
|
|
81
88
|
for t in type:
|
82
89
|
if t in self.providers:
|
83
90
|
self.providers[t][id] = provider
|
91
|
+
|
92
|
+
def get_main_image(self, url: str) -> str or None:
|
93
|
+
"""
|
94
|
+
Get main image from URL
|
95
|
+
|
96
|
+
:param url: URL to get image from
|
97
|
+
:return: image URL
|
98
|
+
"""
|
99
|
+
response = requests.get(url)
|
100
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
101
|
+
|
102
|
+
og_image = soup.find('meta', property='og:image')
|
103
|
+
if og_image and og_image.get('content'):
|
104
|
+
return og_image['content']
|
105
|
+
|
106
|
+
twitter_image = soup.find('meta', attrs={'name': 'twitter:image'})
|
107
|
+
if twitter_image and twitter_image.get('content'):
|
108
|
+
return twitter_image['content']
|
109
|
+
|
110
|
+
link_image = soup.find('link', rel='image_src')
|
111
|
+
if link_image and link_image.get('href'):
|
112
|
+
return link_image['href']
|
113
|
+
|
114
|
+
images = soup.find_all('img')
|
115
|
+
if images:
|
116
|
+
images = [img for img in images if 'logo' not in (img.get('src') or '').lower()]
|
117
|
+
largest_image = None
|
118
|
+
max_area = 0
|
119
|
+
for img in images:
|
120
|
+
src = img.get('src')
|
121
|
+
if not src:
|
122
|
+
continue
|
123
|
+
src = requests.compat.urljoin(url, src)
|
124
|
+
try:
|
125
|
+
img_response = requests.get(src, stream=True, timeout=5)
|
126
|
+
img_response.raw.decode_content = True
|
127
|
+
|
128
|
+
from PIL import Image
|
129
|
+
image = Image.open(img_response.raw)
|
130
|
+
width, height = image.size
|
131
|
+
area = width * height
|
132
|
+
if area > max_area:
|
133
|
+
max_area = area
|
134
|
+
largest_image = src
|
135
|
+
except:
|
136
|
+
continue
|
137
|
+
if largest_image:
|
138
|
+
return largest_image
|
139
|
+
return None
|
140
|
+
|
141
|
+
def get_links(self, url: str) -> list:
|
142
|
+
"""
|
143
|
+
Get links from URL
|
144
|
+
|
145
|
+
:param url: URL to get links from
|
146
|
+
:return: links list
|
147
|
+
"""
|
148
|
+
response = requests.get(url)
|
149
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
150
|
+
links = []
|
151
|
+
urls = []
|
152
|
+
for link in soup.find_all('a'):
|
153
|
+
try:
|
154
|
+
name = link.get_text(strip=True)
|
155
|
+
address = link.get('href')
|
156
|
+
if address:
|
157
|
+
address = urljoin(url, address)
|
158
|
+
if not name:
|
159
|
+
title = link.get('title')
|
160
|
+
if title:
|
161
|
+
name = title
|
162
|
+
else:
|
163
|
+
name = address
|
164
|
+
if address not in urls:
|
165
|
+
urls.append(address)
|
166
|
+
links.append({name: address})
|
167
|
+
except:
|
168
|
+
continue
|
169
|
+
return links
|
170
|
+
|
171
|
+
|
172
|
+
def get_images(self, url: str) -> list:
|
173
|
+
"""
|
174
|
+
Get images from URL
|
175
|
+
|
176
|
+
:param url: URL to get images from
|
177
|
+
:return: images list
|
178
|
+
"""
|
179
|
+
response = requests.get(url)
|
180
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
181
|
+
images = []
|
182
|
+
for img in soup.find_all('img'):
|
183
|
+
try:
|
184
|
+
address = img.get('src')
|
185
|
+
if address:
|
186
|
+
address = urljoin(url, address)
|
187
|
+
if address not in images:
|
188
|
+
images.append(address)
|
189
|
+
except:
|
190
|
+
continue
|
191
|
+
return images
|
192
|
+
|
193
|
+
def download_image(self, img: str) -> str:
|
194
|
+
"""
|
195
|
+
Download image from URL
|
196
|
+
|
197
|
+
:param img: URL to download image from
|
198
|
+
:return: local path to image
|
199
|
+
"""
|
200
|
+
dir = self.window.core.config.get_user_dir("img")
|
201
|
+
response = requests.get(img, stream=True)
|
202
|
+
name = img.replace("http://", "").replace("https://", "").replace("/", "_")
|
203
|
+
path = os.path.join(dir, name)
|
204
|
+
if os.path.exists(path):
|
205
|
+
name = name + uuid.uuid4().hex[:6].upper()
|
206
|
+
download_path = os.path.join(dir, name)
|
207
|
+
with open(download_path, 'wb', ) as f:
|
208
|
+
f.write(response.content)
|
209
|
+
return self.window.core.filesystem.make_local(download_path)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
{
|
2
2
|
"__meta__": {
|
3
|
-
"version": "2.4.
|
4
|
-
"app.version": "2.4.
|
5
|
-
"updated_at": "2024-12-
|
3
|
+
"version": "2.4.40",
|
4
|
+
"app.version": "2.4.40",
|
5
|
+
"updated_at": "2024-12-12T00:00:00"
|
6
6
|
},
|
7
7
|
"access.audio.event.speech": false,
|
8
8
|
"access.audio.event.speech.disabled": [],
|
@@ -134,6 +134,7 @@
|
|
134
134
|
"expert": ""
|
135
135
|
},
|
136
136
|
"debug": false,
|
137
|
+
"debug.render": false,
|
137
138
|
"download.dir": "download",
|
138
139
|
"experts.mode": "chat",
|
139
140
|
"font_size": 12,
|
pygpt_net/data/config/modes.json
CHANGED
@@ -54,6 +54,7 @@ p {{
|
|
54
54
|
font-family: 'Lato';
|
55
55
|
text-decoration: none;
|
56
56
|
padding-bottom: 0px;
|
57
|
+
font-size: 0.9rem;
|
57
58
|
}}
|
58
59
|
|
59
60
|
/* lists, code */
|
@@ -89,6 +90,7 @@ code {{
|
|
89
90
|
.code-header-lang {{
|
90
91
|
float: left;
|
91
92
|
padding-left: .25rem;
|
93
|
+
padding-top: 4px;
|
92
94
|
}}
|
93
95
|
.code-header-copy {{
|
94
96
|
text-align: right;
|
@@ -102,6 +104,13 @@ code {{
|
|
102
104
|
vertical-align: middle;
|
103
105
|
padding-right: 5px;
|
104
106
|
}}
|
107
|
+
.code-header-copy:hover {{
|
108
|
+
text-decoration: none;
|
109
|
+
cursor: pointer;
|
110
|
+
}}
|
111
|
+
.code-header-copy:hover img {{
|
112
|
+
filter: brightness(130%);
|
113
|
+
}}
|
105
114
|
.code-wrapper {{
|
106
115
|
padding: 0px;
|
107
116
|
margin: 0px;
|
@@ -209,6 +218,7 @@ code {{
|
|
209
218
|
.tool-output .content {{
|
210
219
|
padding: 10px;
|
211
220
|
color: gray !important;
|
221
|
+
font-size: 0.9rem;
|
212
222
|
}}
|
213
223
|
.tool-output .toggle-cmd-output {{
|
214
224
|
cursor: pointer;
|
@@ -318,4 +328,12 @@ code {{
|
|
318
328
|
to {{
|
319
329
|
transform: rotate(360deg);
|
320
330
|
}}
|
331
|
+
}}
|
332
|
+
|
333
|
+
/* debug */
|
334
|
+
.debug {{
|
335
|
+
color: orange !important;
|
336
|
+
background: #0d1117;
|
337
|
+
padding: 10px;
|
338
|
+
font-size: 0.9rem;
|
321
339
|
}}
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M520-320h200v-320H520v320Zm-280 0h200v-320H240v320Zm-80 160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm640-560H160v480h640v-480Zm-640 0v480-480Z"/></svg>
|