pygpt-net 2.6.60__py3-none-any.whl → 2.6.62__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 (87) hide show
  1. pygpt_net/CHANGELOG.txt +14 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/chat/common.py +115 -6
  4. pygpt_net/controller/chat/input.py +4 -1
  5. pygpt_net/controller/chat/response.py +8 -2
  6. pygpt_net/controller/presets/presets.py +121 -6
  7. pygpt_net/controller/settings/editor.py +0 -15
  8. pygpt_net/controller/settings/profile.py +16 -4
  9. pygpt_net/controller/settings/workdir.py +30 -5
  10. pygpt_net/controller/theme/common.py +4 -2
  11. pygpt_net/controller/theme/markdown.py +4 -7
  12. pygpt_net/controller/theme/theme.py +2 -1
  13. pygpt_net/controller/ui/ui.py +32 -7
  14. pygpt_net/core/agents/custom/__init__.py +7 -1
  15. pygpt_net/core/agents/custom/llama_index/factory.py +17 -6
  16. pygpt_net/core/agents/custom/llama_index/runner.py +52 -4
  17. pygpt_net/core/agents/custom/llama_index/utils.py +12 -1
  18. pygpt_net/core/agents/custom/router.py +45 -6
  19. pygpt_net/core/agents/custom/runner.py +11 -5
  20. pygpt_net/core/agents/custom/schema.py +3 -1
  21. pygpt_net/core/agents/custom/utils.py +13 -1
  22. pygpt_net/core/agents/runners/llama_workflow.py +65 -5
  23. pygpt_net/core/agents/runners/openai_workflow.py +2 -1
  24. pygpt_net/core/db/viewer.py +11 -5
  25. pygpt_net/core/node_editor/graph.py +18 -9
  26. pygpt_net/core/node_editor/models.py +9 -2
  27. pygpt_net/core/node_editor/types.py +15 -1
  28. pygpt_net/core/presets/presets.py +216 -29
  29. pygpt_net/core/render/markdown/parser.py +0 -2
  30. pygpt_net/core/render/web/renderer.py +76 -11
  31. pygpt_net/data/config/config.json +5 -6
  32. pygpt_net/data/config/models.json +3 -3
  33. pygpt_net/data/config/settings.json +2 -38
  34. pygpt_net/data/css/style.dark.css +18 -0
  35. pygpt_net/data/css/style.light.css +20 -1
  36. pygpt_net/data/locale/locale.de.ini +66 -1
  37. pygpt_net/data/locale/locale.en.ini +64 -3
  38. pygpt_net/data/locale/locale.es.ini +66 -1
  39. pygpt_net/data/locale/locale.fr.ini +66 -1
  40. pygpt_net/data/locale/locale.it.ini +66 -1
  41. pygpt_net/data/locale/locale.pl.ini +67 -2
  42. pygpt_net/data/locale/locale.uk.ini +66 -1
  43. pygpt_net/data/locale/locale.zh.ini +66 -1
  44. pygpt_net/data/locale/plugin.cmd_system.en.ini +62 -66
  45. pygpt_net/item/ctx.py +23 -1
  46. pygpt_net/provider/agents/llama_index/flow_from_schema.py +2 -2
  47. pygpt_net/provider/agents/llama_index/workflow/codeact.py +9 -6
  48. pygpt_net/provider/agents/llama_index/workflow/openai.py +38 -11
  49. pygpt_net/provider/agents/llama_index/workflow/planner.py +36 -16
  50. pygpt_net/provider/agents/llama_index/workflow/supervisor.py +60 -10
  51. pygpt_net/provider/agents/openai/agent.py +3 -1
  52. pygpt_net/provider/agents/openai/agent_b2b.py +13 -9
  53. pygpt_net/provider/agents/openai/agent_planner.py +6 -2
  54. pygpt_net/provider/agents/openai/agent_with_experts.py +4 -1
  55. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -2
  56. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -2
  57. pygpt_net/provider/agents/openai/evolve.py +6 -2
  58. pygpt_net/provider/agents/openai/supervisor.py +3 -1
  59. pygpt_net/provider/api/openai/agents/response.py +1 -0
  60. pygpt_net/provider/core/config/patch.py +18 -1
  61. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -6
  62. pygpt_net/tools/agent_builder/tool.py +48 -26
  63. pygpt_net/tools/agent_builder/ui/dialogs.py +36 -28
  64. pygpt_net/ui/__init__.py +2 -4
  65. pygpt_net/ui/dialog/about.py +58 -38
  66. pygpt_net/ui/dialog/db.py +142 -3
  67. pygpt_net/ui/dialog/preset.py +47 -8
  68. pygpt_net/ui/layout/toolbox/presets.py +64 -16
  69. pygpt_net/ui/main.py +2 -2
  70. pygpt_net/ui/widget/dialog/confirm.py +27 -3
  71. pygpt_net/ui/widget/dialog/db.py +0 -0
  72. pygpt_net/ui/widget/draw/painter.py +90 -1
  73. pygpt_net/ui/widget/lists/preset.py +908 -60
  74. pygpt_net/ui/widget/node_editor/command.py +10 -10
  75. pygpt_net/ui/widget/node_editor/config.py +157 -0
  76. pygpt_net/ui/widget/node_editor/editor.py +223 -153
  77. pygpt_net/ui/widget/node_editor/item.py +12 -11
  78. pygpt_net/ui/widget/node_editor/node.py +246 -13
  79. pygpt_net/ui/widget/node_editor/view.py +179 -63
  80. pygpt_net/ui/widget/tabs/output.py +1 -1
  81. pygpt_net/ui/widget/textarea/input.py +157 -23
  82. pygpt_net/utils.py +114 -2
  83. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/METADATA +26 -100
  84. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/RECORD +86 -85
  85. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/LICENSE +0 -0
  86. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/WHEEL +0 -0
  87. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.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.26 01:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -131,7 +131,7 @@ class Agent(BaseAgent):
