pygpt-net 2.6.29__py3-none-any.whl → 2.6.30__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 (113) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/{container.py → app_core.py} +5 -6
  4. pygpt_net/controller/access/control.py +1 -9
  5. pygpt_net/controller/assistant/assistant.py +4 -4
  6. pygpt_net/controller/assistant/batch.py +7 -7
  7. pygpt_net/controller/assistant/files.py +4 -4
  8. pygpt_net/controller/assistant/threads.py +3 -3
  9. pygpt_net/controller/attachment/attachment.py +4 -7
  10. pygpt_net/controller/chat/common.py +1 -1
  11. pygpt_net/controller/chat/stream.py +961 -294
  12. pygpt_net/controller/chat/vision.py +11 -19
  13. pygpt_net/controller/config/placeholder.py +1 -1
  14. pygpt_net/controller/ctx/ctx.py +1 -1
  15. pygpt_net/controller/ctx/summarizer.py +1 -1
  16. pygpt_net/controller/mode/mode.py +21 -12
  17. pygpt_net/controller/plugins/settings.py +3 -2
  18. pygpt_net/controller/presets/editor.py +112 -99
  19. pygpt_net/controller/theme/theme.py +3 -2
  20. pygpt_net/controller/ui/vision.py +4 -4
  21. pygpt_net/core/agents/legacy.py +2 -2
  22. pygpt_net/core/agents/runners/openai_workflow.py +2 -2
  23. pygpt_net/core/assistants/files.py +5 -5
  24. pygpt_net/core/assistants/store.py +4 -4
  25. pygpt_net/core/bridge/bridge.py +3 -3
  26. pygpt_net/core/bridge/worker.py +28 -9
  27. pygpt_net/core/debug/console/console.py +2 -2
  28. pygpt_net/core/debug/presets.py +2 -2
  29. pygpt_net/core/experts/experts.py +2 -2
  30. pygpt_net/core/modes/modes.py +2 -2
  31. pygpt_net/core/presets/presets.py +3 -3
  32. pygpt_net/core/tokens/tokens.py +4 -4
  33. pygpt_net/core/types/mode.py +5 -2
  34. pygpt_net/core/vision/analyzer.py +1 -1
  35. pygpt_net/data/config/config.json +6 -3
  36. pygpt_net/data/config/models.json +75 -3
  37. pygpt_net/data/config/modes.json +3 -9
  38. pygpt_net/data/config/settings.json +89 -31
  39. pygpt_net/data/config/settings_section.json +2 -2
  40. pygpt_net/data/locale/locale.de.ini +2 -2
  41. pygpt_net/data/locale/locale.en.ini +9 -2
  42. pygpt_net/data/locale/locale.es.ini +2 -2
  43. pygpt_net/data/locale/locale.fr.ini +2 -2
  44. pygpt_net/data/locale/locale.it.ini +2 -2
  45. pygpt_net/data/locale/locale.pl.ini +3 -3
  46. pygpt_net/data/locale/locale.uk.ini +2 -2
  47. pygpt_net/data/locale/locale.zh.ini +2 -2
  48. pygpt_net/item/model.py +23 -3
  49. pygpt_net/plugin/openai_dalle/plugin.py +4 -4
  50. pygpt_net/plugin/openai_vision/plugin.py +12 -13
  51. pygpt_net/provider/agents/openai/agent.py +5 -5
  52. pygpt_net/provider/agents/openai/agent_b2b.py +5 -5
  53. pygpt_net/provider/agents/openai/agent_planner.py +5 -6
  54. pygpt_net/provider/agents/openai/agent_with_experts.py +5 -5
  55. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -4
  56. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -4
  57. pygpt_net/provider/agents/openai/bot_researcher.py +2 -2
  58. pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
  59. pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -1
  60. pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
  61. pygpt_net/provider/agents/openai/evolve.py +5 -5
  62. pygpt_net/provider/agents/openai/supervisor.py +4 -4
  63. pygpt_net/provider/api/__init__.py +27 -0
  64. pygpt_net/provider/api/anthropic/__init__.py +68 -0
  65. pygpt_net/provider/api/google/__init__.py +262 -0
  66. pygpt_net/provider/api/google/audio.py +114 -0
  67. pygpt_net/provider/api/google/chat.py +552 -0
  68. pygpt_net/provider/api/google/image.py +287 -0
  69. pygpt_net/provider/api/google/tools.py +222 -0
  70. pygpt_net/provider/api/google/vision.py +129 -0
  71. pygpt_net/provider/{gpt → api/openai}/__init__.py +2 -2
  72. pygpt_net/provider/{gpt → api/openai}/agents/computer.py +1 -1
  73. pygpt_net/provider/{gpt → api/openai}/agents/experts.py +1 -1
  74. pygpt_net/provider/{gpt → api/openai}/agents/response.py +1 -1
  75. pygpt_net/provider/{gpt → api/openai}/assistants.py +1 -1
  76. pygpt_net/provider/{gpt → api/openai}/chat.py +15 -8
  77. pygpt_net/provider/{gpt → api/openai}/completion.py +1 -1
  78. pygpt_net/provider/{gpt → api/openai}/image.py +1 -1
  79. pygpt_net/provider/{gpt → api/openai}/remote_tools.py +1 -1
  80. pygpt_net/provider/{gpt → api/openai}/responses.py +34 -20
  81. pygpt_net/provider/{gpt → api/openai}/store.py +2 -2
  82. pygpt_net/provider/{gpt → api/openai}/vision.py +1 -1
  83. pygpt_net/provider/{gpt → api/openai}/worker/assistants.py +4 -4
  84. pygpt_net/provider/{gpt → api/openai}/worker/importer.py +10 -10
  85. pygpt_net/provider/audio_input/openai_whisper.py +1 -1
  86. pygpt_net/provider/audio_output/google_tts.py +12 -0
  87. pygpt_net/provider/audio_output/openai_tts.py +1 -1
  88. pygpt_net/provider/core/config/patch.py +11 -0
  89. pygpt_net/provider/core/model/patch.py +9 -0
  90. pygpt_net/provider/core/preset/json_file.py +2 -4
  91. pygpt_net/provider/llms/anthropic.py +2 -5
  92. pygpt_net/provider/llms/base.py +4 -3
  93. pygpt_net/provider/llms/openai.py +1 -1
  94. pygpt_net/provider/loaders/hub/image_vision/base.py +1 -1
  95. pygpt_net/ui/dialog/preset.py +71 -55
  96. pygpt_net/ui/main.py +6 -4
  97. pygpt_net/utils.py +9 -0
  98. {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.30.dist-info}/METADATA +32 -44
  99. {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.30.dist-info}/RECORD +113 -105
  100. /pygpt_net/provider/{gpt → api/openai}/agents/__init__.py +0 -0
  101. /pygpt_net/provider/{gpt → api/openai}/agents/client.py +0 -0
  102. /pygpt_net/provider/{gpt → api/openai}/agents/remote_tools.py +0 -0
  103. /pygpt_net/provider/{gpt → api/openai}/agents/utils.py +0 -0
  104. /pygpt_net/provider/{gpt → api/openai}/audio.py +0 -0
  105. /pygpt_net/provider/{gpt → api/openai}/computer.py +0 -0
  106. /pygpt_net/provider/{gpt → api/openai}/container.py +0 -0
  107. /pygpt_net/provider/{gpt → api/openai}/summarizer.py +0 -0
  108. /pygpt_net/provider/{gpt → api/openai}/tools.py +0 -0
  109. /pygpt_net/provider/{gpt → api/openai}/utils.py +0 -0
  110. /pygpt_net/provider/{gpt → api/openai}/worker/__init__.py +0 -0
  111. {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.30.dist-info}/LICENSE +0 -0
  112. {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.30.dist-info}/WHEEL +0 -0
  113. {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.30.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.14 13:00:00 #
9
+ # Updated Date: 2025.08.28 09:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import datetime
@@ -14,6 +14,7 @@ import os
14
14
  import shutil
15
15
  from typing import Any, Optional, Dict
16
16
 
17
+ from PySide6.QtCore import Slot
17
18
  from PySide6.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout
18
19
 
19
20
  from pygpt_net.core.types import (
@@ -101,10 +102,10 @@ class Editor:
101
102
  "type": "bool",
102
103
  "label": "preset.completion",
103
104
  },
104
- MODE_VISION: {
105
- "type": "bool",
106
- "label": "preset.vision",
107
- },
105
+ #MODE_VISION: {
106
+ # "type": "bool",
107
+ # "label": "preset.vision",
108
+ #},
108
109
  #MODE_LANGCHAIN: {
109
110
  # "type": "bool",
110
111
  # "label": "preset.langchain",
@@ -262,32 +263,30 @@ class Editor:
262
263
  if not self.tab_options_idx:
263
264
  return
264
265
  mode = self.window.core.config.get('mode')
266
+ tabs = self.window.ui.tabs['preset.editor.extra']
265
267
  if mode not in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
266
- # show base prompt
267
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, True)
268
- # hide all tabs
269
- for id in self.tab_options_idx:
270
- for tab_idx in self.tab_options_idx[id]:
271
- if self.window.ui.tabs['preset.editor.extra'].count() >= tab_idx:
272
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(tab_idx, False)
268
+ tabs.setTabVisible(0, True) # show base prompt
269
+ for opt_id in self.tab_options_idx: # hide all tabs
270
+ for tab_idx in self.tab_options_idx[opt_id]:
271
+ if tabs.count() >= tab_idx:
272
+ tabs.setTabVisible(tab_idx, False)
273
273
  return
