pygpt-net 2.6.19.post1__py3-none-any.whl → 2.6.21__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 (96) hide show
  1. pygpt_net/CHANGELOG.txt +14 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +3 -1
  4. pygpt_net/controller/agent/agent.py +130 -2
  5. pygpt_net/controller/agent/experts.py +93 -96
  6. pygpt_net/controller/agent/llama.py +2 -1
  7. pygpt_net/controller/assistant/assistant.py +18 -1
  8. pygpt_net/controller/attachment/attachment.py +17 -1
  9. pygpt_net/controller/camera/camera.py +15 -7
  10. pygpt_net/controller/chat/chat.py +2 -2
  11. pygpt_net/controller/chat/common.py +50 -33
  12. pygpt_net/controller/chat/image.py +67 -77
  13. pygpt_net/controller/chat/input.py +94 -166
  14. pygpt_net/controller/chat/output.py +83 -140
  15. pygpt_net/controller/chat/response.py +83 -102
  16. pygpt_net/controller/chat/text.py +116 -149
  17. pygpt_net/controller/ctx/common.py +2 -1
  18. pygpt_net/controller/ctx/ctx.py +86 -6
  19. pygpt_net/controller/files/files.py +13 -1
  20. pygpt_net/controller/idx/idx.py +26 -2
  21. pygpt_net/controller/kernel/reply.py +53 -66
  22. pygpt_net/controller/kernel/stack.py +16 -16
  23. pygpt_net/controller/model/importer.py +2 -1
  24. pygpt_net/controller/model/model.py +62 -3
  25. pygpt_net/controller/settings/editor.py +4 -4
  26. pygpt_net/controller/ui/ui.py +16 -2
  27. pygpt_net/core/agents/observer/evaluation.py +3 -3
  28. pygpt_net/core/agents/provider.py +25 -3
  29. pygpt_net/core/agents/runner.py +4 -1
  30. pygpt_net/core/agents/runners/llama_workflow.py +19 -7
  31. pygpt_net/core/agents/runners/loop.py +3 -1
  32. pygpt_net/core/agents/runners/openai_workflow.py +17 -3
  33. pygpt_net/core/agents/tools.py +4 -1
  34. pygpt_net/core/bridge/context.py +34 -37
  35. pygpt_net/core/ctx/ctx.py +1 -1
  36. pygpt_net/core/db/database.py +2 -2
  37. pygpt_net/core/debug/debug.py +12 -1
  38. pygpt_net/core/dispatcher/dispatcher.py +24 -1
  39. pygpt_net/core/events/app.py +7 -7
  40. pygpt_net/core/events/control.py +26 -26
  41. pygpt_net/core/events/event.py +6 -3
  42. pygpt_net/core/events/kernel.py +2 -2
  43. pygpt_net/core/events/render.py +13 -13
  44. pygpt_net/core/experts/experts.py +76 -82
  45. pygpt_net/core/experts/worker.py +12 -12
  46. pygpt_net/core/models/models.py +5 -1
  47. pygpt_net/core/models/ollama.py +14 -5
  48. pygpt_net/core/render/web/helpers.py +2 -2
  49. pygpt_net/core/render/web/renderer.py +4 -4
  50. pygpt_net/core/types/__init__.py +2 -1
  51. pygpt_net/core/types/agent.py +4 -4
  52. pygpt_net/core/types/base.py +19 -0
  53. pygpt_net/core/types/console.py +6 -6
  54. pygpt_net/core/types/mode.py +8 -8
  55. pygpt_net/core/types/multimodal.py +3 -3
  56. pygpt_net/core/types/openai.py +2 -1
  57. pygpt_net/data/config/config.json +4 -4
  58. pygpt_net/data/config/models.json +19 -3
  59. pygpt_net/data/config/settings.json +14 -14
  60. pygpt_net/data/locale/locale.en.ini +2 -2
  61. pygpt_net/item/ctx.py +256 -240
  62. pygpt_net/item/model.py +59 -116
  63. pygpt_net/item/preset.py +122 -105
  64. pygpt_net/plugin/server/__init__.py +12 -0
  65. pygpt_net/plugin/server/config.py +301 -0
  66. pygpt_net/plugin/server/plugin.py +111 -0
  67. pygpt_net/plugin/server/worker.py +1057 -0
  68. pygpt_net/provider/agents/llama_index/workflow/planner.py +3 -3
  69. pygpt_net/provider/agents/openai/agent.py +4 -12
  70. pygpt_net/provider/agents/openai/agent_b2b.py +10 -15
  71. pygpt_net/provider/agents/openai/agent_planner.py +4 -4
  72. pygpt_net/provider/agents/openai/agent_with_experts.py +3 -7
  73. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -8
  74. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -8
  75. pygpt_net/provider/agents/openai/bot_researcher.py +2 -18
  76. pygpt_net/provider/agents/openai/bots/__init__.py +0 -0
  77. pygpt_net/provider/agents/openai/bots/research_bot/__init__.py +0 -0
  78. pygpt_net/provider/agents/openai/bots/research_bot/agents/__init__.py +0 -0
  79. pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
  80. pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -0
  81. pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
  82. pygpt_net/provider/agents/openai/bots/research_bot/manager.py +1 -10
  83. pygpt_net/provider/agents/openai/evolve.py +5 -9
  84. pygpt_net/provider/agents/openai/supervisor.py +4 -8
  85. pygpt_net/provider/core/config/patch.py +10 -3
  86. pygpt_net/provider/core/ctx/db_sqlite/utils.py +43 -43
  87. pygpt_net/provider/core/model/patch.py +11 -1
  88. pygpt_net/provider/core/preset/json_file.py +47 -49
  89. pygpt_net/provider/gpt/agents/experts.py +2 -2
  90. pygpt_net/ui/base/config_dialog.py +17 -3
  91. pygpt_net/ui/widget/option/checkbox.py +16 -2
  92. {pygpt_net-2.6.19.post1.dist-info → pygpt_net-2.6.21.dist-info}/METADATA +30 -6
  93. {pygpt_net-2.6.19.post1.dist-info → pygpt_net-2.6.21.dist-info}/RECORD +93 -88
  94. {pygpt_net-2.6.19.post1.dist-info → pygpt_net-2.6.21.dist-info}/LICENSE +0 -0
  95. {pygpt_net-2.6.19.post1.dist-info → pygpt_net-2.6.21.dist-info}/WHEEL +0 -0
  96. {pygpt_net-2.6.19.post1.dist-info → pygpt_net-2.6.21.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.18 01:00:00 #
9
+ # Updated Date: 2025.08.23 15:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -14,7 +14,7 @@ import os
14
14
  from PySide6.QtGui import QTextCursor
15
15
  from PySide6.QtWidgets import QFileDialog, QApplication
16
16
 
17
- from pygpt_net.core.events import Event, AppEvent, RenderEvent
17
+ from pygpt_net.core.events import Event, AppEvent, RenderEvent, KernelEvent
18
18
  from pygpt_net.core.types import MODE_ASSISTANT
19
19
  from pygpt_net.item.ctx import CtxItem
20
20
  from pygpt_net.item.model import ModelItem
@@ -250,6 +250,21 @@ class Common:
250
250
  unlock = False
251
251
  return unlock
252
252
 
253
+ def auto_unlock(self, ctx: CtxItem) -> bool:
254
+ """
255
+ Auto unlock input after end
256
+
257
+ :param ctx: CtxItem
258
+ :return: True if unlocked
259
+ """
260
+ # don't unlock input and leave stop btn if assistant mode or if agent/autonomous is enabled
261
+ # send btn will be unlocked in agent mode on stop
262
+ if self.can_unlock(ctx):
263
+ if not self.window.controller.kernel.stopped():
264
+ self.unlock_input() # unlock input
265
+ return True
266
+ return False
267
+
253
268
  def stop(self, exit: bool = False):
254
269
  """
255
270
  Stop all
@@ -257,41 +272,42 @@ class Common:
257
272
  :param exit: True if called on app exit
258
273
  """
259
274
  QApplication.processEvents()
260
- mode = self.window.core.config.get('mode')
261
- event = Event(Event.FORCE_STOP, {
275
+
276
+ core = self.window.core
277
+ controller = self.window.controller
278
+ dispatch = self.window.dispatch
279
+ dispatch(Event(Event.FORCE_STOP, {
262
280
  "value": True,
263
- })
264
- self.window.dispatch(event) # stop event
265
- event = Event(Event.AUDIO_INPUT_TOGGLE, {
281
+ })) # stop event
282
+
283
+ controller.kernel.stack.clear() # pause reply stack
284
+ controller.agent.experts.stop()
285
+ controller.agent.legacy.on_stop()
286
+ controller.assistant.threads.stop = True
287
+ controller.assistant.threads.reset() # reset run and func calls
288
+ dispatch(Event(Event.AUDIO_INPUT_TOGGLE, {
266
289
  "value": False,
267
- })
268
- self.window.controller.kernel.stack.clear() # pause reply stack
269
- self.window.controller.agent.experts.stop()
270
- self.window.controller.agent.legacy.on_stop()
271
- self.window.controller.assistant.threads.stop = True
272
- self.window.controller.assistant.threads.reset() # reset run and func calls
273
- self.window.dispatch(event) # stop audio input
274
- self.window.controller.kernel.halt = True
275
-
276
- event = RenderEvent(RenderEvent.TOOL_END)
277
- self.window.dispatch(event) # show waiting
278
-
279
- self.window.core.gpt.stop()
290
+ })) # stop audio input
291
+ controller.kernel.halt = True
292
+ dispatch(RenderEvent(RenderEvent.TOOL_END)) # show waiting
293
+
294
+ core.gpt.stop()
280
295
  self.unlock_input()
281
296
 
282
- self.window.controller.chat.input.generating = False
297
+ controller.chat.input.generating = False
283
298
  self.window.update_status(trans('status.stopped'))
284
- self.window.stateChanged.emit(self.window.STATE_IDLE)
299
+ dispatch(KernelEvent(KernelEvent.STATE_IDLE)) # state: idle
285
300
 
286
301
  # remotely stop assistant
302
+ mode = core.config.get('mode')
287
303
  if mode == MODE_ASSISTANT and not exit:
288
304
  try:
289
- self.window.controller.assistant.run_stop()
305
+ controller.assistant.run_stop()
290
306
  except Exception as e:
291
- self.window.core.debug.log(e)
307
+ core.debug.log(e)
292
308
 
293
309
  if not exit:
294
- self.window.dispatch(AppEvent(AppEvent.INPUT_STOPPED)) # app event
310
+ dispatch(AppEvent(AppEvent.INPUT_STOPPED)) # app event
295
311
 
296
312
  def check_api_key(
297
313
  self,
@@ -310,15 +326,16 @@ class Common:
310
326
  if model is None:
311
327
  return True
312
328
 
329
+ config = self.window.core.config
313
330
  provider_keys = {
314
- "openai": self.window.core.config.get('api_key', None),
315
- "azure_openai": self.window.core.config.get('api_key', None),
316
- "anthropic": self.window.core.config.get('api_key_anthropic', None),
317
- "google": self.window.core.config.get('api_key_google', None),
318
- "x_ai": self.window.core.config.get('api_key_xai', None),
319
- "perplexity": self.window.core.config.get('api_key_perplexity', None),
320
- "deepseek_api": self.window.core.config.get('api_key_deepseek', None),
321
- "mistral_ai": self.window.core.config.get('api_key_mistral', None),
331
+ "openai": config.get('api_key', None),
332
+ "azure_openai": config.get('api_key', None),
333
+ "anthropic": config.get('api_key_anthropic', None),
334
+ "google": config.get('api_key_google', None),
335
+ "x_ai": config.get('api_key_xai', None),
336
+ "perplexity": config.get('api_key_perplexity', None),
337
+ "deepseek_api": config.get('api_key_deepseek', None),
338
+ "mistral_ai": config.get('api_key_mistral', None),
322
339
  }
323
340
 
324
341
  if model.provider in provider_keys:
@@ -6,8 +6,9 @@
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.06 01:00:00 #
9
+ # Updated Date: 2025.08.23 15:00:00 #
10
10
  # ================================================== #
11
+
11
12
  import os
12
13
  from typing import Optional, List
13
14
 
@@ -33,16 +34,19 @@ class Image:
33
34
  self,
34
35
  text: str,
35
36
  prev_ctx: Optional[CtxItem] = None,
36
- parent_id: Optional[int] = None,
37
37
  ) -> CtxItem:
38
38
  """
39
39
  Send prompt for image generate
40
40
 
41
41
  :param text: prompt for image generation
42
42
  :param prev_ctx: previous ctx item
43
- :param parent_id: parent ctx id
44
43
  :return: ctx item
45
44
  """
45
+ core = self.window.core
46
+ controller = self.window.controller
47
+ update_status = self.window.update_status
48
+ dispatch = self.window.dispatch
49
+
46
50
  num = int(self.window.ui.config['global']['img_variants'].input.text() or 1)
47
51
  if num < 1:
48
52
  num = 1
@@ -50,79 +54,72 @@ class Image:
50
54
  num = 4
51
55
 
52
56
  # force 1 image if dall-e-3 model is used
53
- mode = self.window.core.config.get('mode')
54
- model = self.window.core.config.get('model')
55
- model_data = self.window.core.models.get(model)
57
+ mode = core.config.get('mode')
58
+ model = core.config.get('model')
59
+ model_data = core.models.get(model)
56
60
  if model_data.id == 'dall-e-3' or model_data.id == 'gpt-image-1':
57
61
  num = 1
58
62
 
59
- self.window.update_status(trans('status.sending'))
63
+ update_status(trans('status.sending'))
60
64
 
61
65
  # create ctx item
62
66
  ctx = CtxItem()
63
67
  ctx.current = True # mark as current context item
64
- ctx.meta = self.window.core.ctx.get_current_meta() # CtxMeta (owner object)
65
- ctx.set_input(text, self.window.core.config.get('user_name'))
68
+ ctx.meta = core.ctx.get_current_meta() # CtxMeta (owner object)
69
+ ctx.set_input(text, core.config.get('user_name'))
66
70
  ctx.prev_ctx = prev_ctx # store previous context item
67
71
  ctx.live = True
68
72
 
69
73
  # event: context before
70
74
  event = Event(Event.CTX_BEFORE)
71
75
  event.ctx = ctx
72
- self.window.dispatch(event)
76
+ dispatch(event)
73
77
 
74
78
  # add ctx to DB
75
- self.window.core.ctx.add(ctx)
79
+ core.ctx.add(ctx)
76
80
 
77
81
  # render: begin
78
- data = {
82
+ dispatch(RenderEvent(RenderEvent.STREAM_BEGIN, {
79
83
  "meta": ctx.meta,
80
84
  "ctx": ctx,
81
85
  "stream": False,
82
- }
83
- event = RenderEvent(RenderEvent.STREAM_BEGIN, data)
84
- self.window.dispatch(event)
86
+ }))
85
87
 
86
- # append input to chat
87
- data = {
88
+ # render: append input
89
+ dispatch(RenderEvent(RenderEvent.INPUT_APPEND, {
88
90
  "meta": ctx.meta,
89
91
  "ctx": ctx,
90
- }
91
- event = RenderEvent(RenderEvent.INPUT_APPEND, data)
92
- self.window.dispatch(event)
92
+ }))
93
93
 
94
94
  # handle ctx name (generate title from summary if not initialized)
95
- if self.window.core.config.get('ctx.auto_summary'):
96
- self.window.controller.ctx.prepare_name(ctx)
95
+ if core.config.get('ctx.auto_summary'):
96
+ controller.ctx.prepare_name(ctx)
97
97
 
98
98
  # get attachments
99
- files = self.window.core.attachments.get_all(mode)
99
+ files = core.attachments.get_all(mode)
100
100
  num_files = len(files)
101
101
  if num_files > 0:
102
- self.window.controller.chat.log("Attachments ({}): {}".format(mode, num_files))
102
+ controller.chat.log(f"Attachments ({mode}): {num_files}")
103
103
 
104
104
  # generate image
105
- bridge_context = BridgeContext(
105
+ context = BridgeContext(
106
106
  ctx=ctx,
107
107
  mode=MODE_IMAGE,
108
108
  model=model_data, # model instance
109
109
  prompt=text,
110
110
  attachments=files,
111
111
  )
112
- extra = {
113
- "num": num,
114
- }
115
112
  try:
116
- event = KernelEvent(KernelEvent.REQUEST, {
117
- 'context': bridge_context,
118
- 'extra': extra,
119
- })
120
- self.window.dispatch(event)
113
+ dispatch(KernelEvent(KernelEvent.REQUEST, {
114
+ 'context': context,
115
+ 'extra': {
116
+ "num": num,
117
+ },
118
+ }))
121
119
  except Exception as e:
122
- self.window.core.debug.log(e)
120
+ core.debug.log(e)
123
121
  self.window.ui.dialogs.alert(e)
124
- self.window.update_status(trans('status.error'))
125
-
122
+ update_status(trans('status.error'))
126
123
  return ctx
127
124
 
128
125
  @Slot(object, list, str)
@@ -139,37 +136,39 @@ class Image:
139
136
  :param paths: list with paths to downloaded images
140
137
  :param prompt: prompt used to generate images
141
138
  """
142
- self.window.dispatch(KernelEvent(KernelEvent.STATE_IDLE, {
139
+ core = self.window.core
140
+ dispatch = self.window.dispatch
141
+
142
+ dispatch(KernelEvent(KernelEvent.STATE_IDLE, {
143
143
  "id": "img",
144
144
  }))
145
145
  string = ""
146
146
  i = 1
147
147
  for path in paths:
148
148
  basename = os.path.basename(path)
149
- string += "[{}]({})".format(basename, path) + "\n"
149
+ string += f"[{basename}]({path})\n"
150
150
  i += 1
151
151
 
152
- if not self.window.core.config.get('img_raw'):
153
- string += "\nPrompt: "
154
- string += prompt
152
+ if not core.config.get('img_raw'):
153
+ string += f"\nPrompt: {prompt}"
155
154
 
156
- local_urls = self.window.core.filesystem.make_local_list(paths)
155
+ local_urls = core.filesystem.make_local_list(paths)
157
156
  ctx.images = local_urls # save images paths
158
157
  ctx.set_output(string.strip())
159
158
 
160
159
  # event: after context
161
160
  event = Event(Event.CTX_AFTER)
162
161
  event.ctx = ctx
163
- self.window.dispatch(event)
162
+ dispatch(event)
164
163
 
165
164
  # store last mode (in text mode this is handled in send_text)
166
- mode = self.window.core.config.get('mode')
167
- self.window.core.ctx.post_update(mode) # post update context, store last mode, etc.
168
- self.window.core.ctx.store() # save current ctx to DB
165
+ mode = core.config.get('mode')
166
+ core.ctx.post_update(mode) # post update context, store last mode, etc.
167
+ core.ctx.store() # save current ctx to DB
169
168
  self.window.update_status(trans('status.img.generated'))
170
169
 
171
170
  # update ctx in DB
172
- self.window.core.ctx.update_item(ctx)
171
+ core.ctx.update_item(ctx)
173
172
 
174
173
  self.window.controller.chat.common.unlock_input() # unlock input
175
174
 
@@ -187,19 +186,23 @@ class Image:
187
186
  :param paths: list with paths to downloaded images
188
187
  :param prompt: prompt used to generate images
189
188
  """
190
- self.window.dispatch(KernelEvent(KernelEvent.STATE_IDLE, {
189
+ core = self.window.core
190
+ controller = self.window.controller
191
+ dispatch = self.window.dispatch
192
+
193
+ dispatch(KernelEvent(KernelEvent.STATE_IDLE, {
191
194
  "id": "img",
192
195
  }))
193
196
  string = ""
194
197
  i = 1
195
198
  for path in paths:
196
- string += "{}) `{}`".format(i, path) + "\n"
199
+ string += f"{i}) `{path}`\n"
197
200
  i += 1
198
201
 
199
- local_urls = self.window.core.filesystem.make_local_list(paths)
202
+ local_urls = core.filesystem.make_local_list(paths)
200
203
  ctx.images = local_urls # save images paths in ctx item here
201
204
 
202
- self.window.core.ctx.update_item(ctx) # update in DB
205
+ core.ctx.update_item(ctx) # update in DB
203
206
  self.window.update_status(trans('status.img.generated')) # update status
204
207
 
205
208
  # WARNING:
@@ -221,41 +224,28 @@ class Image:
221
224
  "meta": ctx.meta,
222
225
  "ctx": ctx,
223
226
  }
224
- event = RenderEvent(RenderEvent.EXTRA_APPEND, data)
225
- self.window.dispatch(event) # show image first
226
-
227
- event = RenderEvent(RenderEvent.EXTRA_END, data)
228
- self.window.dispatch(event) # end extra
227
+ dispatch(RenderEvent(RenderEvent.EXTRA_APPEND, data)) # show image first
228
+ dispatch(RenderEvent(RenderEvent.EXTRA_APPEND, data)) # end extra
229
229
 
230
230
  context = BridgeContext()
231
231
  context.ctx = ctx
232
- extra = {
233
- "flush": True,
234
- }
235
- event = KernelEvent(KernelEvent.REPLY_ADD, {
232
+ dispatch(KernelEvent(KernelEvent.REPLY_ADD, {
236
233
  'context': context,
237
- 'extra': extra,
238
- })
239
- self.window.dispatch(event)
240
- self.window.controller.chat.common.unlock_input() # unlock input
241
-
242
- event = RenderEvent(RenderEvent.TOOL_UPDATE, data)
243
- self.window.dispatch(event) # end of tool, hide spinner icon
234
+ 'extra': {
235
+ "flush": True,
236
+ },
237
+ }))
238
+ controller.chat.common.unlock_input() # unlock input
239
+ dispatch(RenderEvent(RenderEvent.TOOL_UPDATE, data)) # end of tool, hide spinner icon
244
240
  return
245
241
 
246
242
  # NOT internal-mode, user called, so append only img output to chat (show images now):
247
-
248
243
  data = {
249
244
  "meta": ctx.meta,
250
245
  "ctx": ctx,
251
246
  }
252
- event = RenderEvent(RenderEvent.EXTRA_APPEND, data)
253
- self.window.dispatch(event) # show image first
254
-
255
- event = RenderEvent(RenderEvent.EXTRA_END, data)
256
- self.window.dispatch(event) # end extra
257
-
258
- self.window.controller.chat.common.unlock_input() # unlock input
247
+ dispatch(RenderEvent(RenderEvent.EXTRA_APPEND, data)) # show image first
248
+ dispatch(RenderEvent(RenderEvent.EXTRA_END, data)) # end extra
259
249
 
260
- event = RenderEvent(RenderEvent.TOOL_UPDATE, data)
261
- self.window.dispatch(event) # end of tool, hide spinner icon
250
+ controller.chat.common.unlock_input() # unlock input
251
+ dispatch(RenderEvent(RenderEvent.TOOL_UPDATE, data)) # end of tool, hide spinner icon