pygpt-net 2.6.3__py3-none-any.whl → 2.6.4__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 (66) hide show
  1. pygpt_net/CHANGELOG.txt +5 -0
  2. pygpt_net/__init__.py +1 -1
  3. pygpt_net/config.py +55 -65
  4. pygpt_net/controller/chat/chat.py +38 -35
  5. pygpt_net/controller/chat/render.py +144 -217
  6. pygpt_net/controller/chat/stream.py +51 -25
  7. pygpt_net/controller/config/config.py +39 -42
  8. pygpt_net/controller/config/field/checkbox.py +16 -12
  9. pygpt_net/controller/config/field/checkbox_list.py +36 -31
  10. pygpt_net/controller/config/field/cmd.py +51 -57
  11. pygpt_net/controller/config/field/combo.py +33 -16
  12. pygpt_net/controller/config/field/dictionary.py +48 -55
  13. pygpt_net/controller/config/field/input.py +50 -32
  14. pygpt_net/controller/config/field/slider.py +40 -45
  15. pygpt_net/controller/config/field/textarea.py +20 -6
  16. pygpt_net/controller/config/placeholder.py +110 -231
  17. pygpt_net/controller/lang/mapping.py +57 -95
  18. pygpt_net/controller/lang/plugins.py +64 -55
  19. pygpt_net/controller/lang/settings.py +39 -38
  20. pygpt_net/controller/layout/layout.py +11 -2
  21. pygpt_net/controller/plugins/plugins.py +19 -1
  22. pygpt_net/controller/ui/mode.py +107 -125
  23. pygpt_net/core/bridge/bridge.py +5 -5
  24. pygpt_net/core/command/command.py +149 -219
  25. pygpt_net/core/ctx/ctx.py +94 -146
  26. pygpt_net/core/debug/debug.py +48 -58
  27. pygpt_net/core/models/models.py +74 -112
  28. pygpt_net/core/modes/modes.py +13 -21
  29. pygpt_net/core/plugins/plugins.py +154 -177
  30. pygpt_net/core/presets/presets.py +103 -176
  31. pygpt_net/core/render/web/body.py +2 -3
  32. pygpt_net/core/render/web/renderer.py +109 -180
  33. pygpt_net/core/text/utils.py +28 -44
  34. pygpt_net/core/tokens/tokens.py +104 -203
  35. pygpt_net/data/config/config.json +2 -2
  36. pygpt_net/data/config/models.json +2 -2
  37. pygpt_net/item/ctx.py +141 -139
  38. pygpt_net/plugin/agent/plugin.py +2 -1
  39. pygpt_net/plugin/audio_output/plugin.py +5 -2
  40. pygpt_net/plugin/base/plugin.py +77 -93
  41. pygpt_net/plugin/bitbucket/plugin.py +3 -2
  42. pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
  43. pygpt_net/plugin/cmd_custom/plugin.py +3 -2
  44. pygpt_net/plugin/cmd_files/plugin.py +3 -2
  45. pygpt_net/plugin/cmd_history/plugin.py +3 -2
  46. pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
  47. pygpt_net/plugin/cmd_serial/plugin.py +3 -2
  48. pygpt_net/plugin/cmd_system/plugin.py +3 -6
  49. pygpt_net/plugin/cmd_web/plugin.py +3 -2
  50. pygpt_net/plugin/experts/plugin.py +2 -2
  51. pygpt_net/plugin/facebook/plugin.py +3 -4
  52. pygpt_net/plugin/github/plugin.py +4 -2
  53. pygpt_net/plugin/google/plugin.py +3 -3
  54. pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
  55. pygpt_net/plugin/mailer/plugin.py +3 -5
  56. pygpt_net/plugin/openai_vision/plugin.py +3 -2
  57. pygpt_net/plugin/real_time/plugin.py +52 -60
  58. pygpt_net/plugin/slack/plugin.py +3 -4
  59. pygpt_net/plugin/telegram/plugin.py +3 -4
  60. pygpt_net/plugin/twitter/plugin.py +3 -4
  61. pygpt_net/ui/widget/textarea/web.py +18 -14
  62. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/METADATA +7 -2
  63. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/RECORD +66 -66
  64. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/LICENSE +0 -0
  65. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.dist-info}/WHEEL +0 -0
  66. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.4.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.09 19:00:00 #
