pygpt-net 2.4.53__py3-none-any.whl → 2.4.55__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 (35) hide show
  1. CHANGELOG.md +10 -0
  2. README.md +18 -1
  3. pygpt_net/CHANGELOG.txt +10 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/lang/mapping.py +2 -1
  6. pygpt_net/core/audio/capture.py +2 -1
  7. pygpt_net/data/config/config.json +5 -3
  8. pygpt_net/data/config/models.json +3 -3
  9. pygpt_net/data/config/modes.json +3 -3
  10. pygpt_net/data/config/settings.json +24 -0
  11. pygpt_net/data/locale/locale.de.ini +3 -0
  12. pygpt_net/data/locale/locale.en.ini +3 -0
  13. pygpt_net/data/locale/locale.es.ini +3 -0
  14. pygpt_net/data/locale/locale.fr.ini +3 -0
  15. pygpt_net/data/locale/locale.it.ini +3 -0
  16. pygpt_net/data/locale/locale.pl.ini +4 -1
  17. pygpt_net/data/locale/locale.uk.ini +3 -0
  18. pygpt_net/data/locale/locale.zh.ini +3 -0
  19. pygpt_net/data/win32/USER-LICENSE.rtf +0 -0
  20. pygpt_net/plugin/audio_input/simple.py +23 -7
  21. pygpt_net/plugin/audio_input/worker.py +8 -5
  22. pygpt_net/plugin/audio_output/__init__.py +47 -12
  23. pygpt_net/plugin/audio_output/worker.py +114 -20
  24. pygpt_net/provider/core/config/patch.py +9 -1
  25. pygpt_net/ui/__init__.py +2 -1
  26. pygpt_net/ui/dialog/snap.py +3 -2
  27. pygpt_net/ui/layout/chat/input.py +4 -1
  28. pygpt_net/ui/widget/audio/bar.py +103 -0
  29. pygpt_net/ui/widget/audio/input_button.py +5 -46
  30. pygpt_net/ui/widget/dialog/snap.py +37 -6
  31. {pygpt_net-2.4.53.dist-info → pygpt_net-2.4.55.dist-info}/METADATA +19 -2
  32. {pygpt_net-2.4.53.dist-info → pygpt_net-2.4.55.dist-info}/RECORD +35 -34
  33. {pygpt_net-2.4.53.dist-info → pygpt_net-2.4.55.dist-info}/LICENSE +0 -0
  34. {pygpt_net-2.4.53.dist-info → pygpt_net-2.4.55.dist-info}/WHEEL +0 -0
  35. {pygpt_net-2.4.53.dist-info → pygpt_net-2.4.55.dist-info}/entry_points.txt +0 -0
@@ -6,18 +6,26 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.26 19:00:00 #
9
+ # Updated Date: 2025.01.18 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import time
13
+ import pyaudio
14
+ import wave
15
+ import numpy as np
16
+ import io
17
+ from pydub import AudioSegment
13
18
 
14
19
  from PySide6.QtCore import Slot, Signal
20
+
15
21
  from pygpt_net.plugin.base.worker import BaseWorker, BaseSignals
16
22
 
17
23
 
18
24
  class WorkerSignals(BaseSignals):
19
- playback = Signal(object, str)
25
+ volume_changed = Signal(float) # Emits the volume level
26
+ playback = Signal(str)
20
27
  stop = Signal()
28
+ error_playback = Signal(object)
21
29
 
22
30
 
23
31
  class Worker(BaseWorker):
@@ -32,6 +40,9 @@ class Worker(BaseWorker):
32
40
  self.cache_file = None # path to cache file
33
41
  self.mode = "generate" # generate|playback
34
42
  self.audio_file = None
43
+ self.is_stopped = False
44
+ self.p = None
45
+ self.stream = None
35
46
 
36
47
  @Slot()
37
48
  def run(self):
@@ -44,35 +55,118 @@ class Worker(BaseWorker):
44
55
  self.error(e)
45
56
 
46
57
  def generate(self):
47
- """
48
- Generate and play audio file
49
- """
58
+ """Generate and play audio file"""
50
59
  if self.text is None or self.text == "":
51
60
  time.sleep(0.2) # wait
52
61
  return
53
62
  path = self.plugin.get_provider().speech(self.text)
