pygpt-net 2.6.0.post2__py3-none-any.whl → 2.6.2__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 (98) hide show
  1. pygpt_net/CHANGELOG.txt +8 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +27 -9
  4. pygpt_net/controller/chat/response.py +10 -4
  5. pygpt_net/controller/chat/stream.py +40 -2
  6. pygpt_net/controller/model/editor.py +45 -4
  7. pygpt_net/controller/plugins/plugins.py +25 -0
  8. pygpt_net/controller/presets/editor.py +100 -100
  9. pygpt_net/controller/presets/experts.py +20 -1
  10. pygpt_net/controller/presets/presets.py +5 -4
  11. pygpt_net/controller/ui/mode.py +17 -66
  12. pygpt_net/core/agents/provider.py +2 -1
  13. pygpt_net/core/agents/runner.py +123 -9
  14. pygpt_net/core/agents/runners/helpers.py +3 -2
  15. pygpt_net/core/agents/runners/llama_workflow.py +176 -22
  16. pygpt_net/core/agents/runners/loop.py +22 -13
  17. pygpt_net/core/experts/experts.py +19 -25
  18. pygpt_net/core/idx/chat.py +24 -34
  19. pygpt_net/core/idx/response.py +5 -2
  20. pygpt_net/core/locale/locale.py +73 -45
  21. pygpt_net/core/render/web/body.py +152 -207
  22. pygpt_net/core/render/web/renderer.py +4 -2
  23. pygpt_net/data/config/config.json +3 -3
  24. pygpt_net/data/config/models.json +3 -3
  25. pygpt_net/data/locale/locale.de.ini +12 -8
  26. pygpt_net/data/locale/locale.en.ini +12 -8
  27. pygpt_net/data/locale/locale.es.ini +12 -8
  28. pygpt_net/data/locale/locale.fr.ini +12 -8
  29. pygpt_net/data/locale/locale.it.ini +12 -8
  30. pygpt_net/data/locale/locale.pl.ini +12 -8
  31. pygpt_net/data/locale/locale.uk.ini +12 -8
  32. pygpt_net/data/locale/locale.zh.ini +12 -8
  33. pygpt_net/item/ctx.py +2 -1
  34. pygpt_net/plugin/base/plugin.py +35 -3
  35. pygpt_net/plugin/bitbucket/__init__.py +12 -0
  36. pygpt_net/plugin/bitbucket/config.py +267 -0
  37. pygpt_net/plugin/bitbucket/plugin.py +125 -0
  38. pygpt_net/plugin/bitbucket/worker.py +569 -0
  39. pygpt_net/plugin/cmd_files/worker.py +19 -16
  40. pygpt_net/plugin/facebook/__init__.py +12 -0
  41. pygpt_net/plugin/facebook/config.py +359 -0
  42. pygpt_net/plugin/facebook/plugin.py +114 -0
  43. pygpt_net/plugin/facebook/worker.py +698 -0
  44. pygpt_net/plugin/github/__init__.py +12 -0
  45. pygpt_net/plugin/github/config.py +441 -0
  46. pygpt_net/plugin/github/plugin.py +124 -0
  47. pygpt_net/plugin/github/worker.py +674 -0
  48. pygpt_net/plugin/google/__init__.py +12 -0
  49. pygpt_net/plugin/google/config.py +367 -0
  50. pygpt_net/plugin/google/plugin.py +126 -0
  51. pygpt_net/plugin/google/worker.py +826 -0
  52. pygpt_net/plugin/slack/__init__.py +12 -0
  53. pygpt_net/plugin/slack/config.py +349 -0
  54. pygpt_net/plugin/slack/plugin.py +116 -0
  55. pygpt_net/plugin/slack/worker.py +639 -0
  56. pygpt_net/plugin/telegram/__init__.py +12 -0
  57. pygpt_net/plugin/telegram/config.py +308 -0
  58. pygpt_net/plugin/telegram/plugin.py +118 -0
  59. pygpt_net/plugin/telegram/worker.py +563 -0
  60. pygpt_net/plugin/twitter/__init__.py +12 -0
  61. pygpt_net/plugin/twitter/config.py +491 -0
  62. pygpt_net/plugin/twitter/plugin.py +126 -0
  63. pygpt_net/plugin/twitter/worker.py +837 -0
  64. pygpt_net/provider/agents/base.py +4 -1
  65. pygpt_net/provider/agents/llama_index/codeact_workflow.py +95 -0
  66. pygpt_net/provider/agents/llama_index/legacy/__init__.py +0 -0
  67. pygpt_net/provider/agents/llama_index/{openai.py → legacy/openai.py} +2 -2
  68. pygpt_net/provider/agents/llama_index/{openai_assistant.py → legacy/openai_assistant.py} +37 -5
  69. pygpt_net/provider/agents/llama_index/{planner.py → legacy/planner.py} +3 -3
  70. pygpt_net/provider/agents/llama_index/{react.py → legacy/react.py} +3 -3
  71. pygpt_net/provider/agents/llama_index/openai_workflow.py +52 -0
  72. pygpt_net/provider/agents/llama_index/planner_workflow.py +115 -0
  73. pygpt_net/provider/agents/llama_index/react_workflow.py +6 -4
  74. pygpt_net/provider/agents/llama_index/workflow/__init__.py +0 -0
  75. pygpt_net/provider/agents/llama_index/{codeact_agent_custom.py → workflow/codeact.py} +124 -8
  76. pygpt_net/provider/agents/llama_index/workflow/events.py +24 -0
  77. pygpt_net/provider/agents/llama_index/workflow/openai.py +634 -0
  78. pygpt_net/provider/agents/llama_index/workflow/planner.py +601 -0
  79. pygpt_net/provider/agents/openai/agent.py +1 -0
  80. pygpt_net/provider/agents/openai/agent_b2b.py +2 -0
  81. pygpt_net/provider/agents/openai/agent_planner.py +1 -0
  82. pygpt_net/provider/agents/openai/agent_with_experts.py +1 -0
  83. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +1 -0
  84. pygpt_net/provider/agents/openai/agent_with_feedback.py +1 -0
  85. pygpt_net/provider/agents/openai/evolve.py +1 -0
  86. pygpt_net/provider/core/preset/patch.py +11 -17
  87. pygpt_net/ui/base/config_dialog.py +4 -0
  88. pygpt_net/ui/dialog/preset.py +34 -77
  89. pygpt_net/ui/layout/toolbox/presets.py +2 -2
  90. pygpt_net/ui/main.py +3 -1
  91. pygpt_net/ui/widget/lists/experts.py +3 -2
  92. {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/METADATA +155 -4
  93. {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/RECORD +96 -62
  94. pygpt_net/data/config/presets/agent_react_workflow.json +0 -34
  95. pygpt_net/provider/agents/llama_index/code_act.py +0 -58
  96. {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/LICENSE +0 -0
  97. {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/WHEEL +0 -0
  98. {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,11 @@
1
+ 2.6.2 (2025-08-15)
2
+
3
+ - Added plugins (beta): Google, Facebook, X/Twitter, Telegram, Slack, GitHub, Bitbucket.
4
+
5
+ 2.6.1 (2025-08-14)
6
+
7
+ - LlamaIndex Agents refactored to Workflows.
8
+
1
9
  2.6.0 (2025-08-13)
2
10
 
3
11
  - Added split responses to the OpenAI Agents in non-streaming mode.
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.08.13 00:00:00 #
9
+ # Updated Date: 2025.08.15 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.0"
17
- __build__ = "2025-08-13"
16
+ __version__ = "2.6.2"
17
+ __build__ = "2025-08-15"
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
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.09 01:00:00 #
9
+ # Updated Date: 2025.08.15 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -70,14 +70,23 @@ from pygpt_net.plugin.openai_vision import Plugin as OpenAIVisionPlugin
70
70
  from pygpt_net.plugin.real_time import Plugin as RealTimePlugin
71
71
  from pygpt_net.plugin.agent import Plugin as AgentPlugin
72
72
  from pygpt_net.plugin.mailer import Plugin as MailerPlugin
73
+ from pygpt_net.plugin.google import Plugin as GooglePlugin
74
+ from pygpt_net.plugin.twitter import Plugin as TwitterPlugin
75
+ from pygpt_net.plugin.facebook import Plugin as FacebookPlugin
76
+ from pygpt_net.plugin.telegram import Plugin as TelegramPlugin
77
+ from pygpt_net.plugin.slack import Plugin as SlackPlugin
78
+ from pygpt_net.plugin.github import Plugin as GithubPlugin
79
+ from pygpt_net.plugin.bitbucket import Plugin as BitbucketPlugin
73
80
 
74
81
  # agents (Llama-index)
75
- from pygpt_net.provider.agents.llama_index.openai import OpenAIAgent
76
- from pygpt_net.provider.agents.llama_index.openai_assistant import OpenAIAssistantAgent
77
- from pygpt_net.provider.agents.llama_index.planner import PlannerAgent
78
- from pygpt_net.provider.agents.llama_index.react import ReactAgent
82
+ # from pygpt_net.provider.agents.llama_index.legacy.openai import OpenAIAgent
83
+ from pygpt_net.provider.agents.llama_index.legacy.openai_assistant import OpenAIAssistantAgent
84
+ # from pygpt_net.provider.agents.llama_index.legacy.planner import PlannerAgent
85
+ from pygpt_net.provider.agents.llama_index.planner_workflow import PlannerAgent as PlannerWorkflowAgent
86
+ from pygpt_net.provider.agents.llama_index.openai_workflow import OpenAIAgent as OpenAIWorkflowAgent
87
+ # from pygpt_net.provider.agents.llama_index.legacy.react import ReactAgent
79
88
  from pygpt_net.provider.agents.llama_index.react_workflow import ReactWorkflowAgent
80
- from pygpt_net.provider.agents.llama_index.code_act import CodeActAgent
89
+ from pygpt_net.provider.agents.llama_index.codeact_workflow import CodeActAgent
81
90
  from pygpt_net.provider.agents.openai.agent import Agent as OpenAIAgentsBase
82
91
  from pygpt_net.provider.agents.openai.agent_with_experts import Agent as OpenAIAgentsExperts
83
92
  from pygpt_net.provider.agents.openai.agent_with_experts_feedback import Agent as OpenAIAgentsExpertsFeedback
@@ -378,6 +387,13 @@ def run(**kwargs):
378
387
  launcher.add_plugin(IdxLlamaIndexPlugin())
379
388
  launcher.add_plugin(MailerPlugin())
380
389
  launcher.add_plugin(CrontabPlugin())
390
+ launcher.add_plugin(GooglePlugin())
391
+ launcher.add_plugin(TwitterPlugin())
392
+ launcher.add_plugin(FacebookPlugin())
393
+ launcher.add_plugin(TelegramPlugin())
394
+ launcher.add_plugin(SlackPlugin())
395
+ launcher.add_plugin(GithubPlugin())
396
+ launcher.add_plugin(BitbucketPlugin())
381
397
 
382
398
  # register custom plugins
383
399
  plugins = kwargs.get('plugins', None)
@@ -420,10 +436,12 @@ def run(**kwargs):
420
436
  launcher.add_vector_store(store)
421
437
 
422
438
  # register base agents
423
- launcher.add_agent(OpenAIAgent()) # llama-index
439
+ # launcher.add_agent(OpenAIAgent()) # llama-index
440
+ launcher.add_agent(OpenAIWorkflowAgent()) # llama-index
424
441
  launcher.add_agent(OpenAIAssistantAgent()) # llama-index
425
- launcher.add_agent(PlannerAgent()) # llama-index
426
- launcher.add_agent(ReactAgent()) # llama-index
442
+ # launcher.add_agent(PlannerAgent()) # llama-index
443
+ launcher.add_agent(PlannerWorkflowAgent()) # llama-index
444
+ # launcher.add_agent(ReactAgent()) # llama-index
427
445
  launcher.add_agent(ReactWorkflowAgent()) # llama-index
428
446
  launcher.add_agent(CodeActAgent()) # llama-index
429
447
  launcher.add_agent(OpenAIAgentsBase()) # openai-agents
@@ -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.11 19:00:00 #
9
+ # Updated Date: 2025.08.14 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any
@@ -164,14 +164,16 @@ class Response:
164
164
  :param context: BridgeContext
165
165
  :param extra: Extra data
166
166
  """
167
+ global_mode = self.window.core.config.get('mode', MODE_AGENT_LLAMA)
167
168
  ctx = context.ctx
168
169
  if self.window.controller.kernel.stopped():
169
170
  output = ctx.output
170
171
  if output and has_unclosed_code_tag(output):
171
172
  ctx.output += "\n```"
172
173
  ctx.msg_id = None
173
- self.window.core.ctx.add(ctx) # store context to prevent current output from being lost
174
- self.window.controller.ctx.prepare_name(ctx) # summarize if not yet
174
+ if ctx.id is None:
175
+ self.window.core.ctx.add(ctx) # store context to prevent current output from being lost
176
+ self.window.controller.ctx.prepare_name(ctx) # summarize if not yet
175
177
 
176
178
  # finish render
177
179
  self.window.dispatch(AppEvent(AppEvent.CTX_END)) # app event
@@ -227,7 +229,8 @@ class Response:
227
229
  }
228
230
  event = RenderEvent(RenderEvent.INPUT_APPEND, data)
229
231
  self.window.dispatch(event)
230
- self.window.core.ctx.add(ctx)
232
+ if ctx.id is None:
233
+ self.window.core.ctx.add(ctx)
231
234
  self.window.controller.ctx.update(
232
235
  reload=True,
233
236
  all=False,
@@ -266,6 +269,9 @@ class Response:
266
269
  self.window.dispatch(event)
267
270
 
268
271
  # if continue reasoning
272
+ if global_mode not in [MODE_AGENT_LLAMA, MODE_AGENT_OPENAI]:
273
+ return # no agent mode, nothing to do
274
+
269
275
  if ctx.extra is None or (type(ctx.extra) == dict and "agent_finish" not in ctx.extra):
270
276
  self.window.update_status(trans("status.agent.reasoning"))
271
277
  self.window.controller.chat.common.lock_input() # lock input, re-enable stop button
@@ -77,6 +77,21 @@ class StreamWorker(QRunnable):
77
77
  if generator is not None:
78
78
  for chunk in generator:
79
79
  if ctrl.kernel.stopped():
80
+ if hasattr(generator, 'close'):
81
+ try:
82
+ generator.close()
83
+ except Exception:
84
+ pass
85
+ elif hasattr(generator, 'cancel'):
86
+ try:
87
+ generator.cancel()
88
+ except Exception:
89
+ pass
90
+ elif hasattr(generator, 'stop'):
91
+ try:
92
+ generator.stop()
93
+ except Exception:
94
+ pass
80
95
  ctx.msg_id = None
81
96
  stopped = True
82
97
  break
@@ -239,7 +254,6 @@ class StreamWorker(QRunnable):
239
254
  elif etype == "response.image_generation_call.partial_image":
240
255
  image_base64 = chunk.partial_image_b64
241
256
  image_bytes = base64.b64decode(image_base64)
242
- # prosty i bezpieczny overwrite (jak w oryginale)
243
257
  with open(img_path, "wb") as f:
244
258
  f.write(image_bytes)
245
259
  is_image = True
@@ -249,7 +263,6 @@ class StreamWorker(QRunnable):
249
263
  ctx.msg_id = str(chunk.response.id)
250
264
  core.ctx.update_item(ctx)
251
265
 
252
- # end/error etype – nic nie robimy
253
266
  elif etype in {"response.done", "response.failed", "error"}:
254
267
  pass
255
268
 
@@ -407,6 +420,14 @@ class Stream:
407
420
  ):
408
421
  """
409
422
  Asynchronous append of stream worker to the thread.
423
+
424
+ :param ctx: Context item
425
+ :param mode: Mode of operation (e.g., MODE_ASSISTANT)
426
+ :param is_response: Whether this is a response stream
427
+ :param reply: Reply identifier
428
+ :param internal: Whether this is an internal stream
429
+ :param context: Optional BridgeContext for additional context
430
+ :param extra: Additional data to pass to the stream
410
431
  """
411
432
  self.ctx = ctx
412
433
  self.mode = mode
@@ -430,6 +451,8 @@ class Stream:
430
451
  def handleEnd(self, ctx: CtxItem):
431
452
  """
432
453
  Slot for handling end of stream
454
+
455
+ :param ctx: Context item
433
456
  """
434
457
  self.window.controller.ui.update_tokens()
435
458
 
@@ -457,9 +480,19 @@ class Stream:
457
480
  self.worker = None
458
481
 
459
482
  def handleEvent(self, event):
483
+ """
484
+ Slot for handling stream events
485
+
486
+ :param event: RenderEvent
487
+ """
460
488
  self.window.dispatch(event)
461
489
 
462
490
  def handleError(self, error):
491
+ """
492
+ Slot for handling stream errors
493
+
494
+ :param error: Exception or error message
495
+ """
463
496
  self.window.core.debug.log(error)
464
497
  if self.is_response:
465
498
  if not isinstance(self.extra, dict):
@@ -475,4 +508,9 @@ class Stream:
475
508
  )
476
509
 
477
510
  def log(self, data: object):
511
+ """
512
+ Log data to the debug console
513
+
514
+ :param data: object to log
515
+ """
478
516
  self.window.core.debug.info(data)
@@ -6,10 +6,11 @@
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.07.19 17:00:00 #
9
+ # Updated Date: 2025.08.14 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
13
+ import json
13
14
  from typing import Optional, Any
14
15
 
15
16
  from pygpt_net.core.events import Event
@@ -107,7 +108,14 @@ class Editor:
107
108
  "description": "model.llama_index.env.desc",
108
109
  "advanced": True,
109
110
  },
111
+ "extra_json": {
112
+ "type": "textarea",
113
+ "label": "model.extra",
114
+ "description": "model.extra.desc",
115
+ "advanced": True,
116
+ },
110
117
  }
118
+ self.custom_fields = ["extra_json"]
111
119
 
112
120
  def get_options(self):
113
121
  """
@@ -220,8 +228,12 @@ class Editor:
220
228
  model = self.window.core.models.items[self.current]
221
229
  data_dict = model.to_dict()
222
230
  for key in options:
223
- value = data_dict[key]
224
- options[key]["value"] = value
231
+ if key in data_dict:
232
+ value = data_dict[key]
233
+ options[key]["value"] = value
234
+
235
+ # custom fields
236
+ options["extra_json"]["value"] = json.dumps(model.extra, indent=4) if model.extra else ""
225
237
 
226
238
  if self.current is not None and self.current in self.window.core.models.items:
227
239
  self.set_tab_by_id(self.current)
@@ -229,15 +241,24 @@ class Editor:
229
241
  # load and apply options to config dialog
230
242
  self.window.controller.config.load_options("model", options)
231
243
 
232
- def save(self, persist: bool = True):
244
+ def save(
245
+ self,
246
+ persist: bool = True,
247
+ force: bool = False
248
+ ):
233
249
  """
234
250
  Save models editor
235
251
 
236
252
  :param persist: persist to file and close dialog
253
+ :param force: force save without validation
237
254
  """
238
255
  options = copy.deepcopy(self.get_options()) # copy options
239
256
  data_dict = {}
257
+
258
+ # base fields
240
259
  for key in options:
260
+ if key in self.custom_fields:
261
+ continue
241
262
  value = self.window.controller.config.get_value(
242
263
  parent_id="model",
243
264
  key=key,
@@ -245,6 +266,26 @@ class Editor:
245
266
  )
246
267
  data_dict[key] = value
247
268
 
269
+ # custom fields
270
+ if "extra_json" in options:
271
+ extra_json = self.window.controller.config.get_value(
272
+ parent_id="model",
273
+ key="extra_json",
274
+ option=options["extra_json"],
275
+ )
276
+ try:
277
+ if extra_json:
278
+ decoded = json.loads(extra_json)
279
+ data_dict["extra"] = decoded
280
+ else:
281
+ data_dict["extra"] = {}
282
+ except json.JSONDecodeError as error:
283
+ self.window.ui.dialogs.alert(
284
+ "JSON decoding error in 'extra' field. Please check the syntax:\n\n{}".format(error)
285
+ )
286
+ if not force:
287
+ return # if JSON is invalid, do not save
288
+
248
289
  # update current model
249
290
  if self.current in self.window.core.models.items:
250
291
  self.window.core.models.items[self.current].from_dict(data_dict)
@@ -458,6 +458,31 @@ class Plugins:
458
458
  self.settings.setup()
459
459
  self.update()
460
460
 
461
+ def save_all(self):
462
+ """Save plugin settings"""
463
+ for id in self.window.core.plugins.plugins.keys():
464
+ plugin = self.window.core.plugins.plugins[id]
465
+ options = plugin.setup() # get plugin options
466
+
467
+ # add plugin to global config data if not exists
468
+ if id not in self.window.core.config.get('plugins'):
469
+ self.window.core.config.data['plugins'][id] = {}
470
+
471
+ # update config with current values
472
+ for key in options:
473
+ self.window.core.config.data['plugins'][id][key] = self.window.core.plugins.plugins[id].options[key]['value']
474
+
475
+ # remove key from config if plugin option not exists
476
+ for key in list(self.window.core.config.data['plugins'].keys()):
477
+ if key not in self.window.core.plugins.plugins:
478
+ self.window.core.config.data['plugins'].pop(key)
479
+
480
+ # save preset
481
+ self.window.controller.plugins.presets.save_current()
482
+
483
+ # save config
484
+ self.window.core.config.save()
485
+
461
486
  def log(self, data: Any):
462
487
  """
463
488
  Log data to debug