9
+ # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -35,6 +35,22 @@ from pygpt_net.provider.core.preset.json_file import JsonFileProvider
35
35
 
36
36
 
37
37
  class Presets:
38
+ _MODE_TO_ATTR = {
39
+ MODE_CHAT: "chat",
40
+ MODE_COMPLETION: "completion",
41
+ MODE_IMAGE: "img",
42
+ MODE_VISION: "vision",
43
+ MODE_LANGCHAIN: "langchain",
44
+ MODE_ASSISTANT: "assistant",
45
+ MODE_LLAMA_INDEX: "llama_index",
46
+ MODE_AGENT: "agent",
47
+ MODE_AGENT_LLAMA: "agent_llama",
48
+ MODE_AGENT_OPENAI: "agent_openai",
49
+ MODE_EXPERT: "expert",
50
+ MODE_RESEARCH: "research",
51
+ MODE_COMPUTER: "computer",
52
+ MODE_AUDIO: "audio",
53
+ }
38
54
 
39
55
  def __init__(self, window=None):
40
56
  """
@@ -44,7 +60,7 @@ class Presets:
44
60
  """
45
61
  self.window = window
46
62
  self.provider = JsonFileProvider(window)
47
- self.items = {}
63
+ self.items: Dict[str, PresetItem] = {}
48
64
 
49
65
  def install(self):
50
66
  """Install provider data"""
@@ -70,114 +86,34 @@ class Presets:
70
86
 
71
87
  def append_current(self):
72
88
  """Append current presets"""