54
63
  if path:
55
- from pygame import mixer
56
- mixer.init()
57
- playback = mixer.Sound(path)
58
- self.stop_playback() # stop previous playback
59
- playback.play()
60
- self.send(playback) # send playback object to main thread to allow force stop
61
-
64
+ self.play_audio(path)
62
65
  # store in cache if enabled
63
66
  if self.cache_file:
64
67
  self.cache_audio_file(path, self.cache_file)
65
68
 
66
69
  def play(self):
70
+ """Play audio file only"""
71
+ if self.audio_file:
72
+ self.play_audio(self.audio_file)
73
+
74
+ def play_audio(self, audio_file: str):
67
75
  """
68
- Play audio file only
76
+ Play audio file using PyAudio
77
+
78
+ :param audio_file: audio file path
69
79
  """
70
- if self.audio_file:
71
- from pygame import mixer
72
- mixer.init()
73
- playback = mixer.Sound(self.audio_file)
74
- playback.play()
75
- self.send(playback) # send playback object to main thread to allow force stop
80
+ try:
81
+ self.signals.playback.emit(self.event)
82
+ audio = AudioSegment.from_file(audio_file)
83
+ audio = audio.set_frame_rate(44100) # resample to 44.1 kHz
84
+ wav_io = io.BytesIO()
85
+ audio.export(wav_io, format='wav')
86
+ wav_io.seek(0)
87
+ wf = wave.open(wav_io, 'rb')
88
+ self.p = pyaudio.PyAudio()
89
+ self.stream = self.p.open(format=self.p.get_format_from_width(wf.getsampwidth()),
90
+ channels=wf.getnchannels(),
91
+ rate=wf.getframerate(),
92
+ output=True)
93
+
94
+ sample_width = wf.getsampwidth()
95
+ format = self.p.get_format_from_width(sample_width)
96
+
97
+ if format == pyaudio.paInt8:
98
+ dtype = np.int8
99
+ max_value = 2 ** 7 - 1 # 127
100
+ offset = 0
101
+ elif format == pyaudio.paInt16:
102
+ dtype = np.int16
103
+ max_value = 2 ** 15 - 1 # 32767
104
+ offset = 0
105
+ elif format == pyaudio.paInt32:
106
+ dtype = np.int32
107
+ max_value = 2 ** 31 - 1 # 2147483647
108
+ offset = 0
109
+ elif format == pyaudio.paUInt8:
110
+ dtype = np.uint8
111
+ max_value = 2 ** 8 - 1 # 255
112
+ offset = 128 # center unsigned data
113
+ else:
114
+ raise ValueError(f"Unsupported format: {format}")
115
+
116
+ chunk_size = 512
117
+ data = wf.readframes(chunk_size)
118
+
119
+ while data != b'' and not self.is_stopped:
120
+ self.stream.write(data)
121
+
122
+ audio_data = np.frombuffer(data, dtype=dtype)
123
+ if len(audio_data) > 0:
124
+ audio_data = audio_data.astype(np.float32)
125
+ if dtype == np.uint8:
126
+ audio_data -= offset
127
+
128
+ # compute RMS
129
+ rms = np.sqrt(np.mean(audio_data ** 2))
130
+
131
+ if rms > 0:
132
+ # RMS to decibels
133
+ db = 20 * np.log10(rms / max_value)
134
+
135
+ # define minimum and maximum dB levels
136
+ min_db = -60 # adjust as needed
137
+ max_db = 0
138
+
139
+ # clamp the db value to the range [min_db, max_db]
140
+ if db < min_db:
141
+ db = min_db
142
+ elif db > max_db:
143
+ db = max_db
144
+
145
+ # map decibel value to volume percentage
146
+ volume_percentage = ((db - min_db) / (max_db - min_db)) * 100
147
+ else:
148
+ volume_percentage = 0
149
+
150
+ # emit volume signal
151
+ self.signals.volume_changed.emit(volume_percentage)
152
+ else:
153
+ # if empty audio_data
154
+ self.signals.volume_changed.emit(0)
155
+
156
+ data = wf.readframes(chunk_size)
157
+
158
+ # close the stream
159
+ if self.stream is not None:
160
+ if self.stream.is_active():
161
+ self.stream.stop_stream()
162
+ self.stream.close()
163
+ if self.p is not None:
164
+ self.p.terminate()
165
+ wf.close()
166
+ self.signals.volume_changed.emit(0)
167
+ except Exception as e:
168
+ self.signals.volume_changed.emit(0)
169
+ self.signals.error_playback.emit(e)
76
170
 
