pygpt-net 2.7.8__py3-none-any.whl → 2.7.10__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 +14 -0
- pygpt_net/LICENSE +1 -1
- pygpt_net/__init__.py +3 -3
- pygpt_net/config.py +15 -1
- pygpt_net/controller/chat/common.py +5 -4
- pygpt_net/controller/chat/image.py +3 -3
- pygpt_net/controller/chat/stream.py +76 -41
- pygpt_net/controller/chat/stream_worker.py +3 -3
- pygpt_net/controller/ctx/extra.py +3 -1
- pygpt_net/controller/dialogs/debug.py +37 -8
- pygpt_net/controller/kernel/kernel.py +3 -7
- pygpt_net/controller/lang/custom.py +25 -12
- pygpt_net/controller/lang/lang.py +45 -3
- pygpt_net/controller/lang/mapping.py +15 -2
- pygpt_net/controller/notepad/notepad.py +68 -25
- pygpt_net/controller/presets/editor.py +5 -1
- pygpt_net/controller/presets/presets.py +17 -5
- pygpt_net/controller/realtime/realtime.py +13 -1
- pygpt_net/controller/theme/theme.py +11 -2
- pygpt_net/controller/ui/tabs.py +1 -1
- pygpt_net/core/ctx/output.py +38 -12
- pygpt_net/core/db/database.py +4 -2
- pygpt_net/core/debug/console/console.py +30 -2
- pygpt_net/core/debug/context.py +2 -1
- pygpt_net/core/debug/ui.py +26 -4
- pygpt_net/core/filesystem/filesystem.py +6 -2
- pygpt_net/core/notepad/notepad.py +2 -2
- pygpt_net/core/tabs/tabs.py +79 -19
- pygpt_net/data/config/config.json +4 -3
- pygpt_net/data/config/models.json +37 -22
- pygpt_net/data/config/settings.json +12 -0
- pygpt_net/data/locale/locale.ar.ini +1833 -0
- pygpt_net/data/locale/locale.bg.ini +1833 -0
- pygpt_net/data/locale/locale.cs.ini +1833 -0
- pygpt_net/data/locale/locale.da.ini +1833 -0
- pygpt_net/data/locale/locale.de.ini +4 -1
- pygpt_net/data/locale/locale.en.ini +70 -67
- pygpt_net/data/locale/locale.es.ini +4 -1
- pygpt_net/data/locale/locale.fi.ini +1833 -0
- pygpt_net/data/locale/locale.fr.ini +4 -1
- pygpt_net/data/locale/locale.he.ini +1833 -0
- pygpt_net/data/locale/locale.hi.ini +1833 -0
- pygpt_net/data/locale/locale.hu.ini +1833 -0
- pygpt_net/data/locale/locale.it.ini +4 -1
- pygpt_net/data/locale/locale.ja.ini +1833 -0
- pygpt_net/data/locale/locale.ko.ini +1833 -0
- pygpt_net/data/locale/locale.nl.ini +1833 -0
- pygpt_net/data/locale/locale.no.ini +1833 -0
- pygpt_net/data/locale/locale.pl.ini +5 -2
- pygpt_net/data/locale/locale.pt.ini +1833 -0
- pygpt_net/data/locale/locale.ro.ini +1833 -0
- pygpt_net/data/locale/locale.ru.ini +1833 -0
- pygpt_net/data/locale/locale.sk.ini +1833 -0
- pygpt_net/data/locale/locale.sv.ini +1833 -0
- pygpt_net/data/locale/locale.tr.ini +1833 -0
- pygpt_net/data/locale/locale.uk.ini +4 -1
- pygpt_net/data/locale/locale.zh.ini +4 -1
- pygpt_net/item/notepad.py +8 -2
- pygpt_net/migrations/Version20260121190000.py +25 -0
- pygpt_net/migrations/Version20260122140000.py +25 -0
- pygpt_net/migrations/__init__.py +5 -1
- pygpt_net/preload.py +246 -3
- pygpt_net/provider/api/__init__.py +16 -2
- pygpt_net/provider/api/anthropic/__init__.py +21 -7
- pygpt_net/provider/api/google/__init__.py +21 -7
- pygpt_net/provider/api/google/image.py +89 -2
- pygpt_net/provider/api/google/realtime/client.py +70 -24
- pygpt_net/provider/api/google/realtime/realtime.py +48 -12
- pygpt_net/provider/api/google/video.py +2 -2
- pygpt_net/provider/api/openai/__init__.py +26 -11
- pygpt_net/provider/api/openai/image.py +79 -3
- pygpt_net/provider/api/openai/realtime/realtime.py +26 -6
- pygpt_net/provider/api/openai/responses.py +11 -31
- pygpt_net/provider/api/openai/video.py +2 -2
- pygpt_net/provider/api/x_ai/__init__.py +21 -10
- pygpt_net/provider/api/x_ai/realtime/client.py +185 -146
- pygpt_net/provider/api/x_ai/realtime/realtime.py +30 -15
- pygpt_net/provider/api/x_ai/remote_tools.py +83 -0
- pygpt_net/provider/api/x_ai/tools.py +51 -0
- pygpt_net/provider/core/config/patch.py +12 -1
- pygpt_net/provider/core/model/patch.py +36 -1
- pygpt_net/provider/core/notepad/db_sqlite/storage.py +53 -10
- pygpt_net/tools/agent_builder/ui/dialogs.py +2 -1
- pygpt_net/tools/audio_transcriber/ui/dialogs.py +2 -1
- pygpt_net/tools/code_interpreter/ui/dialogs.py +2 -1
- pygpt_net/tools/html_canvas/ui/dialogs.py +2 -1
- pygpt_net/tools/image_viewer/ui/dialogs.py +3 -5
- pygpt_net/tools/indexer/ui/dialogs.py +2 -1
- pygpt_net/tools/media_player/ui/dialogs.py +2 -1
- pygpt_net/tools/translator/ui/dialogs.py +2 -1
- pygpt_net/tools/translator/ui/widgets.py +6 -2
- pygpt_net/ui/dialog/about.py +2 -2
- pygpt_net/ui/dialog/db.py +2 -1
- pygpt_net/ui/dialog/debug.py +169 -6
- pygpt_net/ui/dialog/logger.py +6 -2
- pygpt_net/ui/dialog/models.py +36 -3
- pygpt_net/ui/dialog/preset.py +5 -1
- pygpt_net/ui/dialog/remote_store.py +2 -1
- pygpt_net/ui/main.py +3 -2
- pygpt_net/ui/widget/dialog/editor_file.py +2 -1
- pygpt_net/ui/widget/lists/debug.py +12 -7
- pygpt_net/ui/widget/option/checkbox.py +2 -8
- pygpt_net/ui/widget/option/combo.py +10 -2
- pygpt_net/ui/widget/textarea/console.py +156 -7
- pygpt_net/ui/widget/textarea/highlight.py +66 -0
- pygpt_net/ui/widget/textarea/input.py +624 -57
- pygpt_net/ui/widget/textarea/notepad.py +294 -27
- {pygpt_net-2.7.8.dist-info → pygpt_net-2.7.10.dist-info}/LICENSE +1 -1
- {pygpt_net-2.7.8.dist-info → pygpt_net-2.7.10.dist-info}/METADATA +16 -64
- {pygpt_net-2.7.8.dist-info → pygpt_net-2.7.10.dist-info}/RECORD +112 -91
- {pygpt_net-2.7.8.dist-info → pygpt_net-2.7.10.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.8.dist-info → pygpt_net-2.7.10.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
2.7.10 (2026-02-03)
|
|
2
|
+
|
|
3
|
+
- Fixed an issue where an avatar could be overwritten when creating a new preset.
|
|
4
|
+
- Fixed an issue where a new context was not created when opening a new tab in the second column.
|
|
5
|
+
- Added prompt history navigation to the input field (Ctrl + Up/Down arrow keys).
|
|
6
|
+
- Added initial image centering when loading the Image Viewer.
|
|
7
|
+
- Added a Mark/Unmark feature to the Notepad widget.
|
|
8
|
+
- Added 18 new languages: Arabic (ar), Bulgarian (bg), Czech (cs), Danish (da), Finnish (fi), Hebrew (he), Hindi (hi), Hungarian (hu), Japanese (ja), Korean (ko), Dutch (nl), Norwegian (no), Portuguese (pt), Romanian (ro), Russian (ru), Slovak (sk), Swedish (sv), Turkish (tr).
|
|
9
|
+
|
|
10
|
+
2.7.9 (2026-01-08)
|
|
11
|
+
|
|
12
|
+
- Improved realtime audio mode.
|
|
13
|
+
- Added xAI provider and Grok support in realtime audio mode.
|
|
14
|
+
|
|
1
15
|
2.7.8 (2026-01-06)
|
|
2
16
|
|
|
3
17
|
- Added the xAI Collections remote tool and integrated collections management into the Remote Vector Stores tool.
|
pygpt_net/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 Marcin Szczygliński
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
6
|
|
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: 2026-
|
|
9
|
+
# Updated Date: 2026-02-03 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
__author__ = "Marcin Szczygliński"
|
|
13
13
|
__copyright__ = "Copyright 2026, Marcin Szczygliński"
|
|
14
14
|
__credits__ = ["Marcin Szczygliński"]
|
|
15
15
|
__license__ = "MIT"
|
|
16
|
-
__version__ = "2.7.
|
|
17
|
-
__build__ = "2026-
|
|
16
|
+
__version__ = "2.7.10"
|
|
17
|
+
__build__ = "2026-02-03"
|
|
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/config.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:
|
|
9
|
+
# Updated Date: 2026.01.20 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -25,6 +25,20 @@ from pygpt_net.core.types.console import Color
|
|
|
25
25
|
_RE_VERSION = re.compile(r'__version__\s*=\s*[\'"]([^\'"]*)[\'"]')
|
|
26
26
|
_RE_BUILD = re.compile(r'__build__\s*=\s*[\'"]([^\'"]*)[\'"]')
|
|
27
27
|
|
|
28
|
+
def quick_get_config_value(key: str, default: any = None) -> any:
|
|
29
|
+
"""
|
|
30
|
+
Quick get config value without initializing the whole app
|
|
31
|
+
|
|
32
|
+
:param key: key
|
|
33
|
+
:param default: default value
|
|
34
|
+
:return: value
|
|
35
|
+
"""
|
|
36
|
+
config = Config()
|
|
37
|
+
workdir = config.prepare_workdir()
|
|
38
|
+
config.set_workdir(workdir)
|
|
39
|
+
config.load_config(all=False)
|
|
40
|
+
return config.get(key, default)
|
|
41
|
+
|
|
28
42
|
|
|
29
43
|
class Config:
|
|
30
44
|
CONFIG_DIR = 'pygpt-net'
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.21 13:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -322,9 +322,10 @@ class Common:
|
|
|
322
322
|
|
|
323
323
|
def stop_client(self):
|
|
324
324
|
"""Stop all clients"""
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
try:
|
|
326
|
+
self.window.core.api.stop()
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
328
329
|
|
|
329
330
|
def check_api_key(
|
|
330
331
|
self,
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.23 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -84,11 +84,11 @@ class Image:
|
|
|
84
84
|
if core.config.get("video.remix"):
|
|
85
85
|
tmp_video_id = item.extra.get('video_id')
|
|
86
86
|
if video_id is None and tmp_video_id:
|
|
87
|
-
video_id = tmp_video_id
|
|
87
|
+
video_id = core.filesystem.to_workdir(tmp_video_id, auto_prefix=False)
|
|
88
88
|
if core.config.get("img.remix"):
|
|
89
89
|
tmp_image_id = item.extra.get('image_id')
|
|
90
90
|
if image_id is None and tmp_image_id:
|
|
91
|
-
image_id = tmp_image_id
|
|
91
|
+
image_id = core.filesystem.to_workdir(tmp_image_id)
|
|
92
92
|
if image_id and video_id:
|
|
93
93
|
break
|
|
94
94
|
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.22 04:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
from typing import Optional
|
|
12
|
+
from typing import Optional, Any
|
|
13
13
|
|
|
14
14
|
from PySide6.QtCore import Slot, QObject
|
|
15
15
|
|
|
@@ -29,16 +29,27 @@ class Stream(QObject):
|
|
|
29
29
|
"""
|
|
30
30
|
super().__init__()
|
|
31
31
|
self.window = window
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
self.instance = None # cached get renderer instance method
|
|
33
|
+
self.pids = {} # {pid -> {data}}, workers are tracked per PID
|
|
34
|
+
|
|
35
|
+
def get_pid_by_ctx(self, ctx: CtxItem) -> Optional[int]:
|
|
36
|
+
"""
|
|
37
|
+
Get process ID by context item
|
|
38
|
+
|
|
39
|
+
:param ctx: Context item
|
|
40
|
+
:return: Process ID or None
|
|
41
|
+
"""
|
|
42
|
+
if ctx and ctx.meta:
|
|
43
|
+
return self.window.core.ctx.output.get_pid(ctx.meta)
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def get_pid_ids(self) -> list[int]:
|
|
47
|
+
"""
|
|
48
|
+
Get all active process IDs
|
|
49
|
+
|
|
50
|
+
:return: List of process IDs
|
|
51
|
+
"""
|
|
52
|
+
return list(self.pids.keys())
|
|
42
53
|
|
|
43
54
|
def append(
|
|
44
55
|
self,
|
|
@@ -61,13 +72,20 @@ class Stream(QObject):
|
|
|
61
72
|
:param context: Optional BridgeContext for additional context
|
|
62
73
|
:param extra: Additional data to pass to the stream
|
|
63
74
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
self.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
pid = self.get_pid_by_ctx(ctx)
|
|
76
|
+
if pid is None:
|
|
77
|
+
return # abort streaming if no PID found
|
|
78
|
+
if pid not in self.pids:
|
|
79
|
+
self.pids[pid] = {}
|
|
80
|
+
|
|
81
|
+
pid_data = self.pids[pid]
|
|
82
|
+
pid_data["ctx"] = ctx
|
|
83
|
+
pid_data["mode"] = mode
|
|
84
|
+
pid_data["is_response"] = is_response
|
|
85
|
+
pid_data["reply"] = reply
|
|
86
|
+
pid_data["internal"] = internal
|
|
87
|
+
pid_data["context"] = context
|
|
88
|
+
pid_data["extra"] = extra if extra is not None else {}
|
|
71
89
|
|
|
72
90
|
# cache the get renderer instance method
|
|
73
91
|
if self.instance is None:
|
|
@@ -79,10 +97,10 @@ class Stream(QObject):
|
|
|
79
97
|
worker.signals.errorOccurred.connect(self.handleError)
|
|
80
98
|
worker.signals.end.connect(self.handleEnd)
|
|
81
99
|
worker.signals.chunk.connect(self.handleChunk)
|
|
82
|
-
ctx.stream = None
|
|
83
|
-
self.worker = worker
|
|
100
|
+
ctx.stream = None # clear reference to generator
|
|
84
101
|
|
|
85
|
-
|
|
102
|
+
pid_data["worker"] = worker # keep reference to avoid GC, per PID
|
|
103
|
+
self.window.core.debug.info(f"[chat] Stream begin... PID={pid}")
|
|
86
104
|
self.window.threadpool.start(worker)
|
|
87
105
|
|
|
88
106
|
@Slot(object)
|
|
@@ -92,31 +110,41 @@ class Stream(QObject):
|
|
|
92
110
|
|
|
93
111
|
:param ctx: Context item
|
|
94
112
|
"""
|
|
113
|
+
pid = self.get_pid_by_ctx(ctx)
|
|
114
|
+
if pid is None or pid not in self.pids:
|
|
115
|
+
return # abort if no PID found or not tracked
|
|
116
|
+
pid_data = self.pids[pid]
|
|
117
|
+
|
|
95
118
|
controller = self.window.controller
|
|
96
119
|
controller.ui.update_tokens()
|
|
120
|
+
mode = pid_data["mode"]
|
|
97
121
|
|
|
98
|
-
data = {
|
|
122
|
+
data = {
|
|
123
|
+
"meta": pid_data["ctx"].meta,
|
|
124
|
+
"ctx": pid_data["ctx"]
|
|
125
|
+
}
|
|
99
126
|
event = RenderEvent(RenderEvent.STREAM_END, data)
|
|
100
127
|
self.window.dispatch(event)
|
|
101
128
|
controller.chat.output.handle_after(
|
|
102
129
|
ctx=ctx,
|
|
103
|
-
mode=
|
|
130
|
+
mode=mode,
|
|
104
131
|
stream=True,
|
|
105
132
|
)
|
|
106
133
|
|
|
107
|
-
if
|
|
134
|
+
if mode == MODE_ASSISTANT:
|
|
108
135
|
controller.assistant.threads.handle_output_message_after_stream(ctx)
|
|
109
136
|
else:
|
|
110
|
-
if
|
|
137
|
+
if pid_data["is_response"]:
|
|
111
138
|
controller.chat.response.post_handle(
|
|
112
139
|
ctx=ctx,
|
|
113
|
-
mode=
|
|
140
|
+
mode=mode,
|
|
114
141
|
stream=True,
|
|
115
|
-
reply=
|
|
116
|
-
internal=
|
|
142
|
+
reply=pid_data["reply"],
|
|
143
|
+
internal=pid_data["internal"],
|
|
117
144
|
)
|
|
118
145
|
|
|
119
|
-
|
|
146
|
+
pid_data["worker"] = None # release worker reference
|
|
147
|
+
del self.pids[pid] # remove PID tracking
|
|
120
148
|
|
|
121
149
|
@Slot(object, str, bool)
|
|
122
150
|
def handleChunk(
|
|
@@ -150,26 +178,33 @@ class Stream(QObject):
|
|
|
150
178
|
"""
|
|
151
179
|
self.window.dispatch(event)
|
|
152
180
|
|
|
153
|
-
@Slot(object)
|
|
154
|
-
def handleError(self, error):
|
|
181
|
+
@Slot(object, object)
|
|
182
|
+
def handleError(self, ctx: CtxItem, error: Any):
|
|
155
183
|
"""
|
|
156
184
|
Slot for handling stream errors
|
|
157
185
|
|
|
186
|
+
:param ctx: Context item
|
|
158
187
|
:param error: Exception or error message
|
|
159
188
|
"""
|
|
189
|
+
pid = self.get_pid_by_ctx(ctx)
|
|
190
|
+
if pid is None or pid not in self.pids:
|
|
191
|
+
return # abort if no PID found or not tracked
|
|
192
|
+
pid_data = self.pids[pid]
|
|
193
|
+
|
|
160
194
|
self.window.core.debug.log(error)
|
|
161
|
-
if
|
|
162
|
-
if not isinstance(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
self.window.controller.chat.response.failed(
|
|
195
|
+
if pid_data["is_response"]:
|
|
196
|
+
if not isinstance(pid_data["extra"], dict):
|
|
197
|
+
pid_data["extra"] = {}
|
|
198
|
+
pid_data["extra"]["error"] = error
|
|
199
|
+
self.window.controller.chat.response.failed(pid_data["context"], pid_data["extra"])
|
|
166
200
|
self.window.controller.chat.response.post_handle(
|
|
167
|
-
ctx=
|
|
168
|
-
mode=
|
|
201
|
+
ctx=pid_data["ctx"],
|
|
202
|
+
mode=pid_data["mode"],
|
|
169
203
|
stream=True,
|
|
170
|
-
reply=
|
|
171
|
-
internal=
|
|
204
|
+
reply=pid_data["reply"],
|
|
205
|
+
internal=pid_data["internal"],
|
|
172
206
|
)
|
|
207
|
+
# TODO: remove PID from tracking on error?
|
|
173
208
|
|
|
174
209
|
def log(self, data: object):
|
|
175
210
|
"""
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.22 04:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import io
|
|
@@ -39,7 +39,7 @@ class WorkerSignals(QObject):
|
|
|
39
39
|
- `chunk`: CtxItem, chunk str, begin bool
|
|
40
40
|
"""
|
|
41
41
|
end = Signal(object)
|
|
42
|
-
errorOccurred = Signal(Exception)
|
|
42
|
+
errorOccurred = Signal(object, Exception)
|
|
43
43
|
eventReady = Signal(object)
|
|
44
44
|
chunk = Signal(object, str, bool) # CtxItem, chunk, begin
|
|
45
45
|
|
|
@@ -423,7 +423,7 @@ class StreamWorker(QRunnable):
|
|
|
423
423
|
|
|
424
424
|
# Emit error and end
|
|
425
425
|
if state.error:
|
|
426
|
-
emit_error(state.error)
|
|
426
|
+
emit_error(ctx, state.error)
|
|
427
427
|
ctx.msg_id = None
|
|
428
428
|
# clear response_id on error - this prevents no response_id in API on next call
|
|
429
429
|
# prev messages will be sent again, new response_id will be generated
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.22 06:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtWidgets import QApplication
|
|
@@ -245,6 +245,8 @@ class Extra:
|
|
|
245
245
|
current_item = self.window.core.ctx.get_item_by_id(item_id)
|
|
246
246
|
if prev_item is not None and current_item is not None:
|
|
247
247
|
prev_item.output += "\n\n" + current_item.output
|
|
248
|
+
if current_item.msg_id is not None:
|
|
249
|
+
prev_item.msg_id = current_item.msg_id # update msg_id to the latest
|
|
248
250
|
self.window.core.ctx.update_item(prev_item)
|
|
249
251
|
self.window.core.ctx.remove_item(current_item.id)
|
|
250
252
|
self.window.controller.ctx.refresh()
|
|
@@ -6,9 +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:
|
|
9
|
+
# Updated Date: 2026.01.21 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
+
import time
|
|
12
13
|
from typing import Any
|
|
13
14
|
|
|
14
15
|
from PySide6.QtCore import Qt
|
|
@@ -62,6 +63,8 @@ class Debug:
|
|
|
62
63
|
'ui': UIDebug(self.window)
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
self.is_realtime = False # real-time data update
|
|
67
|
+
|
|
65
68
|
# prepare debug ids
|
|
66
69
|
self.ids = self.workers.keys()
|
|
67
70
|
self.models = {}
|
|
@@ -77,12 +80,30 @@ class Debug:
|
|
|
77
80
|
self.active[id] = False
|
|
78
81
|
self.idx[id] = 0
|
|
79
82
|
|
|
83
|
+
def refresh(self, id: str):
|
|
84
|
+
"""
|
|
85
|
+
Refresh debug data
|
|
86
|
+
|
|
87
|
+
:param id: debug id
|
|
88
|
+
"""
|
|
89
|
+
if id in self.initialized:
|
|
90
|
+
self.initialized[id] = False
|
|
91
|
+
|
|
80
92
|
def begin(self, id: str):
|
|
81
93
|
"""
|
|
82
94
|
Begin debug data
|
|
83
95
|
|
|
84
96
|
:param id: debug id
|
|
85
97
|
"""
|
|
98
|
+
if self.initialized[id] and not self.is_realtime:
|
|
99
|
+
if self.is_active(id):
|
|
100
|
+
return # skip if already initialized and realtime disabled
|
|
101
|
+
|
|
102
|
+
upd_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
|
103
|
+
self.window.ui.debug[id].last_update_label.setText(
|
|
104
|
+
f"Last update: {upd_time}"
|
|
105
|
+
)
|
|
106
|
+
|
|
86
107
|
model = self.models.get(id, None)
|
|
87
108
|
dialog = self.window.ui.debug[id]
|
|
88
109
|
dialog.setModel(model)
|
|
@@ -92,18 +113,28 @@ class Debug:
|
|
|
92
113
|
if id not in self.counters or self.counters[id] != model.rowCount():
|
|
93
114
|
model.removeRows(0, model.rowCount())
|
|
94
115
|
self.initialized[id] = False
|
|
95
|
-
dialog.setUpdatesEnabled(True)
|
|
96
116
|
self.idx[id] = 0
|
|
97
117
|
|
|
118
|
+
def set_realtime(self, id: str, enabled: bool):
|
|
119
|
+
"""
|
|
120
|
+
Set realtime mode
|
|
121
|
+
|
|
122
|
+
:param id: debug id
|
|
123
|
+
:param enabled: enable realtime
|
|
124
|
+
"""
|
|
125
|
+
self.is_realtime = enabled
|
|
126
|
+
|
|
98
127
|
def end(self, id: str):
|
|
99
128
|
"""
|
|
100
129
|
End debug data
|
|
101
130
|
|
|
102
131
|
:param id: debug id
|
|
103
132
|
"""
|
|
133
|
+
dialog = self.window.ui.debug[id]
|
|
104
134
|
self.counters[id] = self.idx[id]
|
|
105
135
|
self.initialized[id] = True
|
|
106
136
|
self.window.ui.debug[id].on_data_end()
|
|
137
|
+
dialog.setUpdatesEnabled(True)
|
|
107
138
|
|
|
108
139
|
def add(self, id: str, k: str, v: Any):
|
|
109
140
|
"""
|
|
@@ -113,23 +144,21 @@ class Debug:
|
|
|
113
144
|
:param k: key
|
|
114
145
|
:param v: value
|
|
115
146
|
"""
|
|
116
|
-
dialog = self.window.ui.debug[id]
|
|
117
|
-
dialog.setUpdatesEnabled(False)
|
|
118
147
|
model = self.models.get(id, None)
|
|
119
148
|
if self.initialized[id] is False:
|
|
120
149
|
idx = model.rowCount()
|
|
121
150
|
model.insertRow(idx)
|
|
122
151
|
model.setData(model.index(idx, self.DBG_KEY), k)
|
|
123
152
|
model.setData(model.index(idx, self.DBG_VALUE), v)
|
|
153
|
+
self.idx[id] += 1
|
|
124
154
|
else:
|
|
125
155
|
for idx in range(0, model.rowCount()):
|
|
156
|
+
self.idx[id] += 1
|
|
126
157
|
if model.index(idx, self.DBG_KEY).data() == k:
|
|
158
|
+
if model.index(idx, self.DBG_VALUE).data() == v:
|
|
159
|
+
continue # no changes, skip update
|
|
127
160
|
model.setData(model.index(idx, self.DBG_VALUE), v)
|
|
128
|
-
self.idx[id] += 1
|
|
129
|
-
dialog.setUpdatesEnabled(True)
|
|
130
161
|
return
|
|
131
|
-
self.idx[id] += 1
|
|
132
|
-
dialog.setUpdatesEnabled(True)
|
|
133
162
|
|
|
134
163
|
def get_ids(self) -> list:
|
|
135
164
|
"""
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.21 13:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import threading
|
|
@@ -404,12 +404,8 @@ class Kernel:
|
|
|
404
404
|
return threading.current_thread() is threading.main_thread()
|
|
405
405
|
|
|
406
406
|
def close_clients(self):
|
|
407
|
-
"""
|
|
408
|
-
Close all active clients associated with the kernel.
|
|
409
|
-
"""
|
|
410
|
-
w = self.window
|
|
407
|
+
"""Safe close all active clients"""
|
|
411
408
|
try:
|
|
412
|
-
|
|
413
|
-
w.core.api.google.safe_close()
|
|
409
|
+
self.window.core.api.close()
|
|
414
410
|
except Exception:
|
|
415
411
|
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:
|
|
9
|
+
# Updated Date: 2026.01.21 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -23,6 +23,8 @@ from pygpt_net.core.types import (
|
|
|
23
23
|
MODE_LLAMA_INDEX,
|
|
24
24
|
MODE_VISION,
|
|
25
25
|
MODE_RESEARCH,
|
|
26
|
+
MODE_AGENT_OPENAI,
|
|
27
|
+
MODE_COMPUTER,
|
|
26
28
|
)
|
|
27
29
|
from pygpt_net.utils import trans
|
|
28
30
|
|
|
@@ -52,17 +54,28 @@ class Custom:
|
|
|
52
54
|
self.window.ui.config['assistant']['tool.code_interpreter'].box.setText(
|
|
53
55
|
trans('assistant.tool.code_interpreter')
|
|
54
56
|
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
self.window.ui.config['preset'][
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
self.window.ui.config['preset'][
|
|
61
|
-
self.window.ui.config['preset'][
|
|
62
|
-
self.window.ui.config['preset'][
|
|
63
|
-
self.window.ui.config['preset'][
|
|
64
|
-
self.window.ui.config['preset'][
|
|
65
|
-
self.window.ui.config['preset'][
|
|
57
|
+
|
|
58
|
+
# preset editor
|
|
59
|
+
self.window.ui.config['preset'][MODE_CHAT].setText(trans("preset.chat"))
|
|
60
|
+
self.window.ui.config['preset'][MODE_COMPLETION].setText(trans("preset.completion"))
|
|
61
|
+
self.window.ui.config['preset'][MODE_IMAGE].setText(trans("preset.img"))
|
|
62
|
+
# self.window.ui.config['preset'][MODE_VISION].setText(trans("preset.vision"))
|
|
63
|
+
# self.window.ui.config['preset'][MODE_LANGCHAIN].setText(trans("preset.langchain"))
|
|
64
|
+
self.window.ui.config['preset'][MODE_LLAMA_INDEX].setText(trans("preset.llama_index"))
|
|
65
|
+
self.window.ui.config['preset'][MODE_AGENT].setText(trans("preset.agent"))
|
|
66
|
+
self.window.ui.config['preset'][MODE_AGENT_LLAMA].setText(trans("preset.agent_llama"))
|
|
67
|
+
self.window.ui.config['preset'][MODE_AGENT_OPENAI].setText(trans("preset.agent_openai"))
|
|
68
|
+
self.window.ui.config['preset'][MODE_EXPERT].setText(trans("preset.expert"))
|
|
69
|
+
self.window.ui.config['preset'][MODE_AUDIO].setText(trans("preset.audio"))
|
|
70
|
+
self.window.ui.config['preset'][MODE_RESEARCH].setText(trans("preset.research"))
|
|
71
|
+
self.window.ui.config['preset'][MODE_COMPUTER].setText(trans("preset.computer"))
|
|
72
|
+
self.window.ui.config['preset']["ai_personalize"].setText(trans("preset.ai_personalize"))
|
|
73
|
+
|
|
74
|
+
self.window.ui.tabs['preset.editor.tabs'].setTabText(0, trans("preset.tab.general"))
|
|
75
|
+
self.window.ui.tabs['preset.editor.tabs'].setTabText(1, trans("preset.tab.personalize"))
|
|
76
|
+
self.window.ui.tabs['preset.editor.tabs'].setTabText(2, trans("preset.tab.experts"))
|
|
77
|
+
self.window.ui.tabs['preset.editor.tabs'].setTabText(3, trans("preset.tab.remote_tools"))
|
|
78
|
+
|
|
66
79
|
|
|
67
80
|
self.window.ui.config['global']['img_raw'].setText(trans("img.raw"))
|
|
68
81
|
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.22 18:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtGui import QAction, QActionGroup
|
|
@@ -20,6 +20,36 @@ from .settings import Settings
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class Lang:
|
|
23
|
+
|
|
24
|
+
LANG_MAPPING = {
|
|
25
|
+
"en": "English (default)",
|
|
26
|
+
"pl": "Polski",
|
|
27
|
+
"de": "Deutsch",
|
|
28
|
+
"es": "Español",
|
|
29
|
+
"fr": "Français",
|
|
30
|
+
"it": "Italiano",
|
|
31
|
+
"uk": "Українська",
|
|
32
|
+
"ru": "Русский",
|
|
33
|
+
"zh": "中文",
|
|
34
|
+
"ja": "日本語",
|
|
35
|
+
"ar": "العربية",
|
|
36
|
+
"pt": "Português",
|
|
37
|
+
"hi": "हिन्दी",
|
|
38
|
+
"ko": "한국어",
|
|
39
|
+
"tr": "Türkçe",
|
|
40
|
+
"he": "עברית",
|
|
41
|
+
"nl": "Nederlands",
|
|
42
|
+
"sv": "Svenska",
|
|
43
|
+
"fi": "Suomi",
|
|
44
|
+
"no": "Norsk",
|
|
45
|
+
"da": "Dansk",
|
|
46
|
+
"cs": "Čeština",
|
|
47
|
+
"sk": "Slovenčina",
|
|
48
|
+
"bg": "Български",
|
|
49
|
+
"hu": "Magyar",
|
|
50
|
+
"ro": "Română",
|
|
51
|
+
}
|
|
52
|
+
|
|
23
53
|
def __init__(self, window=None):
|
|
24
54
|
"""
|
|
25
55
|
Language switch controller
|
|
@@ -56,11 +86,21 @@ class Lang:
|
|
|
56
86
|
menu_lang = menu['lang']
|
|
57
87
|
menu_root = menu['menu.lang']
|
|
58
88
|
for lang in langs:
|
|
59
|
-
|
|
89
|
+
title = self.LANG_MAPPING.get(lang, "")
|
|
90
|
+
act_title = lang.upper() + (" - " + title if title else "")
|
|
91
|
+
act = QAction(act_title, w, checkable=True)
|
|
60
92
|
act.setData(lang)
|
|
61
93
|
menu_lang[lang] = act
|
|
94
|
+
|
|
95
|
+
# sort by code, but put 'en' first
|
|
96
|
+
sorted_langs = sorted(menu_lang.items(), key=lambda x: (x[0] != 'en', x[0]))
|
|
97
|
+
menu_lang = dict(sorted_langs)
|
|
98
|
+
|
|
99
|
+
for act in menu_lang.values():
|
|
62
100
|
self._lang_group.addAction(act)
|
|
63
101
|
menu_root.addAction(act)
|
|
102
|
+
if act.data() == 'en':
|
|
103
|
+
menu_root.addSeparator()
|
|
64
104
|
self.loaded = True
|
|
65
105
|
self.update()
|
|
66
106
|
|
|
@@ -115,4 +155,6 @@ class Lang:
|
|
|
115
155
|
def reload(self):
|
|
116
156
|
"""Reload language"""
|
|
117
157
|
self.reload_config()
|
|
118
|
-
self.setup()
|
|
158
|
+
self.setup()
|
|
159
|
+
current = self.window.core.config.get('lang')
|
|
160
|
+
self.toggle(current)
|