pygpt-net 2.6.59__py3-none-any.whl → 2.6.61__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +9 -5
- pygpt_net/controller/__init__.py +1 -0
- pygpt_net/controller/chat/common.py +115 -6
- pygpt_net/controller/chat/input.py +4 -1
- pygpt_net/controller/presets/editor.py +442 -39
- pygpt_net/controller/presets/presets.py +121 -6
- pygpt_net/controller/settings/editor.py +0 -15
- pygpt_net/controller/theme/markdown.py +2 -5
- pygpt_net/controller/ui/ui.py +4 -7
- pygpt_net/core/agents/custom/__init__.py +281 -0
- pygpt_net/core/agents/custom/debug.py +64 -0
- pygpt_net/core/agents/custom/factory.py +109 -0
- pygpt_net/core/agents/custom/graph.py +71 -0
- pygpt_net/core/agents/custom/llama_index/__init__.py +10 -0
- pygpt_net/core/agents/custom/llama_index/factory.py +100 -0
- pygpt_net/core/agents/custom/llama_index/router_streamer.py +106 -0
- pygpt_net/core/agents/custom/llama_index/runner.py +562 -0
- pygpt_net/core/agents/custom/llama_index/stream.py +56 -0
- pygpt_net/core/agents/custom/llama_index/utils.py +253 -0
- pygpt_net/core/agents/custom/logging.py +50 -0
- pygpt_net/core/agents/custom/memory.py +51 -0
- pygpt_net/core/agents/custom/router.py +155 -0
- pygpt_net/core/agents/custom/router_streamer.py +187 -0
- pygpt_net/core/agents/custom/runner.py +455 -0
- pygpt_net/core/agents/custom/schema.py +127 -0
- pygpt_net/core/agents/custom/utils.py +193 -0
- pygpt_net/core/agents/provider.py +72 -7
- pygpt_net/core/agents/runner.py +7 -4
- pygpt_net/core/agents/runners/helpers.py +1 -1
- pygpt_net/core/agents/runners/llama_workflow.py +3 -0
- pygpt_net/core/agents/runners/openai_workflow.py +8 -1
- pygpt_net/core/db/viewer.py +11 -5
- pygpt_net/{ui/widget/builder → core/node_editor}/__init__.py +2 -2
- pygpt_net/core/{builder → node_editor}/graph.py +28 -226
- pygpt_net/core/node_editor/models.py +118 -0
- pygpt_net/core/node_editor/types.py +78 -0
- pygpt_net/core/node_editor/utils.py +17 -0
- pygpt_net/core/presets/presets.py +216 -29
- pygpt_net/core/render/markdown/parser.py +0 -2
- pygpt_net/core/render/web/renderer.py +10 -8
- pygpt_net/data/config/config.json +5 -6
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +2 -38
- pygpt_net/data/locale/locale.de.ini +64 -1
- pygpt_net/data/locale/locale.en.ini +63 -4
- pygpt_net/data/locale/locale.es.ini +64 -1
- pygpt_net/data/locale/locale.fr.ini +64 -1
- pygpt_net/data/locale/locale.it.ini +64 -1
- pygpt_net/data/locale/locale.pl.ini +65 -2
- pygpt_net/data/locale/locale.uk.ini +64 -1
- pygpt_net/data/locale/locale.zh.ini +64 -1
- pygpt_net/data/locale/plugin.cmd_system.en.ini +62 -66
- pygpt_net/item/agent.py +5 -1
- pygpt_net/item/preset.py +19 -1
- pygpt_net/provider/agents/base.py +33 -2
- pygpt_net/provider/agents/llama_index/flow_from_schema.py +92 -0
- pygpt_net/provider/agents/openai/flow_from_schema.py +96 -0
- pygpt_net/provider/core/agent/json_file.py +11 -5
- pygpt_net/provider/core/config/patch.py +10 -1
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -6
- pygpt_net/tools/agent_builder/tool.py +233 -52
- pygpt_net/tools/agent_builder/ui/dialogs.py +172 -28
- pygpt_net/tools/agent_builder/ui/list.py +37 -10
- pygpt_net/ui/__init__.py +2 -4
- pygpt_net/ui/dialog/about.py +58 -38
- pygpt_net/ui/dialog/db.py +142 -3
- pygpt_net/ui/dialog/preset.py +62 -8
- pygpt_net/ui/layout/toolbox/presets.py +52 -16
- pygpt_net/ui/main.py +1 -1
- pygpt_net/ui/widget/dialog/db.py +0 -0
- pygpt_net/ui/widget/lists/preset.py +644 -60
- pygpt_net/{core/builder → ui/widget/node_editor}/__init__.py +2 -2
- pygpt_net/ui/widget/node_editor/command.py +373 -0
- pygpt_net/ui/widget/node_editor/config.py +157 -0
- pygpt_net/ui/widget/node_editor/editor.py +2070 -0
- pygpt_net/ui/widget/node_editor/item.py +493 -0
- pygpt_net/ui/widget/node_editor/node.py +1460 -0
- pygpt_net/ui/widget/node_editor/utils.py +17 -0
- pygpt_net/ui/widget/node_editor/view.py +364 -0
- pygpt_net/ui/widget/tabs/output.py +1 -1
- pygpt_net/ui/widget/textarea/input.py +2 -2
- pygpt_net/utils.py +114 -2
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/METADATA +80 -93
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/RECORD +88 -61
- pygpt_net/core/agents/custom.py +0 -150
- pygpt_net/ui/widget/builder/editor.py +0 -2001
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.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.
|
|
9
|
+
# Updated Date: 2025.09.24 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -206,6 +206,7 @@ class Editor:
|
|
|
206
206
|
}
|
|
207
207
|
self.id = "preset"
|
|
208
208
|
self.current = None
|
|
209
|
+
self.current_id = None
|
|
209
210
|
|
|
210
211
|
def get_options(self) -> Dict[str, Dict[str, Any]]:
|
|
211
212
|
"""
|
|
@@ -246,12 +247,7 @@ class Editor:
|
|
|
246
247
|
"""Setup preset editor"""
|
|
247
248
|
# update after agents register
|
|
248
249
|
self.append_extra_config()
|
|
249
|
-
self.
|
|
250
|
-
self.window.controller.config.placeholder.apply_by_id('agent_provider_llama')
|
|
251
|
-
)
|
|
252
|
-
self.window.ui.config[self.id]['agent_provider_openai'].set_keys(
|
|
253
|
-
self.window.controller.config.placeholder.apply_by_id('agent_provider_openai')
|
|
254
|
-
)
|
|
250
|
+
self.update_providers_list()
|
|
255
251
|
|
|
256
252
|
# add hooks for config update in real-time
|
|
257
253
|
self.window.ui.add_hook("update.preset.prompt", self.hook_update)
|
|
@@ -268,63 +264,74 @@ class Editor:
|
|
|
268
264
|
tabs.setTabVisible(0, True) # show base prompt
|
|
269
265
|
for opt_id in self.tab_options_idx: # hide all tabs
|
|
270
266
|
for tab_idx in self.tab_options_idx[opt_id]:
|
|
271
|
-
if tabs.count()
|
|
267
|
+
if tabs.count() > tab_idx:
|
|
272
268
|
tabs.setTabVisible(tab_idx, False)
|
|
273
269
|
return
|
|
274
270
|
else:
|
|
275
271
|
for opt_id in self.tab_options_idx: # hide all tabs
|
|
276
272
|
for tab_idx in self.tab_options_idx[opt_id]:
|
|
277
|
-
if tabs.count()
|
|
273
|
+
if tabs.count() > tab_idx:
|
|
278
274
|
tabs.setTabVisible(tab_idx, False)
|
|
279
275
|
|
|
280
276
|
self.toggle_extra_options_by_provider()
|
|
281
277
|
|
|
282
278
|
def toggle_extra_options_by_provider(self):
|
|
283
279
|
"""Toggle extra options in preset editor by provider"""
|
|
280
|
+
tabs = self.window.ui.tabs['preset.editor.extra']
|
|
281
|
+
|
|
284
282
|
if not self.tab_options_idx:
|
|
285
|
-
|
|
283
|
+
tabs.setTabVisible(0, True) # base prompt
|
|
286
284
|
return
|
|
287
285
|
|
|
288
286
|
mode = self.window.core.config.get('mode')
|
|
289
|
-
tabs = self.window.ui.tabs['preset.editor.extra']
|
|
290
287
|
key_agent = ""
|
|
291
288
|
|
|
292
289
|
if mode in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
|
|
293
|
-
|
|
294
|
-
if mode == MODE_AGENT_LLAMA:
|
|
295
|
-
key_agent = "agent_provider"
|
|
296
|
-
elif mode == MODE_AGENT_OPENAI:
|
|
297
|
-
key_agent = "agent_provider_openai"
|
|
290
|
+
key_agent = "agent_provider_openai" if mode == MODE_AGENT_OPENAI else "agent_provider"
|
|
298
291
|
|
|
292
|
+
# 1) try from UI
|
|
299
293
|
current_provider = self.window.controller.config.get_value(
|
|
300
294
|
parent_id=self.id,
|
|
301
295
|
key=key_agent,
|
|
302
296
|
option=self.options[key_agent],
|
|
303
297
|
)
|
|
304
|
-
|
|
305
|
-
|
|
298
|
+
|
|
299
|
+
# 2) fallback to current preset
|
|
300
|
+
if not current_provider or current_provider == "_":
|
|
301
|
+
preset = self.window.core.presets.get_by_uuid(self.current)
|
|
302
|
+
if preset:
|
|
303
|
+
current_provider = getattr(preset, key_agent, None)
|
|
304
|
+
|
|
305
|
+
# 3) if still not set -> show base prompt
|
|
306
|
+
if not current_provider or current_provider == "_":
|
|
307
|
+
tabs.setTabVisible(0, True)
|
|
306
308
|
return
|
|
307
309
|
|
|
308
|
-
#
|
|
310
|
+
# first hide all tabs
|
|
309
311
|
for opt_id in self.tab_options_idx:
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
for tab_idx in self.tab_options_idx[opt_id]:
|
|
317
|
-
if tabs.count() >= tab_idx:
|
|
318
|
-
tabs.setTabVisible(tab_idx, True)
|
|
312
|
+
for tab_idx in self.tab_options_idx[opt_id]:
|
|
313
|
+
if tabs.count() > tab_idx:
|
|
314
|
+
tabs.setTabVisible(tab_idx, False)
|
|
315
|
+
|
|
316
|
+
# hide base prompt
|
|
317
|
+
tabs.setTabVisible(0, False)
|
|
319
318
|
|
|
320
|
-
# show
|
|
321
|
-
|
|
319
|
+
# show tabs for current provider
|
|
320
|
+
for tab_idx in self.tab_options_idx.get(current_provider, []):
|
|
321
|
+
if tabs.count() > tab_idx:
|
|
322
|
+
tabs.setTabVisible(tab_idx, True)
|
|
323
|
+
|
|
324
|
+
# if not found, show base prompt
|
|
325
|
+
agent = self.window.core.agents.provider.get(current_provider, mode)
|
|
322
326
|
if not agent:
|
|
323
327
|
tabs.setTabVisible(0, True)
|
|
324
328
|
return
|
|
325
329
|
option_tabs = agent.get_options()
|
|
326
|
-
if not option_tabs
|
|
330
|
+
if not option_tabs:
|
|
327
331
|
tabs.setTabVisible(0, True)
|
|
332
|
+
else:
|
|
333
|
+
# not agent mode -> show base prompt
|
|
334
|
+
tabs.setTabVisible(0, True)
|
|
328
335
|
|
|
329
336
|
def load_extra_options(self, preset: PresetItem):
|
|
330
337
|
"""
|
|
@@ -345,7 +352,7 @@ class Editor:
|
|
|
345
352
|
return
|
|
346
353
|
|
|
347
354
|
# update options in UI
|
|
348
|
-
agent = self.window.core.agents.provider.get(id)
|
|
355
|
+
agent = self.window.core.agents.provider.get(id, mode)
|
|
349
356
|
if not agent:
|
|
350
357
|
return
|
|
351
358
|
if not preset.extra or id not in preset.extra:
|
|
@@ -398,7 +405,7 @@ class Editor:
|
|
|
398
405
|
|
|
399
406
|
# load defaults for all tabs
|
|
400
407
|
for id in self.tab_options_idx:
|
|
401
|
-
agent = self.window.core.agents.provider.get(id)
|
|
408
|
+
agent = self.window.core.agents.provider.get(id, mode)
|
|
402
409
|
if not agent:
|
|
403
410
|
continue
|
|
404
411
|
option_tabs = agent.get_options()
|
|
@@ -483,7 +490,6 @@ class Editor:
|
|
|
483
490
|
exclude_ids = [
|
|
484
491
|
"__prompt__",
|
|
485
492
|
]
|
|
486
|
-
id = None
|
|
487
493
|
if mode == MODE_AGENT_OPENAI:
|
|
488
494
|
id = preset.agent_provider_openai
|
|
489
495
|
elif mode == MODE_AGENT_LLAMA:
|
|
@@ -493,7 +499,7 @@ class Editor:
|
|
|
493
499
|
|
|
494
500
|
get_value = self.window.controller.config.get_value
|
|
495
501
|
options = {}
|
|
496
|
-
agent = self.window.core.agents.provider.get(id)
|
|
502
|
+
agent = self.window.core.agents.provider.get(id, mode)
|
|
497
503
|
if not agent:
|
|
498
504
|
return options
|
|
499
505
|
option_tabs = agent.get_options()
|
|
@@ -574,6 +580,217 @@ class Editor:
|
|
|
574
580
|
|
|
575
581
|
self.built = True
|
|
576
582
|
|
|
583
|
+
def _ensure_agent_tab_properties(self):
|
|
584
|
+
"""
|
|
585
|
+
Ensure every existing extra tab has the 'agent_id' property set,
|
|
586
|
+
based on current self.tab_options_idx mapping.
|
|
587
|
+
This makes it possible to rebuild the mapping after insertions/removals.
|
|
588
|
+
"""
|
|
589
|
+
tabs = self.window.ui.tabs['preset.editor.extra']
|
|
590
|
+
if not tabs:
|
|
591
|
+
return
|
|
592
|
+
for a_id, indices in self.tab_options_idx.items():
|
|
593
|
+
for idx in indices:
|
|
594
|
+
if 0 < idx < tabs.count():
|
|
595
|
+
w = tabs.widget(idx)
|
|
596
|
+
if w is not None and not w.property("agent_id"):
|
|
597
|
+
w.setProperty("agent_id", a_id)
|
|
598
|
+
|
|
599
|
+
def _rebuild_tab_index_mapping(self):
|
|
600
|
+
"""
|
|
601
|
+
Rebuild self.tab_options_idx by scanning actual tabs and grouping by 'agent_id' property.
|
|
602
|
+
Base prompt tab (index 0) is ignored.
|
|
603
|
+
"""
|
|
604
|
+
tabs = self.window.ui.tabs['preset.editor.extra']
|
|
605
|
+
new_map = {}
|
|
606
|
+
if not tabs:
|
|
607
|
+
self.tab_options_idx = new_map
|
|
608
|
+
return
|
|
609
|
+
|
|
610
|
+
for i in range(1, tabs.count()):
|
|
611
|
+
w = tabs.widget(i)
|
|
612
|
+
if w is None:
|
|
613
|
+
continue
|
|
614
|
+
a_id = w.property("agent_id")
|
|
615
|
+
if not a_id:
|
|
616
|
+
# If a tab has no agent tag, we skip it; it's not an "agent extra" tab.
|
|
617
|
+
continue
|
|
618
|
+
if a_id not in new_map:
|
|
619
|
+
new_map[a_id] = []
|
|
620
|
+
new_map[a_id].append(i)
|
|
621
|
+
|
|
622
|
+
self.tab_options_idx = new_map
|
|
623
|
+
|
|
624
|
+
def update_custom_agent_options(self, agent_id: str):
|
|
625
|
+
"""
|
|
626
|
+
Rebuild extra option tabs for a given agent_id at runtime, keeping indices consistent.
|
|
627
|
+
|
|
628
|
+
What it does:
|
|
629
|
+
- Removes all existing tabs for this agent (and their UI config groups).
|
|
630
|
+
- If agent exists, creates fresh tabs from agent.get_options().
|
|
631
|
+
- Applies saved values from current preset.extra[agent_id] when available.
|
|
632
|
+
- Otherwise applies defaults defined in option schema.
|
|
633
|
+
- If agent does not exist, simply clears tabs and config for this agent.
|
|
634
|
+
- Recomputes self.tab_options_idx so it always matches the current QTabWidget.
|
|
635
|
+
- Finally, re-runs visibility logic so only relevant tabs are shown.
|
|
636
|
+
|
|
637
|
+
Notes:
|
|
638
|
+
- Base prompt tab is index 0 and remains untouched.
|
|
639
|
+
- This method must be called in the UI thread (Qt).
|
|
640
|
+
"""
|
|
641
|
+
tabs = self.window.ui.tabs['preset.editor.extra']
|
|
642
|
+
if not tabs:
|
|
643
|
+
return
|
|
644
|
+
|
|
645
|
+
# Make sure existing tabs have proper metadata to allow safe remapping later.
|
|
646
|
+
self._ensure_agent_tab_properties()
|
|
647
|
+
|
|
648
|
+
mode = self.window.core.config.get('mode')
|
|
649
|
+
exclude_ids = ["__prompt__"]
|
|
650
|
+
|
|
651
|
+
# Old indices for this agent (if any), sorted to compute insertion position and to remove safely.
|
|
652
|
+
old_indices = sorted(self.tab_options_idx.get(agent_id, []))
|
|
653
|
+
# Prefer to keep the first old index as insertion anchor to preserve overall ordering.
|
|
654
|
+
insertion_index = old_indices[0] if old_indices else tabs.count()
|
|
655
|
+
|
|
656
|
+
# 1) Remove old tabs for this agent (descending order to avoid index shifts).
|
|
657
|
+
for idx in sorted(old_indices, reverse=True):
|
|
658
|
+
if 0 < idx < tabs.count():
|
|
659
|
+
w = tabs.widget(idx)
|
|
660
|
+
tabs.removeTab(idx)
|
|
661
|
+
if w is not None:
|
|
662
|
+
w.deleteLater()
|
|
663
|
+
|
|
664
|
+
# Remove old UI config groups for this agent (they will be rebuilt).
|
|
665
|
+
# We assume self.window.ui.config is a dict-like structure holding per-parent groups.
|
|
666
|
+
cfg = self.window.ui.config
|
|
667
|
+
for key in list(cfg.keys()):
|
|
668
|
+
if isinstance(key, str) and key.startswith(f"agent.{agent_id}."):
|
|
669
|
+
try:
|
|
670
|
+
del cfg[key]
|
|
671
|
+
except Exception:
|
|
672
|
+
pass
|
|
673
|
+
|
|
674
|
+
# Clear mapping entry for this agent; we will rebuild mapping from scratch later.
|
|
675
|
+
if agent_id in self.tab_options_idx:
|
|
676
|
+
del self.tab_options_idx[agent_id]
|
|
677
|
+
|
|
678
|
+
# 2) Fetch fresh options from provider.
|
|
679
|
+
agent = self.window.core.agents.provider.get(agent_id, mode)
|
|
680
|
+
if not agent:
|
|
681
|
+
# Agent no longer exists -> just rebuild mapping for remaining tabs and update visibility.
|
|
682
|
+
self._rebuild_tab_index_mapping()
|
|
683
|
+
self.toggle_extra_options_by_provider()
|
|
684
|
+
return
|
|
685
|
+
|
|
686
|
+
option_tabs = agent.get_options() or {}
|
|
687
|
+
|
|
688
|
+
# If agent has no custom option tabs -> nothing to add (base prompt will be used).
|
|
689
|
+
# Still need to rebuild mapping and visibility.
|
|
690
|
+
if len(option_tabs) == 0:
|
|
691
|
+
self._rebuild_tab_index_mapping()
|
|
692
|
+
self.toggle_extra_options_by_provider()
|
|
693
|
+
return
|
|
694
|
+
|
|
695
|
+
build_option_widgets = self.window.ui.dialogs.preset.build_option_widgets
|
|
696
|
+
apply_value = self.window.controller.config.apply_value
|
|
697
|
+
|
|
698
|
+
# Try to load saved values for current preset (if this agent is used in it).
|
|
699
|
+
preset = self.window.core.presets.get_by_uuid(self.current)
|
|
700
|
+
saved_data = None
|
|
701
|
+
if preset and preset.extra and agent_id in preset.extra:
|
|
702
|
+
# Expected structure: preset.extra[agent_id][option_tab_id][key] = value
|
|
703
|
+
saved_data = preset.extra.get(agent_id, None)
|
|
704
|
+
|
|
705
|
+
# Figure out if this agent is the currently selected provider for the active mode.
|
|
706
|
+
# If yes, we will prefer saved values from preset.extra; otherwise we apply defaults.
|
|
707
|
+
selected_provider_id = None
|
|
708
|
+
if mode in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
|
|
709
|
+
key_agent = "agent_provider_openai" if mode == MODE_AGENT_OPENAI else "agent_provider"
|
|
710
|
+
selected_provider_id = self.window.controller.config.get_value(
|
|
711
|
+
parent_id=self.id,
|
|
712
|
+
key=key_agent,
|
|
713
|
+
option=self.options[key_agent],
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
# 3) Create new tabs from fresh schema and insert them at the computed anchor.
|
|
717
|
+
new_indices = []
|
|
718
|
+
for option_tab_id, option_desc in option_tabs.items():
|
|
719
|
+
if option_tab_id in exclude_ids:
|
|
720
|
+
continue
|
|
721
|
+
|
|
722
|
+
title = option_desc.get('label', '')
|
|
723
|
+
config_id = f"agent.{agent_id}.{option_tab_id}"
|
|
724
|
+
schema_options = option_desc.get('options', {}) or {}
|
|
725
|
+
|
|
726
|
+
# Build new option widgets into UI config under config_id.
|
|
727
|
+
widgets, options_layouts = build_option_widgets(config_id, schema_options)
|
|
728
|
+
|
|
729
|
+
# Create layouts similar to initial build (checkboxes at bottom row).
|
|
730
|
+
layout = QVBoxLayout()
|
|
731
|
+
layout.setContentsMargins(0, 10, 0, 10)
|
|
732
|
+
|
|
733
|
+
checkbox_layout = QHBoxLayout()
|
|
734
|
+
for key, opt_layout in options_layouts.items():
|
|
735
|
+
opt_schema = schema_options.get(key, {})
|
|
736
|
+
if opt_schema.get('type') == 'bool':
|
|
737
|
+
checkbox_layout.addLayout(opt_layout)
|
|
738
|
+
else:
|
|
739
|
+
layout.addLayout(opt_layout)
|
|
740
|
+
layout.addStretch(1)
|
|
741
|
+
layout.addLayout(checkbox_layout)
|
|
742
|
+
|
|
743
|
+
# Assemble tab widget and tag it with metadata.
|
|
744
|
+
tab_widget = QWidget()
|
|
745
|
+
tab_widget.setLayout(layout)
|
|
746
|
+
tab_widget.setProperty('agent_id', agent_id)
|
|
747
|
+
tab_widget.setProperty('option_tab_id', option_tab_id)
|
|
748
|
+
|
|
749
|
+
# Insert at a stable anchor to preserve general ordering between agents.
|
|
750
|
+
insertion_index = min(insertion_index, tabs.count())
|
|
751
|
+
tabs.insertTab(insertion_index, tab_widget, title)
|
|
752
|
+
new_indices.append(insertion_index)
|
|
753
|
+
insertion_index += 1
|
|
754
|
+
|
|
755
|
+
# Apply values:
|
|
756
|
+
# - If this agent is currently selected for the active mode and we have saved_data,
|
|
757
|
+
# prefer saved values from preset.extra.
|
|
758
|
+
# - Otherwise apply defaults from schema (when provided).
|
|
759
|
+
group_cfg = self.window.ui.config.get(config_id, {})
|
|
760
|
+
if selected_provider_id == agent_id and saved_data and option_tab_id in saved_data:
|
|
761
|
+
data_dict = saved_data[option_tab_id] or {}
|
|
762
|
+
for key, opt_schema in schema_options.items():
|
|
763
|
+
if key in data_dict:
|
|
764
|
+
apply_value(
|
|
765
|
+
parent_id=config_id,
|
|
766
|
+
key=key,
|
|
767
|
+
option=opt_schema,
|
|
768
|
+
value=data_dict[key],
|
|
769
|
+
)
|
|
770
|
+
elif 'default' in opt_schema:
|
|
771
|
+
apply_value(
|
|
772
|
+
parent_id=config_id,
|
|
773
|
+
key=key,
|
|
774
|
+
option=opt_schema,
|
|
775
|
+
value=opt_schema.get('default'),
|
|
776
|
+
)
|
|
777
|
+
else:
|
|
778
|
+
# Apply defaults only (do not overwrite elsewhere).
|
|
779
|
+
for key, opt_schema in schema_options.items():
|
|
780
|
+
if 'default' in opt_schema:
|
|
781
|
+
apply_value(
|
|
782
|
+
parent_id=config_id,
|
|
783
|
+
key=key,
|
|
784
|
+
option=opt_schema,
|
|
785
|
+
value=opt_schema.get('default'),
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
# 4) Recompute mapping fully based on actual tabs and their 'agent_id' properties.
|
|
789
|
+
self._rebuild_tab_index_mapping()
|
|
790
|
+
|
|
791
|
+
# 5) Update visibility according to current mode/provider.
|
|
792
|
+
self.toggle_extra_options_by_provider()
|
|
793
|
+
|
|
577
794
|
def append_default_prompt(self):
|
|
578
795
|
"""Append default prompt to the preset editor"""
|
|
579
796
|
mode = self.window.core.config.get('mode')
|
|
@@ -616,6 +833,15 @@ class Editor:
|
|
|
616
833
|
value=default_prompt,
|
|
617
834
|
)
|
|
618
835
|
|
|
836
|
+
def update_providers_list(self):
|
|
837
|
+
"""Update providers list in the preset editor"""
|
|
838
|
+
self.window.ui.config[self.id]['agent_provider'].set_keys(
|
|
839
|
+
self.window.controller.config.placeholder.apply_by_id('agent_provider_llama')
|
|
840
|
+
)
|
|
841
|
+
self.window.ui.config[self.id]['agent_provider_openai'].set_keys(
|
|
842
|
+
self.window.controller.config.placeholder.apply_by_id('agent_provider_openai')
|
|
843
|
+
)
|
|
844
|
+
|
|
619
845
|
def hook_update(
|
|
620
846
|
self,
|
|
621
847
|
key: str,
|
|
@@ -660,6 +886,17 @@ class Editor:
|
|
|
660
886
|
self.init(preset)
|
|
661
887
|
self.window.ui.dialogs.open_editor('editor.preset.presets', idx, width=800)
|
|
662
888
|
|
|
889
|
+
def reload_all(self, all: bool = False):
|
|
890
|
+
"""
|
|
891
|
+
Reload all data in the preset editor
|
|
892
|
+
|
|
893
|
+
:param all: reload all custom agent options
|
|
894
|
+
"""
|
|
895
|
+
self.update_providers_list()
|
|
896
|
+
if all:
|
|
897
|
+
self.reload_all_custom_agent_options()
|
|
898
|
+
if self.opened:
|
|
899
|
+
self.init(self.current_id)
|
|
663
900
|
def init(self, id: Optional[str] = None):
|
|
664
901
|
"""
|
|
665
902
|
Initialize preset editor
|
|
@@ -667,6 +904,9 @@ class Editor:
|
|
|
667
904
|
:param id: preset id (filename)
|
|
668
905
|
"""
|
|
669
906
|
self.opened = True
|
|
907
|
+
self.current_id = id
|
|
908
|
+
self.reload_all()
|
|
909
|
+
|
|
670
910
|
data = PresetItem()
|
|
671
911
|
data.name = ""
|
|
672
912
|
data.filename = ""
|
|
@@ -683,6 +923,10 @@ class Editor:
|
|
|
683
923
|
else:
|
|
684
924
|
self.load_extra_defaults()
|
|
685
925
|
|
|
926
|
+
mode = self.window.core.config.get("mode")
|
|
927
|
+
if mode in [MODE_AGENT_OPENAI, MODE_AGENT_LLAMA]:
|
|
928
|
+
self.reload_all_custom_agent_options()
|
|
929
|
+
|
|
686
930
|
if data.name is None:
|
|
687
931
|
data.name = ""
|
|
688
932
|
if data.ai_name is None:
|
|
@@ -696,7 +940,6 @@ class Editor:
|
|
|
696
940
|
|
|
697
941
|
# set current mode at start
|
|
698
942
|
if id is None:
|
|
699
|
-
mode = self.window.core.config.get("mode")
|
|
700
943
|
if mode == MODE_CHAT:
|
|
701
944
|
data.chat = True
|
|
702
945
|
elif mode == MODE_COMPLETION:
|
|
@@ -865,11 +1108,17 @@ class Editor:
|
|
|
865
1108
|
# if agent, assign experts and select only agent mode
|
|
866
1109
|
curr_mode = self.window.core.config.get('mode')
|
|
867
1110
|
if curr_mode == MODE_AGENT:
|
|
868
|
-
self.window.core.presets.items[preset_id]
|
|
1111
|
+
itm = self.window.core.presets.items[preset_id]
|
|
1112
|
+
itm.reset_modes()
|
|
1113
|
+
itm.agent = True
|
|
869
1114
|
elif curr_mode == MODE_AGENT_LLAMA:
|
|
870
|
-
self.window.core.presets.items[preset_id]
|
|
1115
|
+
itm = self.window.core.presets.items[preset_id]
|
|
1116
|
+
itm.reset_modes()
|
|
1117
|
+
itm.agent_llama = True
|
|
871
1118
|
elif curr_mode == MODE_AGENT_OPENAI:
|
|
872
|
-
self.window.core.presets.items[preset_id]
|
|
1119
|
+
itm = self.window.core.presets.items[preset_id]
|
|
1120
|
+
itm.reset_modes()
|
|
1121
|
+
itm.agent_openai = True
|
|
873
1122
|
|
|
874
1123
|
# apply changes to current active preset
|
|
875
1124
|
current = self.window.core.config.get('preset')
|
|
@@ -1126,3 +1375,157 @@ class Editor:
|
|
|
1126
1375
|
else:
|
|
1127
1376
|
tabs.setTabEnabled(idx, False)
|
|
1128
1377
|
tabs.setTabVisible(idx, False)
|
|
1378
|
+
|
|
1379
|
+
def reload_all_custom_agent_options(self, purge_missing_from_preset: bool = False):
|
|
1380
|
+
"""
|
|
1381
|
+
Full, safe rebuild of all agent-specific option tabs.
|
|
1382
|
+
|
|
1383
|
+
When to use:
|
|
1384
|
+
- Call this at editor opening or whenever multiple agent configs may have changed at once.
|
|
1385
|
+
|
|
1386
|
+
What it does:
|
|
1387
|
+
- Updates provider combos (in case providers changed).
|
|
1388
|
+
- Removes ALL existing "agent extra" tabs (keeps base prompt tab at index 0).
|
|
1389
|
+
- Removes all UI config groups prefixed with 'agent.' (will be rebuilt).
|
|
1390
|
+
- Rebuilds tabs from the current provider registry order (provider.all()).
|
|
1391
|
+
- Applies saved values from current preset.extra[agent_id] when available; otherwise applies defaults.
|
|
1392
|
+
- Recomputes self.tab_options_idx from the real QTabWidget.
|
|
1393
|
+
- Restores visibility according to current mode/provider.
|
|
1394
|
+
|
|
1395
|
+
Notes:
|
|
1396
|
+
- Must be called in the Qt GUI thread.
|
|
1397
|
+
- Does not persist changes to preset files; it's a UI rebuild.
|
|
1398
|
+
- Set purge_missing_from_preset=True if you also want to drop missing agents from preset.extra.
|
|
1399
|
+
"""
|
|
1400
|
+
tabs = self.window.ui.tabs['preset.editor.extra']
|
|
1401
|
+
if not tabs:
|
|
1402
|
+
return
|
|
1403
|
+
|
|
1404
|
+
# 0) Provider combos might need to be refreshed (e.g., new agents installed)
|
|
1405
|
+
self.update_providers_list()
|
|
1406
|
+
|
|
1407
|
+
# Make sure existing tabs have agent_id metadata (for older builds).
|
|
1408
|
+
self._ensure_agent_tab_properties()
|
|
1409
|
+
|
|
1410
|
+
mode = self.window.core.config.get('mode')
|
|
1411
|
+
exclude_ids = ["__prompt__"]
|
|
1412
|
+
|
|
1413
|
+
# 1) Snapshot saved values from current preset (if any)
|
|
1414
|
+
preset = self.window.core.presets.get_by_uuid(self.current)
|
|
1415
|
+
saved_all = {}
|
|
1416
|
+
if preset and preset.extra:
|
|
1417
|
+
# expected: preset.extra[agent_id][option_tab_id][key] = value
|
|
1418
|
+
# shallow copy is sufficient (we only read and apply)
|
|
1419
|
+
for a_id, data in preset.extra.items():
|
|
1420
|
+
if isinstance(data, dict):
|
|
1421
|
+
saved_all[a_id] = dict(data)
|
|
1422
|
+
|
|
1423
|
+
# 2) Freeze UI to avoid flicker while we rebuild
|
|
1424
|
+
tabs.setUpdatesEnabled(False)
|
|
1425
|
+
try:
|
|
1426
|
+
# 3) Remove all agent extra tabs (keep base prompt at 0)
|
|
1427
|
+
for i in range(tabs.count() - 1, 1 - 1, -1):
|
|
1428
|
+
w = tabs.widget(i)
|
|
1429
|
+
if w is None:
|
|
1430
|
+
continue
|
|
1431
|
+
if w.property('agent_id'):
|
|
1432
|
+
tabs.removeTab(i)
|
|
1433
|
+
w.deleteLater()
|
|
1434
|
+
|
|
1435
|
+
# 4) Remove all UI config groups for agents (they will be re-created)
|
|
1436
|
+
cfg = self.window.ui.config
|
|
1437
|
+
for key in list(cfg.keys()):
|
|
1438
|
+
if isinstance(key, str) and key.startswith('agent.'):
|
|
1439
|
+
try:
|
|
1440
|
+
del cfg[key]
|
|
1441
|
+
except Exception:
|
|
1442
|
+
pass
|
|
1443
|
+
|
|
1444
|
+
# 5) Optionally purge preset.extra entries for missing agents
|
|
1445
|
+
agents_dict = self.window.core.agents.provider.all() or {}
|
|
1446
|
+
existing_ids = set(agents_dict.keys())
|
|
1447
|
+
if purge_missing_from_preset and preset and isinstance(preset.extra, dict):
|
|
1448
|
+
for a_id in list(preset.extra.keys()):
|
|
1449
|
+
if a_id not in existing_ids:
|
|
1450
|
+
try:
|
|
1451
|
+
del preset.extra[a_id]
|
|
1452
|
+
except Exception:
|
|
1453
|
+
pass
|
|
1454
|
+
# You may call self.window.core.presets.save(preset.filename) later if you want to persist.
|
|
1455
|
+
|
|
1456
|
+
# 6) Rebuild tabs from current registry, in a deterministic order
|
|
1457
|
+
build_option_widgets = self.window.ui.dialogs.preset.build_option_widgets
|
|
1458
|
+
apply_value = self.window.controller.config.apply_value
|
|
1459
|
+
|
|
1460
|
+
insertion_index = 1 # index 0 is the base prompt
|
|
1461
|
+
for a_id, agent in agents_dict.items():
|
|
1462
|
+
if not agent:
|
|
1463
|
+
continue
|
|
1464
|
+
option_tabs = agent.get_options() or {}
|
|
1465
|
+
if not option_tabs:
|
|
1466
|
+
# agent with no custom tabs -> only base prompt applies
|
|
1467
|
+
continue
|
|
1468
|
+
|
|
1469
|
+
for option_tab_id, option_desc in option_tabs.items():
|
|
1470
|
+
if option_tab_id in exclude_ids:
|
|
1471
|
+
continue
|
|
1472
|
+
|
|
1473
|
+
title = option_desc.get('label', '')
|
|
1474
|
+
schema_options = option_desc.get('options', {}) or {}
|
|
1475
|
+
config_id = f"agent.{a_id}.{option_tab_id}"
|
|
1476
|
+
|
|
1477
|
+
# Build UI widgets for this option group
|
|
1478
|
+
widgets, options_layouts = build_option_widgets(config_id, schema_options)
|
|
1479
|
+
|
|
1480
|
+
# Layout: non-bool vertically, bools grouped in bottom row
|
|
1481
|
+
layout = QVBoxLayout()
|
|
1482
|
+
layout.setContentsMargins(0, 10, 0, 10)
|
|
1483
|
+
checkbox_layout = QHBoxLayout()
|
|
1484
|
+
for key, opt_layout in options_layouts.items():
|
|
1485
|
+
opt_schema = schema_options.get(key, {})
|
|
1486
|
+
if opt_schema.get('type') == 'bool':
|
|
1487
|
+
checkbox_layout.addLayout(opt_layout)
|
|
1488
|
+
else:
|
|
1489
|
+
layout.addLayout(opt_layout)
|
|
1490
|
+
layout.addStretch(1)
|
|
1491
|
+
layout.addLayout(checkbox_layout)
|
|
1492
|
+
|
|
1493
|
+
# Create tab widget and tag with metadata
|
|
1494
|
+
tab_widget = QWidget()
|
|
1495
|
+
tab_widget.setLayout(layout)
|
|
1496
|
+
tab_widget.setProperty('agent_id', a_id)
|
|
1497
|
+
tab_widget.setProperty('option_tab_id', option_tab_id)
|
|
1498
|
+
|
|
1499
|
+
# Insert tab and advance anchor
|
|
1500
|
+
insertion_index = min(insertion_index, tabs.count())
|
|
1501
|
+
tabs.insertTab(insertion_index, tab_widget, title)
|
|
1502
|
+
insertion_index += 1
|
|
1503
|
+
|
|
1504
|
+
# Apply saved values (if present) or defaults
|
|
1505
|
+
saved_agent = saved_all.get(a_id, {})
|
|
1506
|
+
saved_tab = saved_agent.get(option_tab_id, {}) if isinstance(saved_agent, dict) else {}
|
|
1507
|
+
|
|
1508
|
+
for key, opt_schema in schema_options.items():
|
|
1509
|
+
if key in saved_tab:
|
|
1510
|
+
apply_value(
|
|
1511
|
+
parent_id=config_id,
|
|
1512
|
+
key=key,
|
|
1513
|
+
option=opt_schema,
|
|
1514
|
+
value=saved_tab[key],
|
|
1515
|
+
)
|
|
1516
|
+
elif 'default' in opt_schema:
|
|
1517
|
+
apply_value(
|
|
1518
|
+
parent_id=config_id,
|
|
1519
|
+
key=key,
|
|
1520
|
+
option=opt_schema,
|
|
1521
|
+
value=opt_schema.get('default'),
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1524
|
+
# 7) Recompute the index mapping strictly from the QTabWidget
|
|
1525
|
+
self._rebuild_tab_index_mapping()
|
|
1526
|
+
|
|
1527
|
+
# 8) Restore proper visibility for the current mode/provider
|
|
1528
|
+
self.toggle_extra_options_by_provider()
|
|
1529
|
+
|
|
1530
|
+
finally:
|
|
1531
|
+
tabs.setUpdatesEnabled(True)
|