73
- # create empty presets
74
- curr_chat = self.build()
75
- curr_completion = self.build()
76
- curr_img = self.build()
77
- curr_vision = self.build()
78
- curr_langchain = self.build()
79
- curr_assistant = self.build()
80
- curr_llama = self.build()
81
- curr_agent = self.build()
82
- curr_agent_llama = self.build()
83
- curr_agent_openai = self.build()
84
- curr_expert = self.build()
85
- curr_audio = self.build()
86
- curr_computer = self.build()
87
-
88
- # prepare ids
89
- id_chat = 'current.chat'
90
- id_completion = 'current.completion'
91
- id_img = 'current.img'
92
- id_vision = 'current.vision'
93
- id_langchain = 'current.langchain'
94
- id_assistant = 'current.assistant'
95
- id_llama = 'current.llama_index'
96
- id_agent = 'current.agent'
97
- id_agent_llama = 'current.agent_llama'
98
- id_agent_openai = 'current.agent_openai'
99
- id_expert = 'current.expert'
100
- id_audio = 'current.audio'
101
- id_computer = 'current.computer'
102
-
103
- # set default initial prompt for chat mode
104
- curr_chat.prompt = self.window.core.prompt.get('default')
105
-
106
- # get data from presets if exists
107
- if id_chat in self.items:
108
- curr_chat = self.items[id_chat]
109
- if id_completion in self.items:
110
- curr_completion = self.items[id_completion]
111
- if id_img in self.items:
112
- curr_img = self.items[id_img]
113
- if id_vision in self.items:
114
- curr_vision = self.items[id_vision]
115
- if id_langchain in self.items:
116
- curr_langchain = self.items[id_langchain]
117
- if id_assistant in self.items:
118
- curr_assistant = self.items[id_assistant]
119
- if id_llama in self.items:
120
- curr_llama = self.items[id_llama]
121
- if id_agent in self.items:
122
- curr_agent = self.items[id_agent]
123
- if id_agent_llama in self.items:
124
- curr_agent_llama = self.items[id_agent_llama]
125
- if id_agent_openai in self.items:
126
- curr_agent_openai = self.items[id_agent_openai]
127
- if id_expert in self.items:
128
- curr_expert = self.items[id_expert]
129
- if id_audio in self.items:
130
- curr_audio = self.items[id_audio]
131
- if id_computer in self.items:
132
- curr_computer = self.items[id_computer]
133
-
134
- # allow usage in specific mode
135
- curr_chat.chat = True
136
- curr_completion.completion = True
137
- curr_img.img = True
138
- curr_vision.vision = True
139
- curr_langchain.langchain = True
140
- curr_assistant.assistant = True
141
- curr_llama.llama_index = True
142
- curr_agent.agent = True
143
- curr_agent_llama.agent_llama = True
144
- curr_agent_openai.agent_openai = True
145
- curr_expert.expert = True
146
- curr_audio.audio = True
147
- curr_computer.computer = True
148
-
149
- # always apply default name
150
- curr_chat.name = '*'
151
- curr_completion.name = '*'
152
- curr_img.name = '*'
153
- curr_vision.name = '*'
154
- curr_langchain.name = '*'
155
- curr_assistant.name = '*'
156
- curr_llama.name = '*'
157
- curr_agent.name = '*'
158
- curr_agent_llama.name = '*'
159
- curr_agent_openai.name = '*'
160
- curr_expert.name = '*'
161
- curr_audio.name = '*'
162
- curr_computer.name = '*'
163
-
164
- # append at first position
165
- self.items = {
166
- id_chat: curr_chat,
167
- id_completion: curr_completion,
168
- id_img: curr_img,
169
- id_vision: curr_vision,
170
- id_langchain: curr_langchain,
171
- id_assistant: curr_assistant,
172
- id_llama: curr_llama,
173
- id_agent: curr_agent,
174
- id_agent_llama: curr_agent_llama,
175
- id_agent_openai: curr_agent_openai,
176
- id_expert: curr_expert,
177
- id_audio: curr_audio,
178
- id_computer: curr_computer,
179
- **self.items
180
- }
89
+ items = self.items
90
+ ids = [
91
+ ("current.chat", "chat"),
92
+ ("current.completion", "completion"),
93
+ ("current.img", "img"),
94
+ ("current.vision", "vision"),
95
+ ("current.langchain", "langchain"),
96
+ ("current.assistant", "assistant"),
97
+ ("current.llama_index", "llama_index"),
98
+ ("current.agent", "agent"),
99
+ ("current.agent_llama", "agent_llama"),
100
+ ("current.agent_openai", "agent_openai"),
101
+ ("current.expert", "expert"),
102
+ ("current.audio", "audio"),
103
+ ("current.computer", "computer"),
104
+ ]
105
+ front = {}
106
+ for pid, attr in ids:
107
+ if pid in items:
108
+ obj = items[pid]
109
+ else:
110
+ obj = self.build()
111
+ if pid == "current.chat":
112
+ obj.prompt = self.window.core.prompt.get("default")
113
+ setattr(obj, attr, True)
114
+ obj.name = "*"
115
+ front[pid] = obj
116
+ self.items = {**front, **items}
181
117
 
182
118
  def exists(self, id: str) -> bool:
183
119
  """
@@ -186,9 +122,7 @@ class Presets:
186
122
  :param id: preset id
187
123
  :return: True if exists
188
124
  """
189
- if id in self.items:
190
- return True
191
- return False
125
+ return id in self.items
192
126
 
193
127
  def exists_uuid(self, uuid: str) -> bool:
194
128
  """
@@ -197,10 +131,7 @@ class Presets:
197
131
  :param uuid: preset uuid
198
132
  :return: True if exists
199
133
  """
200
- for id in self.items:
201
- if self.items[id].uuid == uuid:
202
- return True
203
- return False
134
+ return any(item.uuid == uuid for item in self.items.values())
204
135
 
205
136
  def enable(self, id: str):
206
137
  """
@@ -266,10 +197,11 @@ class Presets:
266
197
  :param id : preset id
267
198
  :return: True if exists
268
199
  """
269
- presets = self.get_by_mode(mode)
270
- if id in presets:
271
- return True
272
- return False
200
+ item = self.items.get(id)
201
+ if not item:
202
+ return False
203
+ attr = self._MODE_TO_ATTR.get(mode)
204
+ return bool(attr and getattr(item, attr, False))
273
205
 
274
206
  def get_by_idx(self, idx: int, mode: str) -> str:
275
207
  """
@@ -279,8 +211,16 @@ class Presets:
279
211
  :param mode: mode
280
212
  :return: preset id
281
213
  """