131
131
  :return: Agent provider instance
132
132
  """
133
133
  kwargs = {
134
- "name": "evaluator",
134
+ "name": "Evaluator",
135
135
  "instructions": instructions,
136
136
  "model": window.core.agents.provider.get_openai_model(model),
137
137
  "output_type": EvaluationFeedback,
@@ -330,6 +330,7 @@ class Agent(BaseAgent):
330
330
  choose_query = self.make_choose_query(results)
331
331
  choose_items.append(choose_query)
332
332
 
333
+ ctx.set_agent_name(chooser.name)
333
334
  chooser_result = await Runner.run(chooser, choose_items)
334
335
  result: ChooseFeedback = chooser_result.final_output
335
336
  choose = result.answer_number
@@ -347,6 +348,7 @@ class Agent(BaseAgent):
347
348
  bridge.on_stop(ctx)
348
349
  break
349
350
 
351
+ ctx.set_agent_name(evaluator.name)
350
352
  evaluator_result = await Runner.run(evaluator, input_items)
351
353
  result: EvaluationFeedback = evaluator_result.final_output
352
354
 
@@ -397,6 +399,7 @@ class Agent(BaseAgent):
397
399
  j = i + 1
398
400
  parent_kwargs = copy.deepcopy(kwargs)
399
401
  parent_kwargs["input"]: list[TResponseInputItem] = copy.deepcopy(input_items)
402
+ ctx.set_agent_name(f"{trans('agent.evolve.generation')} {num_generation}")
400
403
  results[j] = Runner.run_streamed(
401
404
  parents[j],
402
405
  **parent_kwargs
@@ -440,6 +443,7 @@ class Agent(BaseAgent):
440
443
  window.core.api.openai.responses.unpack_agent_response(results[choose], ctx)
441
444
  input_items = results[choose].to_input_list()
442
445
 
446
+ ctx.set_agent_name(evaluator.name)
443
447
  evaluator_result = await Runner.run(evaluator, input_items)
444
448
  result: EvaluationFeedback = evaluator_result.final_output
445
449
 
@@ -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.26 01:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -214,6 +214,7 @@ class Agent(BaseAgent):
214
214
  agent = self.get_agent(window, agent_kwargs)
215
215
 
216
216
  if not stream:
217
+ item_ctx.set_agent_name(agent.name)
217
218
  result = await Runner.run(
218
219
  agent,
219
220
  **kwargs
@@ -223,6 +224,7 @@ class Agent(BaseAgent):
223
224
  if verbose:
224
225
  print("Final response:", result)
225
226
  else:
227
+ item_ctx.set_agent_name(agent.name)
226
228
  result = Runner.run_streamed(
227
229
  agent,
228
230
  **kwargs
@@ -163,6 +163,7 @@ class StreamHandler:
163
163
  elif event.type == "run_item_stream_event":
164
164
  if isinstance(event.item, HandoffOutputItem):
165
165
  s = f"\n\n**Handoff to: {event.item.target_agent.name}**\n\n"
166
+ ctx.set_agent_name(event.item.target_agent.name)
166
167
  self._emit(ctx, s, flush, buffer)
167
168
 
168
169
  if self.finished and not self.files_handled and self.files:
@@ -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.09.22 19:00:00 #
9
+ # Updated Date: 2025.09.26 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -151,6 +151,23 @@ class Patch:
151
151
  data["ctx.urls.internal"] = False
152
152
  updated = True
153
153
 
154
+ # < 2.6.61
155
+ if old < parse_version("2.6.61"):
156
+ print("Migrating config from < 2.6.61...")
157
+ if "presets.drag_and_drop.enabled" not in data:
158
+ data["presets.drag_and_drop.enabled"] = True
159
+ if "presets_order" not in data:
160
+ data["presets_order"] = {}
161
+ updated = True
162
+
163
+ # < 2.6.62
164
+ if old < parse_version("2.6.62"):
165
+ print("Migrating config from < 2.6.62...")
166
+ # add: node editor css
167
+ patch_css('style.light.css', True)
168
+ patch_css('style.dark.css', True)
169
+ updated = True
170
+
154
171
  # update file
155
172
  migrated = False
156
173
  if updated:
@@ -367,8 +367,6 @@ class Patch:
367
367
  # < 2.0.72
368
368
  if old < parse_version("2.0.72"):
369
369
  print("Migrating config from < 2.0.72...")
370
- if 'theme.markdown' not in data:
371
- data['theme.markdown'] = True
372
370
  prompt = 'IMAGE GENERATION: Whenever I provide a basic idea or concept for an image, such as \'a picture of ' \
373
371
  'mountains\', I want you to ALWAYS translate it into English and expand and elaborate on this idea. ' \
374
372
  'Use your knowledge and creativity to add details that would make the image more vivid and ' \
@@ -1301,8 +1299,6 @@ class Patch:
1301
1299
  data["render.code_syntax"] = "github-dark"
1302
1300
  if 'zoom' not in data:
1303
1301
  data["zoom"] = 1.0
1304
- if 'ctx.convert_lists' not in data:
1305
- data["ctx.convert_lists"] = False
1306
1302
  if 'render.engine' not in data:
1307
1303
  data["render.engine"] = "web"
1308
1304
  if 'render.open_gl' not in data:
@@ -1668,8 +1664,6 @@ class Patch:
1668
1664
  # < 2.4.19
1669
1665
  if old < parse_version("2.4.19"):
1670
1666
  print("Migrating config from < 2.4.19...")
1671
- if 'layout.animation.disable' not in data:
1672
- data["layout.animation.disable"] = cfg_get_base('layout.animation.disable')
1673
1667
  if 'cmd_code_interpreter' in data['plugins'] \
1674
1668
  and 'cmd.ipython_execute' in data['plugins']['cmd_code_interpreter']:
1675
1669
  # remove
@@ -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.09.24 00:00:00 #
9
+ # Updated Date: 2025.09.25 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -312,6 +312,14 @@ class AgentBuilder(BaseTool):
312
312
  next_id = idx.data(QtCore.Qt.UserRole)
313
313
  self.edit_agent(next_id)
314
314
 
315
+ def editing_allowed(self) -> bool:
316
+ """
317
+ Check if editing is enabled (dialog is open and not closing/restoring)
318
+
319
+ :return: True if editing is enabled
320
+ """
321
+ return self.opened and not self._closing and not self._restoring and self.current_agent is not None
322
+
315
323
  def update_presets(self):
316
324
  """Update presets in the tools"""
317
325
  self.window.controller.presets.editor.reload_all(all=True)
@@ -380,66 +388,80 @@ class AgentBuilder(BaseTool):
380
388
  :return: NodeTypeRegistry
381
389
  """
382
390
  registry = NodeTypeRegistry(empty=True)
383
- # Tip: to allow multiple connections to an input or output, set allowed_inputs/allowed_outputs to -1.
384
391
 
385
392
  # Start
386
393
  registry.register(NodeTypeSpec(
387
394
  type_name="Flow/Start",
388
- title="Start",
395
+ display_name=trans("node.editor.spec.start.title"),
396
+ title=trans("node.editor.spec.start.title"),
389
397
  base_id="start",
390
398
  export_kind="start",
391
399
  bg_color="#2D5A27",
400
+ max_num=1, # per-layout limit
392
401
  properties=[
393
- PropertySpec(id="output", type="flow", name="Output", editable=False, allowed_inputs=0,
394
- allowed_outputs=1),
395
- PropertySpec(id="memory", type="memory", name="Memory", editable=False, allowed_inputs=0,
396
- allowed_outputs=-1),
397
- # base_id will be auto-injected as read-only property at creation
402
+ PropertySpec(id="output", type="flow", name=trans("node.editor.property.output.name"), editable=False,
403
+ allowed_inputs=0, allowed_outputs=1),
404
+ PropertySpec(id="memory", type="memory", name=trans("node.editor.property.memory.name"), editable=False,
405
+ allowed_inputs=0, allowed_outputs=-1),
398
406
  ],
399
407
  ))
