pygpt-net 2.6.34__py3-none-any.whl → 2.6.35__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 +7 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/chat/common.py +8 -2
- pygpt_net/controller/chat/handler/stream_worker.py +55 -43
- pygpt_net/controller/painter/common.py +13 -1
- pygpt_net/controller/painter/painter.py +11 -2
- pygpt_net/core/bridge/bridge.py +1 -5
- pygpt_net/core/bridge/context.py +81 -36
- pygpt_net/core/bridge/worker.py +3 -1
- pygpt_net/core/ctx/bag.py +4 -0
- pygpt_net/core/events/app.py +10 -17
- pygpt_net/core/events/base.py +17 -25
- pygpt_net/core/events/control.py +9 -17
- pygpt_net/core/events/event.py +9 -62
- pygpt_net/core/events/kernel.py +8 -17
- pygpt_net/core/events/realtime.py +8 -17
- pygpt_net/core/events/render.py +9 -17
- pygpt_net/core/render/web/body.py +394 -36
- pygpt_net/core/render/web/pid.py +39 -24
- pygpt_net/core/render/web/renderer.py +146 -40
- pygpt_net/data/config/config.json +4 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/css/web-blocks.css +3 -2
- pygpt_net/data/css/web-chatgpt.css +3 -1
- pygpt_net/data/css/web-chatgpt_wide.css +3 -1
- 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 +2 -1
- pygpt_net/data/locale/locale.uk.ini +1 -0
- pygpt_net/data/locale/locale.zh.ini +1 -0
- pygpt_net/provider/api/google/__init__.py +14 -5
- pygpt_net/provider/api/openai/__init__.py +13 -10
- pygpt_net/provider/core/config/patch.py +9 -0
- pygpt_net/ui/layout/chat/painter.py +63 -4
- pygpt_net/ui/widget/draw/painter.py +702 -106
- pygpt_net/ui/widget/textarea/web.py +2 -0
- {pygpt_net-2.6.34.dist-info → pygpt_net-2.6.35.dist-info}/METADATA +9 -2
- {pygpt_net-2.6.34.dist-info → pygpt_net-2.6.35.dist-info}/RECORD +44 -44
- {pygpt_net-2.6.34.dist-info → pygpt_net-2.6.35.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.34.dist-info → pygpt_net-2.6.35.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.34.dist-info → pygpt_net-2.6.35.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
2.6.35 (2025-09-04)
|
|
2
|
+
|
|
3
|
+
- Improved responsiveness in chat streaming.
|
|
4
|
+
- Added zoom, fit, and auto-scroll on crop in Painter.
|
|
5
|
+
- Added a scroll-up button in the chat window.
|
|
6
|
+
- Optimized memory usage.
|
|
7
|
+
|
|
1
8
|
2.6.34 (2025-09-03)
|
|
2
9
|
|
|
3
10
|
- Added auto-resizing for input.
|
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.04 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.35"
|
|
17
|
+
__build__ = "2025-09-04"
|
|
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"
|
|
@@ -292,8 +292,6 @@ class Common:
|
|
|
292
292
|
})) # stop audio input
|
|
293
293
|
controller.kernel.halt = True
|
|
294
294
|
dispatch(RenderEvent(RenderEvent.TOOL_END)) # show waiting
|
|
295
|
-
|
|
296
|
-
core.api.openai.stop()
|
|
297
295
|
self.unlock_input()
|
|
298
296
|
|
|
299
297
|
controller.chat.input.generating = False
|
|
@@ -311,6 +309,14 @@ class Common:
|
|
|
311
309
|
if not exit:
|
|
312
310
|
dispatch(AppEvent(AppEvent.INPUT_STOPPED)) # app event
|
|
313
311
|
|
|
312
|
+
self.stop_client() # stop clients
|
|
313
|
+
|
|
314
|
+
def stop_client(self):
|
|
315
|
+
"""Stop all clients"""
|
|
316
|
+
return # TODO: make it work without connection error after close
|
|
317
|
+
self.window.core.api.openai.safe_close()
|
|
318
|
+
self.window.core.api.google.safe_close()
|
|
319
|
+
|
|
314
320
|
def check_api_key(
|
|
315
321
|
self,
|
|
316
322
|
mode: str,
|
|
@@ -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.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
@@ -14,6 +14,7 @@ import io
|
|
|
14
14
|
import json
|
|
15
15
|
from dataclasses import dataclass, field
|
|
16
16
|
from typing import Optional, Literal, Any
|
|
17
|
+
from enum import Enum
|
|
17
18
|
|
|
18
19
|
from PySide6.QtCore import QObject, Signal, Slot, QRunnable
|
|
19
20
|
|
|
@@ -40,16 +41,18 @@ EventType = Literal[
|
|
|
40
41
|
"error",
|
|
41
42
|
]
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
ChunkType
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
44
|
+
|
|
45
|
+
class ChunkType(str, Enum):
|
|
46
|
+
"""
|
|
47
|
+
Enum for chunk type classification.
|
|
48
|
+
"""
|
|
49
|
+
API_CHAT = "api_chat"
|
|
50
|
+
API_CHAT_RESPONSES = "api_chat_responses"
|
|
51
|
+
API_COMPLETION = "api_completion"
|
|
52
|
+
LANGCHAIN_CHAT = "langchain_chat"
|
|
53
|
+
LLAMA_CHAT = "llama_chat"
|
|
54
|
+
GOOGLE = "google"
|
|
55
|
+
RAW = "raw"
|
|
53
56
|
|
|
54
57
|
|
|
55
58
|
class WorkerSignals(QObject):
|
|
@@ -64,10 +67,10 @@ class WorkerSignals(QObject):
|
|
|
64
67
|
eventReady = Signal(object)
|
|
65
68
|
|
|
66
69
|
|
|
67
|
-
@dataclass
|
|
70
|
+
@dataclass(slots=True)
|
|
68
71
|
class WorkerState:
|
|
69
72
|
"""Holds mutable state for the streaming loop."""
|
|
70
|
-
|
|
73
|
+
out: Optional[io.StringIO] = None
|
|
71
74
|
output_tokens: int = 0
|
|
72
75
|
begin: bool = True
|
|
73
76
|
error: Optional[Exception] = None
|
|
@@ -81,7 +84,7 @@ class WorkerState:
|
|
|
81
84
|
is_code: bool = False
|
|
82
85
|
force_func_call: bool = False
|
|
83
86
|
stopped: bool = False
|
|
84
|
-
chunk_type: ChunkType =
|
|
87
|
+
chunk_type: ChunkType = ChunkType.RAW
|
|
85
88
|
generator: Any = None
|
|
86
89
|
usage_vendor: Optional[str] = None
|
|
87
90
|
usage_payload: dict = field(default_factory=dict)
|
|
@@ -90,6 +93,8 @@ class WorkerState:
|
|
|
90
93
|
|
|
91
94
|
|
|
92
95
|
class StreamWorker(QRunnable):
|
|
96
|
+
__slots__ = ("signals", "ctx", "window", "stream")
|
|
97
|
+
|
|
93
98
|
def __init__(self, ctx: CtxItem, window, parent=None):
|
|
94
99
|
super().__init__()
|
|
95
100
|
self.signals = WorkerSignals()
|
|
@@ -134,7 +139,7 @@ class StreamWorker(QRunnable):
|
|
|
134
139
|
if ctx.use_responses_api:
|
|
135
140
|
if hasattr(chunk, 'type'):
|
|
136
141
|
etype = chunk.type # type: ignore[assignment]
|
|
137
|
-
state.chunk_type =
|
|
142
|
+
state.chunk_type = ChunkType.API_CHAT_RESPONSES
|
|
138
143
|
else:
|
|
139
144
|
continue
|
|
140
145
|
else:
|
|
@@ -199,23 +204,21 @@ class StreamWorker(QRunnable):
|
|
|
199
204
|
:param chunk: The chunk object from the stream
|
|
200
205
|
:return: Detected ChunkType
|
|
201
206
|
"""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return
|
|
212
|
-
if hasattr(chunk, '
|
|
213
|
-
return
|
|
214
|
-
if hasattr(chunk,
|
|
215
|
-
return
|
|
216
|
-
|
|
217
|
-
return "google"
|
|
218
|
-
return "raw"
|
|
207
|
+
choices = getattr(chunk, 'choices', None)
|
|
208
|
+
if choices:
|
|
209
|
+
choice0 = choices[0] if len(choices) > 0 else None
|
|
210
|
+
if choice0 is not None and hasattr(choice0, 'delta') and choice0.delta is not None:
|
|
211
|
+
return ChunkType.API_CHAT
|
|
212
|
+
if choice0 is not None and hasattr(choice0, 'text') and choice0.text is not None:
|
|
213
|
+
return ChunkType.API_COMPLETION
|
|
214
|
+
|
|
215
|
+
if hasattr(chunk, 'content') and getattr(chunk, 'content') is not None:
|
|
216
|
+
return ChunkType.LANGCHAIN_CHAT
|
|
217
|
+
if hasattr(chunk, 'delta') and getattr(chunk, 'delta') is not None:
|
|
218
|
+
return ChunkType.LLAMA_CHAT
|
|
219
|
+
if hasattr(chunk, "candidates"):
|
|
220
|
+
return ChunkType.GOOGLE
|
|
221
|
+
return ChunkType.RAW
|
|
219
222
|
|
|
220
223
|
def _append_response(
|
|
221
224
|
self,
|
|
@@ -236,7 +239,10 @@ class StreamWorker(QRunnable):
|
|
|
236
239
|
"""
|
|
237
240
|
if state.begin and response == "":
|
|
238
241
|
return
|
|
239
|
-
|
|
242
|
+
# Use a single expandable buffer to avoid per-chunk list allocations
|
|
243
|
+
if state.out is None:
|
|
244
|
+
state.out = io.StringIO()
|
|
245
|
+
state.out.write(response)
|
|
240
246
|
state.output_tokens += 1
|
|
241
247
|
emit_event(
|
|
242
248
|
RenderEvent(
|
|
@@ -307,9 +313,14 @@ class StreamWorker(QRunnable):
|
|
|
307
313
|
:param state: WorkerState
|
|
308
314
|
:param emit_end: Function to emit end signal
|
|
309
315
|
"""
|
|
310
|
-
# Build final output
|
|
311
|
-
output =
|
|
312
|
-
state.
|
|
316
|
+
# Build final output from the incremental buffer
|
|
317
|
+
output = state.out.getvalue() if state.out is not None else ""
|
|
318
|
+
if state.out is not None:
|
|
319
|
+
try:
|
|
320
|
+
state.out.close()
|
|
321
|
+
except Exception:
|
|
322
|
+
pass
|
|
323
|
+
state.out = None
|
|
313
324
|
|
|
314
325
|
if has_unclosed_code_tag(output):
|
|
315
326
|
output += "\n```"
|
|
@@ -336,6 +347,7 @@ class StreamWorker(QRunnable):
|
|
|
336
347
|
|
|
337
348
|
self.stream = None
|
|
338
349
|
ctx.output = output
|
|
350
|
+
output = None # free ref
|
|
339
351
|
|
|
340
352
|
# Tokens usage
|
|
341
353
|
if state.usage_payload:
|
|
@@ -418,17 +430,17 @@ class StreamWorker(QRunnable):
|
|
|
418
430
|
:return: Response delta string or None
|
|
419
431
|
"""
|
|
420
432
|
t = state.chunk_type
|
|
421
|
-
if t ==
|
|
433
|
+
if t == ChunkType.API_CHAT:
|
|
422
434
|
return self._process_api_chat(ctx, state, chunk)
|
|
423
|
-
if t ==
|
|
435
|
+
if t == ChunkType.API_CHAT_RESPONSES:
|
|
424
436
|
return self._process_api_chat_responses(ctx, core, state, chunk, etype)
|
|
425
|
-
if t ==
|
|
437
|
+
if t == ChunkType.API_COMPLETION:
|
|
426
438
|
return self._process_api_completion(chunk)
|
|
427
|
-
if t ==
|
|
439
|
+
if t == ChunkType.LANGCHAIN_CHAT:
|
|
428
440
|
return self._process_langchain_chat(chunk)
|
|
429
|
-
if t ==
|
|
441
|
+
if t == ChunkType.LLAMA_CHAT:
|
|
430
442
|
return self._process_llama_chat(state, chunk)
|
|
431
|
-
if t ==
|
|
443
|
+
if t == ChunkType.GOOGLE:
|
|
432
444
|
return self._process_google_chunk(ctx, core, state, chunk)
|
|
433
445
|
# raw fallback
|
|
434
446
|
return self._process_raw(chunk)
|
|
@@ -1109,7 +1121,7 @@ class StreamWorker(QRunnable):
|
|
|
1109
1121
|
except Exception:
|
|
1110
1122
|
pass
|
|
1111
1123
|
|
|
1112
|
-
# Bind to ctx on first discovery
|
|
1124
|
+
# Bind to ctx on first discovery
|
|
1113
1125
|
if state.citations and (ctx.urls is None or not ctx.urls):
|
|
1114
1126
|
ctx.urls = list(state.citations)
|
|
1115
1127
|
|
|
@@ -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.02
|
|
9
|
+
# Updated Date: 2025.09.02 15:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Tuple, Optional, Dict, List
|
|
@@ -208,6 +208,18 @@ class Common:
|
|
|
208
208
|
self.window.ui.nodes['painter.select.brush.size'].findText(str(size))
|
|
209
209
|
)
|
|
210
210
|
|
|
211
|
+
def restore_zoom(self):
|
|
212
|
+
"""Restore zoom from config"""
|
|
213
|
+
if self.window.core.config.has('painter.zoom'):
|
|
214
|
+
zoom = int(self.window.core.config.get('painter.zoom', 100))
|
|
215
|
+
self.window.ui.painter.set_zoom_percent(zoom)
|
|
216
|
+
|
|
217
|
+
def save_zoom(self):
|
|
218
|
+
"""Save zoom to config"""
|
|
219
|
+
zoom = self.window.ui.painter.get_zoom_percent()
|
|
220
|
+
self.window.core.config.set('painter.zoom', zoom)
|
|
221
|
+
self.window.core.config.save()
|
|
222
|
+
|
|
211
223
|
def get_colors(self) -> Dict[str, QColor]:
|
|
212
224
|
"""
|
|
213
225
|
Get colors dict
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
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.02 15:00:00 #
|
|
10
|
+
# ================================================== #
|
|
2
11
|
|
|
3
12
|
import os
|
|
4
13
|
|
|
@@ -51,7 +60,7 @@ class Painter:
|
|
|
51
60
|
if not hasattr(self.window.ui, 'painter'):
|
|
52
61
|
return
|
|
53
62
|
path = os.path.join(self.common.get_capture_dir(), '_current.png')
|
|
54
|
-
self.window.ui.painter.
|
|
63
|
+
self.window.ui.painter.save_base(path, include_drawing=True)
|
|
55
64
|
|
|
56
65
|
def save_all(self):
|
|
57
66
|
"""Save all (on exit)"""
|
pygpt_net/core/bridge/bridge.py
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.09.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import time
|
|
@@ -130,7 +130,6 @@ class Bridge:
|
|
|
130
130
|
self.window.core.debug.debug(str(debug))
|
|
131
131
|
|
|
132
132
|
self.apply_rate_limit() # apply RPM limit
|
|
133
|
-
self.last_context = weakref.ref(context) # store last context for call (debug)
|
|
134
133
|
|
|
135
134
|
if extra is None:
|
|
136
135
|
extra = {}
|
|
@@ -202,9 +201,6 @@ class Bridge:
|
|
|
202
201
|
debug = {k: str(v) for k, v in context.to_dict().items()}
|
|
203
202
|
self.window.core.debug.debug(str(debug))
|
|
204
203
|
|
|
205
|
-
# --- DEBUG ONLY ---
|
|
206
|
-
self.last_context_quick = weakref.ref(context) # store last context for quick call (debug)
|
|
207
|
-
|
|
208
204
|
if context.model is not None:
|
|
209
205
|
# check if model is supported by OpenAI API, if not then try to use llama-index or langchain call
|
|
210
206
|
if not context.model.is_supported(MODE_CHAT):
|
pygpt_net/core/bridge/context.py
CHANGED
|
@@ -6,30 +6,101 @@
|
|
|
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.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
|
-
from
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Dict, Any, Optional
|
|
14
15
|
|
|
15
16
|
from pygpt_net.item.ctx import CtxItem
|
|
16
17
|
from pygpt_net.item.model import ModelItem
|
|
17
18
|
|
|
19
|
+
|
|
20
|
+
@dataclass(slots=True)
|
|
21
|
+
class MultimodalContext:
|
|
22
|
+
"""
|
|
23
|
+
Multimodal context
|
|
24
|
+
"""
|
|
25
|
+
is_audio_input: bool = False
|
|
26
|
+
is_audio_output: bool = False
|
|
27
|
+
audio_data: Optional[Any] = None
|
|
28
|
+
audio_format: str = "wav"
|
|
29
|
+
|
|
30
|
+
def __init__(self, **kwargs):
|
|
31
|
+
"""
|
|
32
|
+
Multimodal context
|
|
33
|
+
|
|
34
|
+
:param kwargs: keyword arguments
|
|
35
|
+
"""
|
|
36
|
+
# kwargs-based initialization
|
|
37
|
+
self.is_audio_input = kwargs.get("is_audio_input", False)
|
|
38
|
+
self.is_audio_output = kwargs.get("is_audio_output", False)
|
|
39
|
+
self.audio_data = kwargs.get("audio_data", None)
|
|
40
|
+
self.audio_format = kwargs.get("audio_format", "wav")
|
|
41
|
+
|
|
42
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
43
|
+
"""
|
|
44
|
+
Return as dictionary
|
|
45
|
+
|
|
46
|
+
:return: dictionary
|
|
47
|
+
"""
|
|
48
|
+
data = {
|
|
49
|
+
"is_audio_input": self.is_audio_input,
|
|
50
|
+
"is_audio_output": self.is_audio_output,
|
|
51
|
+
"audio_format": self.audio_format,
|
|
52
|
+
}
|
|
53
|
+
# sort by keys
|
|
54
|
+
return dict(sorted(data.items(), key=lambda item: item[0]))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(slots=True)
|
|
18
58
|
class BridgeContext:
|
|
59
|
+
"""
|
|
60
|
+
Bridge context
|
|
61
|
+
"""
|
|
62
|
+
assistant_id: str = ""
|
|
63
|
+
attachments: dict = field(default_factory=dict)
|
|
64
|
+
ctx: Optional[CtxItem] = None
|
|
65
|
+
external_functions: list = field(default_factory=list)
|
|
66
|
+
file_ids: list = field(default_factory=list)
|
|
67
|
+
force: bool = False # Force mode flag
|
|
68
|
+
force_sync: bool = False # Force sync flag
|
|
69
|
+
history: list = field(default_factory=list)
|
|
70
|
+
idx: Optional[Any] = None
|
|
71
|
+
idx_mode: str = "chat"
|
|
72
|
+
is_expert_call: bool = False # Expert call flag
|
|
73
|
+
max_tokens: int = 0
|
|
74
|
+
mode: Optional[Any] = None
|
|
75
|
+
model: Optional[ModelItem] = None # model instance, not model name
|
|
76
|
+
multimodal_ctx: MultimodalContext = field(default_factory=lambda: MultimodalContext()) # AudioContext
|
|
77
|
+
parent_mode: Optional[Any] = None # real mode (global)
|
|
78
|
+
preset: Optional[Any] = None # PresetItem
|
|
79
|
+
prompt: str = ""
|
|
80
|
+
reply_context: Optional[Any] = None # ReplyContext
|
|
81
|
+
request: bool = False # Use normal request instead of quick call
|
|
82
|
+
stream: bool = False
|
|
83
|
+
system_prompt: str = ""
|
|
84
|
+
system_prompt_raw: str = "" # without plugins addons
|
|
85
|
+
temperature: float = 1.0
|
|
86
|
+
thread_id: str = ""
|
|
87
|
+
tools_outputs: list = field(default_factory=list)
|
|
88
|
+
|
|
19
89
|
def __init__(self, **kwargs):
|
|
20
90
|
"""
|
|
21
91
|
Bridge context
|
|
22
92
|
|
|
23
93
|
:param kwargs: keyword arguments
|
|
24
94
|
"""
|
|
95
|
+
# Assign with defaults
|
|
25
96
|
self.assistant_id = kwargs.get("assistant_id", "")
|
|
26
|
-
self.attachments = kwargs.get("attachments", [])
|
|
97
|
+
self.attachments = dict(kwargs.get("attachments", []))
|
|
27
98
|
self.ctx = kwargs.get("ctx", None)
|
|
28
|
-
self.external_functions = kwargs.get("external_functions", [])
|
|
29
|
-
self.file_ids = kwargs.get("file_ids", [])
|
|
99
|
+
self.external_functions = list(kwargs.get("external_functions", []))
|
|
100
|
+
self.file_ids = list(kwargs.get("file_ids", []))
|
|
30
101
|
self.force = kwargs.get("force", False) # Force mode flag
|
|
31
102
|
self.force_sync = kwargs.get("force_sync", False) # Force sync flag
|
|
32
|
-
self.history = kwargs.get("history", [])
|
|
103
|
+
self.history = list(kwargs.get("history", []))
|
|
33
104
|
self.idx = kwargs.get("idx", None)
|
|
34
105
|
self.idx_mode = kwargs.get("idx_mode", "chat")
|
|
35
106
|
self.is_expert_call = kwargs.get("is_expert_call", False) # Expert call flag
|
|
@@ -40,14 +111,14 @@ class BridgeContext:
|
|
|
40
111
|
self.parent_mode = kwargs.get("parent_mode", None) # real mode (global)
|
|
41
112
|
self.preset = kwargs.get("preset", None) # PresetItem
|
|
42
113
|
self.prompt = kwargs.get("prompt", "")
|
|
43
|
-
self.reply_context = kwargs.get("reply_ctx", None) # ReplyContext
|
|
114
|
+
self.reply_context = kwargs.get("reply_ctx", kwargs.get("reply_context", None)) # ReplyContext
|
|
44
115
|
self.request = kwargs.get("request", False) # Use normal request instead of quick call
|
|
45
116
|
self.stream = kwargs.get("stream", False)
|
|
46
117
|
self.system_prompt = kwargs.get("system_prompt", "")
|
|
47
118
|
self.system_prompt_raw = kwargs.get("system_prompt_raw", "") # without plugins addons
|
|
48
119
|
self.temperature = kwargs.get("temperature", 1.0)
|
|
49
120
|
self.thread_id = kwargs.get("thread_id", "")
|
|
50
|
-
self.tools_outputs = kwargs.get("tools_outputs", [])
|
|
121
|
+
self.tools_outputs = list(kwargs.get("tools_outputs", []))
|
|
51
122
|
|
|
52
123
|
# check types
|
|
53
124
|
if self.ctx is not None and not isinstance(self.ctx, CtxItem):
|
|
@@ -101,36 +172,10 @@ class BridgeContext:
|
|
|
101
172
|
"""
|
|
102
173
|
try:
|
|
103
174
|
return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
|
|
104
|
-
except Exception
|
|
175
|
+
except Exception:
|
|
105
176
|
pass
|
|
106
177
|
return ""
|
|
107
178
|
|
|
108
179
|
def __str__(self):
|
|
109
180
|
"""To string"""
|
|
110
|
-
return self.dump()
|
|
111
|
-
|
|
112
|
-
class MultimodalContext:
|
|
113
|
-
def __init__(self, **kwargs):
|
|
114
|
-
"""
|
|
115
|
-
Multimodal context
|
|
116
|
-
|
|
117
|
-
:param kwargs: keyword arguments
|
|
118
|
-
"""
|
|
119
|
-
self.is_audio_input = kwargs.get("is_audio_input", False)
|
|
120
|
-
self.is_audio_output = kwargs.get("is_audio_output", False)
|
|
121
|
-
self.audio_data = kwargs.get("audio_data", None)
|
|
122
|
-
self.audio_format = kwargs.get("audio_format", "wav")
|
|
123
|
-
|
|
124
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
125
|
-
"""
|
|
126
|
-
Return as dictionary
|
|
127
|
-
|
|
128
|
-
:return: dictionary
|
|
129
|
-
"""
|
|
130
|
-
data = {
|
|
131
|
-
"is_audio_input": self.is_audio_input,
|
|
132
|
-
"is_audio_output": self.is_audio_output,
|
|
133
|
-
"audio_format": self.audio_format,
|
|
134
|
-
}
|
|
135
|
-
# sort by keys
|
|
136
|
-
return dict(sorted(data.items(), key=lambda item: item[0]))
|
|
181
|
+
return self.dump()
|
pygpt_net/core/bridge/worker.py
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.09.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import QObject, Signal, QRunnable, Slot
|
|
@@ -29,6 +29,8 @@ class BridgeSignals(QObject):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class BridgeWorker(QRunnable):
|
|
32
|
+
__slots__ = ('signals', 'rt_signals', 'args', 'kwargs', 'window', 'context', 'extra', 'mode')
|
|
33
|
+
|
|
32
34
|
"""Bridge worker"""
|
|
33
35
|
def __init__(self, *args, **kwargs):
|
|
34
36
|
super().__init__()
|
pygpt_net/core/ctx/bag.py
CHANGED
|
@@ -15,9 +15,13 @@ from pygpt_net.item.ctx import CtxItem
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class Bag:
|
|
18
|
+
__slots__ = ('window', 'meta', 'tab_id', 'items')
|
|
19
|
+
|
|
18
20
|
def __init__(self, window=None):
|
|
19
21
|
"""
|
|
20
22
|
Context bag
|
|
23
|
+
|
|
24
|
+
:param window: Window instance
|
|
21
25
|
"""
|
|
22
26
|
self.window = window
|
|
23
27
|
self.meta = None # current meta
|
pygpt_net/core/events/app.py
CHANGED
|
@@ -6,15 +6,22 @@
|
|
|
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.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import Optional, Dict, Any, ClassVar
|
|
13
14
|
|
|
14
15
|
from .base import BaseEvent
|
|
16
|
+
from ...item.ctx import CtxItem
|
|
15
17
|
|
|
18
|
+
|
|
19
|
+
@dataclass(slots=True)
|
|
16
20
|
class AppEvent(BaseEvent):
|
|
17
21
|
"""Events dispatched by application"""
|
|
22
|
+
# static id for event family
|
|
23
|
+
id: ClassVar[str] = "AppEvent"
|
|
24
|
+
|
|
18
25
|
APP_STARTED = "app.started"
|
|
19
26
|
CAMERA_CAPTURED = "camera.captured"
|
|
20
27
|
CAMERA_DISABLED = "camera.disabled"
|
|
@@ -37,18 +44,4 @@ class AppEvent(BaseEvent):
|
|
|
37
44
|
VOICE_CONTROL_STOPPED = "voice.control.stopped"
|
|
38
45
|
VOICE_CONTROL_SENT = "voice.control.sent"
|
|
39
46
|
VOICE_CONTROL_TOGGLE = "voice.control.toggle"
|
|
40
|
-
VOICE_CONTROL_UNRECOGNIZED = "voice.control.unrecognized"
|
|
41
|
-
|
|
42
|
-
def __init__(
|
|
43
|
-
self,
|
|
44
|
-
name: Optional[str] = None,
|
|
45
|
-
data: Optional[dict] = None,
|
|
46
|
-
):
|
|
47
|
-
"""
|
|
48
|
-
Event object class
|
|
49
|
-
|
|
50
|
-
:param name: event name
|
|
51
|
-
:param data: event data
|
|
52
|
-
"""
|
|
53
|
-
super(AppEvent, self).__init__(name, data)
|
|
54
|
-
self.id = "AppEvent"
|
|
47
|
+
VOICE_CONTROL_UNRECOGNIZED = "voice.control.unrecognized"
|
pygpt_net/core/events/base.py
CHANGED
|
@@ -6,39 +6,31 @@
|
|
|
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.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
|
-
from
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Optional, Dict, Any, ClassVar
|
|
14
15
|
|
|
15
16
|
from pygpt_net.item.ctx import CtxItem
|
|
16
17
|
|
|
17
|
-
class BaseEvent:
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
@dataclass(slots=True)
|
|
20
|
+
class BaseEvent:
|
|
21
|
+
"""Base Event object class"""
|
|
22
|
+
id: ClassVar[str] = None
|
|
23
|
+
name: Optional[str] = None
|
|
24
|
+
data: Optional[dict] = None
|
|
25
|
+
ctx: Optional[CtxItem] = None # CtxItem
|
|
26
|
+
stop: bool = False # True to stop propagation
|
|
27
|
+
internal: bool = False
|
|
28
|
+
call_id: int = 0
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
:param ctx: context instance
|
|
31
|
-
"""
|
|
32
|
-
self.id = None
|
|
33
|
-
self.name = name
|
|
34
|
-
self.data = data
|
|
30
|
+
def __post_init__(self):
|
|
31
|
+
# Normalize None to empty dict for convenience and safety
|
|
35
32
|
if self.data is None:
|
|
36
33
|
self.data = {}
|
|
37
|
-
self.ctx = ctx # CtxItem
|
|
38
|
-
self.stop = False # True to stop propagation
|
|
39
|
-
self.internal = False
|
|
40
|
-
self.call_id = 0
|
|
41
|
-
|
|
42
34
|
|
|
43
35
|
@property
|
|
44
36
|
def full_name(self) -> str:
|
|
@@ -47,7 +39,7 @@ class BaseEvent:
|
|
|
47
39
|
|
|
48
40
|
:return: Full event name
|
|
49
41
|
"""
|
|
50
|
-
return self.id + ": " + self.name
|
|
42
|
+
return self.id + ": " + self.name # type: ignore[operator]
|
|
51
43
|
|
|
52
44
|
def to_dict(self) -> Dict[str, Any]:
|
|
53
45
|
"""Dump event to dict"""
|
|
@@ -66,7 +58,7 @@ class BaseEvent:
|
|
|
66
58
|
"""
|
|
67
59
|
try:
|
|
68
60
|
return json.dumps(self.to_dict())
|
|
69
|
-
except Exception
|
|
61
|
+
except Exception:
|
|
70
62
|
pass
|
|
71
63
|
return ""
|
|
72
64
|
|