pygpt-net 2.6.51__py3-none-any.whl → 2.6.53__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 +10 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +3 -1
- pygpt_net/controller/__init__.py +4 -2
- pygpt_net/controller/audio/audio.py +22 -1
- pygpt_net/controller/chat/chat.py +5 -1
- pygpt_net/controller/chat/remote_tools.py +116 -0
- pygpt_net/controller/ctx/ctx.py +8 -3
- pygpt_net/controller/lang/mapping.py +2 -1
- pygpt_net/controller/mode/mode.py +5 -2
- pygpt_net/controller/plugins/plugins.py +29 -3
- pygpt_net/controller/realtime/realtime.py +8 -3
- pygpt_net/controller/ui/mode.py +11 -5
- pygpt_net/controller/ui/tabs.py +31 -7
- pygpt_net/core/ctx/output.py +4 -2
- pygpt_net/core/render/web/renderer.py +5 -4
- pygpt_net/core/tabs/tab.py +42 -9
- pygpt_net/core/tabs/tabs.py +7 -9
- pygpt_net/data/config/config.json +6 -5
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/icons/web_off.svg +1 -0
- pygpt_net/data/icons/web_on.svg +1 -0
- pygpt_net/data/locale/locale.de.ini +1 -0
- pygpt_net/data/locale/locale.en.ini +3 -2
- pygpt_net/data/locale/locale.es.ini +1 -0
- pygpt_net/data/locale/locale.fr.ini +1 -0
- pygpt_net/data/locale/locale.it.ini +1 -0
- pygpt_net/data/locale/locale.pl.ini +1 -4
- pygpt_net/data/locale/locale.uk.ini +1 -0
- pygpt_net/data/locale/locale.zh.ini +1 -0
- pygpt_net/data/locale/plugin.mcp.en.ini +12 -0
- pygpt_net/icons.qrc +2 -0
- pygpt_net/icons_rc.py +232 -147
- pygpt_net/plugin/mcp/__init__.py +12 -0
- pygpt_net/plugin/mcp/config.py +103 -0
- pygpt_net/plugin/mcp/plugin.py +513 -0
- pygpt_net/plugin/mcp/worker.py +263 -0
- pygpt_net/provider/api/anthropic/tools.py +4 -2
- pygpt_net/provider/api/google/__init__.py +3 -2
- pygpt_net/provider/api/openai/agents/remote_tools.py +14 -4
- pygpt_net/provider/api/openai/chat.py +14 -2
- pygpt_net/provider/api/openai/remote_tools.py +5 -2
- pygpt_net/provider/api/x_ai/remote.py +6 -1
- pygpt_net/provider/core/config/patch.py +8 -1
- pygpt_net/ui/dialog/plugins.py +1 -3
- pygpt_net/ui/layout/chat/output.py +7 -2
- pygpt_net/ui/widget/element/labels.py +1 -2
- pygpt_net/ui/widget/tabs/body.py +24 -5
- {pygpt_net-2.6.51.dist-info → pygpt_net-2.6.53.dist-info}/METADATA +24 -4
- {pygpt_net-2.6.51.dist-info → pygpt_net-2.6.53.dist-info}/RECORD +53 -45
- {pygpt_net-2.6.51.dist-info → pygpt_net-2.6.53.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.51.dist-info → pygpt_net-2.6.53.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.51.dist-info → pygpt_net-2.6.53.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
2.6.53 (2025-09-17)
|
|
2
|
+
|
|
3
|
+
- Added: An icon to enable/disable the web search remote tool in the icon bar, along with remote web search functionality in OpenRouter (#135).
|
|
4
|
+
- Added: The ability to mute audio in real-time mode via the audio icon.
|
|
5
|
+
|
|
6
|
+
2.6.52 (2025-09-17)
|
|
7
|
+
|
|
8
|
+
- Added MCP plugin: Provides access to remote tools via the Model Context Protocol (MCP), including stdio, SSE, and Streamable HTTP transports, with per-server allow/deny filtering, Authorization header support, and a tools cache.
|
|
9
|
+
- Fixed: tab tooltips reload on profile switch.
|
|
10
|
+
|
|
1
11
|
2.6.51 (2025-09-16)
|
|
2
12
|
|
|
3
13
|
- Fix: Automatically reloading calendar notes.
|
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.09.
|
|
9
|
+
# Updated Date: 2025.09.17 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.6.
|
|
17
|
-
__build__ = "2025-09-
|
|
16
|
+
__version__ = "2.6.53"
|
|
17
|
+
__build__ = "2025-09-17"
|
|
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"
|
pygpt_net/app.py
CHANGED
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.16 22:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -96,6 +96,7 @@ from pygpt_net.plugin.bitbucket import Plugin as BitbucketPlugin
|
|
|
96
96
|
from pygpt_net.plugin.server import Plugin as ServerPlugin
|
|
97
97
|
from pygpt_net.plugin.tuya import Plugin as TuyaPlugin
|
|
98
98
|
from pygpt_net.plugin.wikipedia import Plugin as WikipediaPlugin
|
|
99
|
+
from pygpt_net.plugin.mcp import Plugin as MCPPlugin
|
|
99
100
|
|
|
100
101
|
# agents (Llama-index)
|
|
101
102
|
# from pygpt_net.provider.agents.llama_index.legacy.openai import OpenAIAgent
|
|
@@ -425,6 +426,7 @@ def run(**kwargs):
|
|
|
425
426
|
launcher.add_plugin(ServerPlugin())
|
|
426
427
|
launcher.add_plugin(TuyaPlugin())
|
|
427
428
|
launcher.add_plugin(WikipediaPlugin())
|
|
429
|
+
launcher.add_plugin(MCPPlugin())
|
|
428
430
|
|
|
429
431
|
# register custom plugins
|
|
430
432
|
plugins = kwargs.get('plugins', None)
|
pygpt_net/controller/__init__.py
CHANGED
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.16 22:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from .access import Access
|
|
@@ -156,9 +156,12 @@ class Controller:
|
|
|
156
156
|
print(trans("status.reloading.profile.begin"))
|
|
157
157
|
|
|
158
158
|
try:
|
|
159
|
+
self.ui.tabs.locked = True # lock tabs
|
|
159
160
|
self.window.core.reload() # db, config, patch, etc.
|
|
160
161
|
self.ui.tabs.reload()
|
|
161
162
|
self.ctx.reload()
|
|
163
|
+
self.ui.tabs.locked = False # unlock tabs
|
|
164
|
+
|
|
162
165
|
self.settings.reload()
|
|
163
166
|
self.assistant.reload()
|
|
164
167
|
self.attachment.reload()
|
|
@@ -181,7 +184,6 @@ class Controller:
|
|
|
181
184
|
# post-reload
|
|
182
185
|
self.ui.tabs.reload_after()
|
|
183
186
|
self.ctx.reload_after()
|
|
184
|
-
self.ui.tabs.restore_data() # restore opened tabs data
|
|
185
187
|
self.kernel.restart()
|
|
186
188
|
self.theme.reload_all() # do not reload theme if no change
|
|
187
189
|
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.17 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -36,6 +36,7 @@ class Audio:
|
|
|
36
36
|
Tab.TAB_CHAT,
|
|
37
37
|
Tab.TAB_TOOL_CALENDAR,
|
|
38
38
|
]
|
|
39
|
+
self.muted = False
|
|
39
40
|
|
|
40
41
|
def setup(self):
|
|
41
42
|
"""Setup controller"""
|
|
@@ -53,6 +54,26 @@ class Audio:
|
|
|
53
54
|
if self.window.core.config.get("audio.input.loop", False):
|
|
54
55
|
self.window.ui.nodes['audio.loop'].box.setChecked(True)
|
|
55
56
|
|
|
57
|
+
def is_muted(self) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
Check if audio output is muted
|
|
60
|
+
|
|
61
|
+
:return: True if muted
|
|
62
|
+
"""
|
|
63
|
+
return self.muted
|
|
64
|
+
|
|
65
|
+
def set_muted(self, state: bool):
|
|
66
|
+
"""
|
|
67
|
+
Set audio output muted state
|
|
68
|
+
|
|
69
|
+
:param state: True to mute, False to unmute
|
|
70
|
+
"""
|
|
71
|
+
self.muted = state
|
|
72
|
+
if state:
|
|
73
|
+
self.toggle_output_icon(False)
|
|
74
|
+
else:
|
|
75
|
+
self.toggle_output_icon(True)
|
|
76
|
+
|
|
56
77
|
def execute_input_stop(self):
|
|
57
78
|
"""Execute input stop (from UI)"""
|
|
58
79
|
self.window.dispatch(Event(Event.AUDIO_INPUT_RECORD_TOGGLE, {
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.17 05:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any
|
|
@@ -22,6 +22,7 @@ from .files import Files
|
|
|
22
22
|
from .image import Image
|
|
23
23
|
from .input import Input
|
|
24
24
|
from .output import Output
|
|
25
|
+
from .remote_tools import RemoteTools
|
|
25
26
|
from .render import Render
|
|
26
27
|
from .response import Response
|
|
27
28
|
from .stream import Stream
|
|
@@ -46,6 +47,7 @@ class Chat:
|
|
|
46
47
|
self.image = Image(w)
|
|
47
48
|
self.input = Input(w)
|
|
48
49
|
self.output = Output(w)
|
|
50
|
+
self.remote_tools = RemoteTools(w)
|
|
49
51
|
self.render = Render(w)
|
|
50
52
|
self.response = Response(w)
|
|
51
53
|
self.stream = Stream(w)
|
|
@@ -60,12 +62,14 @@ class Chat:
|
|
|
60
62
|
"""Setup"""
|
|
61
63
|
self.common.setup()
|
|
62
64
|
self.attachment.setup()
|
|
65
|
+
self.remote_tools.setup()
|
|
63
66
|
|
|
64
67
|
def reload(self) -> None:
|
|
65
68
|
"""Reload"""
|
|
66
69
|
self.common.setup()
|
|
67
70
|
self.render.reload()
|
|
68
71
|
self.attachment.reload()
|
|
72
|
+
self.remote_tools.setup()
|
|
69
73
|
|
|
70
74
|
def handle_error(self, err: Any) -> None:
|
|
71
75
|
"""
|
|
@@ -0,0 +1,116 @@
|
|
|
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.09.17 05:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
from typing import Union
|
|
13
|
+
|
|
14
|
+
from pygpt_net.item.model import ModelItem
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RemoteTools:
|
|
18
|
+
def __init__(self, window=None):
|
|
19
|
+
"""
|
|
20
|
+
Remote tools controller
|
|
21
|
+
|
|
22
|
+
:param window: Window instance
|
|
23
|
+
"""
|
|
24
|
+
self.window = window
|
|
25
|
+
self.enabled_global = {
|
|
26
|
+
"web_search": False,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def setup(self):
|
|
30
|
+
"""
|
|
31
|
+
Setup remote tools
|
|
32
|
+
|
|
33
|
+
:return: None
|
|
34
|
+
"""
|
|
35
|
+
cfg_get = self.window.core.config.get
|
|
36
|
+
self.enabled_global["web_search"] = cfg_get("remote_tools.global.web_search", False)
|
|
37
|
+
self.update_icons()
|
|
38
|
+
|
|
39
|
+
def enabled(self, model: Union[ModelItem, str], tool_name: str) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
Check if remote tool is enabled
|
|
42
|
+
|
|
43
|
+
:param model: ModelItem or model name
|
|
44
|
+
:param tool_name: Tool name
|
|
45
|
+
:return: True if enabled, False otherwise
|
|
46
|
+
"""
|
|
47
|
+
if isinstance(model, str):
|
|
48
|
+
model = self.window.core.models.get(model)
|
|
49
|
+
if not model:
|
|
50
|
+
return False
|
|
51
|
+
if tool_name == "web_search":
|
|
52
|
+
return self.is_web(model)
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
def is_web(self, model: ModelItem) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
Check if web search is enabled for the given provider
|
|
58
|
+
|
|
59
|
+
:param model: ModelItem
|
|
60
|
+
:return: True if web search is enabled, False otherwise
|
|
61
|
+
"""
|
|
62
|
+
# at first, check provider-specific config
|
|
63
|
+
cfg_get = self.window.core.config.get
|
|
64
|
+
state = False
|
|
65
|
+
if model.provider == "openai": # native SDK, responses API
|
|
66
|
+
state = cfg_get("remote_tools.web_search", False)
|
|
67
|
+
elif model.provider == "google": # native SDK
|
|
68
|
+
state = cfg_get("remote_tools.google.web_search", False)
|
|
69
|
+
elif model.provider == "anthropic": # native SDK
|
|
70
|
+
state = cfg_get("remote_tools.anthropic.web_search", False)
|
|
71
|
+
elif model.provider == "x_ai": # native SDK
|
|
72
|
+
mode = cfg_get("remote_tools.xai.mode", "auto")
|
|
73
|
+
if mode not in ("auto", "on", "off"):
|
|
74
|
+
mode = "auto"
|
|
75
|
+
if mode == "auto" or mode == "on":
|
|
76
|
+
state = True
|
|
77
|
+
|
|
78
|
+
# if not enabled by default or other provider, then use global config
|
|
79
|
+
if not state:
|
|
80
|
+
state = self.enabled_global["web_search"]
|
|
81
|
+
|
|
82
|
+
return state
|
|
83
|
+
|
|
84
|
+
def update_icons(self):
|
|
85
|
+
"""
|
|
86
|
+
Update remote tools icons in chat tabs
|
|
87
|
+
"""
|
|
88
|
+
state = self.enabled_global["web_search"]
|
|
89
|
+
if state:
|
|
90
|
+
self.window.ui.nodes['icon.remote_tool.web'].set_icon(":/icons/web_on.svg")
|
|
91
|
+
else:
|
|
92
|
+
self.window.ui.nodes['icon.remote_tool.web'].set_icon(":/icons/web_off.svg")
|
|
93
|
+
|
|
94
|
+
def toggle(self, tool_name: str):
|
|
95
|
+
"""
|
|
96
|
+
Toggle remote tool (for global toggle button)
|
|
97
|
+
|
|
98
|
+
:param tool_name: Tool name
|
|
99
|
+
"""
|
|
100
|
+
cfg_set = self.window.core.config.set
|
|
101
|
+
|
|
102
|
+
# tool: web search
|
|
103
|
+
if tool_name == "web_search":
|
|
104
|
+
state = not self.enabled_global["web_search"]
|
|
105
|
+
self.enabled_global["web_search"] = state
|
|
106
|
+
cfg_set("remote_tools.global.web_search", state)
|
|
107
|
+
cfg_set("remote_tools.web_search", state)
|
|
108
|
+
cfg_set("remote_tools.google.web_search", state)
|
|
109
|
+
cfg_set("remote_tools.anthropic.web_search", state)
|
|
110
|
+
|
|
111
|
+
# xAI has 3 modes: auto, on, off
|
|
112
|
+
cfg_set("remote_tools.xai.mode", "auto" if state else "off")
|
|
113
|
+
|
|
114
|
+
# save config
|
|
115
|
+
self.window.core.config.save()
|
|
116
|
+
self.update_icons()
|
pygpt_net/controller/ctx/ctx.py
CHANGED
|
@@ -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.09.16
|
|
9
|
+
# Updated Date: 2025.09.16 22:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Optional, List
|
|
@@ -1289,14 +1289,19 @@ class Ctx:
|
|
|
1289
1289
|
|
|
1290
1290
|
def reload(self):
|
|
1291
1291
|
"""Reload ctx"""
|
|
1292
|
+
self.edit_meta_id = None
|
|
1293
|
+
self.edit_item_id = None
|
|
1294
|
+
self.group_id = None
|
|
1295
|
+
self.selected = []
|
|
1296
|
+
self._infinite_scroll_refresh = False
|
|
1292
1297
|
self.reset_loaded_total() # reset paging
|
|
1293
1298
|
self.window.core.ctx.reset()
|
|
1294
1299
|
self.setup()
|
|
1295
|
-
self.update()
|
|
1296
|
-
self.refresh()
|
|
1297
1300
|
|
|
1298
1301
|
def reload_after(self):
|
|
1299
1302
|
"""After reload"""
|
|
1303
|
+
self.update()
|
|
1304
|
+
self.refresh()
|
|
1300
1305
|
self.new_if_empty()
|
|
1301
1306
|
|
|
1302
1307
|
def add_selected(self, id: int):
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.17 05:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Dict
|
|
@@ -423,6 +423,7 @@ class Mapping:
|
|
|
423
423
|
tooltips['icon.video.capture'] = 'icon.video.capture'
|
|
424
424
|
tooltips['icon.audio.output'] = 'icon.audio.output'
|
|
425
425
|
tooltips['icon.audio.input'] = 'icon.audio.input'
|
|
426
|
+
tooltips['icon.remote_tool.web'] = 'icon.remote_tool.web'
|
|
426
427
|
tooltips['assistant.store.btn.refresh_status'] = 'dialog.assistant.store.btn.refresh_status'
|
|
427
428
|
tooltips['agent.llama.loop.score'] = 'toolbox.agent.llama.loop.score.tooltip'
|
|
428
429
|
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.17 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.events import Event, AppEvent
|
|
13
13
|
from pygpt_net.core.types import (
|
|
14
14
|
MODE_ASSISTANT,
|
|
15
|
-
MODE_CHAT,
|
|
15
|
+
MODE_CHAT, MODE_AUDIO,
|
|
16
16
|
)
|
|
17
17
|
from pygpt_net.item.ctx import CtxItem
|
|
18
18
|
from pygpt_net.utils import trans
|
|
@@ -92,6 +92,9 @@ class Mode:
|
|
|
92
92
|
else:
|
|
93
93
|
c.assistant.select_current()
|
|
94
94
|
|
|
95
|
+
elif mode == MODE_AUDIO:
|
|
96
|
+
c.audio.set_muted(False) # un-mute and show audio output icon by default
|
|
97
|
+
|
|
95
98
|
cfg.set('mode', mode)
|
|
96
99
|
|
|
97
100
|
# reset model and preset at start
|
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.17 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import List, Dict, Any, Optional
|
|
@@ -14,7 +14,7 @@ from typing import List, Dict, Any, Optional
|
|
|
14
14
|
from PySide6.QtGui import QAction
|
|
15
15
|
|
|
16
16
|
from pygpt_net.core.types import (
|
|
17
|
-
MODE_AGENT,
|
|
17
|
+
MODE_AGENT, MODE_AUDIO,
|
|
18
18
|
)
|
|
19
19
|
from pygpt_net.controller.plugins.presets import Presets
|
|
20
20
|
from pygpt_net.controller.plugins.settings import Settings
|
|
@@ -194,20 +194,46 @@ class Plugins:
|
|
|
194
194
|
|
|
195
195
|
def toggle(self, id: str):
|
|
196
196
|
"""
|
|
197
|
-
Toggle plugin
|
|
197
|
+
Toggle plugin (from menu)
|
|
198
198
|
|
|
199
199
|
:param id: plugin id
|
|
200
200
|
"""
|
|
201
201
|
if self.window.core.plugins.is_registered(id):
|
|
202
202
|
if self.is_enabled(id):
|
|
203
203
|
self.disable(id)
|
|
204
|
+
if id == "audio_output":
|
|
205
|
+
self.window.controller.audio.set_muted(True)
|
|
204
206
|
else:
|
|
205
207
|
self.enable(id)
|
|
208
|
+
if id == "audio_output":
|
|
209
|
+
self.window.controller.audio.set_muted(False)
|
|
206
210
|
|
|
207
211
|
self.window.controller.ui.update_tokens()
|
|
208
212
|
self.window.controller.attachment.update()
|
|
209
213
|
self.presets.save_current()
|
|
210
214
|
|
|
215
|
+
def toggle_audio_output(self):
|
|
216
|
+
"""
|
|
217
|
+
Toggle plugin (from icon, audio output only)
|
|
218
|
+
"""
|
|
219
|
+
id = "audio_output"
|
|
220
|
+
mode = self.window.core.config.get('mode')
|
|
221
|
+
|
|
222
|
+
if mode == MODE_AUDIO:
|
|
223
|
+
if not self.window.controller.audio.is_muted():
|
|
224
|
+
self.window.controller.audio.set_muted(True)
|
|
225
|
+
else:
|
|
226
|
+
self.window.controller.audio.set_muted(False)
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
if self.window.core.plugins.is_registered(id):
|
|
230
|
+
if self.is_enabled(id):
|
|
231
|
+
self.disable(id)
|
|
232
|
+
else:
|
|
233
|
+
self.enable(id)
|
|
234
|
+
|
|
235
|
+
self.presets.save_current()
|
|
236
|
+
|
|
211
237
|
def set_by_tab(self, idx: int):
|
|
212
238
|
"""
|
|
213
239
|
Set current plugin by tab index
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.17 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Slot, QTimer
|
|
@@ -64,6 +64,8 @@ class Realtime:
|
|
|
64
64
|
|
|
65
65
|
:param event: RealtimeEvent instance
|
|
66
66
|
"""
|
|
67
|
+
is_muted = self.window.controller.audio.is_muted() # global mute state
|
|
68
|
+
|
|
67
69
|
# check if mode is supported
|
|
68
70
|
if not self.is_supported() and isinstance(event, RealtimeEvent):
|
|
69
71
|
event.stop = True # stop further propagation
|
|
@@ -75,7 +77,7 @@ class Realtime:
|
|
|
75
77
|
if event.name == RealtimeEvent.RT_OUTPUT_AUDIO_DELTA:
|
|
76
78
|
self.set_idle()
|
|
77
79
|
payload = event.data.get("payload", None)
|
|
78
|
-
if payload:
|
|
80
|
+
if payload and not is_muted: # do not play if muted
|
|
79
81
|
self.window.core.audio.output.handle_realtime(payload, self.signals)
|
|
80
82
|
|
|
81
83
|
# audio input chunk: send to the active realtime client
|
|
@@ -148,7 +150,10 @@ class Realtime:
|
|
|
148
150
|
|
|
149
151
|
# volume change: update volume in audio output handler
|
|
150
152
|
elif event.name == RealtimeEvent.RT_OUTPUT_AUDIO_VOLUME_CHANGED:
|
|
151
|
-
|
|
153
|
+
if not is_muted:
|
|
154
|
+
volume = event.data.get("volume", 1.0)
|
|
155
|
+
else:
|
|
156
|
+
volume = 0.0
|
|
152
157
|
self.window.controller.audio.ui.on_output_volume_change(volume)
|
|
153
158
|
|
|
154
159
|
# error: audio output error
|
pygpt_net/controller/ui/mode.py
CHANGED
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.17 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.types import (
|
|
@@ -59,11 +59,17 @@ class Mode:
|
|
|
59
59
|
is_audio = mode == MODE_AUDIO
|
|
60
60
|
|
|
61
61
|
if not is_audio:
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
ui_nodes['audio.auto_turn'].setVisible(False)
|
|
63
|
+
ui_nodes["audio.loop"].setVisible(False)
|
|
64
|
+
if not ctrl.plugins.is_enabled('audio_output'):
|
|
65
|
+
ctrl.audio.toggle_output_icon(False)
|
|
64
66
|
else:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
ui_nodes['audio.auto_turn'].setVisible(True)
|
|
68
|
+
ui_nodes["audio.loop"].setVisible(True)
|
|
69
|
+
if not ctrl.audio.is_muted():
|
|
70
|
+
ctrl.audio.toggle_output_icon(True)
|
|
71
|
+
else:
|
|
72
|
+
ctrl.audio.toggle_output_icon(False)
|
|
67
73
|
|
|
68
74
|
if not is_assistant:
|
|
69
75
|
ui_nodes['presets.widget'].setVisible(True)
|
pygpt_net/controller/ui/tabs.py
CHANGED
|
@@ -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.09.16
|
|
9
|
+
# Updated Date: 2025.09.16 22:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Optional, Tuple
|
|
@@ -40,12 +40,13 @@ class Tabs:
|
|
|
40
40
|
self.create_new_on_tab = True
|
|
41
41
|
self.col = {}
|
|
42
42
|
|
|
43
|
-
def setup(self):
|
|
43
|
+
def setup(self, reload: bool = False):
|
|
44
44
|
"""Setup tabs"""
|
|
45
45
|
w = self.window
|
|
46
46
|
w.core.tabs.load()
|
|
47
47
|
w.controller.notepad.load()
|
|
48
|
-
|
|
48
|
+
if not reload:
|
|
49
|
+
self.setup_options()
|
|
49
50
|
self.initialized = True
|
|
50
51
|
|
|
51
52
|
def setup_options(self):
|
|
@@ -130,13 +131,31 @@ class Tabs:
|
|
|
130
131
|
self.col[curr_column] = curr_tab.pid
|
|
131
132
|
self.debug()
|
|
132
133
|
|
|
134
|
+
def unload(self):
|
|
135
|
+
"""Unload tabs"""
|
|
136
|
+
self.active_idx = 0
|
|
137
|
+
self.prev_idx = 0
|
|
138
|
+
self.appended = False
|
|
139
|
+
self.current = 0
|
|
140
|
+
self.column_idx = 0
|
|
141
|
+
self.tmp_column_idx = 0
|
|
142
|
+
self.create_new_on_tab = True
|
|
143
|
+
self.col = {}
|
|
144
|
+
columns = self.window.ui.layout.columns
|
|
145
|
+
for col in columns:
|
|
146
|
+
col.setUpdatesEnabled(False)
|
|
147
|
+
self.window.core.tabs.remove_all()
|
|
148
|
+
for col in columns:
|
|
149
|
+
col.setUpdatesEnabled(True)
|
|
150
|
+
|
|
133
151
|
def reload(self):
|
|
134
152
|
"""Reload tabs"""
|
|
153
|
+
self.unload()
|
|
135
154
|
columns = self.window.ui.layout.columns
|
|
136
155
|
for col in columns:
|
|
137
156
|
col.setUpdatesEnabled(False)
|
|
138
|
-
self.
|
|
139
|
-
self.
|
|
157
|
+
self.setup(reload=True)
|
|
158
|
+
self.restore_data()
|
|
140
159
|
self.window.dispatch(RenderEvent(RenderEvent.PREPARE))
|
|
141
160
|
self.debug()
|
|
142
161
|
for col in columns:
|
|
@@ -172,6 +191,9 @@ class Tabs:
|
|
|
172
191
|
:param idx: tab index
|
|
173
192
|
:param column_idx: column index
|
|
174
193
|
"""
|
|
194
|
+
if idx == -1:
|
|
195
|
+
return
|
|
196
|
+
|
|
175
197
|
w = self.window
|
|
176
198
|
core = w.core
|
|
177
199
|
tabs_core = core.tabs
|
|
@@ -594,7 +616,9 @@ class Tabs:
|
|
|
594
616
|
|
|
595
617
|
:param tooltip: tooltip text
|
|
596
618
|
"""
|
|
597
|
-
tabs = self.window.ui.layout.
|
|
619
|
+
tabs = self.window.ui.layout.get_tabs_by_idx(self.column_idx)
|
|
620
|
+
if tabs is not None and 0 <= self.current < tabs.count():
|
|
621
|
+
tabs.setTabToolTip(self.current, tooltip)
|
|
598
622
|
tabs.setTabToolTip(self.current, tooltip)
|
|
599
623
|
self.debug()
|
|
600
624
|
|
|
@@ -656,7 +680,7 @@ class Tabs:
|
|
|
656
680
|
"""
|
|
657
681
|
if self.get_current_type() != Tab.TAB_CHAT:
|
|
658
682
|
return
|
|
659
|
-
tabs = self.window.ui.layout.
|
|
683
|
+
tabs = self.window.ui.layout.get_tabs_by_idx(self.column_idx)
|
|
660
684
|
tooltip = title
|
|
661
685
|
tabs.setTabToolTip(idx, tooltip)
|
|
662
686
|
if len(title) > self.TAB_CHAT_MAX_CHARS:
|
pygpt_net/core/ctx/output.py
CHANGED
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.09.16 22:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Optional, List, Dict
|
|
@@ -71,7 +71,7 @@ class Output:
|
|
|
71
71
|
col_idx = tab.column_idx
|
|
72
72
|
|
|
73
73
|
col_map = self.mapping.get(col_idx)
|
|
74
|
-
if col_map is None:
|
|
74
|
+
if col_map is None or not isinstance(col_map, dict):
|
|
75
75
|
col_map = self.mapping[col_idx] = {}
|
|
76
76
|
last_map = self.last_pids.get(col_idx)
|
|
77
77
|
if last_map is None:
|
|
@@ -175,6 +175,8 @@ class Output:
|
|
|
175
175
|
return None
|
|
176
176
|
col_idx = tab.column_idx
|
|
177
177
|
col_map = self.mapping.get(col_idx, {})
|
|
178
|
+
if not isinstance(col_map, dict):
|
|
179
|
+
return None
|
|
178
180
|
|
|
179
181
|
candidates = [pid for pid, meta_id in col_map.items() if meta_id == meta.id]
|
|
180
182
|
for pid in candidates:
|
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.17 05:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import gc
|
|
@@ -1289,12 +1289,12 @@ class Renderer(BaseRenderer):
|
|
|
1289
1289
|
:param meta: context meta
|
|
1290
1290
|
"""
|
|
1291
1291
|
tab = node.get_tab()
|
|
1292
|
+
if tab is None:
|
|
1293
|
+
return
|
|
1292
1294
|
layout = tab.child.layout()
|
|
1293
|
-
tab.
|
|
1295
|
+
tab.unwrap(node)
|
|
1294
1296
|
self.window.ui.nodes['output'].pop(tab.pid, None)
|
|
1295
1297
|
|
|
1296
|
-
node.on_delete()
|
|
1297
|
-
|
|
1298
1298
|
view = ChatWebOutput(self.window)
|
|
1299
1299
|
view.set_tab(tab)
|
|
1300
1300
|
view.set_meta(meta)
|
|
@@ -1302,6 +1302,7 @@ class Renderer(BaseRenderer):
|
|
|
1302
1302
|
view.signals.audio_read.connect(self.window.controller.chat.render.handle_audio_read)
|
|
1303
1303
|
|
|
1304
1304
|
layout.addWidget(view) # tab body layout
|
|
1305
|
+
tab.add_ref(view)
|
|
1305
1306
|
view.setVisible(True)
|
|
1306
1307
|
self.window.ui.nodes['output'][tab.pid] = view
|
|
1307
1308
|
try:
|