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
|
@@ -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.07 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import json
|
|
@@ -27,7 +27,7 @@ class Realtime:
|
|
|
27
27
|
|
|
28
28
|
def __init__(self, window=None):
|
|
29
29
|
"""
|
|
30
|
-
|
|
30
|
+
xAI API realtime controller
|
|
31
31
|
|
|
32
32
|
:param window: Window instance
|
|
33
33
|
"""
|
|
@@ -68,15 +68,11 @@ class Realtime:
|
|
|
68
68
|
self.handler.set_debug(is_debug)
|
|
69
69
|
|
|
70
70
|
# tools
|
|
71
|
-
tools =
|
|
72
|
-
'''
|
|
73
|
-
tools = self.window.core.api.xai.tools.prepare(model, context.external_functions)
|
|
74
|
-
'''
|
|
71
|
+
tools = self.window.core.api.xai.tools.prepare_realtime(context.external_functions)
|
|
75
72
|
|
|
76
73
|
# remote tools
|
|
77
74
|
remote_tools = []
|
|
78
|
-
|
|
79
|
-
remote_tools = self.window.core.api.openai.remote_tools.append_to_tools(
|
|
75
|
+
remote_tools = self.window.core.api.xai.remote.append_to_tools(
|
|
80
76
|
mode=context.mode,
|
|
81
77
|
model=model,
|
|
82
78
|
stream=context.stream,
|
|
@@ -84,7 +80,6 @@ class Realtime:
|
|
|
84
80
|
tools=remote_tools,
|
|
85
81
|
preset=context.preset,
|
|
86
82
|
)
|
|
87
|
-
'''
|
|
88
83
|
|
|
89
84
|
# handle sub-reply (tool results from tool calls)
|
|
90
85
|
if context.ctx.internal:
|
|
@@ -107,6 +102,31 @@ class Realtime:
|
|
|
107
102
|
self.handler.update_ctx(context.ctx)
|
|
108
103
|
return True # do not start new session, just send tool results
|
|
109
104
|
|
|
105
|
+
# Resolve last session ID from history only (no fallback)
|
|
106
|
+
last_session_id = extract_last_session_id(context.history) if context.history else None
|
|
107
|
+
if is_debug:
|
|
108
|
+
print("[realtime session] Last ID", last_session_id)
|
|
109
|
+
|
|
110
|
+
# Enforce clean state rules prior to any live updates:
|
|
111
|
+
# - If there is no history: always reset live session to start fresh.
|
|
112
|
+
# - If history exists but has no resumable handle: close any active session to avoid continuation.
|
|
113
|
+
try:
|
|
114
|
+
history_len = len(context.history) if context.history else 0
|
|
115
|
+
except Exception:
|
|
116
|
+
history_len = 0
|
|
117
|
+
|
|
118
|
+
if history_len == 0:
|
|
119
|
+
if self.handler.is_session_active():
|
|
120
|
+
self.handler.close_session_sync()
|
|
121
|
+
try:
|
|
122
|
+
if context.ctx and isinstance(context.ctx.extra, dict):
|
|
123
|
+
context.ctx.extra.pop("rt_session_id", None)
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
last_session_id = None # force new session
|
|
127
|
+
elif not last_session_id and self.handler.is_session_active():
|
|
128
|
+
self.handler.close_session_sync()
|
|
129
|
+
|
|
110
130
|
# update auto-turn in active session
|
|
111
131
|
if (self.handler.is_session_active()
|
|
112
132
|
and (auto_turn != self.prev_auto_turn
|
|
@@ -121,13 +141,8 @@ class Realtime:
|
|
|
121
141
|
self.window.update_status(trans("speech.listening"))
|
|
122
142
|
return True # do not send new request if session is active
|
|
123
143
|
|
|
124
|
-
# Last session ID
|
|
125
|
-
last_session_id = extract_last_session_id(context.history)
|
|
126
|
-
if is_debug:
|
|
127
|
-
print("[realtime session] Last ID", last_session_id)
|
|
128
|
-
|
|
129
144
|
# Voice
|
|
130
|
-
voice = "
|
|
145
|
+
voice = "Ara"
|
|
131
146
|
try:
|
|
132
147
|
v = self.window.core.plugins.get_option("audio_output", "xai_tts_voice")
|
|
133
148
|
if v:
|
|
@@ -28,6 +28,7 @@ except Exception:
|
|
|
28
28
|
x_x_search = None
|
|
29
29
|
x_code_execution = None
|
|
30
30
|
x_mcp = None
|
|
31
|
+
x_collections_search = None
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class Remote:
|
|
@@ -42,6 +43,9 @@ class Remote:
|
|
|
42
43
|
- Builds xAI SDK tool objects for web_search, x_search, code_execution, MCP.
|
|
43
44
|
- Returns include flags, max_turns and use_encrypted_content settings.
|
|
44
45
|
|
|
46
|
+
Realtime (WebSocket) tools builder for xAI:
|
|
47
|
+
- Produces Realtime-compatible tool descriptors to be attached to session.update/response.create.
|
|
48
|
+
|
|
45
49
|
:param window: Window instance
|
|
46
50
|
"""
|
|
47
51
|
self.window = window
|
|
@@ -269,6 +273,85 @@ class Remote:
|
|
|
269
273
|
"reason": http_reason,
|
|
270
274
|
}
|
|
271
275
|
|
|
276
|
+
# ---------- Realtime tools (WebSocket) ----------
|
|
277
|
+
|
|
278
|
+
def append_to_tools(
|
|
279
|
+
self,
|
|
280
|
+
mode: str,
|
|
281
|
+
model: ModelItem,
|
|
282
|
+
stream: bool,
|
|
283
|
+
is_expert_call: bool,
|
|
284
|
+
tools: List[dict],
|
|
285
|
+
preset=None
|
|
286
|
+
) -> List[dict]:
|
|
287
|
+
"""
|
|
288
|
+
Prepare remote tools for xAI Realtime sessions.
|
|
289
|
+
|
|
290
|
+
The returned list contains Realtime/WebSocket tool descriptors understood by xAI’s Grok Voice Agent API.
|
|
291
|
+
Server-side tools are executed by xAI; client-side tools are added separately in the caller.
|
|
292
|
+
|
|
293
|
+
:param mode: Agent mode (unused here, kept for signature compatibility)
|
|
294
|
+
:param model: Model item
|
|
295
|
+
:param stream: Streaming flag
|
|
296
|
+
:param is_expert_call: Expert call flag (unused here for xAI)
|
|
297
|
+
:param tools: Current tools list to extend
|
|
298
|
+
:param preset: Preset item (unused here)
|
|
299
|
+
:return: Extended tools list
|
|
300
|
+
"""
|
|
301
|
+
cfg = self.window.core.config
|
|
302
|
+
enabled_global = self.window.controller.chat.remote_tools.enabled
|
|
303
|
+
|
|
304
|
+
# Toggles
|
|
305
|
+
is_web_enabled = enabled_global(model, "web_search")
|
|
306
|
+
is_x_enabled = bool(cfg.get("remote_tools.xai.x_search", False))
|
|
307
|
+
is_code_enabled = bool(cfg.get("remote_tools.xai.code_execution", False))
|
|
308
|
+
is_mcp_enabled = bool(cfg.get("remote_tools.xai.mcp", False))
|
|
309
|
+
is_collections_enabled = bool(cfg.get("remote_tools.xai.collections", False))
|
|
310
|
+
|
|
311
|
+
# Web search
|
|
312
|
+
if is_web_enabled:
|
|
313
|
+
tools.append({"type": "web_search"})
|
|
314
|
+
|
|
315
|
+
# X search
|
|
316
|
+
if is_x_enabled:
|
|
317
|
+
tools.append({"type": "x_search"})
|
|
318
|
+
|
|
319
|
+
# Code execution (code interpreter)
|
|
320
|
+
if is_code_enabled:
|
|
321
|
+
# Container is optional here; xAI executes code server-side
|
|
322
|
+
tools.append({"type": "code_interpreter"})
|
|
323
|
+
|
|
324
|
+
# Collections search
|
|
325
|
+
if is_collections_enabled:
|
|
326
|
+
ids = cfg.get("remote_tools.xai.collections.args", "")
|
|
327
|
+
ids_list: List[str] = []
|
|
328
|
+
if ids:
|
|
329
|
+
try:
|
|
330
|
+
ids_list = [s.strip() for s in ids.split(",") if s.strip()]
|
|
331
|
+
except Exception:
|
|
332
|
+
ids_list = []
|
|
333
|
+
if ids_list:
|
|
334
|
+
tools.append({
|
|
335
|
+
"type": "collections_search",
|
|
336
|
+
"collection_ids": ids_list,
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
# MCP
|
|
340
|
+
if is_mcp_enabled:
|
|
341
|
+
mcp_tool = cfg.get("remote_tools.xai.mcp.args", "")
|
|
342
|
+
if mcp_tool:
|
|
343
|
+
try:
|
|
344
|
+
parsed = json.loads(mcp_tool)
|
|
345
|
+
# Accept either full dict or minimal {"type":"mcp"}
|
|
346
|
+
if isinstance(parsed, dict):
|
|
347
|
+
tools.append(parsed)
|
|
348
|
+
except Exception:
|
|
349
|
+
tools.append({"type": "mcp"})
|
|
350
|
+
else:
|
|
351
|
+
tools.append({"type": "mcp"})
|
|
352
|
+
|
|
353
|
+
return tools
|
|
354
|
+
|
|
272
355
|
# ---------- helpers ----------
|
|
273
356
|
|
|
274
357
|
def _build_http_params(
|
|
@@ -26,6 +26,7 @@ class Tools:
|
|
|
26
26
|
|
|
27
27
|
- prepare(): legacy OpenAI-compatible dicts (kept for compatibility if needed).
|
|
28
28
|
- prepare_sdk_tools(): xAI SDK client-side tool descriptors for Chat Responses.
|
|
29
|
+
- prepare_realtime(): Realtime/WebSocket-compatible function tools.
|
|
29
30
|
|
|
30
31
|
:param window: Window instance
|
|
31
32
|
"""
|
|
@@ -168,4 +169,54 @@ class Tools:
|
|
|
168
169
|
))
|
|
169
170
|
except Exception:
|
|
170
171
|
continue
|
|
172
|
+
return tools
|
|
173
|
+
|
|
174
|
+
def prepare_realtime(self, functions: list) -> List[dict]:
|
|
175
|
+
"""
|
|
176
|
+
Prepare function tools for Realtime/WebSocket sessions.
|
|
177
|
+
|
|
178
|
+
The returned structure matches the Realtime "tools" schema:
|
|
179
|
+
[
|
|
180
|
+
{
|
|
181
|
+
"type": "function",
|
|
182
|
+
"function": {
|
|
183
|
+
"name": "...",
|
|
184
|
+
"description": "...",
|
|
185
|
+
"parameters": { ... JSON Schema ... }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
:param functions: List of functions with keys: name (str), desc (str), params (JSON Schema str)
|
|
191
|
+
:return: List of function tool descriptors
|
|
192
|
+
"""
|
|
193
|
+
if not functions or not isinstance(functions, list):
|
|
194
|
+
return []
|
|
195
|
+
|
|
196
|
+
tools: List[dict] = []
|
|
197
|
+
for fn in functions:
|
|
198
|
+
name = str(fn.get("name") or "").strip()
|
|
199
|
+
if not name:
|
|
200
|
+
continue
|
|
201
|
+
desc = fn.get("desc") or ""
|
|
202
|
+
params: Optional[dict] = {}
|
|
203
|
+
if fn.get("params"):
|
|
204
|
+
try:
|
|
205
|
+
params = json.loads(fn["params"])
|
|
206
|
+
except Exception:
|
|
207
|
+
params = {}
|
|
208
|
+
params = self._sanitize_schema(params or {})
|
|
209
|
+
if not params.get("type"):
|
|
210
|
+
params["type"] = "object"
|
|
211
|
+
else:
|
|
212
|
+
params = {"type": "object"}
|
|
213
|
+
|
|
214
|
+
tools.append({
|
|
215
|
+
"type": "function",
|
|
216
|
+
"function": {
|
|
217
|
+
"name": name,
|
|
218
|
+
"description": desc,
|
|
219
|
+
"parameters": params,
|
|
220
|
+
}
|
|
221
|
+
})
|
|
171
222
|
return tools
|
|
@@ -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.07 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -274,6 +274,17 @@ class Patch:
|
|
|
274
274
|
data[key] = cfg_get_base(key)
|
|
275
275
|
updated = True
|
|
276
276
|
|
|
277
|
+
# < 2.7.9
|
|
278
|
+
if old < parse_version("2.7.9"):
|
|
279
|
+
print("Migrating config from < 2.7.9...")
|
|
280
|
+
to_add = [
|
|
281
|
+
"api_key_management_xai",
|
|
282
|
+
]
|
|
283
|
+
for key in to_add:
|
|
284
|
+
if key not in data or data[key] is None or data[key] == "None":
|
|
285
|
+
data[key] = ""
|
|
286
|
+
updated = True
|
|
287
|
+
|
|
277
288
|
# update file
|
|
278
289
|
migrated = False
|
|
279
290
|
if updated:
|
|
@@ -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.07 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from packaging.version import parse as parse_version, Version
|
|
@@ -154,6 +154,41 @@ class Patch:
|
|
|
154
154
|
m.input.append("image")
|
|
155
155
|
updated = True
|
|
156
156
|
|
|
157
|
+
# < 2.7.9 <--- add missing audio input
|
|
158
|
+
if old < parse_version("2.7.9"):
|
|
159
|
+
print("Migrating models from < 2.7.9...")
|
|
160
|
+
models_to_update = [
|
|
161
|
+
"grok-4",
|
|
162
|
+
"grok-4-fast-non-reasoning",
|
|
163
|
+
"grok-4-fast-reasoning",
|
|
164
|
+
"grok-4-1-fast-non-reasoning",
|
|
165
|
+
"grok-4-1-fast-reasoning",
|
|
166
|
+
]
|
|
167
|
+
for model in models_to_update:
|
|
168
|
+
if model in data:
|
|
169
|
+
m = data[model]
|
|
170
|
+
if not m.is_audio_input():
|
|
171
|
+
m.input.append("audio")
|
|
172
|
+
if not m.is_audio_output():
|
|
173
|
+
m.output.append("audio")
|
|
174
|
+
if not m.has_mode("audio"):
|
|
175
|
+
m.mode.append("audio")
|
|
176
|
+
models_to_remove = [
|
|
177
|
+
"gemini-2.5-flash-preview-native-audio-dialog",
|
|
178
|
+
]
|
|
179
|
+
for model in models_to_remove:
|
|
180
|
+
if model in data:
|
|
181
|
+
del data[model]
|
|
182
|
+
models_to_add = [
|
|
183
|
+
"gemini-2.5-flash-native-audio-latest",
|
|
184
|
+
]
|
|
185
|
+
for model in models_to_add:
|
|
186
|
+
if model not in data:
|
|
187
|
+
base_model = from_base(model)
|
|
188
|
+
if base_model:
|
|
189
|
+
data[model] = base_model
|
|
190
|
+
updated = True
|
|
191
|
+
|
|
157
192
|
# update file
|
|
158
193
|
if updated:
|
|
159
194
|
# fix empty/broken data
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.22 16:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
|
|
11
|
+
import json
|
|
12
12
|
import time
|
|
13
13
|
from typing import Dict, Any
|
|
14
14
|
|
|
@@ -116,14 +116,18 @@ class Storage:
|
|
|
116
116
|
title = :title,
|
|
117
117
|
content = :content,
|
|
118
118
|
is_initialized = :is_initialized,
|
|
119
|
-
updated_ts = :updated_ts
|
|
119
|
+
updated_ts = :updated_ts,
|
|
120
|
+
highlights_json = :highlights_json,
|
|
121
|
+
scroll_pos = :scroll_pos
|
|
120
122
|
WHERE idx = :idx
|
|
121
123
|
""").bindparams(
|
|
122
124
|
idx=int(notepad.idx or 0),
|
|
123
125
|
title=notepad.title,
|
|
124
126
|
content=notepad.content,
|
|
125
127
|
is_initialized=int(notepad.initialized),
|
|
126
|
-
updated_ts=ts
|
|
128
|
+
updated_ts=ts,
|
|
129
|
+
highlights_json=self.pack_item_value(notepad.highlights),
|
|
130
|
+
scroll_pos=int(notepad.scroll_pos)
|
|
127
131
|
)
|
|
128
132
|
else:
|
|
129
133
|
stmt = text("""
|
|
@@ -136,7 +140,9 @@ class Storage:
|
|
|
136
140
|
created_ts,
|
|
137
141
|
updated_ts,
|
|
138
142
|
is_deleted,
|
|
139
|
-
is_initialized
|
|
143
|
+
is_initialized,
|
|
144
|
+
highlights_json,
|
|
145
|
+
scroll_pos
|
|
140
146
|
)
|
|
141
147
|
VALUES
|
|
142
148
|
(
|
|
@@ -147,7 +153,9 @@ class Storage:
|
|
|
147
153
|
:created_ts,
|
|
148
154
|
:updated_ts,
|
|
149
155
|
:is_deleted,
|
|
150
|
-
:is_initialized
|
|
156
|
+
:is_initialized,
|
|
157
|
+
:highlights_json,
|
|
158
|
+
:scroll_pos
|
|
151
159
|
)
|
|
152
160
|
""").bindparams(
|
|
153
161
|
idx=notepad.idx,
|
|
@@ -157,7 +165,9 @@ class Storage:
|
|
|
157
165
|
created_ts=ts,
|
|
158
166
|
updated_ts=ts,
|
|
159
167
|
is_deleted=int(notepad.deleted),
|
|
160
|
-
is_initialized=int(notepad.initialized)
|
|
168
|
+
is_initialized=int(notepad.initialized),
|
|
169
|
+
highlights_json=self.pack_item_value(notepad.highlights),
|
|
170
|
+
scroll_pos=int(notepad.scroll_pos)
|
|
161
171
|
)
|
|
162
172
|
conn.execute(stmt)
|
|
163
173
|
|
|
@@ -180,7 +190,9 @@ class Storage:
|
|
|
180
190
|
created_ts,
|
|
181
191
|
updated_ts,
|
|
182
192
|
is_deleted,
|
|
183
|
-
is_initialized
|
|
193
|
+
is_initialized,
|
|
194
|
+
highlights_json,
|
|
195
|
+
scroll_pos
|
|
184
196
|
)
|
|
185
197
|
VALUES
|
|
186
198
|
(
|
|
@@ -191,7 +203,9 @@ class Storage:
|
|
|
191
203
|
:created_ts,
|
|
192
204
|
:updated_ts,
|
|
193
205
|
:is_deleted,
|
|
194
|
-
:is_initialized
|
|
206
|
+
:is_initialized,
|
|
207
|
+
:highlights_json,
|
|
208
|
+
:scroll_pos
|
|
195
209
|
)
|
|
196
210
|
""").bindparams(
|
|
197
211
|
idx=int(notepad.idx or 0),
|
|
@@ -201,7 +215,9 @@ class Storage:
|
|
|
201
215
|
created_ts=ts,
|
|
202
216
|
updated_ts=ts,
|
|
203
217
|
is_deleted=int(notepad.deleted),
|
|
204
|
-
is_initialized=int(notepad.initialized)
|
|
218
|
+
is_initialized=int(notepad.initialized),
|
|
219
|
+
highlights_json=self.pack_item_value(notepad.highlights),
|
|
220
|
+
scroll_pos=int(notepad.scroll_pos)
|
|
205
221
|
)
|
|
206
222
|
with db.begin() as conn:
|
|
207
223
|
result = conn.execute(stmt)
|
|
@@ -225,4 +241,31 @@ class Storage:
|
|
|
225
241
|
notepad.content = row['content']
|
|
226
242
|
notepad.deleted = bool(row['is_deleted'])
|
|
227
243
|
notepad.initialized = bool(row['is_initialized'])
|
|
244
|
+
notepad.highlights = self.unpack_item_value(row['highlights_json'])
|
|
245
|
+
notepad.scroll_pos = int(row.get('scroll_pos', -1))
|
|
228
246
|
return notepad
|
|
247
|
+
|
|
248
|
+
def pack_item_value(self, value: Any) -> str:
|
|
249
|
+
"""
|
|
250
|
+
Pack item value to JSON
|
|
251
|
+
|
|
252
|
+
:param value: Value to pack
|
|
253
|
+
:return: JSON string or value itself
|
|
254
|
+
"""
|
|
255
|
+
if isinstance(value, (list, dict)):
|
|
256
|
+
return json.dumps(value)
|
|
257
|
+
return value
|
|
258
|
+
|
|
259
|
+
def unpack_item_value(self, value: Any) -> Any:
|
|
260
|
+
"""
|
|
261
|
+
Unpack item value from JSON
|
|
262
|
+
|
|
263
|
+
:param value: Value to unpack
|
|
264
|
+
:return: Unpacked value
|
|
265
|
+
"""
|
|
266
|
+
if value is None:
|
|
267
|
+
return []
|
|
268
|
+
try:
|
|
269
|
+
return json.loads(value)
|
|
270
|
+
except:
|
|
271
|
+
return value
|
|
@@ -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 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt, QEvent
|
|
@@ -38,6 +38,7 @@ class Builder:
|
|
|
38
38
|
def setup_menu(self, parent=None) -> QMenuBar:
|
|
39
39
|
"""Setup agent_builder dialog menu"""
|
|
40
40
|
self.menu_bar = QMenuBar(parent)
|
|
41
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
41
42
|
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
42
43
|
t = self.tool
|
|
43
44
|
|
|
@@ -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 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -35,6 +35,7 @@ class AudioTranscribe:
|
|
|
35
35
|
def setup_menu(self, parent=None) -> QMenuBar:
|
|
36
36
|
"""Setup audio transcriber dialog menu"""
|
|
37
37
|
self.menu_bar = QMenuBar(parent)
|
|
38
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
38
39
|
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
39
40
|
t = self.window.tools.get("transcriber")
|
|
40
41
|
|
|
@@ -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 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -75,6 +75,7 @@ class Tool:
|
|
|
75
75
|
"""
|
|
76
76
|
# create menu bar
|
|
77
77
|
self.menu_bar = QMenuBar()
|
|
78
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
78
79
|
self.menu["file"] = self.menu_bar.addMenu(trans("interpreter.menu.file"))
|
|
79
80
|
self.menu["kernel"] = self.menu_bar.addMenu(trans("interpreter.menu.kernel"))
|
|
80
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 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import re
|
|
@@ -53,6 +53,7 @@ class Tool:
|
|
|
53
53
|
"""
|
|
54
54
|
# create menu bar
|
|
55
55
|
self.menu_bar = QMenuBar()
|
|
56
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
56
57
|
self.menu["file"] = self.menu_bar.addMenu(trans("menu.file"))
|
|
57
58
|
|
|
58
59
|
self.actions["file.open"] = QAction(QIcon(":/icons/folder.svg"), trans("tool.html_canvas.menu.file.open"))
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
# dialog.py
|
|
2
|
-
|
|
3
|
-
# dialog.py
|
|
4
|
-
|
|
5
1
|
#!/usr/bin/env python3
|
|
6
2
|
# -*- coding: utf-8 -*-
|
|
7
3
|
# ================================================== #
|
|
@@ -10,7 +6,7 @@
|
|
|
10
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
11
7
|
# MIT License #
|
|
12
8
|
# Created By : Marcin Szczygliński #
|
|
13
|
-
# Updated Date: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.22 17:00:00 #
|
|
14
10
|
# ================================================== #
|
|
15
11
|
|
|
16
12
|
from PySide6.QtCore import Qt, QPoint, QSize, QEvent
|
|
@@ -61,6 +57,7 @@ class DialogSpawner:
|
|
|
61
57
|
source.setVisible(False)
|
|
62
58
|
pixmap = ImageLabel(dialog, self.path)
|
|
63
59
|
pixmap.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored))
|
|
60
|
+
pixmap.setAlignment(Qt.AlignCenter)
|
|
64
61
|
|
|
65
62
|
# scrollable container for pixmap
|
|
66
63
|
scroll = QScrollArea(dialog)
|
|
@@ -212,6 +209,7 @@ class ImageViewerDialog(BaseDialog):
|
|
|
212
209
|
:return: QMenuBar
|
|
213
210
|
"""
|
|
214
211
|
self.menu_bar = QMenuBar(self)
|
|
212
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
215
213
|
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
216
214
|
|
|
217
215
|
self.actions["new"] = QAction(self._icon_add, trans("action.new"), 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:
|
|
9
|
+
# Updated Date: 2026.01.22 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -49,6 +49,7 @@ class DialogBuilder:
|
|
|
49
49
|
"""
|
|
50
50
|
# create menu bar
|
|
51
51
|
self.menu_bar = QMenuBar()
|
|
52
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
52
53
|
self.menu["file"] = self.menu_bar.addMenu(trans("menu.file"))
|
|
53
54
|
self.menu["config"] = self.menu_bar.addMenu(trans("menu.config"))
|
|
54
55
|
|
|
@@ -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 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -34,6 +34,7 @@ class VideoPlayer:
|
|
|
34
34
|
def setup_menu(self) -> QMenuBar:
|
|
35
35
|
"""Setup video dialog menu"""
|
|
36
36
|
self.menu_bar = QMenuBar()
|
|
37
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
37
38
|
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
38
39
|
|
|
39
40
|
# open
|
|
@@ -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 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
@@ -51,6 +51,7 @@ class Tool:
|
|
|
51
51
|
"""
|
|
52
52
|
# create menu bar
|
|
53
53
|
self.menu_bar = QMenuBar()
|
|
54
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
54
55
|
self.menu["file"] = self.menu_bar.addMenu(trans("menu.file"))
|
|
55
56
|
self.actions["file.open"] = QAction(QIcon(":/icons/folder.svg"), trans("tool.html_canvas.menu.file.open"))
|
|
56
57
|
self.actions["file.open"].triggered.connect(
|
|
@@ -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
|
from PySide6.QtCore import Qt, Slot, QObject, Signal
|
|
@@ -14,6 +14,7 @@ from PySide6.QtGui import QAction, QIcon, QKeySequence, QFontMetrics
|
|
|
14
14
|
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QTextEdit, QWidget, QSplitter, QPushButton, QLabel
|
|
15
15
|
|
|
16
16
|
from pygpt_net.core.text.finder import Finder
|
|
17
|
+
from pygpt_net.core.types import MODE_CHAT
|
|
17
18
|
from pygpt_net.ui.widget.option.combo import OptionCombo
|
|
18
19
|
|
|
19
20
|
from pygpt_net.ui.widget.textarea.search_input import SearchInput
|
|
@@ -67,6 +68,9 @@ class ToolWidget:
|
|
|
67
68
|
"type": "combo",
|
|
68
69
|
"label": "menu.tools.translator.model",
|
|
69
70
|
"use": "models",
|
|
71
|
+
"use_params": {
|
|
72
|
+
"mode": [MODE_CHAT]
|
|
73
|
+
},
|
|
70
74
|
"value": "gpt-4o-mini",
|
|
71
75
|
}
|
|
72
76
|
self.model_select = OptionCombo(
|
|
@@ -319,7 +323,7 @@ class TextColumn(QWidget):
|
|
|
319
323
|
:param search_text: Text to search in languages
|
|
320
324
|
"""
|
|
321
325
|
key = self.window.core.text.find_lang_id_by_search_string(search_text)
|
|
322
|
-
if key:
|
|
326
|
+
if key and key != "-":
|
|
323
327
|
self.lang_select.set_value(key)
|
|
324
328
|
|
|
325
329
|
def on_clear(self):
|
pygpt_net/ui/dialog/about.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.21 02:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -20,7 +20,7 @@ from pygpt_net.utils import trans
|
|
|
20
20
|
|
|
21
21
|
class About:
|
|
22
22
|
|
|
23
|
-
RELEASE_YEAR =
|
|
23
|
+
RELEASE_YEAR = 2026
|
|
24
24
|
|
|
25
25
|
def __init__(self, window=None):
|
|
26
26
|
"""
|