400
408
  # Agent
401
409
  registry.register(NodeTypeSpec(
402
410
  type_name="Flow/Agent",
403
- title="Agent",
411
+ display_name=trans("node.editor.spec.agent.title"),
412
+ title=trans("node.editor.spec.agent.title"),
404
413
  base_id="agent",
405
414
  export_kind="agent",
406
415
  bg_color="#304A6E",
407
416
  properties=[
408
- PropertySpec(id="name", type="str", name="Name", editable=True, value=""),
409
- PropertySpec(id="instruction", type="text", name="Instruction", editable=True, value=""),
410
- PropertySpec(id="remote_tools", type="bool", name="Remote tools", editable=True, value=True),
411
- PropertySpec(id="local_tools", type="bool", name="Local tools", editable=True, value=True),
412
- PropertySpec(id="input", type="flow", name="Input", editable=False, allowed_inputs=-1,
413
- allowed_outputs=0),
414
- PropertySpec(id="output", type="flow", name="Output", editable=False, allowed_inputs=0,
415
- allowed_outputs=-1),
416
- PropertySpec(id="memory", type="memory", name="Memory", editable=False, allowed_inputs=0,
417
- allowed_outputs=1),
417
+ PropertySpec(id="name", type="str", name=trans("node.editor.property.name.name"), editable=True,
418
+ value="",
419
+ placeholder=trans("node.editor.property.name.placeholder")),
420
+ PropertySpec(id="role", type="str", name=trans("node.editor.property.role.name"), editable=True,
421
+ value="",
422
+ placeholder=trans("node.editor.property.role.placeholder")),
423
+ PropertySpec(id="instruction", type="text", name=trans("node.editor.property.instruction.name"),
424
+ editable=True, value="",
425
+ placeholder=trans("node.editor.property.instruction.placeholder")),
426
+ PropertySpec(id="remote_tools", type="bool", name=trans("node.editor.property.remote_tools.name"),
427
+ editable=True, value=True),
428
+ PropertySpec(id="local_tools", type="bool", name=trans("node.editor.property.local_tools.name"),
429
+ editable=True, value=True),
430
+ PropertySpec(id="input", type="flow", name=trans("node.editor.property.input.name"), editable=False,
431
+ allowed_inputs=-1, allowed_outputs=0),
432
+ PropertySpec(id="output", type="flow", name=trans("node.editor.property.output.name"), editable=False,
433
+ allowed_inputs=0, allowed_outputs=-1),
434
+ PropertySpec(id="memory", type="memory", name=trans("node.editor.property.memory.name"), editable=False,
435
+ allowed_inputs=0, allowed_outputs=1),
418
436
  ],
419
437
  ))
420
438
  # Memory
421
439
  registry.register(NodeTypeSpec(
422
440
  type_name="Flow/Memory",
423
- title="Memory (Context)",
441
+ display_name=trans("node.editor.spec.memory.title"),
442
+ title=trans("node.editor.spec.memory.title"),
424
443
  base_id="mem",
425
444
  export_kind="memory",
426
445
  bg_color="#593E78",
427
446
  properties=[
428
- PropertySpec(id="name", type="str", name="Name", editable=True, value=""),
429
- PropertySpec(id="input", type="memory", name="Agent", editable=False, allowed_inputs=-1,
430
- allowed_outputs=0),
447
+ PropertySpec(id="name", type="str", name=trans("node.editor.property.name.name"), editable=True,
448
+ value=""),
449
+ PropertySpec(id="input", type="memory", name=trans("node.editor.property.agent.name"), editable=False,
450
+ allowed_inputs=-1, allowed_outputs=0),
431
451
  ],
432
452
  ))
433
453
  # End
434
454
  registry.register(NodeTypeSpec(
435
455
  type_name="Flow/End",
436
- title="End",
456
+ display_name=trans("node.editor.spec.end.title"),
457
+ title=trans("node.editor.spec.end.title"),
437
458
  base_id="end",
438
459
  export_kind="end",
439
460
  bg_color="#6B2E2E",
461
+ max_num=1, # per-layout limit
440
462
  properties=[
441
- PropertySpec(id="input", type="flow", name="Input", editable=False, allowed_inputs=-1,
442
- allowed_outputs=0),
463
+ PropertySpec(id="input", type="flow", name=trans("node.editor.property.input.name"), editable=False,
464
+ allowed_inputs=-1, allowed_outputs=0),
443
465
  ],
444
466
  ))
445
467
  return registry
@@ -11,7 +11,7 @@
11
11
 
12
12
  from PySide6.QtCore import Qt, QEvent
13
13
  from PySide6.QtGui import QAction, QIcon
14
- from PySide6.QtWidgets import QVBoxLayout, QMenuBar, QSplitter, QSizePolicy
14
+ from PySide6.QtWidgets import QVBoxLayout, QMenuBar, QSplitter, QSizePolicy, QWidget
15
15
 
16
16
  from pygpt_net.ui.widget.element.labels import HelpLabel
17
17
  from pygpt_net.ui.widget.node_editor.editor import NodeEditor
@@ -41,13 +41,13 @@ class Builder:
41
41
  self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
42
42
  t = self.tool
43
43
 
44
- self.actions["open"] = QAction(QIcon(":/icons/reload.svg"), "Reload", self.menu_bar)
44
+ self.actions["open"] = QAction(QIcon(":/icons/reload.svg"), trans("action.reload"), self.menu_bar)
45
45
  self.actions["open"].triggered.connect(lambda checked=False, t=t: t.load())
46
46
 
47
47
  self.actions["save"] = QAction(QIcon(":/icons/save.svg"), trans("action.save"), self.menu_bar)
48
48
  self.actions["save"].triggered.connect(lambda checked=False, t=t: t.save())
49
49
 
50
- self.actions["clear"] = QAction(QIcon(":/icons/clear.svg"), trans("action.clear"), self.menu_bar)
50
+ self.actions["clear"] = QAction(QIcon(":/icons/close.svg"), trans("action.clear"), self.menu_bar)
51
51
  self.actions["clear"].triggered.connect(lambda checked=False, t=t: t.clear())
52
52
 
53
53
  self.file_menu.addAction(self.actions["open"])
@@ -77,26 +77,8 @@ class Builder:
77
77
  registry=registry
78
78
  ) # parent == dialog
79
79
 
80
- editor.setStyleSheet("""
81
- NodeEditor {
82
- qproperty-gridBackColor: #242629;
83
- qproperty-gridPenColor: #3b3f46;
84
-
85
- qproperty-nodeBackgroundColor: #2d2f34;
86
- qproperty-nodeBorderColor: #4b4f57;
87
- qproperty-nodeSelectionColor: #ff9900;
88
- qproperty-nodeTitleColor: #3a3d44;
89
-
90
- qproperty-portInputColor: #66b2ff;
91
- qproperty-portOutputColor: #70e070;
92
- qproperty-portConnectedColor: #ffd166;
93
-
94
- qproperty-edgeColor: #c0c0c0;
95
- qproperty-edgeSelectedColor: #ff8a5c;
96
- }
97
- """)
98
80
  editor.on_clear = self.tool.clear
99
-
81
+ editor.editing_allowed = self.tool.editing_allowed
100
82
  u.editor[id] = editor
101
83
 
102
84
  layout = QVBoxLayout()
@@ -104,9 +86,28 @@ class Builder:
104
86
 
105
87
  agents_list = AgentsWidget(self.window, tool=self.tool, parent=dlg)
106
88
  list_widget = agents_list.setup()
