pygpt-net 2.4.38__py3-none-any.whl → 2.4.39__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.
Files changed (56) hide show
  1. CHANGELOG.md +6 -0
  2. README.md +7 -1
  3. pygpt_net/CHANGELOG.txt +6 -0
  4. pygpt_net/__init__.py +2 -2
  5. pygpt_net/controller/__init__.py +3 -1
  6. pygpt_net/controller/calendar/__init__.py +3 -1
  7. pygpt_net/controller/chat/render.py +8 -5
  8. pygpt_net/controller/ctx/__init__.py +32 -24
  9. pygpt_net/controller/ctx/common.py +3 -2
  10. pygpt_net/controller/dialogs/confirm.py +2 -2
  11. pygpt_net/controller/lang/custom.py +2 -7
  12. pygpt_net/controller/lang/mapping.py +2 -2
  13. pygpt_net/controller/layout.py +2 -2
  14. pygpt_net/controller/notepad.py +8 -5
  15. pygpt_net/controller/ui/tabs.py +201 -58
  16. pygpt_net/core/ctx/__init__.py +11 -1
  17. pygpt_net/core/ctx/container.py +16 -9
  18. pygpt_net/core/ctx/output.py +86 -67
  19. pygpt_net/core/debug/tabs.py +3 -2
  20. pygpt_net/core/filesystem/url.py +7 -3
  21. pygpt_net/core/render/base.py +14 -3
  22. pygpt_net/core/render/markdown/renderer.py +3 -1
  23. pygpt_net/core/render/plain/renderer.py +3 -3
  24. pygpt_net/core/render/web/body.py +9 -3
  25. pygpt_net/core/render/web/renderer.py +7 -5
  26. pygpt_net/core/tabs/__init__.py +180 -71
  27. pygpt_net/core/tabs/tab.py +13 -4
  28. pygpt_net/data/config/config.json +11 -5
  29. pygpt_net/data/config/models.json +3 -3
  30. pygpt_net/data/config/modes.json +3 -3
  31. pygpt_net/data/locale/locale.de.ini +3 -0
  32. pygpt_net/data/locale/locale.en.ini +3 -0
  33. pygpt_net/data/locale/locale.es.ini +3 -0
  34. pygpt_net/data/locale/locale.fr.ini +3 -0
  35. pygpt_net/data/locale/locale.it.ini +3 -0
  36. pygpt_net/data/locale/locale.pl.ini +4 -1
  37. pygpt_net/data/locale/locale.uk.ini +3 -0
  38. pygpt_net/data/locale/locale.zh.ini +3 -0
  39. pygpt_net/plugin/audio_input/simple.py +4 -2
  40. pygpt_net/provider/core/config/patch.py +8 -1
  41. pygpt_net/provider/core/ctx/base.py +4 -1
  42. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
  43. pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
  44. pygpt_net/ui/layout/chat/input.py +10 -18
  45. pygpt_net/ui/layout/chat/output.py +26 -44
  46. pygpt_net/ui/layout/toolbox/footer.py +18 -2
  47. pygpt_net/ui/widget/tabs/layout.py +195 -0
  48. pygpt_net/ui/widget/tabs/output.py +124 -35
  49. pygpt_net/ui/widget/textarea/html.py +11 -1
  50. pygpt_net/ui/widget/textarea/output.py +10 -1
  51. pygpt_net/ui/widget/textarea/web.py +49 -9
  52. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/METADATA +8 -2
  53. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/RECORD +56 -55
  54. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/LICENSE +0 -0
  55. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/WHEEL +0 -0
  56. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.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: 2024.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.item.ctx import CtxItem, CtxMeta
@@ -16,11 +16,28 @@ class Output:
16
16
  def __init__(self, window=None):
17
17
  """
18
18
  Context output mapping
19
+
20
+ :param window: Window
19
21
  """
20
22
  self.window = window
21
- self.mapping = {} # pid => meta.id
23
+ self.mapping = {} # [column_idx => [pid => meta.id]]
22
24
  self.last_pids = {} # meta.id => pid
23
25
  self.last_pid = 0 # last used PID
