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.
Files changed (91) hide show
  1. pygpt_net/CHANGELOG.txt +11 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +9 -5
  4. pygpt_net/controller/__init__.py +1 -0
  5. pygpt_net/controller/chat/common.py +115 -6
  6. pygpt_net/controller/chat/input.py +4 -1
  7. pygpt_net/controller/presets/editor.py +442 -39
  8. pygpt_net/controller/presets/presets.py +121 -6
  9. pygpt_net/controller/settings/editor.py +0 -15
  10. pygpt_net/controller/theme/markdown.py +2 -5
  11. pygpt_net/controller/ui/ui.py +4 -7
  12. pygpt_net/core/agents/custom/__init__.py +281 -0
  13. pygpt_net/core/agents/custom/debug.py +64 -0
  14. pygpt_net/core/agents/custom/factory.py +109 -0
  15. pygpt_net/core/agents/custom/graph.py +71 -0
  16. pygpt_net/core/agents/custom/llama_index/__init__.py +10 -0
  17. pygpt_net/core/agents/custom/llama_index/factory.py +100 -0
  18. pygpt_net/core/agents/custom/llama_index/router_streamer.py +106 -0
  19. pygpt_net/core/agents/custom/llama_index/runner.py +562 -0
  20. pygpt_net/core/agents/custom/llama_index/stream.py +56 -0
  21. pygpt_net/core/agents/custom/llama_index/utils.py +253 -0
  22. pygpt_net/core/agents/custom/logging.py +50 -0
  23. pygpt_net/core/agents/custom/memory.py +51 -0
  24. pygpt_net/core/agents/custom/router.py +155 -0
  25. pygpt_net/core/agents/custom/router_streamer.py +187 -0
  26. pygpt_net/core/agents/custom/runner.py +455 -0
  27. pygpt_net/core/agents/custom/schema.py +127 -0
  28. pygpt_net/core/agents/custom/utils.py +193 -0
  29. pygpt_net/core/agents/provider.py +72 -7
  30. pygpt_net/core/agents/runner.py +7 -4
  31. pygpt_net/core/agents/runners/helpers.py +1 -1
  32. pygpt_net/core/agents/runners/llama_workflow.py +3 -0
  33. pygpt_net/core/agents/runners/openai_workflow.py +8 -1
  34. pygpt_net/core/db/viewer.py +11 -5
  35. pygpt_net/{ui/widget/builder → core/node_editor}/__init__.py +2 -2
  36. pygpt_net/core/{builder → node_editor}/graph.py +28 -226
  37. pygpt_net/core/node_editor/models.py +118 -0
  38. pygpt_net/core/node_editor/types.py +78 -0
  39. pygpt_net/core/node_editor/utils.py +17 -0
  40. pygpt_net/core/presets/presets.py +216 -29
  41. pygpt_net/core/render/markdown/parser.py +0 -2
  42. pygpt_net/core/render/web/renderer.py +10 -8
  43. pygpt_net/data/config/config.json +5 -6
  44. pygpt_net/data/config/models.json +3 -3
  45. pygpt_net/data/config/settings.json +2 -38
  46. pygpt_net/data/locale/locale.de.ini +64 -1
  47. pygpt_net/data/locale/locale.en.ini +63 -4
  48. pygpt_net/data/locale/locale.es.ini +64 -1
  49. pygpt_net/data/locale/locale.fr.ini +64 -1
  50. pygpt_net/data/locale/locale.it.ini +64 -1
  51. pygpt_net/data/locale/locale.pl.ini +65 -2
  52. pygpt_net/data/locale/locale.uk.ini +64 -1
  53. pygpt_net/data/locale/locale.zh.ini +64 -1
  54. pygpt_net/data/locale/plugin.cmd_system.en.ini +62 -66
  55. pygpt_net/item/agent.py +5 -1
  56. pygpt_net/item/preset.py +19 -1
  57. pygpt_net/provider/agents/base.py +33 -2
  58. pygpt_net/provider/agents/llama_index/flow_from_schema.py +92 -0
  59. pygpt_net/provider/agents/openai/flow_from_schema.py +96 -0
  60. pygpt_net/provider/core/agent/json_file.py +11 -5
  61. pygpt_net/provider/core/config/patch.py +10 -1
  62. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -6
  63. pygpt_net/tools/agent_builder/tool.py +233 -52
  64. pygpt_net/tools/agent_builder/ui/dialogs.py +172 -28
  65. pygpt_net/tools/agent_builder/ui/list.py +37 -10
  66. pygpt_net/ui/__init__.py +2 -4
  67. pygpt_net/ui/dialog/about.py +58 -38
  68. pygpt_net/ui/dialog/db.py +142 -3
  69. pygpt_net/ui/dialog/preset.py +62 -8
  70. pygpt_net/ui/layout/toolbox/presets.py +52 -16
  71. pygpt_net/ui/main.py +1 -1
  72. pygpt_net/ui/widget/dialog/db.py +0 -0
  73. pygpt_net/ui/widget/lists/preset.py +644 -60
  74. pygpt_net/{core/builder → ui/widget/node_editor}/__init__.py +2 -2
  75. pygpt_net/ui/widget/node_editor/command.py +373 -0
  76. pygpt_net/ui/widget/node_editor/config.py +157 -0
  77. pygpt_net/ui/widget/node_editor/editor.py +2070 -0
  78. pygpt_net/ui/widget/node_editor/item.py +493 -0
  79. pygpt_net/ui/widget/node_editor/node.py +1460 -0
  80. pygpt_net/ui/widget/node_editor/utils.py +17 -0
  81. pygpt_net/ui/widget/node_editor/view.py +364 -0
  82. pygpt_net/ui/widget/tabs/output.py +1 -1
  83. pygpt_net/ui/widget/textarea/input.py +2 -2
  84. pygpt_net/utils.py +114 -2
  85. {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/METADATA +80 -93
  86. {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/RECORD +88 -61
  87. pygpt_net/core/agents/custom.py +0 -150
  88. pygpt_net/ui/widget/builder/editor.py +0 -2001
  89. {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/LICENSE +0 -0
  90. {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/WHEEL +0 -0
  91. {pygpt_net-2.6.59.dist-info → pygpt_net-2.6.61.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,14 @@
1
+ 2.6.61 (2025-09-26)
2
+
3
+ - Enhanced the agents node editor, custom agent flow, and instruction following.
4
+ - Added drag-and-drop and reordering functionality to the presets list.
5
+ - Added statistics for response tokens, including time elapsed and tokens per second.
6
+ - Improved UI/UX.
7
+
8
+ 2.6.60 (2025-09-25)
9
+
10
+ - Added a new tool: Agents Builder - allowing visual design of agent workflows using nodes - available in Tools -> Agents Builder (beta).
11
+
1
12
  2.6.59 (2025-09-23)
2
13
 
3
14
  - LlamaIndex has been upgraded to v0.13.6.
pygpt_net/__init__.py CHANGED
@@ -6,15 +6,15 @@
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.23 00:00:00 #
9
+ # Updated Date: 2025.09.26 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  __author__ = "Marcin Szczygliński"
13
13
  __copyright__ = "Copyright 2025, Marcin Szczygliński"
14
14
  __credits__ = ["Marcin Szczygliński"]
15
15
  __license__ = "MIT"
16
- __version__ = "2.6.59"
17
- __build__ = "2025-09-23"
16
+ __version__ = "2.6.61"
17
+ __build__ = "2025-09-26"
18
18
  __maintainer__ = "Marcin Szczygliński"
19
19
  __github__ = "https://github.com/szczyglis-dev/py-gpt"
20
20
  __report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
pygpt_net/app.py CHANGED
@@ -103,15 +103,15 @@ from pygpt_net.plugin.wolfram import Plugin as WolframPlugin
103
103
  from pygpt_net.plugin.osm import Plugin as OSMPlugin
104
104
 
105
105
  # agents (Llama-index)
106
- # from pygpt_net.provider.agents.llama_index.legacy.openai import OpenAIAgent
107
106
  from pygpt_net.provider.agents.llama_index.legacy.openai_assistant import OpenAIAssistantAgent
108
- # from pygpt_net.provider.agents.llama_index.legacy.planner import PlannerAgent
109
107
  from pygpt_net.provider.agents.llama_index.planner_workflow import PlannerAgent as PlannerWorkflowAgent
110
108
  from pygpt_net.provider.agents.llama_index.openai_workflow import OpenAIAgent as OpenAIWorkflowAgent
111
- # from pygpt_net.provider.agents.llama_index.legacy.react import ReactAgent
112
109
  from pygpt_net.provider.agents.llama_index.react_workflow import ReactWorkflowAgent
113
110
  from pygpt_net.provider.agents.llama_index.codeact_workflow import CodeActAgent
114
111
  from pygpt_net.provider.agents.llama_index.supervisor_workflow import SupervisorAgent as LlamaSupervisorAgent
112
+ from pygpt_net.provider.agents.llama_index.flow_from_schema import Agent as LlamaCustomAgent # builder schema
113
+
114
+ # agents (OpenAI-Agents)
115
115
  from pygpt_net.provider.agents.openai.agent import Agent as OpenAIAgentsBase
116
116
  from pygpt_net.provider.agents.openai.agent_with_experts import Agent as OpenAIAgentsExperts
117
117
  from pygpt_net.provider.agents.openai.agent_with_experts_feedback import Agent as OpenAIAgentsExpertsFeedback
@@ -121,6 +121,8 @@ from pygpt_net.provider.agents.openai.agent_planner import Agent as OpenAIAgentP
121
121
  from pygpt_net.provider.agents.openai.evolve import Agent as OpenAIAgentsEvolve
122
122
  from pygpt_net.provider.agents.openai.agent_b2b import Agent as OpenAIAgentsB2B
123
123
  from pygpt_net.provider.agents.openai.supervisor import Agent as OpenAIAgentSupervisor
124
+ from pygpt_net.provider.agents.openai.flow_from_schema import Agent as OpenAICustomAgent # builder schema
125
+
124
126
 
125
127
  # LLM wrapper providers (langchain, llama-index, embeddings)
126
128
  from pygpt_net.provider.llms.anthropic import AnthropicLLM
@@ -204,7 +206,7 @@ from pygpt_net.tools.text_editor import TextEditor as TextEditorTool
204
206
  from pygpt_net.tools.html_canvas import HtmlCanvas as HtmlCanvasTool
205
207
  from pygpt_net.tools.translator import Translator as TranslatorTool
206
208
  from pygpt_net.tools.web_browser import WebBrowser as WebBrowserTool
207
- # from pygpt_net.tools.agent_builder import AgentBuilder as AgentBuilderTool
209
+ from pygpt_net.tools.agent_builder import AgentBuilder as AgentBuilderTool
208
210
 
209
211
  def run(**kwargs):
210
212
  """
@@ -490,6 +492,7 @@ def run(**kwargs):
490
492
  launcher.add_agent(ReactWorkflowAgent()) # llama-index
491
493
  launcher.add_agent(CodeActAgent()) # llama-index
492
494
  launcher.add_agent(LlamaSupervisorAgent()) # llama-index
495
+ launcher.add_agent(LlamaCustomAgent()) # llama-index
493
496
  launcher.add_agent(OpenAIAgentsBase()) # openai-agents
494
497
  launcher.add_agent(OpenAIAgentsExperts()) # openai-agents
495
498
  launcher.add_agent(OpenAIAgentFeedback()) # openai-agents
@@ -499,6 +502,7 @@ def run(**kwargs):
499
502
  launcher.add_agent(OpenAIAgentsEvolve()) # openai-agents
500
503
  launcher.add_agent(OpenAIAgentsB2B()) # openai-agents
501
504
  launcher.add_agent(OpenAIAgentSupervisor()) # openai-agents
505
+ launcher.add_agent(OpenAICustomAgent()) # openai-agents
502
506
 
503
507
  # register custom agents
504
508
  agents = kwargs.get('agents', None)
@@ -516,7 +520,7 @@ def run(**kwargs):
516
520
  launcher.add_tool(HtmlCanvasTool())
517
521
  launcher.add_tool(TranslatorTool())
518
522
  launcher.add_tool(WebBrowserTool())
519
- # launcher.add_tool(AgentBuilderTool())
523
+ launcher.add_tool(AgentBuilderTool())
520
524
 
521
525
  # register custom tools
522
526
  tools = kwargs.get('tools', None)
@@ -165,6 +165,7 @@ class Controller:
165
165
  self.settings.reload()
166
166
  self.assistant.reload()
167
167
  self.attachment.reload()
168
+ self.window.core.agents.custom.reload()
168
169
  self.presets.reload()
169
170
  self.idx.reload()
170
171
  self.agent.reload()
@@ -6,10 +6,12 @@
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.01 23:00:00 #
9
+ # Updated Date: 2025.09.25 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
+ from time import perf_counter
14
+ from typing import Callable, Optional
13
15
 
14
16
  from PySide6.QtGui import QTextCursor
15
17
  from PySide6.QtWidgets import QFileDialog, QApplication
@@ -18,7 +20,7 @@ from pygpt_net.core.events import Event, AppEvent, RenderEvent, KernelEvent
18
20
  from pygpt_net.core.types import MODE_ASSISTANT, MODE_AUDIO
19
21
  from pygpt_net.item.ctx import CtxItem
20
22
  from pygpt_net.item.model import ModelItem
21
- from pygpt_net.utils import trans
23
+ from pygpt_net.utils import trans, short_num
22
24
 
23
25
 
24
26
  class Common:
@@ -30,6 +32,12 @@ class Common:
30
32
  """
31
33
  self.window = window
32
34
  self.initialized = False
35
+ # Counter (seconds, float) – current/last measured time
36
+ self.counter: float = 0.0
37
+ # Private start timestamp
38
+ self._t0: Optional[float] = None
39
+ # Optional number-shortening function (e.g., 10000 -> "10k"); if None, a fallback is used
40
+ self._shortener = None
33
41
 
34
42
  def setup(self):
35
43
  """Set up UI"""
@@ -310,6 +318,7 @@ class Common:
310
318
  dispatch(AppEvent(AppEvent.INPUT_STOPPED)) # app event
311
319
 
312
320
  self.stop_client() # stop clients
321
+ self.reset_counter()
313
322
 
314
323
  def stop_client(self):
315
324
  """Stop all clients"""
@@ -457,15 +466,115 @@ class Common:
457
466
  f.write(str(text).strip())
458
467
  self.window.update_status(f"{trans('status.saved')}: {os.path.basename(file_name)}")
459
468
 
469
+ # --- Timer control ---
470
+
471
+ def start_counter(self) -> None:
472
+ """Start the timer – call when send the request."""
473
+ self._t0 = perf_counter()
474
+
475
+ def stop_counter(self) -> float:
476
+ """
477
+ Stop the timer. Returns elapsed seconds and stores it in self.counter.
478
+ """
479
+ if self._t0 is None:
480
+ raise RuntimeError("Timer was not started (start_counter()).")
481
+ self.counter = perf_counter() - self._t0
482
+ self._t0 = None
483
+ return self.counter
484
+
485
+ def get_counter(self) -> float:
486
+ """
487
+ Current elapsed time (if running) or last measured time (in seconds).
488
+ """
489
+ if self._t0 is not None:
490
+ return perf_counter() - self._t0
491
+ return self.counter
492
+
493
+ def reset_counter(self) -> None:
494
+ """Reset the timer."""
495
+ self._t0 = None
496
+ self.counter = 0.0
497
+
498
+ # --- Calculation and formatting ---
499
+
500
+ def tokens_per_second(self, tokens_generated: int, seconds: Optional[float] = None) -> float:
501
+ """
502
+ Average tokens/s for the given token count and time (defaults to current/last).
503
+ """
504
+ sec = self.get_counter() if seconds is None else max(float(seconds), 0.0)
505
+ if sec <= 0.0:
506
+ return 0.0
507
+ return float(tokens_generated) / sec
508
+
509
+ def format_duration(self, seconds: Optional[float] = None, max_units: int = 2) -> str:
510
+ """
511
+ Pretty duration: e.g., 800ms, 12s, 1m 12s, 3h 5m, 1d 2h (by default up to 2 units).
512
+ """
513
+ sec = self.get_counter() if seconds is None else max(float(seconds), 0.0)
514
+
515
+ if sec < 1.0:
516
+ return f"{int(round(sec * 1000)):d}ms"
517
+
518
+ total = int(sec) # floor to whole seconds
519
+ days, rem = divmod(total, 86400)
520
+ hours, rem = divmod(rem, 3600)
521
+ minutes, seconds = divmod(rem, 60)
522
+
523
+ parts = []
524
+ if days:
525
+ parts.append(f"{days}d")
526
+ if hours:
527
+ parts.append(f"{hours}h")
528
+ if minutes:
529
+ parts.append(f"{minutes}m")
530
+ if seconds or not parts:
531
+ parts.append(f"{seconds}s")
532
+
533
+ return " ".join(parts[:max_units])
534
+
535
+ def _fallback_shorten(self, value: float) -> str:
536
+ """
537
+ Simple fallback shortener (e.g., 15300 -> '15.3k').
538
+ """
539
+ n = abs(value)
540
+ sign = "-" if value < 0 else ""
541
+ if n < 1_000:
542
+ return f"{sign}{int(round(n))}"
543
+ elif n < 1_000_000:
544
+ v = n / 1_000
545
+ s = f"{v:.1f}".rstrip("0").rstrip(".")
546
+ return f"{sign}{s}k"
547
+ elif n < 1_000_000_000:
548
+ v = n / 1_000_000
549
+ s = f"{v:.1f}".rstrip("0").rstrip(".")
550
+ return f"{sign}{s}M"
551
+ else:
552
+ v = n / 1_000_000_000
553
+ s = f"{v:.1f}".rstrip("0").rstrip(".")
554
+ return f"{sign}{s}B"
555
+
556
+ def format_stats(self, tokens_generated: int) -> str:
557
+ """
558
+ Returns a string in the format: "<X> tokens/s - <duration>", e.g., "15k tokens/s - 1m 12s".
559
+ """
560
+ tps = self.tokens_per_second(tokens_generated)
561
+ if self._shortener:
562
+ tps_str = self._shortener(tps if isinstance(tps, (int, float)) else float(tps))
563
+ else:
564
+ tps_str = self._fallback_shorten(tps)
565
+ duration = self.format_duration()
566
+ return f"{tps_str} tokens/s - {duration}"
567
+
460
568
  def show_response_tokens(self, ctx: CtxItem):
461
569
  """
462
570
  Update response tokens
463
571
 
464
572
  :param ctx: CtxItem
465
573
  """
466
- extra_data = ""
467
- if ctx.is_vision:
468
- extra_data = " (VISION)"
574
+ stats = ""
575
+ if ctx.output_tokens > 0 and self.get_counter() > 0:
576
+ stats = f" | {self.format_stats(ctx.output_tokens)}"
577
+ self.reset_counter()
469
578
  self.window.update_status(
470
- f"{trans('status.tokens')}: {ctx.input_tokens} + {ctx.output_tokens} = {ctx.total_tokens}{extra_data}"
579
+ f"{trans('status.tokens')}: {short_num(ctx.input_tokens)} + {short_num(ctx.output_tokens)} = {short_num(ctx.total_tokens)}{stats}"
471
580
  )
@@ -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.23 15:00:00 #
9
+ # Updated Date: 2025.09.25 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Optional, Any, Dict
@@ -206,6 +206,9 @@ class Input:
206
206
  'mode': mode,
207
207
  }))
208
208
 
209
+ # start timer
210
+ self.window.controller.chat.common.start_counter()
211
+
209
212
  # send input to API
210
213
  if mode == MODE_IMAGE:
211
214
  controller.chat.image.send(