pygpt-net 2.5.96__py3-none-any.whl → 2.5.98.post1__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 (84) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/assistant/threads.py +16 -10
  4. pygpt_net/controller/chat/output.py +0 -1
  5. pygpt_net/controller/chat/response.py +7 -1
  6. pygpt_net/controller/chat/stream.py +11 -12
  7. pygpt_net/controller/debug/debug.py +2 -1
  8. pygpt_net/controller/settings/editor.py +4 -2
  9. pygpt_net/controller/settings/workdir.py +16 -4
  10. pygpt_net/core/agents/observer/evaluation.py +10 -3
  11. pygpt_net/core/agents/runners/openai_workflow.py +24 -14
  12. pygpt_net/core/attachments/worker.py +16 -11
  13. pygpt_net/core/bridge/worker.py +10 -11
  14. pygpt_net/core/camera/camera.py +22 -25
  15. pygpt_net/core/debug/console/__init__.py +12 -0
  16. pygpt_net/core/debug/console/console.py +115 -0
  17. pygpt_net/core/debug/debug.py +52 -35
  18. pygpt_net/core/docker/builder.py +14 -2
  19. pygpt_net/core/experts/experts.py +16 -18
  20. pygpt_net/core/idx/worker.py +16 -18
  21. pygpt_net/core/render/plain/pid.py +16 -1
  22. pygpt_net/core/render/plain/renderer.py +3 -4
  23. pygpt_net/core/render/web/pid.py +63 -10
  24. pygpt_net/core/render/web/renderer.py +3 -3
  25. pygpt_net/core/updater/updater.py +18 -20
  26. pygpt_net/core/worker/worker.py +18 -10
  27. pygpt_net/data/config/config.json +4 -3
  28. pygpt_net/data/config/models.json +3 -3
  29. pygpt_net/data/config/settings.json +17 -2
  30. pygpt_net/data/locale/locale.de.ini +2 -0
  31. pygpt_net/data/locale/locale.en.ini +2 -0
  32. pygpt_net/data/locale/locale.es.ini +2 -0
  33. pygpt_net/data/locale/locale.fr.ini +2 -0
  34. pygpt_net/data/locale/locale.it.ini +2 -0
  35. pygpt_net/data/locale/locale.pl.ini +3 -1
  36. pygpt_net/data/locale/locale.uk.ini +2 -0
  37. pygpt_net/data/locale/locale.zh.ini +2 -0
  38. pygpt_net/data/themes/light.css +1 -1
  39. pygpt_net/data/themes/light_gray.css +1 -1
  40. pygpt_net/plugin/audio_input/worker.py +12 -16
  41. pygpt_net/plugin/base/worker.py +18 -16
  42. pygpt_net/plugin/cmd_api/worker.py +30 -32
  43. pygpt_net/plugin/cmd_code_interpreter/worker.py +71 -84
  44. pygpt_net/plugin/cmd_custom/worker.py +30 -32
  45. pygpt_net/plugin/cmd_files/worker.py +116 -117
  46. pygpt_net/plugin/cmd_history/worker.py +48 -50
  47. pygpt_net/plugin/cmd_mouse_control/worker.py +72 -69
  48. pygpt_net/plugin/cmd_serial/worker.py +39 -40
  49. pygpt_net/plugin/cmd_system/worker.py +29 -30
  50. pygpt_net/plugin/cmd_web/worker.py +43 -44
  51. pygpt_net/plugin/idx_llama_index/worker.py +30 -32
  52. pygpt_net/plugin/mailer/worker.py +35 -36
  53. pygpt_net/plugin/openai_vision/worker.py +39 -44
  54. pygpt_net/provider/agents/openai/agent.py +24 -4
  55. pygpt_net/provider/agents/openai/agent_b2b.py +61 -8
  56. pygpt_net/provider/agents/openai/agent_planner.py +48 -9
  57. pygpt_net/provider/agents/openai/agent_with_experts.py +18 -25
  58. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +45 -33
  59. pygpt_net/provider/agents/openai/agent_with_feedback.py +48 -9
  60. pygpt_net/provider/agents/openai/bot_researcher.py +17 -2
  61. pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +4 -1
  62. pygpt_net/provider/agents/openai/evolve.py +47 -9
  63. pygpt_net/provider/core/config/patch.py +7 -0
  64. pygpt_net/provider/core/history/txt_file.py +3 -0
  65. pygpt_net/provider/gpt/__init__.py +0 -3
  66. pygpt_net/provider/gpt/agents/experts.py +89 -0
  67. pygpt_net/provider/gpt/agents/response.py +88 -92
  68. pygpt_net/provider/gpt/image.py +16 -5
  69. pygpt_net/provider/gpt/worker/assistants.py +25 -10
  70. pygpt_net/provider/gpt/worker/importer.py +23 -10
  71. pygpt_net/provider/llms/llama_index/__init__.py +0 -0
  72. pygpt_net/provider/llms/llama_index/openai/__init__.py +4 -0
  73. pygpt_net/provider/llms/llama_index/openai/base.py +1170 -0
  74. pygpt_net/provider/llms/llama_index/openai/py.typed +0 -0
  75. pygpt_net/provider/llms/llama_index/openai/responses.py +983 -0
  76. pygpt_net/provider/llms/llama_index/openai/utils.py +871 -0
  77. pygpt_net/provider/llms/openai.py +2 -2
  78. pygpt_net/ui/dialog/logger.py +13 -1
  79. pygpt_net/ui/widget/textarea/console.py +40 -0
  80. {pygpt_net-2.5.96.dist-info → pygpt_net-2.5.98.post1.dist-info}/METADATA +21 -5
  81. {pygpt_net-2.5.96.dist-info → pygpt_net-2.5.98.post1.dist-info}/RECORD +84 -74
  82. {pygpt_net-2.5.96.dist-info → pygpt_net-2.5.98.post1.dist-info}/LICENSE +0 -0
  83. {pygpt_net-2.5.96.dist-info → pygpt_net-2.5.98.post1.dist-info}/WHEEL +0 -0
  84. {pygpt_net-2.5.96.dist-info → pygpt_net-2.5.98.post1.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,15 @@
1
+ 2.5.98 (2025-08-12)
2
+
3
+ - Added support for GPT-5 in LlamaIndex/Chat with Files mode.
4
+ - Experts are now allowed in all OpenAI agent types.
5
+ - Improved the output of OpenAI agents (separated context items).
6
+ - Refactored memory cleanup for thread workers.
7
+ - Optimized streaming.
8
+
9
+ 2.5.97 (2025-08-11)
10
+
11
+ - Fix: attribute error in prev ctx.
12
+
1
13
  2.5.96 (2025-08-10)
2
14
 
3
15
  - Fixed memory leaks.
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.10 00:00:00 #
9
+ # Updated Date: 2025.08.12 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.5.96"
17
- __build__ = "2025-08-10"
16
+ __version__ = "2.5.98"
17
+ __build__ = "2025-08-12"
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"
@@ -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.07.25 17:00:00 #
9
+ # Updated Date: 2025.08.11 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -613,11 +613,10 @@ class RunSignals(QObject):
613
613
  started = Signal()
614
614
 
615
615
 
616
- class RunWorker(QObject, QRunnable):
616
+ class RunWorker(QRunnable):
617
617
  """Status check async worker"""
618
618
  def __init__(self, *args, **kwargs):
619
- QObject.__init__(self)
620
- QRunnable.__init__(self)
619
+ super().__init__()
621
620
  self.signals = RunSignals()
622
621
  self.args = args
623
622
  self.kwargs = kwargs
@@ -662,14 +661,21 @@ class RunWorker(QObject, QRunnable):
662
661
 
663
662
  if self.signals.destroyed is not None:
664
663
  self.signals.destroyed.emit()
664
+
665
665
  except Exception as e:
666
666
  self.window.core.debug.log(e)
667
667
  if self.signals.destroyed is not None:
668
668
  self.signals.destroyed.emit()
669
+
669
670
  finally:
670
- self.args = None
671
- self.kwargs = None
672
- self.window = None
673
- self.ctx = None
674
- self.signals = None
675
- self.deleteLater()
671
+ self.cleanup()
672
+
673
+ def cleanup(self):
674
+ """Cleanup resources after worker execution."""
675
+ sig = self.signals
676
+ self.signals = None
677
+ if sig is not None:
678
+ try:
679
+ sig.deleteLater()
680
+ except RuntimeError:
681
+ pass
@@ -294,7 +294,6 @@ class Output:
294
294
  event = RenderEvent(RenderEvent.RELOAD)
295
295
  self.window.dispatch(event) # reload chat window
296
296
 
297
- del ctx.prev_ctx # clear previous context to free memory
298
297
  mem_clean()
299
298
 
300
299
  # self.window.core.debug.mem("END") # debug memory usage
@@ -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.01 19:00:00 #
9
+ # Updated Date: 2025.08.11 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any
@@ -199,6 +199,12 @@ class Response:
199
199
  self.window.controller.chat.output.handle_end(ctx=prev_ctx,
200
200
  mode=prev_ctx.mode) # end previous context
201
201
 
202
+
203
+ # if next in agent cycle
204
+ if ctx.partial:
205
+ self.window.dispatch(AppEvent(AppEvent.CTX_END)) # app event
206
+ self.window.dispatch(RenderEvent(RenderEvent.RELOAD)) # reload chat window
207
+
202
208
  # handle current step
203
209
  ctx.current = False # reset current state
204
210
  mode = ctx.mode
@@ -6,13 +6,13 @@
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 00:00:00 #
9
+ # Updated Date: 2025.08.11 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import base64
13
13
  from typing import Optional
14
14
 
15
- from PySide6.QtCore import QObject, Signal, Slot, QRunnable, QMetaObject, Qt
15
+ from PySide6.QtCore import QObject, Signal, Slot, QRunnable
16
16
 
17
17
  from pygpt_net.core.bridge import BridgeContext
18
18
  from pygpt_net.core.events import RenderEvent
@@ -34,7 +34,7 @@ class WorkerSignals(QObject):
34
34
 
35
35
  class StreamWorker(QRunnable):
36
36
  def __init__(self, ctx: CtxItem, window, parent=None):
37
- QRunnable.__init__(self)
37
+ super().__init__()
38
38
  self.signals = WorkerSignals()
39
39
  self.ctx = ctx
40
40
  self.window = window
@@ -367,15 +367,14 @@ class StreamWorker(QRunnable):
367
367
  self.cleanup()
368
368
 
369
369
  def cleanup(self):
370
- """
371
- Cleanup resources after worker execution.
372
- """
373
- self.ctx = None
374
- self.window = None
375
- try:
376
- QMetaObject.invokeMethod(self.signals, "deleteLater", Qt.QueuedConnection)
377
- except Exception:
378
- pass
370
+ """Cleanup resources after worker execution."""
371
+ sig = self.signals
372
+ self.signals = None
373
+ if sig is not None:
374
+ try:
375
+ sig.deleteLater()
376
+ except RuntimeError:
377
+ pass
379
378
 
380
379
 
381
380
  class Stream:
@@ -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.07.20 23:00:00 #
9
+ # Updated Date: 2025.08.11 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from datetime import datetime
@@ -200,6 +200,7 @@ class Debug(QObject):
200
200
  """Open logger dialog"""
201
201
  self.window.ui.dialogs.open('logger', width=800, height=600)
202
202
  self.is_logger = True
203
+ self.window.console.setFocus() # Set focus to console input
203
204
  self.update()
204
205
 
205
206
  def close_logger(self):
@@ -6,12 +6,14 @@
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.07 03:00:00 #
9
+ # Updated Date: 2025.08.11 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
13
13
  from typing import Optional, Any, Dict
14
14
 
15
+ from PySide6.QtCore import QTimer
16
+
15
17
  from pygpt_net.core.events import Event
16
18
  from pygpt_net.utils import trans
17
19
 
@@ -318,7 +320,7 @@ class Editor:
318
320
  # update ctx limit
319
321
  elif key.startswith('ctx.records.limit') and caller == "slider":
320
322
  self.window.core.config.set(key, value)
321
- self.window.controller.ctx.update(True, False)
323
+ QTimer.singleShot(1000, lambda: self.window.controller.ctx.update(True, False))
322
324
 
323
325
  # update layout density
324
326
  elif key == "layout.density" and caller == "slider":
@@ -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.07.18 00:00:00 #
9
+ # Updated Date: 2025.08.11 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -39,7 +39,7 @@ class WorkerSignals(QObject):
39
39
  switch = Signal(str) # switch to profile (after reset)
40
40
 
41
41
 
42
- class WorkdirWorker(QObject, QRunnable):
42
+ class WorkdirWorker(QRunnable):
43
43
  """Worker for handling workdir operations in a separate thread."""
44
44
  def __init__(
45
45
  self,
@@ -53,8 +53,7 @@ class WorkdirWorker(QObject, QRunnable):
53
53
  profile_new_name: Optional[str] = None,
54
54
  profile_new_path: Optional[str] = None,
55
55
  ):
56
- QObject.__init__(self)
57
- QRunnable.__init__(self)
56
+ super().__init__()
58
57
  self.window = window
59
58
  self.action = action
60
59
  self.path = path
@@ -83,11 +82,24 @@ class WorkdirWorker(QObject, QRunnable):
83
82
  self.worker_reset()
84
83
  else:
85
84
  self.signals.error.emit(f"Unknown action: {self.action}")
85
+
86
86
  except Exception as e:
87
87
  self.window.core.debug.log(e)
88
88
  self.signals.error.emit(str(e))
89
+
89
90
  finally:
90
91
  self.signals.finished.emit()
92
+ self.cleanup()
93
+
94
+ def cleanup(self):
95
+ """Cleanup resources after worker execution."""
96
+ sig = self.signals
97
+ self.signals = None
98
+ if sig is not None:
99
+ try:
100
+ sig.deleteLater()
101
+ except RuntimeError:
102
+ pass
91
103
 
92
104
  def worker_delete_files(self):
93
105
  """Delete files and directories associated with the profile"""
@@ -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.03 14:00:00 #
9
+ # Updated Date: 2025.08.12 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import List
@@ -162,13 +162,20 @@ class Evaluation:
162
162
  Get the final response from the agent
163
163
 
164
164
  :param history: ctx items
165
- :return: final response
165
+ :return: final response from agent
166
166
  """
167
167
  output = ""
168
168
  for ctx in history:
169
- if ctx.extra is not None and "agent_finish" in ctx.extra:
169
+ if ctx.extra is not None and "agent_finish" in ctx.extra and "agent_finish_evaluate" not in ctx.extra:
170
170
  if ctx.output:
171
171
  output = ctx.output
172
+
173
+ # feedback for OpenAI agents
174
+ if not output:
175
+ for ctx in history:
176
+ if ctx.extra is not None and "agent_output" in ctx.extra and "agent_finish_evaluate" not in ctx.extra:
177
+ if ctx.output:
178
+ output = ctx.output
172
179
  return output
173
180
 
174
181
  def get_prompt_score(self, history: List[CtxItem]) -> str:
@@ -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.03 14:00:00 #
9
+ # Updated Date: 2025.08.11 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any, List
@@ -111,7 +111,8 @@ class OpenAIWorkflow(BaseRunner):
111
111
  ctx: CtxItem,
112
112
  input: str,
113
113
  output: str,
114
- response_id: str
114
+ response_id: str,
115
+ finish: bool = False
115
116
  ) -> CtxItem:
116
117
  """
117
118
  Callback for next context in cycle
@@ -120,24 +121,28 @@ class OpenAIWorkflow(BaseRunner):
120
121
  :param input: input text
121
122
  :param output: output text
122
123
  :param response_id: response id for OpenAI
124
+ :param finish: If
123
125
  :return: CtxItem - the next context item in the cycle
124
126
  """
125
127
  # finish current stream
126
128
  ctx.stream = "\n"
129
+ ctx.extra["agent_output"] = True # allow usage in history
130
+ ctx.output = output # set output to current context
131
+ self.window.core.ctx.update_item(ctx)
127
132
  self.send_stream(ctx, signals, False)
128
133
  self.end_stream(ctx, signals)
129
134
 
130
- # send current response
131
- # TODO: disable evaluation for now in chat response controller
132
- response_ctx = self.make_response(ctx, input, output, response_id)
133
- response_ctx.partial = True # do not finish and evaluate yet
134
- self.send_response(response_ctx, signals, KernelEvent.APPEND_DATA)
135
-
136
135
  # create and return next context item
137
136
  next_ctx = self.add_next_ctx(ctx)
138
137
  next_ctx.set_input(input)
139
- self.window.core.ctx.set_last_item(next_ctx)
140
- self.window.core.ctx.add(next_ctx)
138
+ next_ctx.partial = True
139
+ next_ctx.extra["agent_output"] = True # allow usage in history
140
+
141
+ if finish:
142
+ next_ctx.extra["agent_finish"] = True # mark as finished and ready for evaluation
143
+ next_ctx.extra["agent_finish_evaluate"] = True # mark as feedback response (ignored in loop evaluation)
144
+
145
+ self.send_response(next_ctx, signals, KernelEvent.APPEND_DATA)
141
146
  return next_ctx
142
147
 
143
148
  def on_error(error: Any):
@@ -170,14 +175,19 @@ class OpenAIWorkflow(BaseRunner):
170
175
  if previous_response_id:
171
176
  run_kwargs["previous_response_id"] = previous_response_id
172
177
 
178
+ # split response messages to separated context items
179
+ run_kwargs["use_partial_ctx"] = self.window.core.config.get("agent.openai.response.split", True)
180
+
173
181
  # run agent
174
182
  ctx, output, response_id = await run(**run_kwargs)
175
183
 
176
- # prepare response
177
- response_ctx = self.make_response(ctx, prompt, output, response_id)
184
+ if not ctx.partial:
185
+ response_ctx = self.make_response(ctx, prompt, output, response_id)
186
+ self.send_response(response_ctx, signals, KernelEvent.APPEND_DATA)
187
+ else:
188
+ ctx.partial = False # last part, not partial anymore
189
+ # already handled in next_ctx(), so do not return response
178
190
 
179
- # send response
180
- self.send_response(response_ctx, signals, KernelEvent.APPEND_DATA)
181
191
  self.set_idle(signals)
182
192
  return True
183
193
 
@@ -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.06 01:00:00 #
9
+ # Updated Date: 2025.08.11 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Signal, QObject, QRunnable, Slot
@@ -17,10 +17,9 @@ class WorkerSignals(QObject):
17
17
  error = Signal(object)
18
18
 
19
19
 
20
- class AttachmentWorker(QObject, QRunnable):
20
+ class AttachmentWorker(QRunnable):
21
21
  def __init__(self, *args, **kwargs):
22
- QObject.__init__(self)
23
- QRunnable.__init__(self)
22
+ super().__init__()
24
23
  self.signals = WorkerSignals()
25
24
  self.args = args
26
25
  self.kwargs = kwargs
@@ -35,16 +34,22 @@ class AttachmentWorker(QObject, QRunnable):
35
34
  try:
36
35
  self.window.controller.chat.attachment.upload(self.meta, self.mode, self.prompt)
37
36
  self.signals.success.emit(self.prompt)
37
+
38
38
  except Exception as e:
39
39
  if self.signals is not None:
40
40
  self.signals.error.emit(e)
41
41
  self.window.core.debug.error(e)
42
42
  print("Attachment indexing error", e)
43
+
43
44
  finally:
44
- if self.signals is not None:
45
- self.signals.success.disconnect()
46
- self.signals.error.disconnect()
47
- self.window = None
48
- self.meta = None
49
- self.mode = None
50
- self.deleteLater()
45
+ self.cleanup()
46
+
47
+ def cleanup(self):
48
+ """Cleanup resources after worker execution."""
49
+ sig = self.signals
50
+ self.signals = None
51
+ if sig is not None:
52
+ try:
53
+ sig.deleteLater()
54
+ except RuntimeError:
55
+ pass
@@ -6,10 +6,10 @@
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 00:00:00 #
9
+ # Updated Date: 2025.08.11 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtCore import QObject, Signal, QRunnable, Slot, QMetaObject, Qt
12
+ from PySide6.QtCore import QObject, Signal, QRunnable, Slot
13
13
 
14
14
  from pygpt_net.core.types import (
15
15
  MODE_AGENT_LLAMA,
@@ -127,15 +127,14 @@ class BridgeWorker(QRunnable):
127
127
  self.cleanup()
128
128
 
129
129
  def cleanup(self):
130
- self.window = None
131
- self.context = None
132
- self.extra = None
133
- self.args = None
134
- self.kwargs = None
135
- try:
136
- QMetaObject.invokeMethod(self.signals, "deleteLater", Qt.QueuedConnection)
137
- except Exception:
138
- pass
130
+ """Cleanup resources after worker execution."""
131
+ sig = self.signals
132
+ self.signals = None
133
+ if sig is not None:
134
+ try:
135
+ sig.deleteLater()
136
+ except RuntimeError:
137
+ pass
139
138
 
140
139
  def handle_post_prompt_async(self):
141
140
  """Handle post prompt async event"""
@@ -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.06 01:00:00 #
9
+ # Updated Date: 2025.08.11 14:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -43,10 +43,9 @@ class CaptureSignals(QObject):
43
43
  error = Signal(object)
44
44
 
45
45
 
46
- class CaptureWorker(QObject, QRunnable):
46
+ class CaptureWorker(QRunnable):
47
47
  def __init__(self, *args, **kwargs):
48
- QObject.__init__(self)
49
- QRunnable.__init__(self)
48
+ super().__init__()
50
49
  self.signals = CaptureSignals()
51
50
  self.args = args
52
51
  self.kwargs = kwargs
@@ -101,32 +100,20 @@ class CaptureWorker(QObject, QRunnable):
101
100
  if now - last_frame_time >= fps_interval:
102
101
  self.signals.capture.emit(frame)
103
102
  last_frame_time = now
103
+
104
104
  except Exception as e:
105
105
  self.window.core.debug.log(e)
106
106
  if self.signals is not None:
107
107
  self.signals.error.emit(e)
108
108
 
109
- # release camera
110
- self.release()
111
- if self.signals is not None:
112
- if self.allow_finish:
113
- self.signals.finished.emit()
114
- else:
115
- self.signals.unfinished.emit()
116
-
117
- # cleanup
118
- if self.signals is not None:
119
- self.signals.error.disconnect()
120
- self.signals.finished.disconnect()
121
- self.signals.destroyed.disconnect()
122
- self.signals.unfinished.disconnect()
123
- self.signals.capture.disconnect()
124
- self.signals.stopped.disconnect()
125
- self.window = None
126
- self.capture = None
127
- self.frame = None
128
- self.allow_finish = False
129
- self.deleteLater()
109
+ finally:
110
+ self.release() # release camera
111
+ if self.signals is not None:
112
+ if self.allow_finish:
113
+ self.signals.finished.emit()
114
+ else:
115
+ self.signals.unfinished.emit()
116
+ self.cleanup()
130
117
 
131
118
  def release(self):
132
119
  """Release camera"""
@@ -135,3 +122,13 @@ class CaptureWorker(QObject, QRunnable):
135
122
  self.capture = None
136
123
  self.frame = None
137
124
  self.initialized = False
125
+
126
+ def cleanup(self):
127
+ """Cleanup resources after worker execution."""
128
+ sig = self.signals
129
+ self.signals = None
130
+ if sig is not None:
131
+ try:
132
+ sig.deleteLater()
133
+ except RuntimeError:
134
+ pass
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2025.08.11 18:00:00 #
10
+ # ================================================== #
11
+
12
+ from .console import Console
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2025.08.11 18:00:00 #
10
+ # ================================================== #
11
+
12
+ from qasync import QApplication
13
+
14
+ from pygpt_net.utils import mem_clean
15
+
16
+
17
+ class Console:
18
+ def __init__(self, window=None):
19
+ """
20
+ Console core
21
+
22
+ :param window: Window instance
23
+ """
24
+ self.window = window
25
+
26
+ def on_send(self):
27
+ """Called on send message from console"""
28
+ msg = self.window.console.text().strip()
29
+ if msg:
30
+ self.clear()
31
+ self.log(msg)
32
+ QApplication.processEvents()
33
+ self.handle(msg)
34
+
35
+ def clear(self):
36
+ """Clear console input"""
37
+ self.window.console.clear()
38
+
39
+ def log(self, msg: str):
40
+ """
41
+ Log message to console
42
+
43
+ :param msg: Message to log
44
+ """
45
+ self.window.core.debug.log("[Console] " + str(msg))
46
+
47
+ def handle(self, msg: str):
48
+ """
49
+ Handle message from console
50
+
51
+ :param msg: Message from console
52
+ """
53
+ if msg == 'clr':
54
+ self.window.logger.clear()
55
+ elif msg == "mem":
56
+ res = "\n" + self.window.core.debug.mem("Console")
57
+ self.log(res)
58
+ elif msg == "free":
59
+ mem_clean()
60
+ self.log("Memory cleaned")
61
+ elif msg in ["quit", "exit", "/q"]:
62
+ self.window.close()
63
+ elif msg == "css":
64
+ self.window.controller.theme.reload(force=True)
65
+ self.log("Theme reloaded")
66
+ elif msg == "lang":
67
+ self.window.controller.lang.reload()
68
+ self.log("Language reloaded")
69
+ elif msg.lower() == "mpkfa":
70
+ self.log("GOD MODE ACTIVATED ;)")
71
+ elif msg == "oclr":
72
+ if self.window.core.gpt.client:
73
+ self.window.core.gpt.client.close()
74
+ self.log("OpenAI client closed")
75
+ else:
76
+ self.log("OpenAI client not initialized")
77
+ elif msg.lower() in ["help", "/help", "/h"]:
78
+ self.log(self.get_help())
79
+ elif msg.startswith("dump(") and msg.endswith(")"):
80
+ expr = msg[5:-1].strip()
81
+ self.log(f"{expr}:")
82
+ self.log(self.dump(expr))
83
+ else:
84
+ self.log(f"Unknown command: {msg}. Type 'help' for available commands.")
85
+
86
+ def dump(self, expr: str) -> str:
87
+ """
88
+ Dump object in console
89
+
90
+ :param expr: Path to object or eval() expression
91
+ :return: Dumped object or error message
92
+ """
93
+ try:
94
+ return eval(expr)
95
+ except Exception as e:
96
+ return f"Error while dumping: {str(e)}"
97
+
98
+ def get_help(self):
99
+ """
100
+ Get help message for console commands
101
+
102
+ :return: Help message
103
+ """
104
+ return (
105
+ "\n\n================\nAvailable commands:\n================\n"
106
+ " clr - clear output\n"
107
+ " mem - show memory usage\n"
108
+ " free - clean memory\n"
109
+ " css - reload theme CSS\n"
110
+ " lang - reload language\n"
111
+ " oclr - close OpenAI client\n"
112
+ " dump(object|expr) - dump object or eval() expression\n"
113
+ " help - show this help message\n"
114
+ " quit|exit - close application\n"
115
+ )