77
171
  def cache_audio_file(self, src: str, dst: str):
78
172
  """
@@ -102,4 +196,4 @@ class Worker(BaseWorker):
102
196
 
103
197
  def stop(self):
104
198
  """Send stop signal to main thread"""
105
- self.signals.stop.emit()
199
+ self.is_stopped = 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.01.17 02:00:00 #
9
+ # Updated Date: 2025.01.18 16:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -1811,6 +1811,14 @@ class Patch:
1811
1811
  if 'audio.input.continuous' not in data:
1812
1812
  data["audio.input.continuous"] = False
1813
1813
 
1814
+ # < 2.4.55
1815
+ if old < parse_version("2.4.55"):
1816
+ print("Migrating config from < 2.4.55...")
1817
+ if 'audio.input.timeout' not in data:
1818
+ data["audio.input.timeout"] = 120
1819
+ if 'audio.input.timeout.continuous' not in data:
1820
+ data["audio.input.timeout.continuous"] = False
1821
+
1814
1822
  # update file
1815
1823
  migrated = False
1816
1824
  if updated:
pygpt_net/ui/__init__.py CHANGED
@@ -91,7 +91,8 @@ class UI:
91
91
 
92
92
  # FIRST RUN: initial sizes if not set yet
93
93
  if not self.window.core.config.has("layout.splitters") \
94
- or self.window.core.config.get("layout.splitters") == {}:
94
+ or self.window.core.config.get("layout.splitters") == {}\
95
+ or self.window.core.config.get("license.accepted") == False:
95
96
  self.set_initial_size()
96
97
 
97
98
  # menus
@@ -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: 2024.02.26 22:00:00 #
9
+ # Updated Date: 2025.01.18 05:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from pygpt_net.ui.widget.dialog.snap import SnapDialogCamera, SnapDialogAudioInput
12
+ from pygpt_net.ui.widget.dialog.snap import SnapDialogCamera, SnapDialogAudioInput, SnapDialogAudioOutput
13
13
 
14
14
 
15
15
  class Snap:
@@ -25,3 +25,4 @@ class Snap:
25
25
  """Setup snap dialog"""
26
26
  self.window.ui.dialog['snap_camera'] = SnapDialogCamera(self.window)
27
27
  self.window.ui.dialog['snap_audio_input'] = SnapDialogAudioInput(self.window)
28
+ self.window.ui.dialog['snap_audio_output'] = SnapDialogAudioOutput(self.window)
@@ -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: 2024.12.09 03:00:00 #
9
+ # Updated Date: 2025.01.18 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -18,6 +18,7 @@ from pygpt_net.ui.layout.chat.attachments import Attachments
18
18
  from pygpt_net.ui.layout.chat.attachments_uploaded import AttachmentsUploaded
19
19
  from pygpt_net.ui.layout.chat.attachments_ctx import AttachmentsCtx
20
20
  from pygpt_net.ui.layout.status import Status
21
+ from pygpt_net.ui.widget.audio.bar import OutputBar
21
22
  from pygpt_net.ui.widget.audio.input import AudioInput
22
23
  from pygpt_net.ui.widget.audio.input_button import AudioInputButton
23
24
  from pygpt_net.ui.widget.element.labels import HelpLabel
@@ -176,8 +177,10 @@ class Input:
176
177
 
177
178
  :return: QHBoxLayout
178
179
  """
180
+ self.window.ui.plugin_addon['audio.output.bar'] = OutputBar(self.window)
179
181
  layout = QHBoxLayout()
180
182
  layout.addLayout(self.status.setup())
183
+ layout.addWidget(self.window.ui.plugin_addon['audio.output.bar'], alignment=Qt.AlignCenter)
181
184
  layout.addLayout(self.setup_buttons())
182
185
  layout.setContentsMargins(2, 0, 2, 0)
183
186
 
@@ -0,0 +1,103 @@
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.01.18 03:00:00 #
10
+ # ================================================== #
11
+
12
+ from PySide6.QtCore import Qt
13
+ from PySide6.QtGui import QPainter, QPalette
14
+ from PySide6.QtWidgets import QWidget
15
+
16
+ import pygpt_net.icons_rc
17
+
18
+ class InputBar(QWidget):
19
+ def __init__(self, parent=None):
20
+ super().__init__(parent)
21
+ self._level = 0.0 # level from 0.0 to 100.0
22
+ self.setFixedSize(200, 5) # bar size
23
+
24
+ def setLevel(self, level):
25
+ """
26
+ Set volume level
27
+
28
+ :param level: level
29
+ """
30
+ self._level = level
31
+ self.update()
32
+
33
+ def paintEvent(self, event):
34
+ """
35
+ Paint event
36
+
37
+ :param event: event
38
+ """
39
+ palette = self.palette()
40
+ painter = QPainter(self)
41
+ painter.fillRect(self.rect(), Qt.transparent)
42
+ level_width = (self._level / 100.0) * self.width()
43
+ painter.setBrush(palette.color(QPalette.ButtonText))
44
+ painter.setPen(Qt.NoPen)
45
+ painter.drawRect(0, 0, level_width, self.height())
46
+
47
+ """
48
+ # --- bar from center ---
49
+ def paintEvent(self, event):
50
+ painter = QPainter(self)
51
+ painter.fillRect(self.rect(), Qt.transparent)
52
+ level_width = (self._level / 100.0) * self.width()
53
+ half_level_width = level_width / 2
54
+ center_x = self.width() / 2
55
+ rect_x = center_x - half_level_width
56
+ painter.setBrush(Qt.green)
57
+ painter.setPen(Qt.NoPen)
58
+ painter.drawRect(rect_x, 0, level_width, self.height())
59
+ """
60
+
61
+
62
+ class OutputBar(QWidget):
63
+ def __init__(self, parent=None):
64
+ super().__init__(parent)
65
+ self._level = 0.0 # level from 0.0 to 100.0
66
+ self.setFixedSize(200, 5) # bar size
67
+
68
+ def setLevel(self, level):
69
+ """
70
+ Set volume level
71
+
72
+ :param level: level
73
+ """
74
+ self._level = level
75
+ self.update()
76
+
77
+ def paintEvent(self, event):
78
+ """
79
+ Paint event
80
+
81
+ :param event: event
82
+ """
83
+ palette = self.palette()
84
+ painter = QPainter(self)
85
+ painter.fillRect(self.rect(), Qt.transparent)
86
+ level_width = (self._level / 100.0) * self.width()
87
+ painter.setBrush(palette.color(QPalette.ButtonText))
88
+ painter.setPen(Qt.NoPen)
89
+ painter.drawRect(0, 0, level_width, self.height())
90
+
91
+ """
92
+ # --- bar from center ---
93
+ def paintEvent(self, event):
94
+ painter = QPainter(self)
95
+ painter.fillRect(self.rect(), Qt.transparent)
96
+ level_width = (self._level / 100.0) * self.width()
97
+ half_level_width = level_width / 2
98
+ center_x = self.width() / 2
99
+ rect_x = center_x - half_level_width
100
+ painter.setBrush(Qt.green)
101
+ painter.setPen(Qt.NoPen)
102
+ painter.drawRect(rect_x, 0, level_width, self.height())
103
+ """
@@ -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.01.17 02:00:00 #
9
+ # Updated Date: 2025.01.18 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -16,6 +16,8 @@ from PySide6.QtWidgets import QLabel, QHBoxLayout, QWidget, QPushButton, QVBoxLa
16
16
  from pygpt_net.core.events import Event, AppEvent
17
17
  from pygpt_net.ui.widget.option.toggle_label import ToggleLabel
18
18
  from pygpt_net.utils import trans
19
+ from .bar import InputBar
20
+
19
21
  import pygpt_net.icons_rc
20
22
 
21
23
  class VoiceControlButton(QWidget):
@@ -34,7 +36,7 @@ class VoiceControlButton(QWidget):
34
36
  self.btn_toggle.setCursor(Qt.PointingHandCursor)
35
37
  self.btn_toggle.setMinimumWidth(200)
36
38
 
37
- self.bar = LevelBar(self)
39
+ self.bar = InputBar(self)
38
40
  self.bar.setLevel(0)
39
41
 
40
42
  # status
@@ -89,7 +91,7 @@ class AudioInputButton(QWidget):
89
91
  self.btn_toggle.setCursor(Qt.PointingHandCursor)
90
92
  self.btn_toggle.setMinimumWidth(200)
91
93
 
92
- self.bar = LevelBar(self)
94
+ self.bar = InputBar(self)
93
95
  self.bar.setLevel(0)
94
96
 
95
97
  btn_layout = QVBoxLayout()
@@ -133,46 +135,3 @@ class AudioInputButton(QWidget):
133
135
  """Toggle recording"""
134
136
  event = Event(Event.AUDIO_INPUT_RECORD_TOGGLE)
135
137
  self.window.dispatch(event)
136
-
137
-
138
- class LevelBar(QWidget):
139
- def __init__(self, parent=None):
140
- super().__init__(parent)
141
- self._level = 0.0 # level from 0.0 to 100.0
142
- self.setFixedSize(200, 5) # bar size
143
-
144
- def setLevel(self, level):
145
- """
146
- Set volume level
147
-
148
- :param level: level
149
- """
150
- self._level = level
151
- self.update()
152
-
153
- def paintEvent(self, event):
154
- """
155
- Paint event
156
-
157
- :param event: event
158
- """
159
- painter = QPainter(self)
160
- painter.fillRect(self.rect(), Qt.transparent)
161
- level_width = (self._level / 100.0) * self.width()
162
- painter.setBrush(Qt.green)
163
- painter.setPen(Qt.NoPen)
164
- painter.drawRect(0, 0, level_width, self.height())
165
-
166
- """
167
- # --- bar from center ---
168
- def paintEvent(self, event):
169
- painter = QPainter(self)
170
- painter.fillRect(self.rect(), Qt.transparent)
171
- level_width = (self._level / 100.0) * self.width()
172
- half_level_width = level_width / 2
173
- center_x = self.width() / 2
174
- rect_x = center_x - half_level_width
175
- painter.setBrush(Qt.green)
176
- painter.setPen(Qt.NoPen)
177
- painter.drawRect(rect_x, 0, level_width, self.height())
178
- """
@@ -24,7 +24,7 @@ class SnapDialogCamera(QDialog):
24
24
  super(SnapDialogCamera, self).__init__(window)
25
25
  self.window = window
26
26
  self.setParent(window)
27
- self.setWindowTitle("Snap detected")
27
+ self.setWindowTitle("Snap version detected")
28
28
  self.cmd = CmdLabel(self.window, "sudo snap connect pygpt:camera")
29
29
 
30
30
  self.btn = QPushButton("OK")
@@ -34,8 +34,8 @@ class SnapDialogCamera(QDialog):
34
34
  # layout
35
35
  self.layout = QVBoxLayout()
36
36
  self.message = QLabel(
37
- "Camera is not connected! It must be connected in Snap environment.\n"
38
- "Run the following command to enable the camera:")
37
+ "Camera not connected? It must be connected in the Snap environment.\n"
38
+ "Run the following command to enable the camera and restart the application:")
39
39
  self.message.setStyleSheet("margin: 10px 0px 10px 0px;")
40
40
  self.layout.addWidget(self.message)
41
41
  self.layout.addWidget(self.cmd)
@@ -54,7 +54,7 @@ class SnapDialogAudioInput(QDialog):
54
54
  super(SnapDialogAudioInput, self).__init__(window)
55
55
  self.window = window
56
56
  self.setParent(window)
57
- self.setWindowTitle("Snap is detected")
57
+ self.setWindowTitle("Snap version is detected")
58
58
  self.cmd = CmdLabel(self.window, "sudo snap connect pygpt:alsa && sudo snap connect pygpt:audio-record :audio-record")
59
59
 
60
60
  self.btn = QPushButton("OK")
@@ -64,8 +64,39 @@ class SnapDialogAudioInput(QDialog):
64
64
  # layout
65
65
  self.layout = QVBoxLayout()