282
- presets = self.get_by_mode(mode)
283
- return list(presets.keys())[idx]
214
+ attr = self._MODE_TO_ATTR.get(mode)
215
+ if not attr:
216
+ raise IndexError(idx)
217
+ i = 0
218
+ for key, item in self.items.items():
219
+ if getattr(item, attr, False):
220
+ if i == idx:
221
+ return key
222
+ i += 1
223
+ raise IndexError(idx)
284
224
 
285
225
  def get_by_id(self, mode: str, id: str) -> Optional[PresetItem]:
286
226
  """
@@ -290,10 +230,13 @@ class Presets:
290
230
  :param id: preset id
291
231
  :return: preset item
292
232
  """
293
- presets = self.get_by_mode(mode)
294
- if id in presets:
295
- return presets[id]
296
- return None
233
+ item = self.items.get(id)
234
+ if not item:
235
+ return None
236
+ attr = self._MODE_TO_ATTR.get(mode)
237
+ if not attr:
238
+ return None
239
+ return item if getattr(item, attr, False) else None
297
240
 
298
241
  def get(self, id: str) -> Optional[PresetItem]:
299
242
  """
@@ -302,9 +245,7 @@ class Presets:
302
245
  :param id: preset ID
303
246
  :return: preset item
304
247
  """
305
- if id in self.items:
306
- return self.items[id]
307
- return None
248
+ return self.items.get(id)
308
249
 
309
250
  def get_by_uuid(self, uuid: str) -> Optional[PresetItem]:
310
251
  """
@@ -313,10 +254,7 @@ class Presets:
313
254
  :param uuid: preset UUID
314
255
  :return: preset item
315
256
  """
316
- for id in self.items:
317
- if self.items[id].uuid == uuid:
318
- return self.items[id]
319
- return None
257
+ return next((item for item in self.items.values() if item.uuid == uuid), None)
320
258
 
321
259
  def get_by_mode(self, mode: str) -> Dict[str, PresetItem]:
322
260
  """
@@ -325,24 +263,10 @@ class Presets:
325
263
  :param mode: mode name
326
264
  :return: presets dict for mode
327
265
  """
328
- presets = {}
329
- for id in self.items:
330
- if (mode == MODE_CHAT and self.items[id].chat) \
331
- or (mode == MODE_COMPLETION and self.items[id].completion) \
332
- or (mode == MODE_IMAGE and self.items[id].img) \
333
- or (mode == MODE_VISION and self.items[id].vision) \
334
- or (mode == MODE_LANGCHAIN and self.items[id].langchain) \
335
- or (mode == MODE_ASSISTANT and self.items[id].assistant) \
336
- or (mode == MODE_LLAMA_INDEX and self.items[id].llama_index) \
337
- or (mode == MODE_AGENT and self.items[id].agent) \
338
- or (mode == MODE_AGENT_LLAMA and self.items[id].agent_llama) \
339
- or (mode == MODE_AGENT_OPENAI and self.items[id].agent_openai) \
340
- or (mode == MODE_EXPERT and self.items[id].expert) \
341
- or (mode == MODE_RESEARCH and self.items[id].research) \
342
- or (mode == MODE_COMPUTER and self.items[id].computer) \
343
- or (mode == MODE_AUDIO and self.items[id].audio):
344
- presets[id] = self.items[id]
345
- return presets
266
+ attr = self._MODE_TO_ATTR.get(mode)
267
+ if not attr:
268
+ return {}
269
+ return {id: item for id, item in self.items.items() if getattr(item, attr, False)}
346
270
 
347
271
  def get_idx_by_id(self, mode: str, id: str) -> int:
348
272
  """
@@ -352,12 +276,15 @@ class Presets:
352
276
  :param id: preset id
353
277
  :return: preset idx
354
278
  """
355
- presets = self.get_by_mode(mode)
279
+ attr = self._MODE_TO_ATTR.get(mode)
280
+ if not attr:
281
+ return 0
356
282
  i = 0
357
- for key in presets:
358
- if key == id:
359
- return i
360
- i += 1
283
+ for key, item in self.items.items():
284
+ if getattr(item, attr, False):
285
+ if key == id:
286
+ return i
287
+ i += 1
361
288
  return 0
