pygpt-net 2.6.11__py3-none-any.whl → 2.6.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +7 -1
- pygpt_net/controller/chat/output.py +2 -2
- pygpt_net/controller/chat/response.py +9 -4
- pygpt_net/controller/chat/stream.py +10 -4
- pygpt_net/controller/presets/editor.py +1 -1
- pygpt_net/core/agents/runner.py +2 -0
- pygpt_net/core/agents/runners/llama_workflow.py +13 -4
- pygpt_net/core/ctx/bag.py +2 -1
- pygpt_net/core/debug/debug.py +12 -3
- pygpt_net/core/experts/experts.py +1 -1
- pygpt_net/core/presets/presets.py +1 -2
- pygpt_net/core/render/web/body.py +52 -10
- pygpt_net/core/render/web/pid.py +20 -4
- pygpt_net/core/render/web/renderer.py +395 -88
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/item/ctx.py +3 -3
- pygpt_net/launcher.py +2 -9
- pygpt_net/provider/gpt/__init__.py +13 -4
- pygpt_net/tools/code_interpreter/body.py +2 -3
- pygpt_net/ui/layout/toolbox/presets.py +0 -4
- pygpt_net/ui/main.py +5 -2
- pygpt_net/ui/widget/textarea/html.py +2 -7
- pygpt_net/ui/widget/textarea/web.py +35 -27
- pygpt_net/utils.py +15 -8
- {pygpt_net-2.6.11.dist-info → pygpt_net-2.6.13.dist-info}/METADATA +13 -2
- {pygpt_net-2.6.11.dist-info → pygpt_net-2.6.13.dist-info}/RECORD +32 -32
- {pygpt_net-2.6.11.dist-info → pygpt_net-2.6.13.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.11.dist-info → pygpt_net-2.6.13.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.11.dist-info → pygpt_net-2.6.13.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
2.6.13 (2025-08-19)
|
|
2
|
+
|
|
3
|
+
- Fix: Do not load the index in experts if it is not provided.
|
|
4
|
+
- Fix: Load remote images in the webview.
|
|
5
|
+
- Fix: Presets list refresh.
|
|
6
|
+
- Optimize context items reload.
|
|
7
|
+
|
|
8
|
+
2.6.12 (2025-08-19)
|
|
9
|
+
|
|
10
|
+
- Optimized web renderer memory cleanup.
|
|
11
|
+
|
|
1
12
|
2.6.11 (2025-08-18)
|
|
2
13
|
|
|
3
14
|
- Added the ability to close the dialog window with the Esc key.
|
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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 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-08-
|
|
16
|
+
__version__ = "2.6.13"
|
|
17
|
+
__build__ = "2025-08-19"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
20
20
|
__report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
|
pygpt_net/app.py
CHANGED
|
@@ -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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -25,6 +25,12 @@ if platform.system() == 'Windows':
|
|
|
25
25
|
# enable debug logging
|
|
26
26
|
# os.environ["QT_LOGGING_RULES"] = "*.debug=true"
|
|
27
27
|
# os.environ["QTWEBENGINE_REMOTE_DEBUGGING"] = "9222"
|
|
28
|
+
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = (
|
|
29
|
+
"--renderer-process-limit=1 "
|
|
30
|
+
"--process-per-site "
|
|
31
|
+
"--enable-precise-memory-info "
|
|
32
|
+
"--js-flags=--expose-gc"
|
|
33
|
+
)
|
|
28
34
|
|
|
29
35
|
_original_open = builtins.open
|
|
30
36
|
|
|
@@ -291,8 +291,8 @@ class Output:
|
|
|
291
291
|
|
|
292
292
|
if mode != MODE_ASSISTANT:
|
|
293
293
|
self.window.controller.kernel.stack.handle() # handle reply
|
|
294
|
-
event = RenderEvent(RenderEvent.RELOAD)
|
|
295
|
-
self.window.dispatch(event) # reload chat window
|
|
294
|
+
# event = RenderEvent(RenderEvent.RELOAD)
|
|
295
|
+
# self.window.dispatch(event) # reload chat window
|
|
296
296
|
|
|
297
297
|
mem_clean()
|
|
298
298
|
|
|
@@ -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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Dict, Any
|
|
@@ -238,7 +238,7 @@ class Response:
|
|
|
238
238
|
self.window.core.ctx.update_item(ctx)
|
|
239
239
|
|
|
240
240
|
# update ctx meta
|
|
241
|
-
if mode in
|
|
241
|
+
if mode in (MODE_AGENT_LLAMA, MODE_AGENT_OPENAI) and ctx.meta is not None:
|
|
242
242
|
self.window.core.ctx.replace(ctx.meta)
|
|
243
243
|
self.window.core.ctx.save(ctx.meta.id)
|
|
244
244
|
# update preset if exists
|
|
@@ -265,11 +265,16 @@ class Response:
|
|
|
265
265
|
self.window.dispatch(event) # show cmd waiting
|
|
266
266
|
self.window.controller.chat.output.handle_end(ctx, mode) # handle end.
|
|
267
267
|
|
|
268
|
-
|
|
268
|
+
data = {
|
|
269
|
+
"meta": ctx.meta,
|
|
270
|
+
"ctx": ctx,
|
|
271
|
+
"stream": self.window.core.config.get("stream", False),
|
|
272
|
+
}
|
|
273
|
+
event = RenderEvent(RenderEvent.END, data)
|
|
269
274
|
self.window.dispatch(event)
|
|
270
275
|
|
|
271
276
|
# if continue reasoning
|
|
272
|
-
if global_mode not in
|
|
277
|
+
if global_mode not in (MODE_AGENT_LLAMA, MODE_AGENT_OPENAI):
|
|
273
278
|
return # no agent mode, nothing to do
|
|
274
279
|
|
|
275
280
|
if ctx.extra is None or (type(ctx.extra) == dict and "agent_finish" not in ctx.extra):
|
|
@@ -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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
@@ -65,6 +65,7 @@ class StreamWorker(QRunnable):
|
|
|
65
65
|
self.signals = WorkerSignals()
|
|
66
66
|
self.ctx = ctx
|
|
67
67
|
self.window = window
|
|
68
|
+
self.stream = None
|
|
68
69
|
|
|
69
70
|
@Slot()
|
|
70
71
|
def run(self):
|
|
@@ -90,8 +91,7 @@ class StreamWorker(QRunnable):
|
|
|
90
91
|
force_func_call = False
|
|
91
92
|
stopped = False
|
|
92
93
|
chunk_type: ChunkType = "raw"
|
|
93
|
-
generator =
|
|
94
|
-
ctx.stream = None
|
|
94
|
+
generator = self.stream
|
|
95
95
|
|
|
96
96
|
base_data = {
|
|
97
97
|
"meta": ctx.meta,
|
|
@@ -363,6 +363,7 @@ class StreamWorker(QRunnable):
|
|
|
363
363
|
finally:
|
|
364
364
|
output = "".join(output_parts)
|
|
365
365
|
output_parts.clear()
|
|
366
|
+
del output_parts
|
|
366
367
|
|
|
367
368
|
if has_unclosed_code_tag(output):
|
|
368
369
|
output += "\n```"
|
|
@@ -374,11 +375,14 @@ class StreamWorker(QRunnable):
|
|
|
374
375
|
pass
|
|
375
376
|
|
|
376
377
|
del generator
|
|
378
|
+
self.stream = None
|
|
377
379
|
|
|
378
380
|
ctx.output = output
|
|
379
381
|
ctx.set_tokens(ctx.input_tokens, output_tokens)
|
|
380
382
|
core.ctx.update_item(ctx)
|
|
381
383
|
|
|
384
|
+
output = None
|
|
385
|
+
|
|
382
386
|
if files and not stopped:
|
|
383
387
|
core.debug.info("[chat] Container files found, downloading...")
|
|
384
388
|
try:
|
|
@@ -464,11 +468,13 @@ class Stream:
|
|
|
464
468
|
self.extra = extra if extra is not None else {}
|
|
465
469
|
|
|
466
470
|
worker = StreamWorker(ctx, self.window)
|
|
467
|
-
self.worker = worker
|
|
468
471
|
|
|
472
|
+
worker.stream = ctx.stream
|
|
469
473
|
worker.signals.eventReady.connect(self.handleEvent)
|
|
470
474
|
worker.signals.errorOccurred.connect(self.handleError)
|
|
471
475
|
worker.signals.end.connect(self.handleEnd)
|
|
476
|
+
ctx.stream = None
|
|
477
|
+
self.worker = worker
|
|
472
478
|
|
|
473
479
|
self.window.core.debug.info("[chat] Stream begin...")
|
|
474
480
|
self.window.threadpool.start(worker)
|
pygpt_net/core/agents/runner.py
CHANGED
|
@@ -237,6 +237,7 @@ class Runner:
|
|
|
237
237
|
model = context.model
|
|
238
238
|
vector_store_idx = extra.get("agent_idx", None)
|
|
239
239
|
system_prompt = context.system_prompt
|
|
240
|
+
is_expert_call = context.is_expert_call
|
|
240
241
|
max_steps = self.window.core.config.get("agent.llama.steps", 10)
|
|
241
242
|
is_cmd = self.window.core.command.is_cmd(inline=False)
|
|
242
243
|
llm = self.window.core.idx.llm.get(model, stream=False)
|
|
@@ -301,6 +302,7 @@ class Runner:
|
|
|
301
302
|
"verbose": verbose,
|
|
302
303
|
"history": history,
|
|
303
304
|
"llm": llm,
|
|
305
|
+
"is_expert_call": is_expert_call,
|
|
304
306
|
}
|
|
305
307
|
# TODO: add support for other modes
|
|
306
308
|
if mode == AGENT_MODE_WORKFLOW:
|
|
@@ -106,6 +106,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
106
106
|
verbose: bool = False,
|
|
107
107
|
history: List[CtxItem] = None,
|
|
108
108
|
llm: Any = None,
|
|
109
|
+
is_expert_call: bool = False,
|
|
109
110
|
) -> Union[CtxItem, None]:
|
|
110
111
|
"""
|
|
111
112
|
Run agent workflow
|
|
@@ -117,6 +118,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
117
118
|
:param verbose: verbose mode
|
|
118
119
|
:param history: chat history
|
|
119
120
|
:param llm: LLM instance
|
|
121
|
+
:param is_expert_call: if True, run as expert call
|
|
120
122
|
:return: True if success
|
|
121
123
|
"""
|
|
122
124
|
if self.is_stopped():
|
|
@@ -124,6 +126,9 @@ class LlamaWorkflow(BaseRunner):
|
|
|
124
126
|
|
|
125
127
|
memory = self.window.core.idx.chat.get_memory_buffer(history, llm)
|
|
126
128
|
agent_ctx = Context(agent)
|
|
129
|
+
flush = True
|
|
130
|
+
if is_expert_call:
|
|
131
|
+
flush = False
|
|
127
132
|
try:
|
|
128
133
|
ctx = await self.run_agent(
|
|
129
134
|
agent=agent,
|
|
@@ -134,6 +139,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
134
139
|
item_ctx=ctx,
|
|
135
140
|
signals=signals,
|
|
136
141
|
use_partials=False, # use partials for streaming
|
|
142
|
+
flush=flush, # flush output buffer to webview
|
|
137
143
|
)
|
|
138
144
|
except WorkflowCancelledByUser:
|
|
139
145
|
print("\n\n[STOP] Workflow stopped by user.")
|
|
@@ -202,6 +208,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
202
208
|
item_ctx: Optional[CtxItem] = None,
|
|
203
209
|
signals: Optional[BridgeSignals] = None,
|
|
204
210
|
use_partials: bool = True,
|
|
211
|
+
flush: bool = True,
|
|
205
212
|
):
|
|
206
213
|
"""
|
|
207
214
|
Run agent workflow
|
|
@@ -215,6 +222,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
215
222
|
:param item_ctx: Optional CtxItem for additional context
|
|
216
223
|
:param signals: Optional BridgeSignals for communication
|
|
217
224
|
:param use_partials: If True, use partial context items for streaming
|
|
225
|
+
:param flush: If True, flush the output buffer before starting
|
|
218
226
|
:return: handler for the agent workflow
|
|
219
227
|
"""
|
|
220
228
|
handler = agent.run(
|
|
@@ -237,7 +245,8 @@ class LlamaWorkflow(BaseRunner):
|
|
|
237
245
|
# persist current output on stop
|
|
238
246
|
item_ctx.output = item_ctx.live_output
|
|
239
247
|
self.window.core.ctx.update_item(item_ctx)
|
|
240
|
-
|
|
248
|
+
if flush:
|
|
249
|
+
self.end_stream(item_ctx, signals)
|
|
241
250
|
await handler.cancel_run() # cancel, will raise WorkflowCancelledByUser
|
|
242
251
|
break
|
|
243
252
|
if isinstance(event, ToolCallResult):
|
|
@@ -247,7 +256,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
247
256
|
formatted = "\n```output\n" + str(event.tool_output) + "\n```\n"
|
|
248
257
|
item_ctx.live_output += formatted
|
|
249
258
|
item_ctx.stream = formatted
|
|
250
|
-
if item_ctx.stream_agent_output:
|
|
259
|
+
if item_ctx.stream_agent_output and flush:
|
|
251
260
|
self.send_stream(item_ctx, signals, begin)
|
|
252
261
|
elif isinstance(event, ToolCall):
|
|
253
262
|
if "code" in event.tool_kwargs:
|
|
@@ -257,7 +266,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
257
266
|
formatted = "\n```python\n" + str(event.tool_kwargs['code']) + "\n```\n"
|
|
258
267
|
item_ctx.live_output += formatted
|
|
259
268
|
item_ctx.stream = formatted
|
|
260
|
-
if item_ctx.stream_agent_output:
|
|
269
|
+
if item_ctx.stream_agent_output and flush:
|
|
261
270
|
self.send_stream(item_ctx, signals, begin)
|
|
262
271
|
elif isinstance(event, StepEvent):
|
|
263
272
|
self.set_busy(signals)
|
|
@@ -278,7 +287,7 @@ class LlamaWorkflow(BaseRunner):
|
|
|
278
287
|
if event.delta:
|
|
279
288
|
item_ctx.live_output += event.delta
|
|
280
289
|
item_ctx.stream = event.delta
|
|
281
|
-
if item_ctx.stream_agent_output:
|
|
290
|
+
if item_ctx.stream_agent_output and flush:
|
|
282
291
|
self.send_stream(item_ctx, signals, begin) # send stream to webview
|
|
283
292
|
begin = False
|
|
284
293
|
elif isinstance(event, AgentOutput):
|
pygpt_net/core/ctx/bag.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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import List
|
|
@@ -44,6 +44,7 @@ class Bag:
|
|
|
44
44
|
def clear_items(self):
|
|
45
45
|
"""Clear items"""
|
|
46
46
|
self.items.clear()
|
|
47
|
+
self.items = []
|
|
47
48
|
|
|
48
49
|
def count_items(self) -> int:
|
|
49
50
|
"""
|
pygpt_net/core/debug/debug.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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import gc
|
|
@@ -338,8 +338,17 @@ class Debug:
|
|
|
338
338
|
:param label: label for memory usage
|
|
339
339
|
:return: formatted memory usage string
|
|
340
340
|
"""
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
rss_mb = self._process.memory_info().rss / (1024 * 1024)
|
|
342
|
+
uss_mb = getattr(self._process.memory_full_info(), "uss", 0) / 1024 / 1024
|
|
343
|
+
data = f"RSS={rss_mb:.0f} MB USS={uss_mb:.0f} MB"
|
|
344
|
+
|
|
345
|
+
children_parts = []
|
|
346
|
+
for c in self._process.children(recursive=True):
|
|
347
|
+
children_parts.append(
|
|
348
|
+
f"{c.pid} {c.name()} {round(c.memory_info().rss / 1024 / 1024)} MB"
|
|
349
|
+
)
|
|
350
|
+
if children_parts:
|
|
351
|
+
data += "\n" + "\n".join(children_parts)
|
|
343
352
|
print(f"[{label}] {data}")
|
|
344
353
|
return data
|
|
345
354
|
|
|
@@ -213,14 +213,13 @@ class Presets:
|
|
|
213
213
|
"""
|
|
214
214
|
attr = self._MODE_TO_ATTR.get(mode)
|
|
215
215
|
if not attr:
|
|
216
|
-
|
|
216
|
+
return
|
|
217
217
|
i = 0
|
|
218
218
|
for key, item in self.items.items():
|
|
219
219
|
if getattr(item, attr, False):
|
|
220
220
|
if i == idx:
|
|
221
221
|
return key
|
|
222
222
|
i += 1
|
|
223
|
-
raise IndexError(idx)
|
|
224
223
|
|
|
225
224
|
def get_by_id(self, mode: str, id: str) -> Optional[PresetItem]:
|
|
226
225
|
"""
|
|
@@ -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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -42,9 +42,7 @@ class Body:
|
|
|
42
42
|
<script type="text/javascript" src="qrc:///js/highlight.min.js"></script>
|
|
43
43
|
<script type="text/javascript" src="qrc:///js/katex.min.js"></script>
|
|
44
44
|
<script>
|
|
45
|
-
|
|
46
45
|
const DEBUG_MODE = false;
|
|
47
|
-
|
|
48
46
|
let scrollTimeout = null;
|
|
49
47
|
let prevScroll = 0;
|
|
50
48
|
let bridge;
|
|
@@ -69,6 +67,23 @@ class Body:
|
|
|
69
67
|
let pendingHighlightMath = false;
|
|
70
68
|
let scrollScheduled = false;
|
|
71
69
|
|
|
70
|
+
// timers
|
|
71
|
+
let tipsTimers = [];
|
|
72
|
+
|
|
73
|
+
// clear previous references
|
|
74
|
+
function resetEphemeralDomRefs() {
|
|
75
|
+
domLastCodeBlock = null;
|
|
76
|
+
domLastParagraphBlock = null;
|
|
77
|
+
}
|
|
78
|
+
function dropIfDetached() {
|
|
79
|
+
if (domLastCodeBlock && !domLastCodeBlock.isConnected) domLastCodeBlock = null;
|
|
80
|
+
if (domLastParagraphBlock && !domLastParagraphBlock.isConnected) domLastParagraphBlock = null;
|
|
81
|
+
}
|
|
82
|
+
function stopTipsTimers() {
|
|
83
|
+
tipsTimers.forEach(clearTimeout);
|
|
84
|
+
tipsTimers = [];
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
history.scrollRestoration = "manual";
|
|
73
88
|
document.addEventListener('keydown', function(event) {
|
|
74
89
|
if (event.ctrlKey && event.key === 'f') {
|
|
@@ -137,6 +152,7 @@ class Body:
|
|
|
137
152
|
}
|
|
138
153
|
function hideTips() {
|
|
139
154
|
if (tips_hidden) return;
|
|
155
|
+
stopTipsTimers();
|
|
140
156
|
const t = els.tips || document.getElementById('tips');
|
|
141
157
|
if (t) t.style.display = 'none';
|
|
142
158
|
tips_hidden = true;
|
|
@@ -151,21 +167,23 @@ class Body:
|
|
|
151
167
|
function cycleTips() {
|
|
152
168
|
if (tips_hidden) return;
|
|
153
169
|
if (tips.length === 0) return;
|
|
154
|
-
let tipContainer = els.tips || document.getElementById('tips');
|
|
155
170
|
let currentTip = 0;
|
|
156
171
|
function showNextTip() {
|
|
157
172
|
if (tips_hidden) return;
|
|
173
|
+
const tipContainer = els.tips || document.getElementById('tips');
|
|
174
|
+
if (!tipContainer) return;
|
|
158
175
|
tipContainer.innerHTML = tips[currentTip];
|
|
159
176
|
tipContainer.classList.add('visible');
|
|
160
|
-
setTimeout(function() {
|
|
177
|
+
tipsTimers.push(setTimeout(function() {
|
|
161
178
|
if (tips_hidden) return;
|
|
162
179
|
tipContainer.classList.remove('visible');
|
|
163
|
-
setTimeout(function(){
|
|
180
|
+
tipsTimers.push(setTimeout(function(){
|
|
164
181
|
currentTip = (currentTip + 1) % tips.length;
|
|
165
182
|
showNextTip();
|
|
166
|
-
}, 1000);
|
|
167
|
-
}, 15000);
|
|
183
|
+
}, 1000));
|
|
184
|
+
}, 15000));
|
|
168
185
|
}
|
|
186
|
+
stopTipsTimers();
|
|
169
187
|
showNextTip();
|
|
170
188
|
}
|
|
171
189
|
function renderMath(root) {
|
|
@@ -224,7 +242,7 @@ class Body:
|
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
function getStreamContainer() {
|
|
227
|
-
if (domOutputStream &&
|
|
245
|
+
if (domOutputStream && domOutputStream.isConnected) {
|
|
228
246
|
return domOutputStream;
|
|
229
247
|
}
|
|
230
248
|
let element = els.appendOutput || document.getElementById('_append_output_');
|
|
@@ -247,6 +265,24 @@ class Body:
|
|
|
247
265
|
scheduleScroll();
|
|
248
266
|
}
|
|
249
267
|
}
|
|
268
|
+
function clean() {
|
|
269
|
+
if (DEBUG_MODE) {
|
|
270
|
+
log("-- CLEAN DOM --");
|
|
271
|
+
}
|
|
272
|
+
const el = els.nodes || document.getElementById('_nodes_');
|
|
273
|
+
if (el) {
|
|
274
|
+
el.replaceChildren();
|
|
275
|
+
}
|
|
276
|
+
resetEphemeralDomRefs();
|
|
277
|
+
els = {};
|
|
278
|
+
try {
|
|
279
|
+
if (window.gc) {
|
|
280
|
+
window.gc();
|
|
281
|
+
}
|
|
282
|
+
} catch (e) {
|
|
283
|
+
// gc not available
|
|
284
|
+
}
|
|
285
|
+
}
|
|
250
286
|
function appendExtra(id, content) {
|
|
251
287
|
hideTips();
|
|
252
288
|
prevScroll = 0;
|
|
@@ -270,6 +306,7 @@ class Body:
|
|
|
270
306
|
if (element) {
|
|
271
307
|
element.remove();
|
|
272
308
|
}
|
|
309
|
+
resetEphemeralDomRefs();
|
|
273
310
|
highlightCode();
|
|
274
311
|
scheduleScroll();
|
|
275
312
|
}
|
|
@@ -280,13 +317,14 @@ class Body:
|
|
|
280
317
|
const elements = container.querySelectorAll('.msg-box');
|
|
281
318
|
let remove = false;
|
|
282
319
|
elements.forEach(function(element) {
|
|
283
|
-
if (element.id.endsWith('-' + id)) {
|
|
320
|
+
if (element.id && element.id.endsWith('-' + id)) {
|
|
284
321
|
remove = true;
|
|
285
322
|
}
|
|
286
323
|
if (remove) {
|
|
287
324
|
element.remove();
|
|
288
325
|
}
|
|
289
326
|
});
|
|
327
|
+
resetEphemeralDomRefs();
|
|
290
328
|
highlightCode(true, container);
|
|
291
329
|
scheduleScroll();
|
|
292
330
|
}
|
|
@@ -347,6 +385,7 @@ class Body:
|
|
|
347
385
|
}
|
|
348
386
|
}
|
|
349
387
|
function appendStream(name_header, content, chunk, replace = false, is_code_block = false) {
|
|
388
|
+
dropIfDetached(); // clear references to detached elements
|
|
350
389
|
hideTips();
|
|
351
390
|
if (DEBUG_MODE) {
|
|
352
391
|
log("APPEND CHUNK: {" + chunk + "}, CONTENT: {"+content+"}, replace: " + replace + ", is_code_block: " + is_code_block);
|
|
@@ -561,6 +600,7 @@ class Body:
|
|
|
561
600
|
element.replaceChildren();
|
|
562
601
|
element.classList.add('empty_list');
|
|
563
602
|
}
|
|
603
|
+
resetEphemeralDomRefs();
|
|
564
604
|
}
|
|
565
605
|
function clearInput() {
|
|
566
606
|
const element = els.appendInput || document.getElementById('_append_input_');
|
|
@@ -585,9 +625,11 @@ class Body:
|
|
|
585
625
|
element.classList.add('hidden');
|
|
586
626
|
setTimeout(function() {
|
|
587
627
|
element.replaceChildren();
|
|
628
|
+
resetEphemeralDomRefs();
|
|
588
629
|
}, 1000);
|
|
589
630
|
} else {
|
|
590
631
|
element.replaceChildren();
|
|
632
|
+
resetEphemeralDomRefs();
|
|
591
633
|
}
|
|
592
634
|
}
|
|
593
635
|
}
|
pygpt_net/core/render/web/pid.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.08.
|
|
9
|
+
# Updated Date: 2025.08.19 07:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import io
|
|
@@ -36,6 +36,7 @@ class PidData:
|
|
|
36
36
|
self.last_time_called = 0
|
|
37
37
|
self.cooldown = 1 / 6
|
|
38
38
|
self.throttling_min_chars = 5000
|
|
39
|
+
self.header = None
|
|
39
40
|
|
|
40
41
|
@property
|
|
41
42
|
def buffer(self) -> str:
|
|
@@ -43,6 +44,9 @@ class PidData:
|
|
|
43
44
|
|
|
44
45
|
@buffer.setter
|
|
45
46
|
def buffer(self, value: str):
|
|
47
|
+
if value is None or value == "":
|
|
48
|
+
self._buffer.close()
|
|
49
|
+
self._buffer = io.StringIO()
|
|
46
50
|
self._buffer.seek(0)
|
|
47
51
|
self._buffer.truncate(0)
|
|
48
52
|
if value:
|
|
@@ -57,6 +61,9 @@ class PidData:
|
|
|
57
61
|
|
|
58
62
|
@live_buffer.setter
|
|
59
63
|
def live_buffer(self, value: str):
|
|
64
|
+
if value is None or value == "":
|
|
65
|
+
self._live_buffer.close()
|
|
66
|
+
self._live_buffer = io.StringIO()
|
|
60
67
|
self._live_buffer.seek(0)
|
|
61
68
|
self._live_buffer.truncate(0)
|
|
62
69
|
if value:
|
|
@@ -71,6 +78,9 @@ class PidData:
|
|
|
71
78
|
|
|
72
79
|
@html.setter
|
|
73
80
|
def html(self, value: str):
|
|
81
|
+
if value is None or value == "":
|
|
82
|
+
self._html.close()
|
|
83
|
+
self._html = io.StringIO()
|
|
74
84
|
self._html.seek(0)
|
|
75
85
|
self._html.truncate(0)
|
|
76
86
|
if value:
|
|
@@ -85,6 +95,9 @@ class PidData:
|
|
|
85
95
|
|
|
86
96
|
@document.setter
|
|
87
97
|
def document(self, value: str):
|
|
98
|
+
if value is None or value == "":
|
|
99
|
+
self._document.close()
|
|
100
|
+
self._document = io.StringIO()
|
|
88
101
|
self._document.seek(0)
|
|
89
102
|
self._document.truncate(0)
|
|
90
103
|
if value:
|
|
@@ -99,9 +112,12 @@ class PidData:
|
|
|
99
112
|
|
|
100
113
|
:param all: If True, clear all data, otherwise only buffers
|
|
101
114
|
"""
|
|
102
|
-
for
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
for name in ("_buffer", "_live_buffer", "_html", "_document"):
|
|
116
|
+
try:
|
|
117
|
+
getattr(self, name).close()
|
|
118
|
+
except Exception:
|
|
119
|
+
pass
|
|
120
|
+
setattr(self, name, io.StringIO())
|
|
105
121
|
|
|
106
122
|
if all:
|
|
107
123
|
self.item = None
|