66
66
  self.message = QLabel(
67
- "Microphone is not connected! It must be connected in Snap environment.\n"
68
- "Run the following command to enable the microphone:")
67
+ "Tip: Microphone must be manually connected in the Snap environment.\n"
68
+ "If it is connected, click on the OK button, and this warning will not be displayed again.\n"
69
+ "If it is NOT connected yet, run the following command and restart the application:")
70
+ self.message.setStyleSheet("margin: 10px 0px 10px 0px;")
71
+ self.layout.addWidget(self.message)
72
+ self.layout.addWidget(self.cmd)
73
+ self.layout.addWidget(self.btn)
74
+ self.layout.addStretch()
75
+ self.setLayout(self.layout)
76
+
77
+
78
+ class SnapDialogAudioOutput(QDialog):
79
+ def __init__(self, window=None):
80
+ """
81
+ Snap dialog for audio output
82
+
83
+ :param window: main window
84
+ """
85
+ super(SnapDialogAudioOutput, self).__init__(window)
86
+ self.window = window
87
+ self.setParent(window)
88
+ self.setWindowTitle("Snap version is detected")
89
+ self.cmd = CmdLabel(self.window, "sudo snap connect pygpt:alsa && sudo snap connect pygpt:audio-playback")
90
+
91
+ self.btn = QPushButton("OK")
92
+ self.btn.clicked.connect(self.accept)
93
+ self.btn.setStyleSheet("margin: 10px 0px 0px 0px;")
94
+
95
+ # layout
96
+ self.layout = QVBoxLayout()
97
+ self.message = QLabel(
98
+ "Audio Device not connected? It must be connected in the Snap environment.\n"
99
+ "Run the following command to enable the audio output and restart the application:")
69
100
  self.message.setStyleSheet("margin: 10px 0px 10px 0px;")
70
101
  self.layout.addWidget(self.message)
71
102
  self.layout.addWidget(self.cmd)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pygpt-net
3
- Version: 2.4.53
3
+ Version: 2.4.55
4
4
  Summary: Desktop AI Assistant powered by models: OpenAI o1, GPT-4o, GPT-4, GPT-4 Vision, GPT-3.5, DALL-E 3, Llama 3, Mistral, Gemini, Claude, Bielik, and other models supported by Langchain, Llama Index, and Ollama. Features include chatbot, text completion, image generation, vision analysis, speech-to-text, internet access, file handling, command execution and more.
5
5
  Home-page: https://pygpt.net
6
6
  License: MIT
@@ -93,7 +93,7 @@ Description-Content-Type: text/markdown
93
93
 
94
94
  [![pygpt](https://snapcraft.io/pygpt/badge.svg)](https://snapcraft.io/pygpt)
95
95
 
96
- Release: **2.4.53** | build: **2025.01.17** | Python: **>=3.10, <3.13**
96
+ Release: **2.4.55** | build: **2025.01.18** | Python: **>=3.10, <3.13**
97
97
 
98
98
  > Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
99
99
  >
@@ -212,6 +212,13 @@ sudo snap connect pygpt:audio-record :audio-record
212
212
  sudo snap connect pygpt:alsa
213
213
  ```
214
214
 
215
+ **Using audio output:** to use audio output in Snap version you must connect the audio with:
216
+
217
+ ```commandline
218
+ sudo snap connect pygpt:audio-playback
219
+ sudo snap connect pygpt:alsa
220
+ ```
221
+
215
222
  **Connecting IPython in Docker in Snap version**:
216
223
 
217
224
  To use IPython in the Snap version, you must connect PyGPT to the Docker daemon:
@@ -4044,6 +4051,16 @@ may consume additional tokens that are not displayed in the main window.
4044
4051
 
4045
4052
  ## Recent changes:
4046
4053
 
4054
+ **2.4.55 (2025-01-18)**
4055
+
4056
+ - Added a new option in settings: Audio -> Recording timeout.
4057
+ - Added a new option in settings: Audio -> Enable timeout in continuous mode.
4058
+
4059
+ **2.4.54 (2025-01-18)**
4060
+
4061
+ - Audio output switched from PyGame to PyAudio. It may be necessary to manually connect Alsa in Snap version with: "sudo snap connect pygpt:alsa".
4062
+ - Added audio output volume progress bar.
4063
+
4047
4064
  **2.4.53 (2025-01-17)**
4048
4065
 
4049
4066
  - Fix: issue #89