pygpt-net 2.6.26__py3-none-any.whl → 2.6.27__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 (34) hide show
  1. pygpt_net/CHANGELOG.txt +4 -0
  2. pygpt_net/__init__.py +1 -1
  3. pygpt_net/controller/access/voice.py +3 -5
  4. pygpt_net/controller/audio/audio.py +9 -6
  5. pygpt_net/controller/audio/ui.py +263 -0
  6. pygpt_net/controller/chat/common.py +17 -1
  7. pygpt_net/controller/theme/theme.py +4 -1
  8. pygpt_net/core/audio/backend/native.py +113 -79
  9. pygpt_net/core/audio/backend/pyaudio.py +16 -19
  10. pygpt_net/core/audio/backend/pygame.py +12 -15
  11. pygpt_net/core/audio/capture.py +10 -9
  12. pygpt_net/core/audio/context.py +3 -6
  13. pygpt_net/data/config/config.json +6 -4
  14. pygpt_net/data/config/models.json +2 -2
  15. pygpt_net/data/config/settings.json +24 -10
  16. pygpt_net/data/locale/locale.de.ini +2 -0
  17. pygpt_net/data/locale/locale.en.ini +2 -0
  18. pygpt_net/data/locale/locale.es.ini +2 -0
  19. pygpt_net/data/locale/locale.fr.ini +2 -0
  20. pygpt_net/data/locale/locale.it.ini +2 -0
  21. pygpt_net/data/locale/locale.pl.ini +3 -1
  22. pygpt_net/data/locale/locale.uk.ini +2 -0
  23. pygpt_net/data/locale/locale.zh.ini +2 -0
  24. pygpt_net/plugin/audio_input/simple.py +5 -10
  25. pygpt_net/plugin/audio_output/plugin.py +4 -17
  26. pygpt_net/ui/layout/chat/input.py +5 -2
  27. pygpt_net/ui/main.py +1 -2
  28. pygpt_net/ui/widget/audio/bar.py +5 -1
  29. pygpt_net/ui/widget/textarea/input.py +475 -50
  30. {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/METADATA +40 -35
  31. {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/RECORD +34 -33
  32. {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/LICENSE +0 -0
  33. {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/WHEEL +0 -0
  34. {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,7 @@
1
+ 2.6.27 (2025-08-26)
2
+
3
+ - Simplified audio input: A microphone icon has been added to the input field.
4
+
1
5
  2.6.26 (2025-08-26)
2
6
 
3
7
  - Added new provider: OpenRouter (#133).
pygpt_net/__init__.py CHANGED
@@ -13,7 +13,7 @@ __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.26"
16
+ __version__ = "2.6.27"
17
17
  __build__ = "2025-08-26"
18
18
  __maintainer__ = "Marcin Szczygliński"
19
19
  __github__ = "https://github.com/szczyglis-dev/py-gpt"
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2025.08.27 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Optional, List, Dict, Any
@@ -233,10 +233,8 @@ class Voice(QObject):
233
233
  # stop audio output if playing
234
234
  self.window.controller.audio.stop_output()
235
235
 
236
- # set audio volume bar
237
- self.window.core.audio.capture.set_bar(
238
- self.window.ui.nodes['voice.control.btn'].bar
239
- )
236
+ # set audio input mode
237
+ self.window.core.audio.capture.set_mode("control")
240
238
 
241
239
  # start timeout timer to prevent infinite recording
242
240
  timeout = int(self.window.core.config.get('audio.input.timeout', 120) or 0) # get timeout
@@ -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.24 23:00:00 #
9
+ # Updated Date: 2025.08.27 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -19,6 +19,8 @@ from pygpt_net.core.events import Event, BaseEvent
19
19
  from pygpt_net.item.ctx import CtxItem
20
20
  from pygpt_net.utils import trans
21
21
 
22
+ from .ui import UI
23
+
22
24
 
23
25
  class Audio:
24
26
  def __init__(self, window=None):
@@ -28,6 +30,7 @@ class Audio:
28
30
  :param window: Window instance
29
31
  """
30
32
  self.window = window
33
+ self.ui = UI(window)
31
34
  self.input_allowed_tabs = [
32
35
  Tab.TAB_NOTEPAD,
33
36
  Tab.TAB_CHAT,
@@ -410,18 +413,18 @@ class Audio:
410
413
  # show/hide extra options
411
414
  tab = self.window.controller.ui.tabs.get_current_tab()
412
415
  if tab.type == Tab.TAB_NOTEPAD:
413
- self.window.ui.plugin_addon['audio.input.btn'].notepad_footer.setVisible(True)
416
+ self.window.controller.audio.ui.on_input_continuous_enable("input")
414
417
  else:
415
- self.window.ui.plugin_addon['audio.input.btn'].notepad_footer.setVisible(False)
418
+ self.window.controller.audio.ui.on_input_continuous_disable("input")
416
419
  if is_advanced:
417
- self.window.ui.plugin_addon['audio.input.btn'].setVisible(False)
420
+ self.window.controller.audio.ui.on_input_disable("input")
418
421
  self.window.ui.plugin_addon['audio.input'].setVisible(True)
419
422
  else:
420
- self.window.ui.plugin_addon['audio.input.btn'].setVisible(True) # simple recording
423
+ self.window.controller.audio.ui.on_input_enable("input")
421
424
  self.window.ui.plugin_addon['audio.input'].setVisible(False) # advanced recording
422
425
  self.toggle_input_icon(True)
423
426
  else:
424
- self.window.ui.plugin_addon['audio.input.btn'].setVisible(False) # simple recording
427
+ self.window.controller.audio.ui.on_input_disable("input") # simple recording
425
428
  self.window.ui.plugin_addon['audio.input'].setVisible(False) # advanced recording
426
429
  self.toggle_input_icon(False)
427
430
 
@@ -0,0 +1,263 @@
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.27 07:00:00 #
10
+ # ================================================== #
11
+
12
+ from pygpt_net.utils import trans
13
+
14
+
15
+ class UI:
16
+ def __init__(self, window=None):
17
+ """
18
+ Audio/voice UI controller
19
+
20
+ :param window: Window instance
21
+ """
22
+ self.window = window
23
+ self.recording = False
24
+
25
+ self._input_bar = None
26
+ self._input_btn = None
27
+ self._input_control_bar = None
28
+ self._input_control_btn = None
29
+ self._output_bar = None
30
+ self._output_btn = None
31
+
32
+ def get_input_bar(self):
33
+ """
34
+ Get input bar widget
35
+
36
+ :return: input bar widget
37
+ """
38
+ if self._input_bar is not None:
39
+ return self._input_bar
40
+ if "audio.input.btn" not in self.window.ui.plugin_addon:
41
+ return None
42
+ self._input_bar = self.window.ui.plugin_addon['audio.input.btn'].bar
43
+ return self._input_bar
44
+
45
+ def get_input_control_bar(self):
46
+ """
47
+ Get input control bar widget
48
+
49
+ :return: input control bar widget
50
+ """
51
+ if self._input_control_bar is not None:
52
+ return self._input_control_bar
53
+ if 'voice.control.btn' not in self.window.ui.nodes:
54
+ return None
55
+ self._input_control_bar = self.window.ui.nodes['voice.control.btn'].bar
56
+ return self._input_control_bar
57
+
58
+ def get_output_bar(self):
59
+ """
60
+ Get output bar widget
61
+
62
+ :return: output bar widget
63
+ """
64
+ if self._output_bar is not None:
65
+ return self._output_bar
66
+ if "audio.output.bar" not in self.window.ui.plugin_addon:
67
+ return None
68
+ self._output_bar = self.window.ui.plugin_addon['audio.output.bar']
69
+ return self._output_bar
70
+
71
+ def get_input_btn(self):
72
+ """
73
+ Get input bar widget
74
+
75
+ :return: input btn widget
76
+ """
77
+ if self._input_btn is not None:
78
+ return self._input_btn
79
+ if "audio.input.btn" not in self.window.ui.plugin_addon:
80
+ return None
81
+ self._input_btn = self.window.ui.plugin_addon['audio.input.btn']
82
+ return self._input_btn
83
+
84
+ def get_input_control_btn(self):
85
+ """
86
+ Get input control btn widget
87
+
88
+ :return: input control btn widget
89
+ """
90
+ if self._input_control_btn is not None:
91
+ return self._input_control_btn
92
+ if 'voice.control.btn' not in self.window.ui.nodes:
93
+ return None
94
+ self._input_control_btn = self.window.ui.nodes['voice.control.btn']
95
+ return self._input_control_btn
96
+
97
+ def get_output_btn(self):
98
+ """
99
+ Get output btn widget
100
+
101
+ :return: output btn widget
102
+ """
103
+ if self._output_btn is not None:
104
+ return self._output_btn
105
+ if "audio.output" not in self.window.ui.plugin_addon:
106
+ return None
107
+ self._output_btn = self.window.ui.plugin_addon['audio.output']
108
+ return self._output_btn
109
+
110
+ # --- Input events ---
111
+
112
+ def on_input_volume_change(self, value: int, mode: str = 'input'):
113
+ """
114
+ Input volume slider change event
115
+
116
+ :param value: slider value
117
+ :param mode: 'input' or 'control' mode
118
+ """
119
+ bar = self.get_output_bar()
120
+ if bar:
121
+ bar.setLevel(value)
122
+
123
+ def on_input_device_change(self, index: int):
124
+ """
125
+ Input device combo change event
126
+
127
+ :param index: combo index
128
+ """
129
+ pass
130
+
131
+ def on_input_enable(self, mode: str = 'input'):
132
+ """
133
+ Input enable checkbox change event
134
+
135
+ :param mode: 'input' or 'control' mode
136
+ """
137
+ self.window.ui.nodes['input'].set_icon_visible("mic", True)
138
+ if mode == "input":
139
+ return
140
+ btn = self.get_input_btn() if mode == 'input' else self.get_input_control_btn()
141
+ if btn:
142
+ btn.setVisible(True)
143
+
144
+ def on_input_disable(self, mode: str = 'input'):
145
+ """
146
+ Input disabled button click event
147
+
148
+ :param mode: 'input' or 'control' mode
149
+ """
150
+ self.window.ui.nodes['input'].set_icon_visible("mic", False)
151
+ if mode == "input":
152
+ return
153
+ btn = self.get_input_btn() if mode == 'input' else self.get_input_control_btn()
154
+ if btn:
155
+ btn.setVisible(False)
156
+
157
+ def on_input_continuous_enable(self, checked: bool, mode: str = 'input'):
158
+ """
159
+ Input enable checkbox change event
160
+
161
+ :param checked: checkbox state
162
+ :param mode: 'input' or 'control' mode
163
+ """
164
+ btn = self.get_input_btn() if mode == 'input' else self.get_input_control_btn()
165
+ if btn:
166
+ btn.notepad_footer.setVisible(True)
167
+
168
+ def on_input_continuous_disable(self, mode: str = 'input'):
169
+ """
170
+ Input disabled button click event
171
+
172
+ :param mode: 'input' or 'control' mode
173
+ """
174
+ btn = self.get_input_btn() if mode == 'input' else self.get_input_control_btn()
175
+ if btn:
176
+ btn.notepad_footer.setVisible(False)
177
+
178
+ def on_input_begin(self, mode: str = 'input'):
179
+ """
180
+ Input begin button click event
181
+
182
+ :param mode: 'input' or 'control' mode
183
+ """
184
+ self.recording = True
185
+ self.window.ui.nodes['input'].set_icon_state("mic", True)
186
+ if mode == "input":
187
+ self.window.controller.chat.common.lock_input()
188
+ return
189
+ btn = self.get_input_btn() if mode == 'input' else self.get_input_control_btn()
190
+ btn.btn_toggle.setText(trans('audio.speak.btn.stop'))
191
+ btn.btn_toggle.setToolTip(trans('audio.speak.btn.stop.tooltip'))
192
+
193
+ def on_input_end(self, mode: str = 'input'):
194
+ """
195
+ Input end button click event
196
+
197
+ :param mode: 'input' or 'control' mode
198
+ """
199
+ self.recording = False
200
+ self.window.ui.nodes['input'].set_icon_state("mic", False)
201
+ if mode == "input":
202
+ self.window.controller.chat.common.unlock_input()
203
+ return
204
+ btn = self.get_input_btn() if mode == 'input' else self.get_input_control_btn()
205
+ btn.btn_toggle.setText(trans('audio.speak.btn'))
206
+ btn.btn_toggle.setToolTip(trans('audio.speak.btn.tooltip'))
207
+
208
+ def on_input_cancel(self):
209
+ """
210
+ Input cancel button click event
211
+ """
212
+ pass
213
+
214
+ # --- Output events ---
215
+
216
+ def on_output_volume_change(self, value: int):
217
+ """
218
+ Output volume slider change event
219
+ :param value: slider value
220
+ """
221
+ if self.recording:
222
+ return
223
+ bar = self.get_output_bar()
224
+ if bar:
225
+ bar.setLevel(value)
226
+
227
+ def on_output_device_change(self, index: int):
228
+ """
229
+ Output device combo change event
230
+
231
+ :param index: combo index
232
+ """
233
+ pass
234
+
235
+ def on_output_enable(self):
236
+ """
237
+ Output enable checkbox change event
238
+ """
239
+ pass
240
+
241
+ def on_output_disable(self):
242
+ """
243
+ Output disabled button click event
244
+ """
245
+ pass
246
+
247
+ def on_output_begin(self):
248
+ """
249
+ Output begin button click event
250
+ """
251
+ self.window.controller.audio.start_speaking()
252
+
253
+ def on_output_end(self):
254
+ """
255
+ Output end button click event
256
+ """
257
+ pass
258
+
259
+ def on_output_cancel(self):
260
+ """
261
+ Output cancel button click event
262
+ """
263
+ pass
@@ -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.08.27 07:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -250,6 +250,22 @@ class Common:
250
250
  unlock = False
251
251
  return unlock
252
252
 
253
+ def handle_stop(self):
254
+ """Handle stop"""
255
+ # stop voice recording if active
256
+ if self.window.controller.access.voice.is_recording:
257
+ self.window.controller.access.voice.stop_recording(timeout=True)
258
+
259
+ if self.window.core.plugins.get("audio_input").handler_simple.is_recording:
260
+ self.window.core.plugins.get("audio_input").handler_simple.stop_recording(timeout=False)
261
+ return
262
+
263
+ # stop audio output if playing
264
+ self.window.controller.audio.stop_output()
265
+
266
+ # stop generating if active
267
+ self.window.controller.kernel.stop()
268
+
253
269
  def auto_unlock(self, ctx: CtxItem) -> bool:
254
270
  """
255
271
  Auto unlock input after end
@@ -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.25 20:00:00 #
9
+ # Updated Date: 2025.08.27 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -50,6 +50,9 @@ class Theme:
50
50
 
51
51
  :param name: theme name
52
52
  """
53
+ current = self.window.core.config.get('theme')
54
+ if name == current:
55
+ return
53
56
  self.window.update_status(trans("status.reloading"))
54
57
  QApplication.processEvents()
55
58
  self.toggle(name, force=True)