274
274
  else:
275
- # hide all tabs
276
- for id in self.tab_options_idx:
277
- for tab_idx in self.tab_options_idx[id]:
278
- if self.window.ui.tabs['preset.editor.extra'].count() >= tab_idx:
279
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(tab_idx, False)
275
+ for opt_id in self.tab_options_idx: # hide all tabs
276
+ for tab_idx in self.tab_options_idx[opt_id]:
277
+ if tabs.count() >= tab_idx:
278
+ tabs.setTabVisible(tab_idx, False)
280
279
 
281
280
  self.toggle_extra_options_by_provider()
282
281
 
283
282
  def toggle_extra_options_by_provider(self):
284
283
  """Toggle extra options in preset editor by provider"""
285
284
  if not self.tab_options_idx:
286
- # show base prompt
287
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, True)
285
+ self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, True) # show base prompt
288
286
  return
289
287
 
290
288
  mode = self.window.core.config.get('mode')
289
+ tabs = self.window.ui.tabs['preset.editor.extra']
291
290
  key_agent = ""
292
291
 
293
292
  if mode in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
@@ -303,32 +302,29 @@ class Editor:
303
302
  option=self.options[key_agent],
304
303
  )
305
304
  if current_provider is None or current_provider == "":
306
- # show base prompt
307
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, True)
305
+ tabs.setTabVisible(0, True) # show base prompt
308
306
  return