362
289
 
363
290
  def get_default(self, mode: str) -> Optional[str]:
@@ -367,10 +294,13 @@ class Presets:
367
294
  :param mode: mode name
368
295
  :return: default prompt name
369
296
  """
370
- presets = self.get_by_mode(mode)
371
- if len(presets) == 0:
297
+ attr = self._MODE_TO_ATTR.get(mode)
298
+ if not attr:
372
299
  return None
373
- return list(presets.keys())[0]
300
+ for key, item in self.items.items():
301
+ if getattr(item, attr, False):
302
+ return key
303
+ return None
374
304
 
375
305
  def get_duplicate_name(self, id: str) -> Tuple[str, str]:
376
306
  """
@@ -400,7 +330,7 @@ class Presets:
400
330
  self.items[id] = copy.deepcopy(self.items[prev_id])
401
331
  self.items[id].name = name
402
332
  self.items[id].filename = id
403
- self.items[id].uuid = str(uuid.uuid4()) # generate new uuid
333
+ self.items[id].uuid = str(uuid.uuid4())
404
334
  self.sort_by_name()
405
335
  return id
406
336
 
@@ -420,7 +350,6 @@ class Presets:
420
350
 
421
351
  if remove_file:
422
352
  self.provider.remove(id)
423
- # self.load_presets()
424
353
 
425
354
  def sort_by_name(self):
426
355
  """Sort presets by name"""
@@ -479,10 +408,8 @@ class Presets:
479
408
  def load(self):
480
409
  """Load presets templates"""
481
410
  self.items = self.provider.load()
482
- self.patch_empty() # patch empty UUIDs and filenames
483
- self.patch_duplicated() # patch duplicated UUIDs
484
-
485
- # sort presets
411
+ self.patch_empty()
412
+ self.patch_duplicated()
486
413
  self.sort_by_name()
487
414
  self.append_current()
488
415
 
@@ -538,12 +465,12 @@ class Presets:
538
465
  def patch_empty(self):
539
466
  """Patch UUIDs for all presets"""
540
467
  patched = False
541
- for id in self.items:
542
- if self.items[id].uuid is None:
543
- self.items[id].uuid = str(uuid.uuid4())
468
+ for key, item in self.items.items():
469
+ if item.uuid is None:
470
+ item.uuid = str(uuid.uuid4())
544
471
  patched = True
545
- if self.items[id].filename is None or self.items[id].filename == "":
546
- self.items[id].filename = id
472
+ if item.filename is None or item.filename == "":
473
+ item.filename = key
547
474
  patched = True
548
475
  if patched:
549
476
  self.save_all()
@@ -551,11 +478,11 @@ class Presets:
551
478
  def patch_duplicated(self):
552
479
  """Patch UUIDs for all presets"""
553
480
  patched = False
554
- uuids = []
555
- for id in self.items:
556
- if self.items[id].uuid in uuids:
557
- self.items[id].uuid = str(uuid.uuid4())
481
+ uuids = set()
482
+ for item in self.items.values():
483
+ if item.uuid in uuids:
484
+ item.uuid = str(uuid.uuid4())
558
485
  patched = True
559
- uuids.append(self.items[id].uuid)
486
+ uuids.add(item.uuid)
560
487
  if patched:
561
488
  self.save_all()
@@ -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.15 03:00:00 #
9
+ # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -200,7 +200,7 @@ class Body:
200
200
  }
201
201
  function scrollToBottom(live = false) {
202
202
  const el = document.scrollingElement || document.documentElement;
203
- const marginPx = 300;
203
+ const marginPx = 450;
204
204
  let behavior = 'instant';
205
205
  if (live == true) {
206
206
  behavior = 'instant';
@@ -211,7 +211,6 @@ class Body:
211
211
  el.scrollTo({ top: el.scrollHeight, behavior });
212
212
  }
213
213
  prevScroll = el.scrollHeight;
214
- getScrollPosition();
215
214
  }
216
215
  function sanitize(content) {
217
216
  return content.replace(/</g, '<').replace(/>/g, '>');