pygpt-net 2.6.15__py3-none-any.whl → 2.6.17__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 (57) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/__init__.py +8 -2
  4. pygpt_net/controller/chat/command.py +18 -6
  5. pygpt_net/controller/ctx/ctx.py +2 -2
  6. pygpt_net/controller/mode/mode.py +3 -2
  7. pygpt_net/controller/plugins/plugins.py +31 -15
  8. pygpt_net/controller/presets/editor.py +11 -32
  9. pygpt_net/controller/settings/profile.py +16 -3
  10. pygpt_net/controller/settings/workdir.py +184 -124
  11. pygpt_net/controller/theme/theme.py +11 -5
  12. pygpt_net/core/agents/observer/evaluation.py +3 -14
  13. pygpt_net/core/agents/runners/llama_workflow.py +7 -6
  14. pygpt_net/core/command/command.py +5 -3
  15. pygpt_net/core/experts/experts.py +58 -13
  16. pygpt_net/core/plugins/plugins.py +12 -1
  17. pygpt_net/core/render/plain/body.py +10 -19
  18. pygpt_net/core/render/plain/renderer.py +27 -27
  19. pygpt_net/data/config/config.json +6 -6
  20. pygpt_net/data/config/models.json +3 -3
  21. pygpt_net/data/locale/locale.en.ini +2 -2
  22. pygpt_net/data/locale/plugin.openai_dalle.de.ini +1 -1
  23. pygpt_net/data/locale/plugin.openai_dalle.en.ini +1 -1
  24. pygpt_net/data/locale/plugin.openai_dalle.es.ini +1 -1
  25. pygpt_net/data/locale/plugin.openai_dalle.fr.ini +1 -1
  26. pygpt_net/data/locale/plugin.openai_dalle.it.ini +1 -1
  27. pygpt_net/data/locale/plugin.openai_dalle.pl.ini +1 -1
  28. pygpt_net/data/locale/plugin.openai_dalle.uk.ini +1 -1
  29. pygpt_net/data/locale/plugin.openai_dalle.zh.ini +1 -1
  30. pygpt_net/data/locale/plugin.openai_vision.de.ini +1 -1
  31. pygpt_net/data/locale/plugin.openai_vision.en.ini +1 -1
  32. pygpt_net/data/locale/plugin.openai_vision.es.ini +1 -1
  33. pygpt_net/data/locale/plugin.openai_vision.fr.ini +1 -1
  34. pygpt_net/data/locale/plugin.openai_vision.it.ini +1 -1
  35. pygpt_net/data/locale/plugin.openai_vision.pl.ini +1 -1
  36. pygpt_net/data/locale/plugin.openai_vision.uk.ini +1 -1
  37. pygpt_net/data/locale/plugin.openai_vision.zh.ini +1 -1
  38. pygpt_net/item/ctx.py +5 -4
  39. pygpt_net/plugin/idx_llama_index/plugin.py +9 -5
  40. pygpt_net/plugin/idx_llama_index/worker.py +5 -2
  41. pygpt_net/plugin/openai_dalle/plugin.py +1 -1
  42. pygpt_net/tools/translator/ui/dialogs.py +1 -0
  43. pygpt_net/tools/translator/ui/widgets.py +1 -2
  44. pygpt_net/ui/__init__.py +12 -10
  45. pygpt_net/ui/base/config_dialog.py +15 -10
  46. pygpt_net/ui/dialog/about.py +26 -18
  47. pygpt_net/ui/dialog/plugins.py +6 -4
  48. pygpt_net/ui/dialog/settings.py +75 -87
  49. pygpt_net/ui/dialog/workdir.py +7 -2
  50. pygpt_net/ui/main.py +5 -1
  51. pygpt_net/ui/widget/textarea/editor.py +1 -2
  52. pygpt_net/ui/widget/textarea/web.py +22 -16
  53. {pygpt_net-2.6.15.dist-info → pygpt_net-2.6.17.dist-info}/METADATA +26 -14
  54. {pygpt_net-2.6.15.dist-info → pygpt_net-2.6.17.dist-info}/RECORD +57 -57
  55. {pygpt_net-2.6.15.dist-info → pygpt_net-2.6.17.dist-info}/LICENSE +0 -0
  56. {pygpt_net-2.6.15.dist-info → pygpt_net-2.6.17.dist-info}/WHEEL +0 -0
  57. {pygpt_net-2.6.15.dist-info → pygpt_net-2.6.17.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.11 14:00:00 #
9
+ # Updated Date: 2025.08.20 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -16,6 +16,7 @@ from typing import Optional
16
16
  from uuid import uuid4
17
17
 
18
18
  from PySide6.QtCore import QObject, QRunnable, Signal, Slot
19
+ from PySide6.QtWidgets import QApplication
19
20
 
20
21
  from pygpt_net.utils import trans
21
22
 
@@ -32,7 +33,7 @@ class WorkerSignals(QObject):
32
33
  updateStatus = Signal(str) # update status in dialog
33
34
  updateGlobalStatus = Signal(str) # update global status
34
35
  alert = Signal(object) # dialog alert
35
- reload = Signal() # on reload components (e.g. after workdir change)
36
+ after_migrate = Signal(bool, str, str, str) # result, profile_name, current_path, new_path
36
37
  confirm = Signal(str, str, str) # confirm dialog (action, path, message)
37
38
  restored = Signal(str) # after restoring workdir
38
39
  updated = Signal(str) # after updating workdir
@@ -68,12 +69,8 @@ class WorkdirWorker(QRunnable):
68
69
  @Slot()
69
70
  def run(self):
70
71
  try:
71
- if self.action == "update":
72
- self.worker_update()
73
- elif self.action == "migrate":
72
+ if self.action == "migrate":
74
73
  self.worker_migrate()
75
- elif self.action == "restore":
76
- self.worker_restore()
77
74
  elif self.action == "delete":
78
75
  self.worker_delete_files()
79
76
  elif self.action == "duplicate":
@@ -116,7 +113,7 @@ class WorkdirWorker(QRunnable):
116
113
  if not os.path.exists(path) or not os.path.isdir(path):
117
114
  self.signals.alert.emit(trans("dialog.profile.alert.path.not_exists"))
118
115
  return
119
- print("Clearing workdir: ", path)
116
+ print(f"Clearing workdir: {path}")
120
117
  self.window.core.filesystem.clear_workdir(
121
118
  path,
122
119
  remove_db=remove_db,
@@ -149,7 +146,7 @@ class WorkdirWorker(QRunnable):
149
146
  # copy files from workdir
150
147
  path_from = profile['workdir'].replace("%HOME%", str(Path.home()))
151
148
  path_to = new_path
152
- print("Copying all files from {} to: {}".format(path_from, path_to))
149
+ print(f"Copying all files from {path_from} to: {path_to}")
153
150
  self.signals.updateGlobalStatus.emit("Copying files...")
154
151
  result = self.window.core.filesystem.copy_workdir(
155
152
  path_from,
@@ -175,7 +172,7 @@ class WorkdirWorker(QRunnable):
175
172
  profile = profiles[uuid]
176
173
  path = profile['workdir'].replace("%HOME%", str(Path.home()))
177
174
  if not os.path.exists(path) or not os.path.isdir(path):
178
- self.signals.alert.emit("Directory not exists!")
175
+ self.signals.alert.emit(f"Directory not exists: {path}")
179
176
  return
180
177
  print("Clearing workdir: ", path)
181
178
  self.window.core.db.close()
@@ -186,53 +183,7 @@ class WorkdirWorker(QRunnable):
186
183
  )
187
184
  if uuid == current:
188
185
  self.signals.switch.emit(uuid) # switch to profile
189
- self.signals.updateGlobalStatus.emit("Profile cleared: " + profile['name'])
190
-
191
- def worker_update(self):
192
- """Switch working directory to the existing one"""
193
- print("\n====================")
194
- print("Changing workdir to: ", self.path)
195
- print("====================\n")
196
- current_path = self.window.core.config.get_user_path()
197
- default_path = self.window.core.config.get_base_workdir()
198
-
199
- if self.force:
200
- self.signals.updateStatus.emit(trans("dialog.workdir.result.wait"))
201
-
202
- lock_file = os.path.join(default_path, 'path.cfg') # put "path.cfg"
203
- lock_path = self.path.replace(str(Path.home()), "%HOME%")
204
- if self.path == default_path:
205
- lock_path = "" # set empty if default dir
206
- with open(lock_file, 'w', encoding='utf-8') as f:
207
- f.write(lock_path)
208
-
209
- # update path in current profile
210
- self.window.core.config.profile.update_current_workdir(self.path)
211
-
212
- # reload config
213
- self.window.core.config.set_workdir(self.path, reload=True)
214
- self.window.core.config.set('license.accepted', True) # accept license to prevent show dialog again
215
-
216
- # reload components
217
- if self.force:
218
- try:
219
- self.signals.reload.emit()
220
- success_msg = trans("dialog.workdir.result.success").format(path=self.path)
221
- self.signals.updateStatus.emit(success_msg)
222
- self.signals.alert.emit(success_msg)
223
- except Exception as e:
224
- self.window.core.debug.log(e)
225
- self.signals.alert.emit(str(e))
226
- print("Error reloading components: ", e)
227
- self.worker_restore(custom_current=current_path)
228
- self.window.controller.reloading = False # unlock
229
- else:
230
- # always reload
231
- self.signals.reload.emit()
232
-
233
- # update profile after workdir change
234
- if self.profile_name:
235
- self.signals.updated.emit(self.profile_name)
186
+ self.signals.updateGlobalStatus.emit(f"Profile cleared: {profile['name']}")
236
187
 
237
188
  def worker_migrate(self):
238
189
  """Migrate working directory"""
@@ -267,7 +218,7 @@ class WorkdirWorker(QRunnable):
267
218
 
268
219
  self.signals.hideStatus.emit()
269
220
  self.window.controller.settings.workdir.busy = True
270
- print("Migrating workdir from: ", current, " to: ", self.path)
221
+ print(f"Migrating workdir from: {current} to: {self.path}...")
271
222
 
272
223
  # check if path exists
273
224
  if not os.path.exists(self.path) or not os.path.isdir(self.path):
@@ -289,6 +240,8 @@ class WorkdirWorker(QRunnable):
289
240
  return
290
241
 
291
242
  # copy workdir
243
+ self.signals.updateGlobalStatus.emit(trans("dialog.workdir.result.wait"))
244
+ QApplication.processEvents() # process events to update UI
292
245
  try:
293
246
  result = self.window.core.filesystem.copy_workdir(current, self.path)
294
247
  except Exception as e:
@@ -297,46 +250,28 @@ class WorkdirWorker(QRunnable):
297
250
  print("Error migrating workdir: ", e)
298
251
  result = False
299
252
 
300
- if result:
301
- try:
302
- # remove old workdir
303
- self.window.core.debug.info("Clearing old workdir: {}".format(current))
304
- try:
305
- # allow errors here
306
- self.window.core.filesystem.clear_workdir(current)
307
- except Exception as e:
308
- self.window.core.debug.log(e)
309
- print("Error clearing old workdir: ", e)
253
+ # reload UI, config, etc.
254
+ self.signals.after_migrate.emit(result, self.profile_name, current, self.path)
310
255
 
311
- # update workdir to new path
312
- self.worker_update()
313
- success_msg = trans("dialog.workdir.result.success").format(path=self.path)
314
- print(success_msg)
315
- self.signals.updateStatus.emit(success_msg)
316
- self.signals.alert.emit(success_msg)
317
- except Exception as e:
318
- self.window.core.debug.log(e)
319
- self.signals.alert.emit(str(e))
320
- print("Error migrating workdir: ", e)
321
- self.worker_restore(custom_current=current)
322
- self.window.controller.reloading = False
323
- else:
324
- self.signals.updateStatus.emit(trans("dialog.workdir.result.failed"))
325
- self.signals.alert.emit(trans("dialog.workdir.result.failed"))
326
- self.worker_restore(custom_current=current)
327
- self.window.controller.reloading = False
328
256
 
329
- self.window.controller.settings.workdir.busy = False
330
- self.window.core.debug.info("Finished migrating workdir from: {} to: {}".format(current, self.path))
257
+ class Workdir:
258
+ def __init__(self, window=None):
259
+ """
260
+ Workdir controller
331
261
 
332
- def worker_restore(self, custom_current: str = None):
262
+ :param window: window instance
333
263
  """
334
- Restore default working directory
264
+ self.window = window
265
+ self.is_dialog = False
266
+ self.busy = False
267
+
268
+ def rollback(self, current: str = None):
269
+ """
270
+ Rollback to the previous working directory
335
271
 
336
- :param custom_current: custom current working directory (optional)
272
+ :param current: current working directory (optional)
337
273
  """
338
- current = custom_current if custom_current is not None else self.current
339
- print("Reverting workdir to: ", current)
274
+ print(f"Reverting workdir to: {current}...")
340
275
  self.window.core.config.set_workdir(current, reload=True)
341
276
  default_path = self.window.core.config.get_base_workdir()
342
277
  lock_file = os.path.join(default_path, 'path.cfg')
@@ -345,23 +280,151 @@ class WorkdirWorker(QRunnable):
345
280
  lock_path = ""
346
281
  with open(lock_file, 'w', encoding='utf-8') as f:
347
282
  f.write(lock_path)
348
- self.signals.restored.emit(current)
349
- self.signals.updateStatus.emit("Failed. Reverted to current workdir: {}".format(current))
350
- self.signals.reload.emit()
283
+ self.window.ui.dialogs.workdir.set_path(current)
284
+ self.window.ui.dialogs.workdir.show_status(f"Failed. Reverted to previous workdir: {current}.")
285
+ self.window.controller.reload()
351
286
  self.window.core.config.profile.update_current_workdir(current)
352
287
 
288
+ def update_workdir(
289
+ self,
290
+ force: bool = False,
291
+ path: str = None
292
+ ):
293
+ """
294
+ Update working directory
353
295
 
354
- class Workdir:
355
- def __init__(self, window=None):
296
+ :param force: boolean indicating if update should be forced (confirm)
297
+ :param path: new working directory to set
356
298
  """
357
- Workdir controller
299
+ print("\n====================")
300
+ print(f"Changing workdir to: {path}")
301
+ print("====================\n")
302
+ default_path = self.window.core.config.get_base_workdir()
303
+ if force:
304
+ self.window.ui.dialogs.workdir.show_status(trans("dialog.workdir.result.wait"))
358
305
 
359
- :param window: window instance
306
+ lock_file = os.path.join(default_path, 'path.cfg') # put "path.cfg"
307
+ lock_path = path.replace(str(Path.home()), "%HOME%")
308
+ if path == default_path:
309
+ lock_path = "" # set empty if default dir
310
+ with open(lock_file, 'w', encoding='utf-8') as f:
311
+ f.write(lock_path)
312
+
313
+ # update path in current profile
314
+ self.window.core.config.profile.update_current_workdir(path)
315
+
316
+ # reload config
317
+ self.window.core.config.set_workdir(path, reload=True)
318
+ self.window.core.config.set('license.accepted', True) # accept license to prevent show dialog again
319
+
320
+ @Slot(bool, str, str, str)
321
+ def do_update(
322
+ self,
323
+ force: bool,
324
+ profile_name: str,
325
+ current_path: str,
326
+ new_path: str
327
+ ) -> bool:
360
328
  """
361
- self.window = window
362
- self.is_dialog = False
363
- self.busy = False
364
- self.worker = None
329
+ Update working directory
330
+
331
+ :param force: boolean indicating if update should be forced (confirm)
332
+ :param profile_name: profile name to update after workdir change
333
+ :param current_path: current working directory before update
334
+ :param new_path: new working directory to set
335
+ :return: boolean indicating if update was successful
336
+ """
337
+ self.update_workdir(
338
+ force=force,
339
+ path=new_path,
340
+ )
341
+ rollback = False
342
+ success = False
343
+ if force:
344
+ try:
345
+ self.window.ui.dialogs.workdir.show_status(trans("dialog.workdir.result.wait"))
346
+ self.window.controller.reload()
347
+ self.window.ui.dialogs.workdir.show_status(trans("dialog.workdir.result.wait"))
348
+ msg = trans("dialog.workdir.result.success").format(path=new_path)
349
+ self.window.ui.dialogs.workdir.show_status(msg)
350
+ self.window.ui.dialogs.alert(msg)
351
+ success = True
352
+ except Exception as e:
353
+ rollback = True
354
+ self.window.core.debug.log(e)
355
+ self.window.ui.dialogs.alert(str(e))
356
+ print("Error reloading components: ", e)
357
+ self.window.controller.reloading = False # unlock
358
+ else:
359
+ self.window.controller.reload() # reload only
360
+
361
+ if rollback: # if failed
362
+ self.rollback(current=current_path) # revert to previous workdir
363
+ else:
364
+ # update profile after workdir change
365
+ if profile_name:
366
+ self.window.controller.settings.profile.after_update(profile_name)
367
+ return success
368
+
369
+ @Slot(bool, str, str, str)
370
+ def do_migrate(
371
+ self,
372
+ result: bool,
373
+ profile_name: str,
374
+ current_path: str,
375
+ new_path: str
376
+ ) -> bool:
377
+ """
378
+ Handle migration result
379
+
380
+ :param result: boolean indicating if migration was successful
381
+ :param profile_name: profile name to update after migration
382
+ :param current_path: current working directory before migration
383
+ :param new_path: new working directory after migration
384
+ :return: boolean indicating if migration was successful
385
+ """
386
+ success = False
387
+ if result:
388
+ try:
389
+ # update workdir to new path
390
+ success = self.do_update(
391
+ force=True,
392
+ profile_name=profile_name,
393
+ current_path=current_path,
394
+ new_path=new_path,
395
+ ) # with rollback if failed
396
+
397
+ if not success:
398
+ raise Exception("Migration failed, workdir not updated.")
399
+
400
+ msg = trans("dialog.workdir.result.success").format(path=new_path)
401
+ self.window.ui.dialogs.workdir.show_status(msg)
402
+ self.window.ui.dialogs.alert(msg)
403
+
404
+ # remove old workdir only if success
405
+ self.window.core.debug.info(f"Clearing old workdir: {current_path}...")
406
+ try:
407
+ self.window.core.filesystem.clear_workdir(current_path) # allow errors here
408
+ self.window.core.debug.info(f"Old workdir cleared: {current_path}.")
409
+ except Exception as e:
410
+ self.window.core.debug.log(e)
411
+ print("Error clearing old workdir: ", e)
412
+
413
+ except Exception as e:
414
+ self.window.core.debug.log(e)
415
+ self.window.ui.dialogs.alert(str(e))
416
+ print("Error migrating workdir: ", e)
417
+ self.window.controller.reloading = False
418
+ else:
419
+ # if migration failed
420
+ self.window.ui.dialogs.workdir.show_status(trans("dialog.workdir.result.failed"))
421
+ self.window.ui.dialogs.alert(trans("dialog.workdir.result.failed"))
422
+ self.window.controller.reloading = False
423
+
424
+ self.window.controller.settings.workdir.busy = False
425
+ if success:
426
+ self.window.core.debug.info(f"Finished migrating workdir from: {current_path} to: {new_path}.")
427
+ return success
365
428
 
366
429
  def change(self):
367
430
  """Change working directory (open dialog)"""
@@ -402,7 +465,7 @@ class Workdir:
402
465
  :param profile_new_name: new profile name (optional, for duplicate action)
403
466
  :param profile_new_path: new profile path (optional, for duplicate action)
404
467
  """
405
- self.worker = WorkdirWorker(
468
+ worker = WorkdirWorker(
406
469
  window=self.window,
407
470
  action=action,
408
471
  path=path,
@@ -415,21 +478,21 @@ class Workdir:
415
478
  )
416
479
 
417
480
  # connect signals
418
- self.worker.signals.updateGlobalStatus.connect(self.window.update_status)
419
- self.worker.signals.updateStatus.connect(self.window.ui.dialogs.workdir.show_status)
420
- self.worker.signals.hideStatus.connect(self.window.ui.dialogs.workdir.hide_status)
421
- self.worker.signals.alert.connect(self.window.ui.dialogs.alert)
422
- self.worker.signals.reload.connect(self.window.controller.reload)
423
- self.worker.signals.error.connect(lambda err: self.window.core.debug.log(f"Worker error: {err}"))
424
- self.worker.signals.confirm.connect(self.window.ui.dialogs.confirm)
425
- self.worker.signals.restored.connect(lambda current: self.window.ui.dialogs.workdir.set_path(current))
426
- self.worker.signals.updated.connect(self.window.controller.settings.profile.after_update)
427
- self.worker.signals.deleted.connect(self.window.controller.settings.profile.after_delete)
428
- self.worker.signals.duplicated.connect(self.window.controller.settings.profile.after_duplicate)
429
- self.worker.signals.switch.connect(self.window.controller.settings.profile.switch_current)
481
+ worker.signals.updateGlobalStatus.connect(self.window.update_status)
482
+ worker.signals.updateStatus.connect(self.window.ui.dialogs.workdir.show_status)
483
+ worker.signals.hideStatus.connect(self.window.ui.dialogs.workdir.hide_status)
484
+ worker.signals.alert.connect(self.window.ui.dialogs.alert)
485
+ worker.signals.error.connect(lambda err: self.window.core.debug.log(f"Worker error: {err}"))
486
+ worker.signals.confirm.connect(self.window.ui.dialogs.confirm)
487
+ worker.signals.restored.connect(lambda current: self.window.ui.dialogs.workdir.set_path(current))
488
+ worker.signals.updated.connect(self.window.controller.settings.profile.after_update)
489
+ worker.signals.deleted.connect(self.window.controller.settings.profile.after_delete)
490
+ worker.signals.duplicated.connect(self.window.controller.settings.profile.after_duplicate)
491
+ worker.signals.switch.connect(self.window.controller.settings.profile.switch_current)
492
+ worker.signals.after_migrate.connect(self.do_migrate)
430
493
 
431
494
  # start worker in thread pool
432
- self.window.threadpool.start(self.worker)
495
+ self.window.threadpool.start(worker)
433
496
 
434
497
  def update(
435
498
  self,
@@ -444,11 +507,11 @@ class Workdir:
444
507
  :param force: force update (confirm)
445
508
  :param profile_name: profile name (optional, for future use)
446
509
  """
447
- self.run_action(
448
- action="update",
449
- path=path,
510
+ self.do_update(
450
511
  force=force,
451
512
  profile_name=profile_name,
513
+ current_path=self.window.core.config.get_user_path(),
514
+ new_path=path,
452
515
  )
453
516
 
454
517
  def migrate(
@@ -477,10 +540,7 @@ class Workdir:
477
540
 
478
541
  :param current: current working directory
479
542
  """
480
- self.run_action(
481
- action="restore",
482
- current=current,
483
- )
543
+ self.rollback(current=current)
484
544
 
485
545
  def delete_files(
486
546
  self,
@@ -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.15 03:00:00 #
9
+ # Updated Date: 2025.08.20 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -248,8 +248,14 @@ class Theme:
248
248
  """
249
249
  return self.common.get_style(element)
250
250
 
251
- def reload_all(self):
252
- """Reload all"""
253
- self.setup()
254
- self.update_style()
251
+ def reload_all(self, prev_theme: Optional[str] = None):
252
+ """
253
+ Reload all
254
+
255
+ :param prev_theme: previous theme name
256
+ """
257
+ current_theme = self.window.core.config.get('theme')
258
+ if not prev_theme or prev_theme != current_theme:
259
+ self.setup()
260
+ self.update_style()
255
261
  self.update_syntax()
@@ -188,24 +188,13 @@ class Evaluation:
188
188
  """
189
189
  outputs = []
190
190
  for ctx in history:
191
- if self.is_output(ctx):
192
- if ctx.output:
193
- outputs.append(ctx.output)
194
-
195
191
  # if next input then clear outputs - use only output after last user input
196
192
  if self.is_input(ctx):
197
193
  outputs.clear()
198
194
 
199
- # feedback for OpenAI agents
200
- if len(outputs) == 0:
201
- for ctx in history:
202
- if self.is_output(ctx):
203
- if ctx.output:
204
- outputs.append(ctx.output)
205
-
206
- # if next input then clear outputs - use only output after last user input
207
- if self.is_input(ctx):
208
- outputs.clear()
195
+ if self.is_output(ctx):
196
+ if ctx.output:
197
+ outputs.append(ctx.output)
209
198
 
210
199
  return "\n\n".join(outputs) if outputs else ""
211
200
 
@@ -275,12 +275,13 @@ class LlamaWorkflow(BaseRunner):
275
275
  if verbose:
276
276
  print("\n\n-----STEP-----\n\n")
277
277
  print(f"[{event.name}] {event.index}/{event.total} meta={event.meta}")
278
- item_ctx = self.on_next_ctx(
279
- item_ctx,
280
- signals=signals,
281
- begin=begin,
282
- stream=True,
283
- )
278
+ if flush:
279
+ item_ctx = self.on_next_ctx(
280
+ item_ctx,
281
+ signals=signals,
282
+ begin=begin,
283
+ stream=True,
284
+ )
284
285
  elif isinstance(event, AgentStream):
285
286
  if verbose:
286
287
  print(f"{event.delta}", end="", flush=True)
@@ -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.15 23:00:00 #
9
+ # Updated Date: 2025.08.20 09:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -574,11 +574,12 @@ class Command:
574
574
  pass
575
575
  return params
576
576
 
577
- def is_native_enabled(self, force: bool = False) -> bool:
577
+ def is_native_enabled(self, force: bool = False, model: str = None) -> bool:
578
578
  """
579
579
  Check if native tool calls are enabled
580
580
 
581
581
  :param force: force check, ignore config
582
+ :param model: model name (optional)
582
583
  :return: True if enabled
583
584
  """
584
585
  disabled_modes = [
@@ -592,7 +593,8 @@ class Command:
592
593
  return False
593
594
 
594
595
  if not force:
595
- model = self.window.core.config.get('model')
596
+ if model is None:
597
+ model = self.window.core.config.get('model') # get from globals
596
598
  if model:
597
599
  model_data = self.window.core.models.get(model)
598
600
  if model_data: