pygpt-net 2.6.52__py3-none-any.whl → 2.6.54__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 +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +4 -0
- 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/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 +17 -5
- pygpt_net/core/agents/provider.py +16 -9
- pygpt_net/core/models/models.py +25 -1
- pygpt_net/core/render/web/renderer.py +3 -1
- pygpt_net/data/config/config.json +5 -4
- 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/js/app.js +19 -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 +4 -4
- pygpt_net/data/locale/plugin.osm.en.ini +35 -0
- pygpt_net/data/locale/plugin.wolfram.en.ini +24 -0
- pygpt_net/icons.qrc +2 -0
- pygpt_net/icons_rc.py +232 -147
- pygpt_net/js_rc.py +10490 -10432
- pygpt_net/plugin/base/worker.py +7 -1
- pygpt_net/plugin/osm/__init__.py +12 -0
- pygpt_net/plugin/osm/config.py +267 -0
- pygpt_net/plugin/osm/plugin.py +87 -0
- pygpt_net/plugin/osm/worker.py +719 -0
- pygpt_net/plugin/wolfram/__init__.py +12 -0
- pygpt_net/plugin/wolfram/config.py +214 -0
- pygpt_net/plugin/wolfram/plugin.py +115 -0
- pygpt_net/plugin/wolfram/worker.py +551 -0
- pygpt_net/provider/api/anthropic/tools.py +4 -2
- pygpt_net/provider/api/google/__init__.py +3 -2
- pygpt_net/provider/api/google/video.py +0 -0
- pygpt_net/provider/api/openai/agents/experts.py +1 -1
- pygpt_net/provider/api/openai/agents/remote_tools.py +14 -4
- pygpt_net/provider/api/openai/chat.py +7 -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/provider/llms/anthropic.py +29 -1
- pygpt_net/provider/llms/google.py +30 -1
- pygpt_net/provider/llms/open_router.py +3 -1
- pygpt_net/provider/llms/x_ai.py +21 -1
- pygpt_net/ui/layout/chat/output.py +7 -2
- {pygpt_net-2.6.52.dist-info → pygpt_net-2.6.54.dist-info}/METADATA +37 -2
- {pygpt_net-2.6.52.dist-info → pygpt_net-2.6.54.dist-info}/RECORD +60 -47
- {pygpt_net-2.6.52.dist-info → pygpt_net-2.6.54.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.52.dist-info → pygpt_net-2.6.54.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.52.dist-info → pygpt_net-2.6.54.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
2.6.54 (2025-09-18)
|
|
2
|
+
|
|
3
|
+
- Added: Remote tools (like web search) are now also available in the Chat with Files and Agents (LlamaIndex) modes.
|
|
4
|
+
- Added: Two new plugins: Wolfram Alpha and OpenStreetMap.
|
|
5
|
+
- Fixed: Enabled local file-like schemes in links/images in the markdown-it parser.
|
|
6
|
+
|
|
7
|
+
2.6.53 (2025-09-17)
|
|
8
|
+
|
|
9
|
+
- Added: An icon to enable/disable the web search remote tool in the icon bar, along with remote web search functionality in OpenRouter (#135).
|
|
10
|
+
- Added: The ability to mute audio in real-time mode via the audio icon.
|
|
11
|
+
|
|
1
12
|
2.6.52 (2025-09-17)
|
|
2
13
|
|
|
3
14
|
- 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.
|
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.18 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.54"
|
|
17
|
+
__build__ = "2025-09-18"
|
|
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
|
@@ -97,6 +97,8 @@ 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
99
|
from pygpt_net.plugin.mcp import Plugin as MCPPlugin
|
|
100
|
+
from pygpt_net.plugin.wolfram import Plugin as WolframPlugin
|
|
101
|
+
from pygpt_net.plugin.osm import Plugin as OSMPlugin
|
|
100
102
|
|
|
101
103
|
# agents (Llama-index)
|
|
102
104
|
# from pygpt_net.provider.agents.llama_index.legacy.openai import OpenAIAgent
|
|
@@ -427,6 +429,8 @@ def run(**kwargs):
|
|
|
427
429
|
launcher.add_plugin(TuyaPlugin())
|
|
428
430
|
launcher.add_plugin(WikipediaPlugin())
|
|
429
431
|
launcher.add_plugin(MCPPlugin())
|
|
432
|
+
launcher.add_plugin(WolframPlugin())
|
|
433
|
+
launcher.add_plugin(OSMPlugin())
|
|
430
434
|
|
|
431
435
|
# register custom plugins
|
|
432
436
|
plugins = kwargs.get('plugins', None)
|
|
@@ -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 20: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", "on" if state else "off")
|
|
113
|
+
|
|
114
|
+
# save config
|
|
115
|
+
self.window.core.config.save()
|
|
116
|
+
self.update_icons()
|
|
@@ -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 20: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)
|
|
@@ -189,6 +195,12 @@ class Mode:
|
|
|
189
195
|
show = self.are_attachments(mode)
|
|
190
196
|
ui_tabs['input'].setTabVisible(1, show)
|
|
191
197
|
|
|
198
|
+
# remote tools icon visibility
|
|
199
|
+
if not is_image and not is_completion:
|
|
200
|
+
ui_nodes['icon.remote_tool.web'].setVisible(True)
|
|
201
|
+
else:
|
|
202
|
+
ui_nodes['icon.remote_tool.web'].setVisible(False)
|
|
203
|
+
|
|
192
204
|
ui_tabs['input'].setTabVisible(2, is_assistant)
|
|
193
205
|
ui_tabs['input'].setTabVisible(3, (not is_assistant) and (not is_image))
|
|
194
206
|
|
|
@@ -6,9 +6,9 @@
|
|
|
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 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
from typing import List, Dict, Any
|
|
13
13
|
|
|
14
14
|
from pygpt_net.core.types import MODE_CHAT
|
|
@@ -108,11 +108,18 @@ class Provider:
|
|
|
108
108
|
from agents import (
|
|
109
109
|
OpenAIChatCompletionsModel,
|
|
110
110
|
)
|
|
111
|
-
|
|
111
|
+
models = self.window.core.models
|
|
112
|
+
if isinstance(model, str):
|
|
113
|
+
model = models.get(model)
|
|
114
|
+
|
|
115
|
+
model_id = model.id
|
|
116
|
+
if model.provider in ("openai", "azure_openai"):
|
|
112
117
|
return model.id
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
elif model.provider == "open_router":
|
|
119
|
+
model_id = models.get_openrouter_model(model)
|
|
120
|
+
|
|
121
|
+
args = models.prepare_client_args(MODE_CHAT, model)
|
|
122
|
+
return OpenAIChatCompletionsModel(
|
|
123
|
+
model=model_id,
|
|
124
|
+
openai_client=AsyncOpenAI(**args),
|
|
125
|
+
)
|
pygpt_net/core/models/models.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 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -526,6 +526,30 @@ class Models:
|
|
|
526
526
|
self.window.core.debug.info("[api] No model provided, using default OpenAI client")
|
|
527
527
|
return args
|
|
528
528
|
|
|
529
|
+
def get_openrouter_model(self, model: ModelItem) -> str:
|
|
530
|
+
"""
|
|
531
|
+
Get OpenRouter model by model id
|
|
532
|
+
|
|
533
|
+
:param model: ModelItem
|
|
534
|
+
:return: OpenRouter model id
|
|
535
|
+
"""
|
|
536
|
+
if isinstance(model, str):
|
|
537
|
+
model = self.get(model)
|
|
538
|
+
if not model or model.provider != "open_router":
|
|
539
|
+
return model.id if model else None
|
|
540
|
+
|
|
541
|
+
# OpenRouter: add web search remote tool (if enabled)
|
|
542
|
+
# https://openrouter.ai/docs/features/web-search
|
|
543
|
+
model_id = model.id
|
|
544
|
+
is_web = self.window.controller.chat.remote_tools.enabled(model, "web_search") # web search config
|
|
545
|
+
if is_web:
|
|
546
|
+
if not model_id.endswith(":online"):
|
|
547
|
+
model_id += ":online"
|
|
548
|
+
else:
|
|
549
|
+
if model_id.endswith(":online"):
|
|
550
|
+
model_id = model_id.replace(":online", "")
|
|
551
|
+
return model_id
|
|
552
|
+
|
|
529
553
|
def is_tool_call_allowed(self, mode: str, model: ModelItem) -> bool:
|
|
530
554
|
"""
|
|
531
555
|
Check if native tool call is allowed for model and mode
|
|
@@ -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,6 +1289,8 @@ 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
1295
|
tab.unwrap(node)
|
|
1294
1296
|
self.window.ui.nodes['output'].pop(tab.pid, None)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"app.version": "2.6.
|
|
5
|
-
"updated_at": "2025-09-
|
|
3
|
+
"version": "2.6.54",
|
|
4
|
+
"app.version": "2.6.54",
|
|
5
|
+
"updated_at": "2025-09-18T00:00:00"
|
|
6
6
|
},
|
|
7
7
|
"access.audio.event.speech": false,
|
|
8
8
|
"access.audio.event.speech.disabled": [],
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"api_native_xai": true,
|
|
98
98
|
"api_proxy": "",
|
|
99
99
|
"api_use_responses": true,
|
|
100
|
-
"api_use_responses_llama":
|
|
100
|
+
"api_use_responses_llama": true,
|
|
101
101
|
"app.env": [
|
|
102
102
|
{
|
|
103
103
|
"name": "OLLAMA_API_BASE",
|
|
@@ -417,6 +417,7 @@
|
|
|
417
417
|
"remote_tools.computer_use.env": "",
|
|
418
418
|
"remote_tools.file_search": false,
|
|
419
419
|
"remote_tools.file_search.args": "",
|
|
420
|
+
"remote_tools.global.web_search": true,
|
|
420
421
|
"remote_tools.google.code_interpreter": false,
|
|
421
422
|
"remote_tools.google.url_ctx": false,
|
|
422
423
|
"remote_tools.google.web_search": true,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"app.version": "2.6.
|
|
5
|
-
"updated_at": "2025-09-
|
|
3
|
+
"version": "2.6.54",
|
|
4
|
+
"app.version": "2.6.54",
|
|
5
|
+
"updated_at": "2025-09-18T00:00:00"
|
|
6
6
|
},
|
|
7
7
|
"items": {
|
|
8
8
|
"SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M819-28 701-146q-48 32-103.5 49T480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-62 17-117.5T146-701L27-820l57-57L876-85l-57 57ZM440-162v-78q-33 0-56.5-23.5T360-320v-40L168-552q-3 18-5.5 36t-2.5 36q0 121 79.5 212T440-162Zm374-99-58-58q21-37 32.5-77.5T800-480q0-98-54.5-179T600-776v16q0 33-23.5 56.5T520-680h-80v45L261-814q48-31 103-48.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 61-17.5 116T814-261Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm-40-82v-78q-33 0-56.5-23.5T360-320v-40L168-552q-3 18-5.5 36t-2.5 36q0 121 79.5 212T440-162Zm276-102q41-45 62.5-100.5T800-480q0-98-54.5-179T600-776v16q0 33-23.5 56.5T520-680h-80v80q0 17-11.5 28.5T400-560h-80v80h240q17 0 28.5 11.5T600-440v120h40q26 0 47 15.5t29 40.5Z"/></svg>
|
pygpt_net/data/js/app.js
CHANGED
|
@@ -2082,6 +2082,25 @@
|
|
|
2082
2082
|
// Streaming renderer (no linkify) – hot path
|
|
2083
2083
|
this.MD_STREAM = window.markdownit({ html: false, linkify: false, breaks: true, highlight: () => '' });
|
|
2084
2084
|
|
|
2085
|
+
//allow local file-like schemes in links/images (markdown-it blocks file:// by default).
|
|
2086
|
+
const installLinkValidator = (md) => {
|
|
2087
|
+
const orig = (md && typeof md.validateLink === 'function') ? md.validateLink.bind(md) : null;
|
|
2088
|
+
md.validateLink = (url) => {
|
|
2089
|
+
try {
|
|
2090
|
+
const s = String(url || '').trim().toLowerCase();
|
|
2091
|
+
if (s.startsWith('file:')) return true; // local files
|
|
2092
|
+
if (s.startsWith('qrc:')) return true; // Qt resources
|
|
2093
|
+
if (s.startsWith('bridge:')) return true; // app bridge scheme
|
|
2094
|
+
if (s.startsWith('blob:')) return true; // blobs
|
|
2095
|
+
if (s.startsWith('data:image/')) return true; // inline images
|
|
2096
|
+
} catch (_) {}
|
|
2097
|
+
return orig ? orig(url) : true;
|
|
2098
|
+
};
|
|
2099
|
+
};
|
|
2100
|
+
|
|
2101
|
+
installLinkValidator(this.MD);
|
|
2102
|
+
installLinkValidator(this.MD_STREAM);
|
|
2103
|
+
|
|
2085
2104
|
// SAFETY: disable CommonMark "indented code blocks" unless explicitly enabled.
|
|
2086
2105
|
if (!this.cfg.MD || this.cfg.MD.ALLOW_INDENTED_CODE !== true) {
|
|
2087
2106
|
try { this.MD.block.ruler.disable('code'); } catch (_) {}
|
|
@@ -677,6 +677,7 @@ html_canvas.btn.edit = Quellcode bearbeiten
|
|
|
677
677
|
html_canvas.clear.confirm = HTML-Canvas-Ausgabe löschen?
|
|
678
678
|
icon.audio.input = Audioeingang aktivieren/deaktivieren
|
|
679
679
|
icon.audio.output = Audioausgang aktivieren/deaktivieren
|
|
680
|
+
icon.remote_tool.web = Websuche (ferngesteuertes Werkzeug, kein lokales Werkzeug)
|
|
680
681
|
icon.video.capture = Videoaufnahme von der Kamera aktivieren/deaktivieren
|
|
681
682
|
idx.btn.clear = Index löschen
|
|
682
683
|
idx.btn.index_all = Alles indizieren
|
|
@@ -680,6 +680,7 @@ html_canvas.btn.edit = Edit source
|
|
|
680
680
|
html_canvas.clear.confirm = Clear HTML canvas output?
|
|
681
681
|
icon.audio.input = Enable/disable audio input
|
|
682
682
|
icon.audio.output = Enable/disable audio output
|
|
683
|
+
icon.remote_tool.web = Web Search (remote tool, not a local tool)
|
|
683
684
|
icon.video.capture = Enable/disable camera capture
|
|
684
685
|
idx.btn.clear = Clear index
|
|
685
686
|
idx.btn.index_all = Index all
|
|
@@ -1391,6 +1392,8 @@ settings.render.code_syntax.stream_n_line.desc = Syntax highlight: highlight eve
|
|
|
1391
1392
|
settings.render.engine = Rendering engine
|
|
1392
1393
|
settings.render.memory.limit = Memory Limit
|
|
1393
1394
|
settings.render.memory.limit.desc = Renderer memory limit; set to 0 to disable. If > 0, the app will try to free memory after the limit is reached. Accepted formats: 3.5GB, 2GB, 2048MB, 1_000_000. Minimum: 2GB.
|
|
1395
|
+
settings.render.msg.user.collapse.px = Auto-collapse user message (px)
|
|
1396
|
+
settings.render.msg.user.collapse.px.desc = Auto-collapse user message after N pixels of height, set to 0 to disable auto-collapse
|
|
1394
1397
|
settings.render.open_gl = OpenGL hardware acceleration
|
|
1395
1398
|
settings.render.plain = Disable markdown formatting in output (RAW plain text mode)
|
|
1396
1399
|
settings.render.web.only.desc = WebEngine / Chromium rendering engine only
|
|
@@ -1649,5 +1652,3 @@ vision.capture.manual.captured.success = Image captured from the camera:
|
|
|
1649
1652
|
vision.capture.name.prefix = Camera capture:
|
|
1650
1653
|
vision.capture.options.title = Video capture
|
|
1651
1654
|
vision.checkbox.tooltip = If checked, the vision model is active. It will be automatically activated upon image upload. You can deactivate it in real-time.
|
|
1652
|
-
settings.render.msg.user.collapse.px = Auto-collapse user message (px)
|
|
1653
|
-
settings.render.msg.user.collapse.px.desc = Auto-collapse user message after N pixels of height, set to 0 to disable auto-collapse
|
|
@@ -678,6 +678,7 @@ html_canvas.btn.edit = Editar código fuente
|
|
|
678
678
|
html_canvas.clear.confirm = ¿Limpiar la salida del lienzo HTML?
|
|
679
679
|
icon.audio.input = Activar/desactivar entrada de audio
|
|
680
680
|
icon.audio.output = Activar/desactivar salida de audio
|
|
681
|
+
icon.remote_tool.web = Búsqueda en la Web (herramienta remota, no una herramienta local)
|
|
681
682
|
icon.video.capture = Activar/desactivar captura de vídeo desde la cámara
|
|
682
683
|
idx.btn.clear = Limpiar índice
|
|
683
684
|
idx.btn.index_all = Indexar todo
|