309
307
 
310
308
  # show all tabs for current provider
311
- for id in self.tab_options_idx:
312
-
313
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, False)
314
-
315
- if id != current_provider:
316
- for tab_idx in self.tab_options_idx[id]:
317
- if self.window.ui.tabs['preset.editor.extra'].count() >= tab_idx:
318
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(tab_idx, False)
309
+ for opt_id in self.tab_options_idx:
310
+ tabs.setTabVisible(0, False)
311
+ if opt_id != current_provider:
312
+ for tab_idx in self.tab_options_idx[opt_id]:
313
+ if tabs.count() >= tab_idx:
314
+ tabs.setTabVisible(tab_idx, False)
319
315
  else:
320
- for tab_idx in self.tab_options_idx[id]:
321
- if self.window.ui.tabs['preset.editor.extra'].count() >= tab_idx:
322
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(tab_idx, True)
316
+ for tab_idx in self.tab_options_idx[opt_id]:
317
+ if tabs.count() >= tab_idx:
318
+ tabs.setTabVisible(tab_idx, True)
323
319
 
324
320
  # show base prompt if no custom options in current agent
325
321
  agent = self.window.core.agents.provider.get(current_provider)
326
322
  if not agent:
327
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, True)
323
+ tabs.setTabVisible(0, True)
328
324
  return
329
325
  option_tabs = agent.get_options()
330
326
  if not option_tabs or len(option_tabs) == 0:
331
- self.window.ui.tabs['preset.editor.extra'].setTabVisible(0, True)
327
+ tabs.setTabVisible(0, True)
332
328
 
333
329
  def load_extra_options(self, preset: PresetItem):
334
330
  """
@@ -337,7 +333,6 @@ class Editor:
337
333
  :param preset: preset item
338
334
  """
339
335
  mode = self.window.core.config.get('mode')
340
- id = None
341
336
  if mode == MODE_AGENT_OPENAI:
342
337
  if preset.agent_provider_openai is None or preset.agent_provider_openai == "":
343
338
  return
@@ -356,6 +351,8 @@ class Editor:
356
351
  if not preset.extra or id not in preset.extra:
357
352
  return
358
353
 
354
+ apply_value = self.window.controller.config.apply_value
355
+
359
356
  data_dict = preset.extra[id]
360
357
  option_tabs = agent.get_options()