26
+ self.initialized = False
27
+
28
+ def init(self, force: bool = False):
29
+ """
30
+ Initialize mappings
31
+
32
+ :param force: Force reinitialize
33
+ """
34
+ if self.initialized and not force:
35
+ return
36
+ self.clear()
37
+ for n in range(0, self.window.core.tabs.NUM_COLS):
38
+ self.mapping[n] = {}
39
+ self.last_pids[n] = {}
40
+ self.initialized = True
24
41
 
25
42
  def store(self, meta: CtxMeta) -> int:
26
43
  """
@@ -29,31 +46,20 @@ class Output:
29
46
  :param meta: Meta
30
47
  :return: PID
31
48
  """
49
+ self.init()
32
50
  pid = self.window.core.tabs.get_active_pid()
33
- # check if allowed tab
34
51
  tab = self.window.core.tabs.get_tab_by_pid(pid)
35
52
  if tab is None or tab.type != Tab.TAB_CHAT:
36
53
  return 0
37
- self.mapping[pid] = meta.id
38
- self.last_pids[meta.id] = pid
54
+ col_idx = tab.column_idx
55
+ self.mapping[col_idx][pid] = meta.id
56
+ self.last_pids[col_idx][meta.id] = pid
39
57
  self.last_pid = pid
40
58
  tab = self.window.core.tabs.get_tab_by_pid(pid)
41
59
  if tab is not None:
42
60
  tab.data_id = meta.id # store meta id in tab data
43
61
  return pid
44
62
 
45
- def meta_id_in_tab(self, meta_id: int) -> bool:
46
- """
47
- Check if meta id is in tab
48
-
49
- :param meta_id: Meta ID
50
- :return: bool
51
- """
52
- for pid, mid in self.mapping.items():
53
- if mid == meta_id:
54
- return True
55
- return False
56
-
57
63
  def is_mapped(self, meta: CtxMeta) -> bool:
58
64
  """
59
65
  Check if meta is mapped
@@ -61,19 +67,46 @@ class Output:
61
67
  :param meta: Meta
62
68
  :return: bool
63
69
  """
64
- return meta.id in self.mapping.values()
70
+ self.init()
71
+ for col_idx in self.mapping:
72
+ if meta.id in self.mapping[col_idx].values():
73
+ return True
74
+ return False
65
75
 
66
- def get_meta(self, pid: int) -> CtxMeta or None:
76
+ def get_meta(self, pid: int) -> int or None:
67
77
  """
68
78
  Get meta by PID
69
79
 
70
80
  :param pid: PID
71
- :return: Meta or None
81
+ :return: meta ID or None
72
82
  """
73
- if pid in self.mapping:
74
- return self.mapping[pid]
83
+ self.init()
84
+ for col_idx in self.mapping:
85
+ if pid in self.mapping[col_idx]:
86
+ return self.mapping[col_idx][pid]
75
87
  return
76
88
 
89
+ def prepare_meta(self, tab: Tab) -> int or None:
90
+ """
91
+ Get meta ID by PID
92
+
93
+ :param tab: Tab
94
+ :return: Meta ID or None
95
+ """
96
+ self.init()
97
+ pid = tab.pid
98
+ col_idx = tab.column_idx
99
+ meta_id = None
100
+ if pid in self.mapping[col_idx]:
101
+ return self.mapping[col_idx][pid]
102
+ if meta_id is None:
103
+ meta_id = tab.data_id
104
+ if meta_id is not None:
105
+ self.mapping[col_idx][pid] = meta_id
106
+ self.last_pids[col_idx][meta_id] = pid
107
+ self.last_pid = pid
108
+ return meta_id
109
+
77
110
  def get_mapped(self, meta: CtxMeta) -> int or None:
78
111
  """
79
112
  Get PID by meta
@@ -81,75 +114,59 @@ class Output:
81
114
  :param meta: Meta
82
115
  :return: PID or None
83
116
  """
117
+ self.init()
84
118
  all = []
85
119
  pid = None
86
120
  active_pid = self.window.core.tabs.get_active_pid()