107
- list_widget.setFixedWidth(250)
89
+
90
+ # Left side container: list fills all space, help label stays at the bottom
91
+ left_side = QWidget(dlg)
92
+ left_layout = QVBoxLayout(left_side)
93
+ left_layout.setContentsMargins(0, 0, 0, 0)
94
+ left_layout.setSpacing(6)
95
+
96
+ left_help_label = HelpLabel(trans("node.editor.list.tip"))
97
+ left_help_label.setWordWrap(True)
98
+ left_help_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
99
+ left_help_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
100
+ left_help_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
101
+
102
+ left_layout.addWidget(list_widget)
103
+ left_layout.addWidget(left_help_label)
104
+ left_layout.setStretch(0, 1) # list -> fills all vertical space
105
+
106
+ # Fix the width of the whole left panel (not only the list)
107
+ left_side.setFixedWidth(250)
108
+
108
109
  center_splitter = QSplitter(Qt.Horizontal)
109
- center_splitter.addWidget(list_widget)
110
+ center_splitter.addWidget(left_side)
110
111
  center_splitter.addWidget(u.editor[id])
111
112
  center_splitter.setStretchFactor(0, 1)
112
113
  center_splitter.setStretchFactor(1, 8)
@@ -115,10 +116,7 @@ class Builder:
115
116
  layout.setStretch(0, 1)
116
117
 
117
118
  # Bottom legend as a compact, centered help label
118
- legend_label = HelpLabel(
119
- "Right-click: add node / undo / redo • Middle-click: pan view • Ctrl + Mouse wheel: zoom • "
120
- "Left-click a port: create connection • Ctrl + Left-click a port: rewire or detach • Right-click or DEL a node/connection: remove"
121
- )
119
+ legend_label = HelpLabel(trans("node.editor.bottom.tip"))
122
120
  legend_label.setAlignment(Qt.AlignCenter)
123
121
  legend_label.setWordWrap(True)
124
122
  legend_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
@@ -129,6 +127,7 @@ class Builder:
129
127
  u.nodes["agent.builder.splitter"] = center_splitter
130
128
  u.nodes["agent.builder.list"] = agents_list
131
129
  u.nodes["agent.builder.legend"] = legend_label
130
+ u.nodes["agent.builder.list.help"] = left_help_label
132
131
 
133
132
  dlg.setLayout(layout)
134
133
  dlg.setWindowTitle(trans("agent.builder.title"))
@@ -231,6 +230,15 @@ class BuilderDialog(BaseDialog):
231
230
  except Exception:
232
231
  pass
233
232
 
233
+ # Dispose left-side help label safely
234
+ try:
235
+ help_lbl = u.nodes.pop("agent.builder.list.help", None)
236
+ if help_lbl is not None:
237
+ help_lbl.setParent(None)
238
+ help_lbl.deleteLater()
239
+ except Exception:
240
+ pass
241
+
234
242
  # Drop splitter reference
235
243
  try:
236
244
  u.nodes.pop("agent.builder.splitter", None)
pygpt_net/ui/__init__.py CHANGED
@@ -172,15 +172,13 @@ class UI:
172
172
 
173
173
  def show_loading(self):
174
174
  """Show loading"""
175
- if self.window.core.config.get('layout.animation.disable', False):
176
- return
175
+ return
177
176
  self.window.ui.nodes['anim.loading'].start_anim()
178
177
  self.window.ui.nodes['anim.loading'].show()
179
178
 
180
179
  def hide_loading(self):
181
180
  """Hide loading"""
182
- if self.window.core.config.get('layout.animation.disable', False):
183
- return
181
+ return
184
182
  self.window.ui.nodes['anim.loading'].stop_anim()
185
183
  self.window.ui.nodes['anim.loading'].hide()
186
184
 
@@ -19,6 +19,9 @@ from pygpt_net.utils import trans
19
19
 
20
20
 
21
21
  class About:
22
+
23
+ RELEASE_YEAR = 2025
24
+
22
25
  def __init__(self, window=None):
23
26
  """
24
27
  About dialog
@@ -36,34 +39,65 @@ class About:
36
39
  """
37
40
  return self.window.core.updater.get_fetch_thanks()
38
41
 