361
358
  for option_tab_id in data_dict:
@@ -372,7 +369,7 @@ class Editor:
372
369
  for key in extra_options:
373
370
  value = data_dict[option_tab_id].get(key, None)
374
371
  if value is not None:
375
- self.window.controller.config.apply_value(
372
+ apply_value(
376
373
  parent_id=option_key,
377
374
  key=key,
378
375
  option=extra_options[key],
@@ -382,7 +379,7 @@ class Editor:
382
379
  # from defaults
383
380
  if "default" not in extra_options[key]:
384
381
  continue
385
- self.window.controller.config.apply_value(
382
+ apply_value(
386
383
  parent_id=option_key,
387
384
  key=key,
388
385
  option=extra_options[key],
@@ -397,6 +394,8 @@ class Editor:
397
394
  if mode not in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
398
395
  return
399
396
 
397
+ apply_value = self.window.controller.config.apply_value
398
+
400
399
  # load defaults for all tabs
401
400
  for id in self.tab_options_idx:
402
401
  agent = self.window.core.agents.provider.get(id)
@@ -411,7 +410,7 @@ class Editor:
411
410
  for key in extra_options:
412
411
  value = extra_options[key].get('default', None)
413
412
  if value is not None:
414
- self.window.controller.config.apply_value(
413
+ apply_value(
415
414
  parent_id=option_key,
416
415
  key=key,
417
416
  option=extra_options[key],
@@ -440,11 +439,13 @@ class Editor:
440
439
  elif mode == MODE_AGENT_LLAMA:
441
440
  current_provider_id = preset.agent_provider if preset else None
442
441
 
442
+ get_value = self.window.controller.config.get_value
443
+ apply_value = self.window.controller.config.apply_value
444
+
443
445
  # load defaults for all tabs
444
446
  for id in self.tab_options_idx:
445
- # skip current provider
446
447
  if current_provider_id and id == current_provider_id:
447
- continue
448
+ continue # skip current provider
448
449
  agent = self.window.core.agents.provider.get(id)
449
450
  if not agent:
450
451
  continue
@@ -456,9 +457,8 @@ class Editor:
456
457
  extra_options = option_tabs[option_tab_id]['options']
457
458
  for key in extra_options:
458
459
  value = extra_options[key].get('default', None)
459
- if value is not None:
460
- # check current, apply only if current is empty
461
- current_value = self.window.controller.config.get_value(
460
+ if value is not None: # check current, apply only if current is empty
461
+ current_value = get_value(
462
462
  parent_id=option_key,
463
463
  key=key,
464
464
  option=extra_options[key],
@@ -466,7 +466,7 @@ class Editor:
466
466
  if current_value is not None and current_value != "":
467
467
  continue
468
468
 
469
- self.window.controller.config.apply_value(
469
+ apply_value(
470
470
  parent_id=option_key,
471
471
  key=key,
472
472
  option=extra_options[key],
@@ -491,6 +491,7 @@ class Editor:
491
491
  else:
492
492
  return
493
493
 
494
+ get_value = self.window.controller.config.get_value
494
495
  options = {}
495
496
  agent = self.window.core.agents.provider.get(id)
496
497
  if not agent:
@@ -509,7 +510,7 @@ class Editor:
509
510
  data_dict[option_tab_id] = {}
510
511
  extra_options = option_tabs[option_tab_id]['options']
511
512
  for key in extra_options:
512
- data_dict[option_tab_id][key] = self.window.controller.config.get_value(
513
+ data_dict[option_tab_id][key] = get_value(
513
514
  parent_id=option_key,
514
515
  key=key,
515
516
  option=extra_options[key],
@@ -528,6 +529,8 @@ class Editor:
528
529
  ]
529
530
  agents = self.window.core.agents.provider.all()
530
531
  tabs = self.window.ui.tabs['preset.editor.extra']
532
+ build_option_widgets = self.window.ui.dialogs.preset.build_option_widgets
533
+
531
534
  tab_idx = 1
532
535
  for id in agents:
533
536
  agent = agents[id]
@@ -543,20 +546,19 @@ class Editor:
543
546
  option = option_tabs[option_tab_id]
544
547
  title = option.get('label', '')
545
548
  config_id = "agent." + id + "." + option_tab_id
546
- widgets, options = self.window.ui.dialogs.preset.build_option_widgets(config_id, option['options'])
549
+ widgets, options = build_option_widgets(config_id, option['options'])
547
550
  layout = QVBoxLayout()
548
551
  layout.setContentsMargins(0, 10, 0, 10)
549
552
 
550
- checkboxLayout = QHBoxLayout()
553
+ checkbox_layout = QHBoxLayout()
551
554
  for key in options:
552
555
  opt_layout = options[key]
553
556
  if option['options'][key]['type'] == 'bool':
554
- # checkbox
555
- checkboxLayout.addLayout(opt_layout)
557
+ checkbox_layout.addLayout(opt_layout) # checkbox
556
558
  else:
557
559
  layout.addLayout(opt_layout)
558
560
  layout.addStretch(1)
559
- layout.addLayout(checkboxLayout)
561
+ layout.addLayout(checkbox_layout)
560
562
 
561
563
  # as tab
562
564
  tab_widget = QWidget()
@@ -575,7 +577,10 @@ class Editor:
575
577
  def append_default_prompt(self):
576
578
  """Append default prompt to the preset editor"""
577
579
  mode = self.window.core.config.get('mode')
578
- if mode not in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
580
+ if mode not in [
581
+ MODE_AGENT_OPENAI,
582
+ MODE_AGENT_LLAMA
583
+ ]:
579
584
  return
580
585
 
581
586
  parent_key = ""
@@ -698,8 +703,8 @@ class Editor:
698
703
  data.completion = True
699
704
  elif mode == MODE_IMAGE:
700
705
  data.img = True
701
- elif mode == MODE_VISION:
702
- data.vision = True
706
+ # elif mode == MODE_VISION:
707
+ # data.vision = True
703
708
  # elif mode == MODE_LANGCHAIN:
704
709
  # data.langchain = True
705
710
  # elif mode == MODE_ASSISTANT:
@@ -768,7 +773,7 @@ class Editor:
768
773
  :param force: force overwrite file
769
774
  :param close: close dialog
770
775
  """
771
- id = self.window.controller.config.get_value(
776
+ preset_id = self.window.controller.config.get_value(
772
777
  parent_id=self.id,
773
778
  key="filename",
774
779
  option=self.options["filename"],
@@ -778,7 +783,7 @@ class Editor:
778
783
  MODE_CHAT,
779
784
  MODE_COMPLETION,
780
785
  MODE_IMAGE,
781
- MODE_VISION,
786
+ # MODE_VISION,
782
787
  # MODE_LANGCHAIN,
783
788
  MODE_LLAMA_INDEX,
784
789
  MODE_EXPERT,
@@ -790,11 +795,11 @@ class Editor:
790
795
  ]
791
796
 
792
797
  # disallow editing default preset
793
- if id == "current." + mode:
798
+ if preset_id == "current." + mode:
794
799
  self.window.ui.dialogs.alert("Reserved ID. Please use another ID.")
795
800
  return
796
801
 
797
- if id is None or id == "":
802
+ if preset_id is None or preset_id == "":
798
803
  name = self.window.controller.config.get_value(
799
804
  parent_id=self.id,
800
805
  key="name",
@@ -806,33 +811,34 @@ class Editor:
806
811
  return
807
812
 
808
813
  # generate new filename
809
- id = self.window.controller.presets.make_filename(name)
814
+ preset_id = self.window.controller.presets.make_filename(name)
810
815
  path = os.path.join(
811
816
  self.window.core.config.path,
812
817
  "presets",
813
- id + ".json",
818
+ preset_id + ".json",
814
819
  )
815
820
  if os.path.exists(path) and not force:
816
- id = id + '_' + datetime.datetime.now().strftime('%Y%m%d%H%M%S')
821
+ preset_id += '_' + datetime.datetime.now().strftime('%Y%m%d%H%M%S')
817
822
 
818
823
  # validate filename
819
- id = self.window.controller.presets.validate_filename(id)
824
+ preset_id = self.window.controller.presets.validate_filename(preset_id)
820
825
  is_new = False
821
- if id not in self.window.core.presets.items:
826
+ if preset_id not in self.window.core.presets.items:
822
827
  is_new = True
823
- self.window.core.presets.items[id] = self.window.core.presets.build()
828
+ self.window.core.presets.items[preset_id] = self.window.core.presets.build()
824
829
  elif not force:
825
830
  self.window.ui.dialogs.confirm(
826
831
  type='preset_exists',
827
- id=id,
832
+ id=preset_id,
828
833
  msg=trans('confirm.preset.overwrite'),
829
834
  )
830
835
  return
831
836
 
832
837
  # check if at least one mode is selected
833
838
  is_mode = False
839
+ get_value = self.window.controller.config.get_value
834
840
  for check in modes:
835
- if self.window.controller.config.get_value(
841
+ if get_value(
836
842
  parent_id=self.id,
837
843
  key=check,
838
844
  option=self.options[check],
@@ -846,12 +852,12 @@ class Editor:
846
852
  return
847
853
 
848
854
  # assign data from fields to preset object in items
849
- self.assign_data(id)
855
+ self.assign_data(preset_id)
850
856
 
851
857
  if is_new:
852
858
  # assign tmp avatar
853
859
  if self.tmp_avatar is not None:
854
- self.window.core.presets.items[id].ai_avatar = self.tmp_avatar
860
+ self.window.core.presets.items[preset_id].ai_avatar = self.tmp_avatar
855
861
  self.tmp_avatar = None
856
862
  else:
857
863
  self.tmp_avatar = None
@@ -859,26 +865,26 @@ class Editor:
859
865
  # if agent, assign experts and select only agent mode
860
866
  curr_mode = self.window.core.config.get('mode')
861
867
  if curr_mode == MODE_AGENT:
862
- self.window.core.presets.items[id].mode = [MODE_AGENT]
868
+ self.window.core.presets.items[preset_id].mode = [MODE_AGENT]
863
869
  elif curr_mode == MODE_AGENT_LLAMA:
864
- self.window.core.presets.items[id].mode = [MODE_AGENT_LLAMA]
870
+ self.window.core.presets.items[preset_id].mode = [MODE_AGENT_LLAMA]
865
871
  elif curr_mode == MODE_AGENT_OPENAI:
866
- self.window.core.presets.items[id].mode = [MODE_AGENT_OPENAI]
872
+ self.window.core.presets.items[preset_id].mode = [MODE_AGENT_OPENAI]
867
873
 
868
874
  # apply changes to current active preset
869
875
  current = self.window.core.config.get('preset')
870
- if current is not None and current == id:
871
- self.to_current(self.window.core.presets.items[id])
876
+ if current is not None and current == preset_id:
877
+ self.to_current(self.window.core.presets.items[preset_id])
872
878
  self.window.core.config.save()
873
879
 
874
880
  # update current uuid
875
- self.current = self.window.core.presets.items[id].uuid
881
+ self.current = self.window.core.presets.items[preset_id].uuid
876
882
 
877
883
  # save
878
884
  no_scroll = False
879
885
  if not is_new:
880
886
  no_scroll = True
881
- self.window.core.presets.save(id)
887
+ self.window.core.presets.save(preset_id)
882
888
  self.window.controller.presets.refresh(no_scroll=no_scroll)
883
889
 
884
890
  # close dialog
@@ -890,7 +896,7 @@ class Editor:
890
896
  parent_id=self.id,
891
897
  key="filename",
892
898
  option=self.options["filename"],
893
- value=id,
899
+ value=preset_id,
894
900
  )
895
901
  self.window.update_status(trans('status.preset.saved'))
896
902
 
@@ -898,7 +904,7 @@ class Editor:
898
904
  self.window.core.presets.sort_by_name()
899
905
 
900
906
  # switch to editing preset on save
901
- self.window.controller.presets.set(mode, id)
907
+ self.window.controller.presets.set(mode, preset_id)
902
908
  self.window.controller.presets.select_model()
903
909
 
904
910
  # update presets list
@@ -914,12 +920,12 @@ class Editor:
914
920
 
915
921
  :param id: preset id (filename)
916
922
  """
923
+ get_value = self.window.controller.config.get_value
917
924
  data_dict = {}
918
925
  for key in self.options:
919
- # assigned separately
920
926
  if key == "tool.function":
921
- continue
922
- data_dict[key] = self.window.controller.config.get_value(
927
+ continue # assigned separately
928
+ data_dict[key] = get_value(
923
929
  parent_id=self.id,
924
930
  key=key,
925
931
  option=self.options[key],
@@ -948,42 +954,46 @@ class Editor:
948
954
 
949
955
  :param preset: preset item
950
956
  """
951
- self.window.core.config.set('ai_name', preset.ai_name)
952
- self.window.core.config.set('user_name', preset.user_name)
953
- self.window.core.config.set('prompt', preset.prompt)
954
- self.window.core.config.set('temperature', preset.temperature)
957
+ config = self.window.core.config
958
+ config.set('ai_name', preset.ai_name)
959
+ config.set('user_name', preset.user_name)
960
+ config.set('prompt', preset.prompt)
961
+ config.set('temperature', preset.temperature)
955
962
 
963
+ @Slot()
956
964
  def from_current(self):
957
965
  """Copy data from current active preset"""
958
- self.window.controller.config.apply_value(
966
+ apply_value = self.window.controller.config.apply_value
967
+ get_config = self.window.core.config.get
968
+ apply_value(
959
969
  parent_id=self.id,
960
970
  key="ai_name",
961
971
  option=self.options["ai_name"],
962
- value=self.window.core.config.get('ai_name'),
972
+ value=get_config('ai_name'),
963
973
  )
964
- self.window.controller.config.apply_value(
974
+ apply_value(
965
975
  parent_id=self.id,
966
976
  key="user_name",
967
977
  option=self.options["user_name"],
968
- value=self.window.core.config.get('user_name'),
978
+ value=get_config('user_name'),
969
979
  )
970
- self.window.controller.config.apply_value(
980
+ apply_value(
971
981
  parent_id=self.id,
972
982
  key="prompt",
973
983
  option=self.options["prompt"],
974
- value=self.window.core.config.get('prompt'),
984
+ value=get_config('prompt'),
975
985
  )
976
- self.window.controller.config.apply_value(
986
+ apply_value(
977
987
  parent_id=self.id,
978
988
  key="temperature",
979
989
  option=self.options["temperature"],
980
- value=self.window.core.config.get('temperature'),
990
+ value=get_config('temperature'),
981
991
  )
982
- self.window.controller.config.apply_value(
992
+ apply_value(
983
993
  parent_id=self.id,
984
994
  key="model",
985
995
  option=self.options["model"],
986
- value=self.window.core.config.get('model'),
996
+ value=get_config('model'),
987
997
  )
988
998
 
989
999
  def update_from_global(self, key: str, value: Any):
@@ -1021,6 +1031,8 @@ class Editor:
1021
1031
  store_name = preset_name + "_" + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + file_ext
1022
1032
  avatar_path = os.path.join(avatars_dir, store_name)
1023
1033
 
1034
+ avatar_widget = self.window.ui.nodes['preset.editor.avatar']
1035
+
1024
1036
  # copy avatar to avatars directory
1025
1037
  if os.path.exists(avatar_path):
1026
1038
  os.remove(avatar_path)
@@ -1036,8 +1048,8 @@ class Editor:
1036
1048
  option=self.options["ai_avatar"],
1037
1049
  value=store_name,
1038
1050
  )
1039
- self.window.ui.nodes['preset.editor.avatar'].load_avatar(avatar_path)
1040
- self.window.ui.nodes['preset.editor.avatar'].enable_remove_button(True)
1051
+ avatar_widget.load_avatar(avatar_path)
1052
+ avatar_widget.enable_remove_button(True)
1041
1053
  return avatar_path
1042
1054
 
1043
1055
  def update_avatar_config(self, preset: PresetItem):
@@ -1046,6 +1058,7 @@ class Editor:
1046
1058
 
1047
1059
  :param preset: preset item
1048
1060
  """
1061
+ avatar_widget = self.window.ui.nodes['preset.editor.avatar']
1049
1062
  avatar_path = preset.ai_avatar
1050
1063
  if avatar_path:
1051
1064
  file_path = os.path.join(
@@ -1054,13 +1067,13 @@ class Editor:
1054
1067
  avatar_path,
1055
1068
  )
1056
1069
  if not os.path.exists(file_path):
1057
- self.window.ui.nodes['preset.editor.avatar'].remove_avatar()
1070
+ avatar_widget.remove_avatar()
1058
1071
  print("Avatar file does not exist:", file_path)
1059
1072
  return
1060
- self.window.ui.nodes['preset.editor.avatar'].load_avatar(file_path)
1061
- self.window.ui.nodes['preset.editor.avatar'].enable_remove_button(True)
1073
+ avatar_widget.load_avatar(file_path)
1074
+ avatar_widget.enable_remove_button(True)
1062
1075
  else:
1063
- self.window.ui.nodes['preset.editor.avatar'].remove_avatar()
1076
+ avatar_widget.remove_avatar()
1064
1077
 
1065
1078
  def remove_avatar(self, force: bool = False):
1066
1079
  """
@@ -15,7 +15,7 @@ from typing import Any, Optional
15
15
  from PySide6.QtWidgets import QApplication
16
16
 
17
17
  from pygpt_net.core.events import RenderEvent
18
- from pygpt_net.utils import trans
18
+ from pygpt_net.utils import trans, freeze_updates
19
19
 
20
20
  from .common import Common
21
21
  from .markdown import Markdown
@@ -55,7 +55,8 @@ class Theme:
55
55
  return
56
56
  self.window.update_status(trans("status.reloading"))
57
57
  QApplication.processEvents()
58
- self.toggle(name, force=True)
58
+ with freeze_updates(self.window):
59
+ self.toggle(name, force=True)
59
60
  self.window.update_status("")
60
61
 
61
62
  def toggle_option_by_menu(self, name: str, value: Any = None):
@@ -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.28 09:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.core.types import (
@@ -40,9 +40,9 @@ class Vision:
40
40
  mode = self.window.core.config.get("mode")
41
41
  if mode == MODE_IMAGE:
42
42
  return False
43
- if mode == MODE_VISION:
44
- return True
45
- if self.window.controller.plugins.is_type_enabled('vision'):
43
+ # if mode == MODE_VISION:
44
+ # return True
45
+ if self.window.controller.chat.vision.allowed():
46
46
  return True
47
47
  if self.is_vision_model() and mode in (
48
48
  MODE_CHAT,
@@ -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.06.28 16:00:00 #
9
+ # Updated Date: 2025.08.28 09:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import List
@@ -32,7 +32,7 @@ class Legacy:
32
32
  self.allowed_modes = [
33
33
  MODE_CHAT,
34
34
  MODE_COMPLETION,
35
- MODE_VISION,
35
+ # MODE_VISION,
36
36
  # MODE_LANGCHAIN,
37
37
  MODE_LLAMA_INDEX,
38
38
  MODE_AUDIO,
@@ -68,8 +68,8 @@ class OpenAIWorkflow(BaseRunner):
68
68
  context = agent_kwargs.get("context", BridgeContext())
69
69
  attachments = context.attachments if context else []
70
70
  history, previous_response_id = self.window.core.agents.memory.prepare_openai(context)
71
- msg = self.window.core.gpt.vision.build_agent_input(prompt, attachments) # build content with attachments
72
- self.window.core.gpt.vision.append_images(ctx) # append images to ctx if provided
71
+ msg = self.window.core.api.openai.vision.build_agent_input(prompt, attachments) # build content with attachments
72
+ self.window.core.api.openai.vision.append_images(ctx) # append images to ctx if provided
73
73
  history = history + msg
74
74
 
75
75
  # ------------ callbacks ----------------
@@ -245,12 +245,12 @@ class Files:
245
245
  if store_id is None or store_id == "":
246
246
  continue # skip if no store_id
247
247
  try:
248
- self.window.core.gpt.store.delete_store_file(store_id, file_id) # remove from vector store
248
+ self.window.core.api.openai.store.delete_store_file(store_id, file_id) # remove from vector store
249
249
  except Exception as e:
250
250
  self.window.core.debug.log("Failed to delete file from vector store: " + str(e))
251
251
  self.provider.delete_by_id(file.record_id) # delete file in DB
252
252
  try:
253
- self.window.core.gpt.store.delete_file(file.file_id) # delete file in API
253
+ self.window.core.api.openai.store.delete_file(file.file_id) # delete file in API
254
254
  except Exception as e:
255
255
  self.window.core.debug.log("Failed to delete remote file: " + str(e))
256
256
  if file.record_id in self.items:
@@ -290,9 +290,9 @@ class Files:
290
290
  :return: True if truncated
291
291
  """
292
292
  if store_id is not None:
293
- self.window.core.gpt.store.remove_from_store(store_id) # remove files from vector store
293
+ self.window.core.api.openai.store.remove_from_store(store_id) # remove files from vector store
294
294
  else:
295
- self.window.core.gpt.store.remove_from_stores() # remove files from all vector stores
295
+ self.window.core.api.openai.store.remove_from_stores() # remove files from all vector stores
296
296
  return self.truncate_local(store_id) # truncate files in DB
297
297
 
298
298
  def truncate_local(self, store_id: Optional[str] = None) -> bool:
@@ -316,7 +316,7 @@ class Files:
316
316
  :param store_id: store ID
317
317
  :return: True if imported
318
318
  """
319
- files = self.window.core.gpt.store.import_store_files(store_id)
319
+ files = self.window.core.api.openai.store.import_store_files(store_id)
320
320
  for file in files:
321
321
  self.create(file.assistant, file.thread_id, file.file_id, file.name, file.path, file.size)
322
322
  return True