87
- for pid, meta_id in self.mapping.items():
121
+ tab = self.window.core.tabs.get_tab_by_pid(active_pid)
122
+ if tab is None:
123
+ return None
124
+ col_idx = tab.column_idx
125
+ for pid, meta_id in self.mapping[col_idx].items():
88
126
  if meta_id == meta.id:
89
127
  all.append(pid)
90
128
  for pid in all:
91
- if pid == active_pid: # at first check if active
129
+ if pid == active_pid: # at first check for current tab
92
130
  return pid
93
131
  return pid
94
132
 
95
- def get_mapped_last(self, meta: CtxMeta) -> int or None:
96
- """
97
- Get last PID by meta
98
-
99
- :param meta: Meta
100
- :return: PID or None
101
- """
102
- active_pid = self.window.core.tabs.get_active_pid()
103
- if self.mapping[active_pid] == meta.id:
104
- return active_pid # return active PID at first
105
- if meta.id in self.last_pids:
106
- return self.last_pids[meta.id]
107
- return
108
-
109
- def prepare(self, meta: CtxMeta):
110
- """
111
- Clear mapping
112
-
113
- :param meta: meta to prepare
114
- """
115
- active_pid = self.window.core.tabs.get_active_pid()
116
- if active_pid in self.mapping:
117
- del self.mapping[active_pid] # remove from mapping
118
-
119
- def clear(self):
120
- """
121
- Clear mapping
122
- """
123
- self.mapping = {}
124
- self.last_pids = {}
125
- self.last_pid = 0
126
-
127
133
  def is_empty(self) -> bool:
128
134
  """
129
135
  Check if mapping is empty
130
136
 
131
137
  :return: bool
132
138
  """
139
+ self.init()
133
140
  active_pid = self.window.core.tabs.get_active_pid()
134
- if active_pid in self.mapping:
135
- return False
141
+ for col_idx in list(self.mapping.keys()):
142
+ if active_pid in self.mapping[col_idx]:
143
+ return False
136
144
  return True
137
145
 
138
- def get_pid(self, meta: CtxMeta = None) -> int:
146
+ def get_pid(self, meta: CtxMeta = None) -> int or None:
139
147
  """
140
148
  Get PID by meta
141
149
 
142
150
  :param meta: Meta
143
151
  :return: PID
144
152
  """
153
+ self.init()
154
+ active_pid = self.window.core.tabs.get_active_pid()
145
155
  if meta is None:
146
- return self.window.core.tabs.get_active_pid()
156
+ return None
147
157
  if self.is_mapped(meta):
148
- if self.window.core.tabs.get_active_pid() != self.get_mapped(meta): # if loaded in another tab
149
- return self.store(meta) # update to current
150
- return self.get_mapped_last(meta)
151
- else:
152
- return self.store(meta)
158
+ if active_pid == self.get_mapped(meta): # if loaded in current tab
159
+ return active_pid # return current
160
+ return self.store(meta)
161
+
162
+ def clear(self):
163
+ """
164
+ Clear mapping
165
+ """
166
+ self.mapping = {}
167
+ self.last_pids = {}
168
+ self.last_pid = 0
169
+ self.initialized = False
153
170
 
154
171
  def get_current(self, meta: CtxMeta = None):
155
172
  """
@@ -176,7 +193,8 @@ class Output:
176
193
  if pid in self.window.ui.nodes['output_plain']:
177
194
  return self.window.ui.nodes['output_plain'][pid]
178
195
  else:
179
- return self.window.ui.nodes['output_plain'][0]
196
+ for pid in self.window.ui.nodes['output_plain']:
197
+ return self.window.ui.nodes['output_plain'][pid]
180
198
 
181
199
  def get_by_pid(self, pid = None):
182
200
  """
@@ -201,7 +219,8 @@ class Output:
201
219
  if pid in self.window.ui.nodes['output_plain']:
202
220
  return self.window.ui.nodes['output_plain'][pid]
203
221
  else:
204
- return self.window.ui.nodes['output_plain'][0]
222
+ for pid in self.window.ui.nodes['output_plain']:
223
+ return self.window.ui.nodes['output_plain'][pid]
205
224
 
206
225
  def get_all(self) -> list:
207
226
  """
@@ -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.11.07 23:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  class TabsDebug:
@@ -25,6 +25,7 @@ class TabsDebug:
25
25
  self.window.core.debug.add(self.id, 'current PID', str(self.window.controller.ui.tabs.get_current_pid()))
26
26
  self.window.core.debug.add(self.id, 'current IDX', str(self.window.controller.ui.tabs.get_current_idx()))
27
27
  self.window.core.debug.add(self.id, 'current Type', str(self.window.controller.ui.tabs.get_current_type()))
28
+ self.window.core.debug.add(self.id, 'current Column', str(self.window.controller.ui.tabs.get_current_column_idx()))
28
29
  self.window.core.debug.add(self.id, 'last_pid', str(self.window.core.tabs.last_pid))
29
30
  self.window.core.debug.add(self.id, 'count(pids)', str(len(self.window.core.tabs.pids)))
30
31
  self.window.core.debug.add(self.id, 'count(ctx bags)', str(len(self.window.core.ctx.container.bags)))
@@ -38,6 +39,6 @@ class TabsDebug:
38
39
 
39
40
  # mapping PID => meta.id
40
41
  self.window.core.debug.add(self.id, 'PID => meta.id', str(self.window.core.ctx.output.mapping))
41
- self.window.core.debug.add(self.id, '(last) PID => meta.id', str(self.window.core.ctx.output.last_pids))
42
+ self.window.core.debug.add(self.id, '(last) meta.id => PID', str(self.window.core.ctx.output.last_pids))
42
43
  self.window.core.debug.add(self.id, '(last) PID', str(self.window.core.ctx.output.last_pid))
43
44
  self.window.core.debug.end(self.id)
@@ -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.11.08 06:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import QUrl
@@ -38,14 +38,18 @@ class Url:
38
38
  ]
39
39
 
40
40
  # JS bridge
41
- if url.toString() == 'bridge://open_find':
42
- pid = self.window.controller.ui.tabs.get_current_pid()
41
+ if url.toString().startswith('bridge://open_find'):
42
+ pid = int(url.toString().split(':')[2])
43
43
  if pid in self.window.ui.nodes['output']:
44
44
  self.window.ui.nodes['output'][pid].find_open()
45
45
  return
46
46
  elif url.toString() == 'bridge://escape':
47
47
  self.window.controller.access.on_escape()
48
48
  return
49
+ elif url.toString() == 'bridge://focus':
50
+ pid = self.window.controller.ui.tabs.get_current_pid()
51
+ if pid in self.window.ui.nodes['output']:
52
+ self.window.ui.nodes['output'][pid].on_focus_js()
49
53
 
50
54
  # -------------
51
55
 
@@ -6,9 +6,10 @@
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.11.07 23:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
+ from pygpt_net.core.tabs import Tab
12
13
  from pygpt_net.item.ctx import CtxItem, CtxMeta
13
14
 
14
15
 
@@ -20,6 +21,15 @@ class BaseRenderer:
20
21
  :param window: Window instance
21
22
  """
22
23
  self.window = window
24
+ self.tab = None
25
+
26
+ def set_tab(self, tab: Tab):
27
+ """
28
+ Append tab
29
+
30
+ :param tab: Tab
31
+ """
32
+ self.tab = tab
23
33
 
24
34
  def prepare(self):
25
35
  """
@@ -218,13 +228,14 @@ class BaseRenderer:
218
228
  """
219
229
  pass
220
230
 
221
- def on_page_loaded(self, meta: CtxMeta = None):
231
+ def on_page_loaded(self, meta: CtxMeta = None, tab: Tab = None):
222
232
  """
223
233
  On page loaded callback
224
234
 
225
235
  :param meta: context meta
236
+ :param tab: Tab
226
237
  """
227
- pass
238
+ self.tab = tab
228
239
 
229
240
  def on_enable_edit(self, live: bool = True):
230
241
  """
@@ -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.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
@@ -50,6 +50,8 @@ class Renderer(BaseRenderer):
50
50
 
51
51
  :param meta: context PID
52
52
  """
53
+ if self.tab is not None:
54
+ return self.tab.pid # get PID from tab if exists
53
55
  return self.window.core.ctx.output.get_pid(meta)
