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.
- pygpt_net/CHANGELOG.txt +4 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/controller/access/voice.py +3 -5
- pygpt_net/controller/audio/audio.py +9 -6
- pygpt_net/controller/audio/ui.py +263 -0
- pygpt_net/controller/chat/common.py +17 -1
- pygpt_net/controller/theme/theme.py +4 -1
- pygpt_net/core/audio/backend/native.py +113 -79
- pygpt_net/core/audio/backend/pyaudio.py +16 -19
- pygpt_net/core/audio/backend/pygame.py +12 -15
- pygpt_net/core/audio/capture.py +10 -9
- pygpt_net/core/audio/context.py +3 -6
- pygpt_net/data/config/config.json +6 -4
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/data/config/settings.json +24 -10
- pygpt_net/data/locale/locale.de.ini +2 -0
- pygpt_net/data/locale/locale.en.ini +2 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +2 -0
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/plugin/audio_input/simple.py +5 -10
- pygpt_net/plugin/audio_output/plugin.py +4 -17
- pygpt_net/ui/layout/chat/input.py +5 -2
- pygpt_net/ui/main.py +1 -2
- pygpt_net/ui/widget/audio/bar.py +5 -1
- pygpt_net/ui/widget/textarea/input.py +475 -50
- {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/METADATA +40 -35
- {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/RECORD +34 -33
- {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.26.dist-info → pygpt_net-2.6.27.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
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.
|
|
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.
|
|
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
|
|
237
|
-
self.window.core.audio.capture.
|
|
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.
|
|
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.
|
|
416
|
+
self.window.controller.audio.ui.on_input_continuous_enable("input")
|
|
414
417
|
else:
|
|
415
|
-
self.window.
|
|
418
|
+
self.window.controller.audio.ui.on_input_continuous_disable("input")
|
|
416
419
|
if is_advanced:
|
|
417
|
-
self.window.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|