Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ urllib3 = "2.5.0"
wikipedia = "^1.4.0"
xai-sdk = "^1.5.0"
youtube-transcript-api = "^0.6.3"
openwakeword = {version = "^0.6.0", optional = true}
pycaw = {version = "^20240210", optional = true}

[tool.poetry.group.dev.dependencies]
pyinstaller = "^6.4.0"
Expand Down
6 changes: 6 additions & 0 deletions src/pygpt_net/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ def run(**kwargs):
from pygpt_net.plugin.mcp import Plugin as MCPPlugin
from pygpt_net.plugin.wolfram import Plugin as WolframPlugin
from pygpt_net.plugin.osm import Plugin as OSMPlugin
from pygpt_net.plugin.wake_word import Plugin as WakeWordPlugin
from pygpt_net.plugin.cmd_app_launcher import Plugin as AppLauncherPlugin
from pygpt_net.plugin.assistant_mode import Plugin as AssistantModePlugin

# agents (Llama-index)
from pygpt_net.provider.agents.llama_index.legacy.openai_assistant import OpenAIAssistantAgent
Expand Down Expand Up @@ -465,6 +468,9 @@ def run(**kwargs):
launcher.add_plugin(MCPPlugin())
launcher.add_plugin(WolframPlugin())
launcher.add_plugin(OSMPlugin())
launcher.add_plugin(WakeWordPlugin())
launcher.add_plugin(AppLauncherPlugin())
launcher.add_plugin(AssistantModePlugin())

# register custom plugins
plugins = kwargs.get('plugins', None)
Expand Down
16 changes: 10 additions & 6 deletions src/pygpt_net/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,17 +516,21 @@ def load_config(self, all: bool = True):

:param all: load all configs
"""
self.data = self.provider.load(all)
if self.data is not None:
self.data = dict(sorted(self.data.items(), key=itemgetter(0)))
loaded = self.provider.load(all)
if loaded is None:
self.data = {}
else:
self.data = dict(sorted(loaded.items(), key=itemgetter(0)))

def load_base_config(self):
"""
Load app config from JSON file
"""
self.data_base = self.provider.load_base()
if self.data_base is not None:
self.data_base = dict(sorted(self.data_base.items(), key=itemgetter(0)))
loaded_base = self.provider.load_base()
if loaded_base is None:
self.data_base = {}
else:
self.data_base = dict(sorted(loaded_base.items(), key=itemgetter(0)))
self.initialized_base = True

def from_base_config(self):
Expand Down
8 changes: 3 additions & 5 deletions src/pygpt_net/controller/plugins/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ def __init__(self, window=None):
self.height = 500

def setup(self):
"""Set up plugin settings"""
idx = None
if 'plugin.settings' in self.window.ui.tabs:
idx = self.window.ui.tabs['plugin.settings'].currentIndex()
self.window.plugin_settings.setup(idx)
"""Set up plugin settings (skeleton now, full build deferred)"""
self.window.plugin_settings.setup_skeleton()
self.config_initialized = False

def toggle_editor(self):
"""Toggle plugin settings dialog"""
Expand Down
12 changes: 7 additions & 5 deletions src/pygpt_net/core/profile/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,16 @@ def load(self):
"""Load profiles"""
f = os.path.join(self.base_workdir, self.PROFILE_FILE)
if os.path.exists(f):
with open(f, 'r', encoding='utf-8') as f:
try:
data = json.load(f)
try:
with open(f, 'r', encoding='utf-8') as fh:
data = json.load(fh)
self.current = data['current']
self.profiles = data['profiles']
self.initialized = True
except Exception as e:
print('CRITICAL: Error loading profile file:', e)
except FileNotFoundError:
pass # race condition: file removed between exists() and open()
except Exception as e:
print('CRITICAL: Error loading profile file:', e)

def save(self):
"""Save profiles"""
Expand Down
12 changes: 12 additions & 0 deletions src/pygpt_net/plugin/assistant_mode/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ================================================== #
# This file is a part of PYGPT package #
# Website: https://pygpt.net #
# GitHub: https://github.com/szczyglis-dev/py-gpt #
# MIT License #
# Created By : PYGPT Contributors #
# Updated Date: 2026.03.11 00:00:00 #
# ================================================== #

from .plugin import Plugin
97 changes: 97 additions & 0 deletions src/pygpt_net/plugin/assistant_mode/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ================================================== #
# This file is a part of PYGPT package #
# Website: https://pygpt.net #
# GitHub: https://github.com/szczyglis-dev/py-gpt #
# MIT License #
# Created By : PYGPT Contributors #
# Updated Date: 2026.03.11 00:00:00 #
# ================================================== #

from pygpt_net.plugin.base.config import BaseConfig, BasePlugin


class Config(BaseConfig):
def __init__(self, plugin: BasePlugin = None, *args, **kwargs):
super(Config, self).__init__(plugin)
self.plugin = plugin

def from_defaults(self, plugin: BasePlugin = None):
"""
Set default options for plugin

:param plugin: plugin instance
"""
plugin.add_option(
"auto_listen_after_response",
type="bool",
value=True,
label="Auto-listen after response",
description="Automatically start listening for voice input after the AI finishes "
"speaking its response. Creates a continuous conversation loop. Default: True",
)
plugin.add_option(
"require_wake_word_each_turn",
type="bool",
value=False,
label="Require wake word each turn",
description="If enabled, the user must say the wake word before each command. "
"If disabled, after the first wake word the assistant stays in "
"continuous conversation mode until a stop word is detected. Default: False",
)
plugin.add_option(
"conversation_timeout",
type="int",
value=30,
label="Conversation timeout (seconds)",
description="If no input is received within this time, the assistant goes back to "
"wake word listening mode. Set to 0 to disable. Default: 30",
min=0,
max=120,
slider=True,
tooltip="Conversation timeout in seconds, default: 30, 0 = disabled",
)
plugin.add_option(
"stop_words",
type="text",
value="goodbye, bye, stop listening, that's all, go to sleep",
label="Stop words",
description="Phrases that end the conversation loop and return to wake word mode. "
"Separate with commas.",
)
plugin.add_option(
"greeting_enabled",
type="bool",
value=True,
label="Greeting on activation",
description="Speak a short greeting when the assistant is activated by wake word. "
"Default: True",
)
plugin.add_option(
"greeting_text",
type="text",
value="Yes?",
label="Greeting text",
description="Text to speak when the assistant is activated. Default: Yes?",
)
plugin.add_option(
"auto_enable_plugins",
type="bool",
value=True,
label="Auto-enable required plugins",
description="Automatically enable Wake Word, Audio Input, and Audio Output plugins "
"when Assistant Mode is enabled. Default: True",
)
plugin.add_option(
"response_delay",
type="float",
value=0.5,
label="Post-response delay (seconds)",
description="Delay after audio response finishes before listening again. "
"Prevents the assistant from hearing its own voice. Default: 0.5",
min=0.0,
max=3.0,
slider=True,
multiplier=10,
)
Loading