54
56
 
55
57
  def get_or_create_pid(self, meta: CtxMeta):
@@ -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.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from datetime import datetime
@@ -128,7 +128,7 @@ class Renderer(BaseRenderer):
128
128
  :param clear: True if clear all output before append
129
129
  """
130
130
  if clear:
131
- self.clear_output()
131
+ self.clear_output(meta)
132
132
 
133
133
  i = 0
134
134
  for item in items:
@@ -399,7 +399,7 @@ class Renderer(BaseRenderer):
399
399
  :param meta: context meta
400
400
  """
401
401
  self.reset()
402
- self.get_output_node().clear()
402
+ self.get_output_node(meta).clear()
403
403
 
404
404
  def clear_input(self):
405
405
  """Clear input"""
@@ -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.07 21:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -325,7 +325,7 @@ class Body:
325
325
  html += "</div>"
326
326
  return html
327
327
 
328
- def get_html(self) -> str:
328
+ def get_html(self, pid: int) -> str:
329
329
  """
330
330
  Build webview HTML code
331
331
 
@@ -370,13 +370,14 @@ class Body:
370
370
  let scrollTimeout = null;
371
371
  let prevScroll = 0;
372
372
  let bridge;
373
+ let pid = """ + str(pid) + """
373
374
  new QWebChannel(qt.webChannelTransport, function (channel) {
374
375
  bridge = channel.objects.bridge;
375
376
  });
376
377
  history.scrollRestoration = "manual";
377
378
  document.addEventListener('keydown', function(event) {
378
379
  if (event.ctrlKey && event.key === 'f') {
379
- window.location.href = 'bridge://open_find'; // send to bridge
380
+ window.location.href = 'bridge://open_find:' + pid; // send to bridge
380
381
  event.preventDefault();
381
382
  }
382
383
  if (event.key === 'Escape') {
@@ -384,6 +385,11 @@ class Body:
384
385
  event.preventDefault();
385
386
  }
386
387
  });
388
+ document.addEventListener('click', function(event) {
389
+ if (event.target.tagName !== 'A' && !event.target.closest('a')) {
390
+ window.location.href = 'bridge://focus';
391
+ }
392
+ });
387
393
  function highlightCode() {
388
394
  document.querySelectorAll('pre code').forEach(el => {
389
395
  if (!el.classList.contains('hljs')) hljs.highlightElement(el);
@@ -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.11.26 04:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -19,6 +19,7 @@ from pygpt_net.core.text.utils import has_unclosed_code_tag
19
19
  from pygpt_net.item.ctx import CtxItem, CtxMeta
20
20
  from pygpt_net.ui.widget.textarea.input import ChatInput
21
21
  from pygpt_net.utils import trans
22
+ from pygpt_net.core.tabs import Tab
22
23
 
23
24
  from .body import Body
24
25
  from .helpers import Helpers
@@ -59,16 +60,17 @@ class Renderer(BaseRenderer):
59
60
  node.set_meta(meta)
60
61
  self.reset(meta)
61
62
 
62
- def on_page_loaded(self, meta: CtxMeta = None):
63
+ def on_page_loaded(self, meta: CtxMeta = None, tab: Tab = None):
63
64
  """
64
65
  On page loaded callback from WebEngine widget
65
66
 
66
67
  :param meta: context meta
68
+ :param tab: Tab
67
69
  """
68
70
  if meta is None:
69
71
  return
70
- pid = self.get_or_create_pid(meta)
71
- if pid is None:
72
+ pid = tab.pid
73
+ if pid is None or pid not in self.pids:
72
74
  return
73
75
  self.pids[pid].loaded = True
74
76
  if self.pids[pid].html != "" and not self.pids[pid].use_buffer:
@@ -818,7 +820,7 @@ class Renderer(BaseRenderer):
818
820
  if self.pids[pid].loaded:
819
821
  return # wait for page load
820
822
 
821
- html = self.body.get_html()
823
+ html = self.body.get_html(pid)
822
824
  self.pids[pid].document = html
823
825
  self.get_output_node_by_pid(pid).setHtml(html, baseUrl="file://")
824
826