42
+ def build_versions_str(self, lib_versions: dict, break_after: str = "LlamaIndex") -> str:
43
+ parts = []
44
+ line = []
45
+ for k, v in lib_versions.items():
46
+ line.append(f"{k}: {v}")
47
+ if k == break_after:
48
+ parts.append(", ".join(line))
49
+ line = []
50
+ if line:
51
+ parts.append(", ".join(line))
52
+ return "\n".join(parts)
53
+
39
54
  def prepare_content(self) -> str:
40
55
  """
41
56
  Get info text
42
57
 
43
58
  :return: info text
44
59
  """
45
- llama_index_version = None
46
- langchain_version = None
47
- openai_version = None
48
- versions = True
60
+ lib_versions = {}
61
+
62
+ try:
63
+ import platform
64
+ lib_versions['Python'] = platform.python_version()
65
+ except ImportError:
66
+ pass
49
67
 
50
68
  try:
51
- from llama_index.core import __version__ as llama_index_version
52
- # from langchain import __version__ as langchain_version
53
69
  from openai.version import VERSION as openai_version
70
+ lib_versions['OpenAI API'] = openai_version
71
+ except ImportError:
72
+ pass
73
+
74
+ try:
75
+ from llama_index.core import __version__ as llama_index_version
76
+ lib_versions['LlamaIndex'] = llama_index_version
77
+ except ImportError:
78
+ pass
79
+
80
+ try:
81
+ from anthropic import __version__ as anthropic_version
82
+ lib_versions['Anthropic API'] = anthropic_version
83
+ except ImportError:
84
+ pass
85
+
86
+ try:
87
+ from google.genai import __version__ as google_genai_version
88
+ lib_versions['Google API'] = google_genai_version
54
89
  except ImportError:
55
90
  pass
56
91
 
57
- lib_versions = ""
58
- if llama_index_version is None or openai_version is None:
59
- versions = False
92
+ try:
93
+ from xai_sdk import __version__ as xai_sdk_version
94
+ lib_versions['xAI API'] = xai_sdk_version
95
+ except ImportError:
96
+ pass
60
97
 
61
- if versions:
62
- lib_versions = "OpenAI API: {}, LlamaIndex: {}\n\n".format(
63
- openai_version,
64
- # langchain_version,
65
- llama_index_version,
66
- )
98
+ versions_str = ""
99
+ if lib_versions:
100
+ versions_str = self.build_versions_str(lib_versions, break_after="LlamaIndex")
67
101
 
68
102
  platform = self.window.core.platforms.get_as_string()
69
103
  version = self.window.meta['version']
@@ -80,29 +114,14 @@ class About:
80
114
  label_github = trans("dialog.about.github")
81
115
  label_docs = trans("dialog.about.docs")
82
116
 
83
- data = "{label_version}: {version}, {platform}\n" \
84
- "{label_build}: {build}\n" \
85
- "{lib_versions}" \
86
- "{label_website}: {website}\n" \
87
- "{label_github}: {github}\n" \
88
- "{label_docs}: {docs}\n\n" \
89
- "(c) 2025 {author}\n" \
90
- "{email}\n".format(
91
- label_version=label_version,
92
- version=version,
93
- platform=platform,
94
- label_build=label_build,
95
- build=build.replace('.', '-'),
96
- label_website=label_website,
97
- website=website,
98
- label_github=label_github,
99
- github=github,
100
- label_docs=label_docs,
101
- docs=docs,
102
- author=author,
103
- email=email,
104
- lib_versions=lib_versions,
105
- )
117
+ data = f"{label_version}: {version}, {platform}\n" \
118
+ f"{label_build}: {build.replace('.', '-')}\n\n" \
119
+ f"{versions_str}\n\n" \
120
+ f"{label_website}: {website}\n" \
121
+ f"{label_github}: {github}\n" \
122
+ f"{label_docs}: {docs}\n\n" \
123
+ f"(c) {self.RELEASE_YEAR} {author}\n" \
124
+ f"{email}\n"
106
125
  return data
107
126
 
108
127
  def setup(self):
@@ -139,6 +158,7 @@ class About:
139
158
  string = self.prepare_content()
140
159
  content = QLabel(string)
141
160
  content.setTextInteractionFlags(Qt.TextSelectableByMouse)
161
+ content.setWordWrap(True)
142
162
  self.window.ui.nodes['dialog.about.content'] = content
143
163
 
144
164
  thanks = QLabel(trans('about.thanks'))