pygpt-net 2.6.1__py3-none-any.whl → 2.6.6__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 +23 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +20 -1
- pygpt_net/config.py +55 -65
- pygpt_net/controller/__init__.py +5 -2
- pygpt_net/controller/calendar/note.py +101 -126
- pygpt_net/controller/chat/chat.py +38 -35
- pygpt_net/controller/chat/render.py +154 -214
- pygpt_net/controller/chat/response.py +5 -3
- pygpt_net/controller/chat/stream.py +92 -27
- pygpt_net/controller/config/config.py +39 -42
- pygpt_net/controller/config/field/checkbox.py +16 -12
- pygpt_net/controller/config/field/checkbox_list.py +36 -31
- pygpt_net/controller/config/field/cmd.py +51 -57
- pygpt_net/controller/config/field/combo.py +33 -16
- pygpt_net/controller/config/field/dictionary.py +48 -55
- pygpt_net/controller/config/field/input.py +50 -32
- pygpt_net/controller/config/field/slider.py +40 -45
- pygpt_net/controller/config/field/textarea.py +20 -6
- pygpt_net/controller/config/placeholder.py +110 -231
- pygpt_net/controller/ctx/common.py +48 -48
- pygpt_net/controller/ctx/ctx.py +91 -132
- pygpt_net/controller/lang/mapping.py +57 -95
- pygpt_net/controller/lang/plugins.py +64 -55
- pygpt_net/controller/lang/settings.py +39 -38
- pygpt_net/controller/layout/layout.py +176 -109
- pygpt_net/controller/mode/mode.py +88 -85
- pygpt_net/controller/model/model.py +73 -73
- pygpt_net/controller/plugins/plugins.py +209 -223
- pygpt_net/controller/plugins/presets.py +54 -55
- pygpt_net/controller/plugins/settings.py +54 -69
- pygpt_net/controller/presets/editor.py +33 -88
- pygpt_net/controller/presets/experts.py +20 -1
- pygpt_net/controller/presets/presets.py +293 -298
- pygpt_net/controller/settings/profile.py +16 -4
- pygpt_net/controller/theme/theme.py +72 -81
- pygpt_net/controller/ui/mode.py +118 -186
- pygpt_net/controller/ui/tabs.py +69 -90
- pygpt_net/controller/ui/ui.py +47 -56
- pygpt_net/controller/ui/vision.py +24 -23
- pygpt_net/core/agents/runner.py +15 -7
- pygpt_net/core/bridge/bridge.py +5 -5
- pygpt_net/core/command/command.py +149 -219
- pygpt_net/core/ctx/ctx.py +94 -146
- pygpt_net/core/debug/debug.py +48 -58
- pygpt_net/core/experts/experts.py +3 -3
- pygpt_net/core/models/models.py +74 -112
- pygpt_net/core/modes/modes.py +13 -21
- pygpt_net/core/plugins/plugins.py +154 -177
- pygpt_net/core/presets/presets.py +103 -176
- pygpt_net/core/render/web/body.py +217 -215
- pygpt_net/core/render/web/renderer.py +330 -474
- pygpt_net/core/text/utils.py +28 -44
- pygpt_net/core/tokens/tokens.py +104 -203
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/locale/locale.de.ini +2 -0
- pygpt_net/data/locale/locale.en.ini +2 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +2 -0
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/item/ctx.py +141 -139
- pygpt_net/plugin/agent/plugin.py +2 -1
- pygpt_net/plugin/audio_output/plugin.py +5 -2
- pygpt_net/plugin/base/plugin.py +101 -85
- pygpt_net/plugin/bitbucket/__init__.py +12 -0
- pygpt_net/plugin/bitbucket/config.py +267 -0
- pygpt_net/plugin/bitbucket/plugin.py +126 -0
- pygpt_net/plugin/bitbucket/worker.py +569 -0
- pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
- pygpt_net/plugin/cmd_custom/plugin.py +3 -2
- pygpt_net/plugin/cmd_files/plugin.py +3 -2
- pygpt_net/plugin/cmd_history/plugin.py +3 -2
- pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
- pygpt_net/plugin/cmd_serial/plugin.py +3 -2
- pygpt_net/plugin/cmd_system/plugin.py +3 -6
- pygpt_net/plugin/cmd_web/plugin.py +3 -2
- pygpt_net/plugin/experts/plugin.py +2 -2
- pygpt_net/plugin/facebook/__init__.py +12 -0
- pygpt_net/plugin/facebook/config.py +359 -0
- pygpt_net/plugin/facebook/plugin.py +113 -0
- pygpt_net/plugin/facebook/worker.py +698 -0
- pygpt_net/plugin/github/__init__.py +12 -0
- pygpt_net/plugin/github/config.py +441 -0
- pygpt_net/plugin/github/plugin.py +126 -0
- pygpt_net/plugin/github/worker.py +674 -0
- pygpt_net/plugin/google/__init__.py +12 -0
- pygpt_net/plugin/google/config.py +367 -0
- pygpt_net/plugin/google/plugin.py +126 -0
- pygpt_net/plugin/google/worker.py +826 -0
- pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
- pygpt_net/plugin/mailer/plugin.py +3 -5
- pygpt_net/plugin/openai_vision/plugin.py +3 -2
- pygpt_net/plugin/real_time/plugin.py +52 -60
- pygpt_net/plugin/slack/__init__.py +12 -0
- pygpt_net/plugin/slack/config.py +349 -0
- pygpt_net/plugin/slack/plugin.py +115 -0
- pygpt_net/plugin/slack/worker.py +639 -0
- pygpt_net/plugin/telegram/__init__.py +12 -0
- pygpt_net/plugin/telegram/config.py +308 -0
- pygpt_net/plugin/telegram/plugin.py +117 -0
- pygpt_net/plugin/telegram/worker.py +563 -0
- pygpt_net/plugin/twitter/__init__.py +12 -0
- pygpt_net/plugin/twitter/config.py +491 -0
- pygpt_net/plugin/twitter/plugin.py +125 -0
- pygpt_net/plugin/twitter/worker.py +837 -0
- pygpt_net/provider/agents/llama_index/legacy/openai_assistant.py +35 -3
- pygpt_net/tools/code_interpreter/tool.py +0 -1
- pygpt_net/tools/translator/tool.py +1 -1
- pygpt_net/ui/base/config_dialog.py +86 -100
- pygpt_net/ui/base/context_menu.py +48 -46
- pygpt_net/ui/dialog/preset.py +34 -77
- pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
- pygpt_net/ui/layout/toolbox/presets.py +41 -41
- pygpt_net/ui/main.py +49 -31
- pygpt_net/ui/tray.py +61 -60
- pygpt_net/ui/widget/calendar/select.py +86 -70
- pygpt_net/ui/widget/lists/attachment.py +86 -44
- pygpt_net/ui/widget/lists/base_list_combo.py +85 -33
- pygpt_net/ui/widget/lists/context.py +135 -188
- pygpt_net/ui/widget/lists/preset.py +59 -61
- pygpt_net/ui/widget/textarea/web.py +161 -48
- pygpt_net/utils.py +8 -1
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +164 -2
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +131 -103
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/entry_points.txt +0 -0
|
@@ -6,17 +6,14 @@
|
|
|
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.16 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
import html
|
|
13
12
|
import json
|
|
14
13
|
import os
|
|
15
14
|
import re
|
|
16
15
|
from datetime import datetime
|
|
17
|
-
from typing import Optional, List
|
|
18
|
-
|
|
19
|
-
from PySide6.QtCore import QTimer
|
|
16
|
+
from typing import Optional, List
|
|
20
17
|
|
|
21
18
|
from pygpt_net.core.render.base import BaseRenderer
|
|
22
19
|
from pygpt_net.core.text.utils import has_unclosed_code_tag
|
|
@@ -36,6 +33,16 @@ from pygpt_net.core.events import RenderEvent
|
|
|
36
33
|
class Renderer(BaseRenderer):
|
|
37
34
|
NODE_INPUT = 0
|
|
38
35
|
NODE_OUTPUT = 1
|
|
36
|
+
ENDINGS_CODE = (
|
|
37
|
+
"</code></pre></div>",
|
|
38
|
+
"</code></pre></div><br/>",
|
|
39
|
+
"</code></pre></div><br>"
|
|
40
|
+
)
|
|
41
|
+
ENDINGS_LIST = (
|
|
42
|
+
"</ul>",
|
|
43
|
+
"</ol>",
|
|
44
|
+
"</li>"
|
|
45
|
+
)
|
|
39
46
|
|
|
40
47
|
def __init__(self, window=None):
|
|
41
48
|
super(Renderer, self).__init__(window)
|
|
@@ -51,16 +58,17 @@ class Renderer(BaseRenderer):
|
|
|
51
58
|
self.pids = {} # per node data
|
|
52
59
|
self.prev_chunk_replace = False
|
|
53
60
|
self.prev_chunk_newline = False
|
|
54
|
-
|
|
55
|
-
self.
|
|
56
|
-
self.
|
|
57
|
-
self.
|
|
61
|
+
|
|
62
|
+
app_path = self.window.core.config.get_app_path() if self.window else ""
|
|
63
|
+
self._icon_expand = os.path.join(app_path, "data", "icons", "expand.svg")
|
|
64
|
+
self._icon_sync = os.path.join(app_path, "data", "icons", "sync.svg")
|
|
65
|
+
self._file_prefix = 'file:///' if self.window and self.window.core.platforms.is_windows() else 'file://'
|
|
58
66
|
|
|
59
67
|
def prepare(self):
|
|
60
68
|
"""
|
|
61
69
|
Prepare renderer
|
|
62
70
|
"""
|
|
63
|
-
self.pids
|
|
71
|
+
self.pids = {}
|
|
64
72
|
|
|
65
73
|
def on_load(self, meta: CtxMeta = None):
|
|
66
74
|
"""
|
|
@@ -73,7 +81,7 @@ class Renderer(BaseRenderer):
|
|
|
73
81
|
self.reset(meta)
|
|
74
82
|
self.parser.reset()
|
|
75
83
|
try:
|
|
76
|
-
node.page().runJavaScript(
|
|
84
|
+
node.page().runJavaScript("if (typeof window.prepare !== 'undefined') prepare();")
|
|
77
85
|
except Exception as e:
|
|
78
86
|
pass
|
|
79
87
|
|
|
@@ -88,28 +96,27 @@ class Renderer(BaseRenderer):
|
|
|
88
96
|
:param meta: context meta
|
|
89
97
|
:param tab: Tab
|
|
90
98
|
"""
|
|
91
|
-
if meta is None:
|
|
99
|
+
if meta is None or tab is None:
|
|
92
100
|
return
|
|
93
101
|
pid = tab.pid
|
|
94
102
|
if pid is None or pid not in self.pids:
|
|
95
103
|
return
|
|
96
|
-
|
|
97
|
-
p.loaded = True
|
|
104
|
+
self.pids[pid].loaded = True
|
|
98
105
|
node = self.get_output_node(meta)
|
|
99
106
|
|
|
100
|
-
if
|
|
107
|
+
if self.pids[pid].html != "" and not self.pids[pid].use_buffer:
|
|
101
108
|
self.clear_chunks_input(pid)
|
|
102
109
|
self.clear_chunks_output(pid)
|
|
103
110
|
self.clear_nodes(pid)
|
|
104
|
-
self.append(pid,
|
|
105
|
-
|
|
111
|
+
self.append(pid, self.pids[pid].html, flush=True)
|
|
112
|
+
self.pids[pid].html = ""
|
|
106
113
|
|
|
107
114
|
node.setUpdatesEnabled(True)
|
|
108
115
|
|
|
109
116
|
def get_pid(self, meta: CtxMeta):
|
|
110
117
|
"""
|
|
111
118
|
Get PID for context meta
|
|
112
|
-
|
|
119
|
+
|
|
113
120
|
:param meta: context PID
|
|
114
121
|
"""
|
|
115
122
|
return self.window.core.ctx.output.get_pid(meta)
|
|
@@ -117,7 +124,7 @@ class Renderer(BaseRenderer):
|
|
|
117
124
|
def get_or_create_pid(self, meta: CtxMeta):
|
|
118
125
|
"""
|
|
119
126
|
Get PID for context meta and create PID data (if not exists)
|
|
120
|
-
|
|
127
|
+
|
|
121
128
|
:param meta: context PID
|
|
122
129
|
"""
|
|
123
130
|
if meta is not None:
|
|
@@ -133,7 +140,7 @@ class Renderer(BaseRenderer):
|
|
|
133
140
|
):
|
|
134
141
|
"""
|
|
135
142
|
Create PID data
|
|
136
|
-
|
|
143
|
+
|
|
137
144
|
:param pid: PID
|
|
138
145
|
:param meta: context meta
|
|
139
146
|
"""
|
|
@@ -163,7 +170,6 @@ class Renderer(BaseRenderer):
|
|
|
163
170
|
:param state: state name
|
|
164
171
|
:param meta: context meta
|
|
165
172
|
"""
|
|
166
|
-
# BUSY: current pid only
|
|
167
173
|
if state == RenderEvent.STATE_BUSY:
|
|
168
174
|
if meta:
|
|
169
175
|
pid = self.get_pid(meta)
|
|
@@ -171,29 +177,27 @@ class Renderer(BaseRenderer):
|
|
|
171
177
|
node = self.get_output_node_by_pid(pid)
|
|
172
178
|
try:
|
|
173
179
|
node.page().runJavaScript(
|
|
174
|
-
|
|
180
|
+
"if (typeof window.showLoading !== 'undefined') showLoading();")
|
|
175
181
|
except Exception as e:
|
|
176
182
|
pass
|
|
177
183
|
|
|
178
|
-
# IDLE: all pids
|
|
179
184
|
elif state == RenderEvent.STATE_IDLE:
|
|
180
|
-
for pid in self.pids
|
|
185
|
+
for pid in self.pids:
|
|
181
186
|
node = self.get_output_node_by_pid(pid)
|
|
182
187
|
if node is not None:
|
|
183
188
|
try:
|
|
184
189
|
node.page().runJavaScript(
|
|
185
|
-
|
|
190
|
+
"if (typeof window.hideLoading !== 'undefined') hideLoading();")
|
|
186
191
|
except Exception as e:
|
|
187
192
|
pass
|
|
188
193
|
|
|
189
|
-
# ERROR: all pids
|
|
190
194
|
elif state == RenderEvent.STATE_ERROR:
|
|
191
|
-
for pid in self.pids
|
|
195
|
+
for pid in self.pids:
|
|
192
196
|
node = self.get_output_node_by_pid(pid)
|
|
193
197
|
if node is not None:
|
|
194
198
|
try:
|
|
195
199
|
node.page().runJavaScript(
|
|
196
|
-
|
|
200
|
+
"if (typeof window.hideLoading !== 'undefined') hideLoading();")
|
|
197
201
|
except Exception as e:
|
|
198
202
|
pass
|
|
199
203
|
|
|
@@ -213,7 +217,7 @@ class Renderer(BaseRenderer):
|
|
|
213
217
|
pid = self.get_or_create_pid(meta)
|
|
214
218
|
self.init(pid)
|
|
215
219
|
self.reset_names(meta)
|
|
216
|
-
self.tool_output_end()
|
|
220
|
+
self.tool_output_end()
|
|
217
221
|
self.prev_chunk_replace = False
|
|
218
222
|
|
|
219
223
|
def end(
|
|
@@ -229,14 +233,13 @@ class Renderer(BaseRenderer):
|
|
|
229
233
|
:param ctx: context item
|
|
230
234
|
:param stream: True if it is a stream
|
|
231
235
|
"""
|
|
232
|
-
self._stream_state.clear() # reset stream state
|
|
233
236
|
pid = self.get_or_create_pid(meta)
|
|
234
237
|
if pid is None:
|
|
235
238
|
return
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
self.
|
|
239
|
-
|
|
239
|
+
if self.pids[pid].item is not None and stream:
|
|
240
|
+
self.append_context_item(meta, self.pids[pid].item)
|
|
241
|
+
self.pids[pid].item = None
|
|
242
|
+
self.pids[pid].clear()
|
|
240
243
|
|
|
241
244
|
def end_extra(
|
|
242
245
|
self,
|
|
@@ -246,7 +249,7 @@ class Renderer(BaseRenderer):
|
|
|
246
249
|
):
|
|
247
250
|
"""
|
|
248
251
|
Render end extra
|
|
249
|
-
|
|
252
|
+
|
|
250
253
|
:param meta: context meta
|
|
251
254
|
:param ctx: context item
|
|
252
255
|
:param stream: True if it is a stream
|
|
@@ -260,10 +263,10 @@ class Renderer(BaseRenderer):
|
|
|
260
263
|
):
|
|
261
264
|
"""
|
|
262
265
|
Render stream begin
|
|
263
|
-
|
|
266
|
+
|
|
264
267
|
:param meta: context meta
|
|
265
268
|
:param ctx: context item
|
|
266
|
-
"""
|
|
269
|
+
"""
|
|
267
270
|
self.prev_chunk_replace = False
|
|
268
271
|
try:
|
|
269
272
|
self.get_output_node(meta).page().runJavaScript("beginStream();")
|
|
@@ -277,23 +280,19 @@ class Renderer(BaseRenderer):
|
|
|
277
280
|
):
|
|
278
281
|
"""
|
|
279
282
|
Render stream end
|
|
280
|
-
|
|
283
|
+
|
|
281
284
|
:param meta: context meta
|
|
282
285
|
:param ctx: context item
|
|
283
286
|
"""
|
|
284
|
-
self._stream_state.clear() # reset stream state
|
|
285
287
|
self.prev_chunk_replace = False
|
|
286
288
|
pid = self.get_or_create_pid(meta)
|
|
287
289
|
if pid is None:
|
|
288
290
|
return
|
|
289
|
-
|
|
290
|
-
p = self.pids[pid]
|
|
291
291
|
if self.window.controller.agent.legacy.enabled():
|
|
292
|
-
if
|
|
293
|
-
self.append_context_item(meta,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
p.clear() # reset buffers
|
|
292
|
+
if self.pids[pid].item is not None:
|
|
293
|
+
self.append_context_item(meta, self.pids[pid].item)
|
|
294
|
+
self.pids[pid].item = None
|
|
295
|
+
self.pids[pid].clear()
|
|
297
296
|
try:
|
|
298
297
|
self.get_output_node(meta).page().runJavaScript("endStream();")
|
|
299
298
|
except Exception as e:
|
|
@@ -307,7 +306,7 @@ class Renderer(BaseRenderer):
|
|
|
307
306
|
):
|
|
308
307
|
"""
|
|
309
308
|
Append all context to output
|
|
310
|
-
|
|
309
|
+
|
|
311
310
|
:param meta: Context meta
|
|
312
311
|
:param items: context items
|
|
313
312
|
:param clear: True if clear all output before append
|
|
@@ -321,35 +320,32 @@ class Renderer(BaseRenderer):
|
|
|
321
320
|
|
|
322
321
|
if clear:
|
|
323
322
|
self.reset(meta)
|
|
324
|
-
i = 0
|
|
325
323
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
p.html = ""
|
|
324
|
+
self.pids[pid].use_buffer = True
|
|
325
|
+
self.pids[pid].html = ""
|
|
329
326
|
prev_ctx = None
|
|
330
|
-
|
|
327
|
+
total = len(items)
|
|
328
|
+
for i, item in enumerate(items):
|
|
331
329
|
self.update_names(meta, item)
|
|
332
330
|
item.idx = i
|
|
333
331
|
if i == 0:
|
|
334
332
|
item.first = True
|
|
335
|
-
next_item = items[i + 1] if i + 1 <
|
|
333
|
+
next_item = items[i + 1] if i + 1 < total else None
|
|
336
334
|
self.append_context_item(
|
|
337
335
|
meta,
|
|
338
336
|
item,
|
|
339
337
|
prev_ctx=prev_ctx,
|
|
340
338
|
next_ctx=next_item
|
|
341
|
-
)
|
|
339
|
+
)
|
|
342
340
|
prev_ctx = item
|
|
343
|
-
|
|
344
|
-
p.use_buffer = False
|
|
341
|
+
self.pids[pid].use_buffer = False
|
|
345
342
|
|
|
346
|
-
|
|
347
|
-
if p.html != "":
|
|
343
|
+
if self.pids[pid].html != "":
|
|
348
344
|
self.append(
|
|
349
345
|
pid,
|
|
350
|
-
|
|
346
|
+
self.pids[pid].html,
|
|
351
347
|
flush=True
|
|
352
|
-
)
|
|
348
|
+
)
|
|
353
349
|
|
|
354
350
|
def append_input(
|
|
355
351
|
self, meta: CtxMeta,
|
|
@@ -365,7 +361,7 @@ class Renderer(BaseRenderer):
|
|
|
365
361
|
:param flush: flush HTML
|
|
366
362
|
:param append: True if force append node
|
|
367
363
|
"""
|
|
368
|
-
self.tool_output_end()
|
|
364
|
+
self.tool_output_end()
|
|
369
365
|
pid = self.get_or_create_pid(meta)
|
|
370
366
|
if not flush:
|
|
371
367
|
self.clear_chunks_input(pid)
|
|
@@ -376,7 +372,6 @@ class Renderer(BaseRenderer):
|
|
|
376
372
|
|
|
377
373
|
text = ctx.input
|
|
378
374
|
|
|
379
|
-
# if sub-reply
|
|
380
375
|
if isinstance(ctx.extra, dict) and "sub_reply" in ctx.extra and ctx.extra["sub_reply"]:
|
|
381
376
|
try:
|
|
382
377
|
json_encoded = json.loads(text)
|
|
@@ -387,18 +382,16 @@ class Renderer(BaseRenderer):
|
|
|
387
382
|
except json.JSONDecodeError:
|
|
388
383
|
pass
|
|
389
384
|
|
|
390
|
-
# hidden internal call
|
|
391
385
|
if ctx.internal \
|
|
392
386
|
and not ctx.first \
|
|
393
387
|
and not ctx.input.strip().startswith("user: ") \
|
|
394
|
-
and not ctx.input.strip().startswith("@"):
|
|
388
|
+
and not ctx.input.strip().startswith("@"):
|
|
395
389
|
return
|
|
396
390
|
else:
|
|
397
|
-
# don't show user prefix if provided in internal call goal update
|
|
398
391
|
if ctx.internal and ctx.input.startswith("user: "):
|
|
399
392
|
text = re.sub(r'^user: ', '> ', ctx.input)
|
|
400
393
|
|
|
401
|
-
if flush:
|
|
394
|
+
if flush:
|
|
402
395
|
if self.is_stream() and not append:
|
|
403
396
|
content = self.prepare_node(meta, ctx, text.strip(), self.NODE_INPUT)
|
|
404
397
|
self.append_chunk_input(meta, ctx, content, False)
|
|
@@ -416,24 +409,22 @@ class Renderer(BaseRenderer):
|
|
|
416
409
|
):
|
|
417
410
|
"""
|
|
418
411
|
Append text output to output
|
|
419
|
-
|
|
412
|
+
|
|
420
413
|
:param meta: context meta
|
|
421
414
|
:param ctx: context item
|
|
422
415
|
:param flush: flush HTML
|
|
423
416
|
:param prev_ctx: previous context
|
|
424
417
|
:param next_ctx: next context
|
|
425
418
|
"""
|
|
426
|
-
self.tool_output_end()
|
|
419
|
+
self.tool_output_end()
|
|
427
420
|
output = ctx.output
|
|
428
|
-
if
|
|
429
|
-
and "output" in ctx.extra
|
|
430
|
-
and ctx.extra["output"]):
|
|
421
|
+
if isinstance(ctx.extra, dict) and ctx.extra.get("output"):
|
|
431
422
|
if self.window.core.config.get("llama.idx.chat.agent.render.all", False):
|
|
432
|
-
output = "__agent_begin__
|
|
423
|
+
output = f"__agent_begin__{ctx.output}__agent_end__{ctx.extra['output']}"
|
|
433
424
|
else:
|
|
434
425
|
output = ctx.extra["output"]
|
|
435
426
|
else:
|
|
436
|
-
if
|
|
427
|
+
if not output:
|
|
437
428
|
return
|
|
438
429
|
self.append_node(
|
|
439
430
|
meta=meta,
|
|
@@ -444,124 +435,6 @@ class Renderer(BaseRenderer):
|
|
|
444
435
|
next_ctx=next_ctx
|
|
445
436
|
)
|
|
446
437
|
|
|
447
|
-
def _get_stream_state(self, pid: int):
|
|
448
|
-
"""
|
|
449
|
-
Get stream state for PID
|
|
450
|
-
|
|
451
|
-
:param pid: context PID
|
|
452
|
-
:return: dict – stream state
|
|
453
|
-
"""
|
|
454
|
-
# pid state for micro chunk
|
|
455
|
-
st = self._stream_state.get(pid)
|
|
456
|
-
if st is None:
|
|
457
|
-
st = {
|
|
458
|
-
"pending": [], # list[str] – uncommitted chunks
|
|
459
|
-
"pending_bytes": 0, # fast check for flush
|
|
460
|
-
"scheduled": False, # is flush scheduled?
|
|
461
|
-
"gen": 0, # count of stream generations (for flush)
|
|
462
|
-
"last_ctx": None, # last ctx for name_header
|
|
463
|
-
"last_meta": None, # last meta (to get_output_node)
|
|
464
|
-
}
|
|
465
|
-
self._stream_state[pid] = st
|
|
466
|
-
return st
|
|
467
|
-
|
|
468
|
-
def _schedule_flush(
|
|
469
|
-
self,
|
|
470
|
-
pid: int,
|
|
471
|
-
gen: int,
|
|
472
|
-
delay_ms: int
|
|
473
|
-
):
|
|
474
|
-
"""
|
|
475
|
-
Schedule flush for stream output
|
|
476
|
-
|
|
477
|
-
:param pid: context PID
|
|
478
|
-
:param gen: generation number (to avoid flush on reset)
|
|
479
|
-
:param delay_ms: delay in milliseconds
|
|
480
|
-
"""
|
|
481
|
-
# no leaks, only primitive closures
|
|
482
|
-
def _run():
|
|
483
|
-
# if reset or new generation, don't flush
|
|
484
|
-
st = self._stream_state.get(pid)
|
|
485
|
-
if not st or st["gen"] != gen:
|
|
486
|
-
return
|
|
487
|
-
self._flush_stream(pid)
|
|
488
|
-
QTimer.singleShot(max(0, int(delay_ms)), _run)
|
|
489
|
-
|
|
490
|
-
def _flush_stream(self, pid: int):
|
|
491
|
-
"""
|
|
492
|
-
Flush stream output
|
|
493
|
-
|
|
494
|
-
:param pid: context PID
|
|
495
|
-
"""
|
|
496
|
-
st = self._get_stream_state(pid)
|
|
497
|
-
if not st["pending"]:
|
|
498
|
-
st["scheduled"] = False
|
|
499
|
-
return
|
|
500
|
-
|
|
501
|
-
p = self.pids[pid]
|
|
502
|
-
|
|
503
|
-
# confirm cumulated chunks
|
|
504
|
-
raw_chunk = "".join(st["pending"])
|
|
505
|
-
st["pending"].clear()
|
|
506
|
-
st["pending_bytes"] = 0
|
|
507
|
-
st["scheduled"] = False
|
|
508
|
-
|
|
509
|
-
# header+update_names based on last ctx
|
|
510
|
-
ctx = st["last_ctx"] if st["last_ctx"] is not None else p.item
|
|
511
|
-
meta = st["last_meta"]
|
|
512
|
-
|
|
513
|
-
name_header = self.get_name_header(ctx)
|
|
514
|
-
self.update_names(meta, ctx)
|
|
515
|
-
|
|
516
|
-
# buffer commited only on flush (less copies)
|
|
517
|
-
# begin reset was already called in append_chunk gdy begin=True
|
|
518
|
-
p.append_buffer(raw_chunk)
|
|
519
|
-
buffer = p.buffer
|
|
520
|
-
|
|
521
|
-
has_unclosed = has_unclosed_code_tag
|
|
522
|
-
parse_buffer = buffer + "\n```" if has_unclosed(buffer) else buffer
|
|
523
|
-
html = self.parser.parse(parse_buffer)
|
|
524
|
-
|
|
525
|
-
code_endings = ("</code></pre></div>", "</code></pre></div><br/>", "</code></pre></div><br>")
|
|
526
|
-
list_endings = ("</ul>", "</ol>", "</li>")
|
|
527
|
-
is_code_block = html.endswith(code_endings)
|
|
528
|
-
is_list_block = html.endswith(list_endings)
|
|
529
|
-
|
|
530
|
-
newline_in_chunk = "\n" in raw_chunk
|
|
531
|
-
is_newline = newline_in_chunk or buffer.endswith("\n") or is_code_block or is_list_block
|
|
532
|
-
|
|
533
|
-
force_replace = self.prev_chunk_newline
|
|
534
|
-
self.prev_chunk_newline = newline_in_chunk
|
|
535
|
-
|
|
536
|
-
replace_bool = is_newline or force_replace
|
|
537
|
-
if is_code_block and not newline_in_chunk:
|
|
538
|
-
replace_bool = False
|
|
539
|
-
|
|
540
|
-
# prepare for DOM
|
|
541
|
-
out_chunk = raw_chunk
|
|
542
|
-
if not is_code_block:
|
|
543
|
-
out_chunk = out_chunk.replace("\n", "<br/>")
|
|
544
|
-
else:
|
|
545
|
-
if self.prev_chunk_replace and not has_unclosed(out_chunk):
|
|
546
|
-
out_chunk = "\n" + out_chunk
|
|
547
|
-
|
|
548
|
-
# JSON-escape
|
|
549
|
-
name_header_json = json.dumps(name_header)
|
|
550
|
-
html_json = json.dumps(html)
|
|
551
|
-
chunk_json = json.dumps(out_chunk)
|
|
552
|
-
|
|
553
|
-
self.prev_chunk_replace = replace_bool
|
|
554
|
-
|
|
555
|
-
replace_js = "true" if replace_bool else "false"
|
|
556
|
-
code_block_js = "true" if is_code_block else "false"
|
|
557
|
-
|
|
558
|
-
try:
|
|
559
|
-
self.get_output_node(meta).page().runJavaScript(
|
|
560
|
-
f"appendStream({name_header_json}, {html_json}, {chunk_json}, {replace_js}, {code_block_js});"
|
|
561
|
-
)
|
|
562
|
-
except Exception:
|
|
563
|
-
pass
|
|
564
|
-
|
|
565
438
|
def append_chunk(
|
|
566
439
|
self,
|
|
567
440
|
meta: CtxMeta,
|
|
@@ -570,7 +443,7 @@ class Renderer(BaseRenderer):
|
|
|
570
443
|
begin: bool = False
|
|
571
444
|
):
|
|
572
445
|
"""
|
|
573
|
-
Append output chunk to output
|
|
446
|
+
Append output chunk to output
|
|
574
447
|
|
|
575
448
|
:param meta: context meta
|
|
576
449
|
:param ctx: context item
|
|
@@ -578,56 +451,74 @@ class Renderer(BaseRenderer):
|
|
|
578
451
|
:param begin: if it is the beginning of the text
|
|
579
452
|
"""
|
|
580
453
|
pid = self.get_or_create_pid(meta)
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
# empty chunk
|
|
454
|
+
pctx = self.pids[pid]
|
|
455
|
+
pctx.item = ctx
|
|
585
456
|
if not text_chunk:
|
|
586
457
|
if begin:
|
|
587
|
-
|
|
458
|
+
pctx.clear()
|
|
588
459
|
return
|
|
589
460
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
461
|
+
name_header_str = self.get_name_header(ctx)
|
|
462
|
+
self.update_names(meta, ctx)
|
|
463
|
+
text_chunk = text_chunk if isinstance(text_chunk, str) else str(text_chunk)
|
|
464
|
+
text_chunk = text_chunk.translate({ord('<'): '<', ord('>'): '>'})
|
|
593
465
|
|
|
594
|
-
# on begin, reset generation and pending chunks
|
|
595
|
-
raw_chunk = text_chunk if isinstance(text_chunk, str) else str(text_chunk)
|
|
596
466
|
if begin:
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
467
|
+
if self.is_debug():
|
|
468
|
+
debug = self.append_debug(ctx, pid, "stream")
|
|
469
|
+
if debug:
|
|
470
|
+
text_chunk = debug + text_chunk
|
|
471
|
+
pctx.clear() # reset buffer
|
|
472
|
+
pctx.is_cmd = False # reset command flag
|
|
602
473
|
self.clear_chunks_output(pid)
|
|
603
474
|
self.prev_chunk_replace = False
|
|
604
475
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
476
|
+
pctx.append_buffer(text_chunk)
|
|
477
|
+
|
|
478
|
+
buffer = pctx.buffer
|
|
479
|
+
if has_unclosed_code_tag(buffer):
|
|
480
|
+
buffer_to_parse = "".join((buffer, "\n```"))
|
|
481
|
+
else:
|
|
482
|
+
buffer_to_parse = buffer
|
|
483
|
+
|
|
484
|
+
html = self.parser.parse(buffer_to_parse)
|
|
485
|
+
is_code_block = html.endswith(self.ENDINGS_CODE)
|
|
486
|
+
is_list = html.endswith(self.ENDINGS_LIST)
|
|
487
|
+
is_newline = ("\n" in text_chunk) or buffer.endswith("\n") or is_code_block
|
|
488
|
+
force_replace = False
|
|
489
|
+
if self.prev_chunk_newline:
|
|
490
|
+
force_replace = True
|
|
491
|
+
if "\n" in text_chunk:
|
|
492
|
+
self.prev_chunk_newline = True
|
|
493
|
+
else:
|
|
494
|
+
self.prev_chunk_newline = False
|
|
495
|
+
|
|
496
|
+
replace_bool = False
|
|
497
|
+
if is_newline or force_replace or is_list:
|
|
498
|
+
replace_bool = True
|
|
499
|
+
if is_code_block:
|
|
500
|
+
# don't replace if it is a code block
|
|
501
|
+
if "\n" not in text_chunk:
|
|
502
|
+
# if there is no newline in raw_chunk, then don't replace
|
|
503
|
+
replace_bool = False
|
|
504
|
+
|
|
505
|
+
if not is_code_block:
|
|
506
|
+
text_chunk = text_chunk.replace("\n", "<br/>")
|
|
629
507
|
else:
|
|
630
|
-
|
|
508
|
+
if self.prev_chunk_replace and not has_unclosed_code_tag(text_chunk):
|
|
509
|
+
# if previous chunk was replaced and current is code block, then add \n to chunk
|
|
510
|
+
text_chunk = "".join(("\n", text_chunk)) # add newline to chunk
|
|
511
|
+
|
|
512
|
+
self.prev_chunk_replace = replace_bool
|
|
513
|
+
try:
|
|
514
|
+
self.get_output_node(meta).page().bridge.chunk.emit(
|
|
515
|
+
name_header_str or "",
|
|
516
|
+
html if replace_bool else "",
|
|
517
|
+
text_chunk if not replace_bool else "",
|
|
518
|
+
bool(replace_bool),
|
|
519
|
+
bool(is_code_block),
|
|
520
|
+
)
|
|
521
|
+
except Exception as e:
|
|
631
522
|
pass
|
|
632
523
|
|
|
633
524
|
def next_chunk(
|
|
@@ -642,15 +533,14 @@ class Renderer(BaseRenderer):
|
|
|
642
533
|
:param ctx: context item
|
|
643
534
|
"""
|
|
644
535
|
pid = self.get_or_create_pid(meta)
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
p.buffer = "" # always reset buffer
|
|
536
|
+
self.pids[pid].item = ctx
|
|
537
|
+
self.pids[pid].buffer = ""
|
|
648
538
|
self.update_names(meta, ctx)
|
|
649
539
|
self.prev_chunk_replace = False
|
|
650
540
|
self.prev_chunk_newline = False
|
|
651
541
|
try:
|
|
652
542
|
self.get_output_node(meta).page().runJavaScript(
|
|
653
|
-
|
|
543
|
+
"nextStream();")
|
|
654
544
|
except Exception as e:
|
|
655
545
|
pass
|
|
656
546
|
|
|
@@ -699,32 +589,27 @@ class Renderer(BaseRenderer):
|
|
|
699
589
|
:param begin: if it is the beginning of the text
|
|
700
590
|
"""
|
|
701
591
|
pid = self.get_or_create_pid(meta)
|
|
702
|
-
|
|
703
|
-
p.item = ctx
|
|
592
|
+
self.pids[pid].item = ctx
|
|
704
593
|
if text_chunk is None or text_chunk == "":
|
|
705
594
|
if begin:
|
|
706
|
-
|
|
595
|
+
self.pids[pid].live_buffer = ""
|
|
707
596
|
return
|
|
708
597
|
self.update_names(meta, ctx)
|
|
709
|
-
raw_chunk = str(text_chunk)
|
|
710
|
-
raw_chunk = raw_chunk.replace("<", "<")
|
|
711
|
-
raw_chunk = raw_chunk.replace(">", ">")
|
|
598
|
+
raw_chunk = str(text_chunk).translate({ord('<'): '<', ord('>'): '>'})
|
|
712
599
|
if begin:
|
|
713
|
-
# debug
|
|
714
600
|
debug = ""
|
|
715
601
|
if self.is_debug():
|
|
716
602
|
debug = self.append_debug(ctx, pid, "stream")
|
|
717
603
|
if debug:
|
|
718
604
|
raw_chunk = debug + raw_chunk
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
self.clear_live(meta, ctx)
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
to_append += "\n```" # fix for code block without closing ```
|
|
605
|
+
self.pids[pid].live_buffer = ""
|
|
606
|
+
self.pids[pid].is_cmd = False
|
|
607
|
+
self.clear_live(meta, ctx)
|
|
608
|
+
self.pids[pid].append_live_buffer(raw_chunk)
|
|
609
|
+
|
|
610
|
+
to_append = self.pids[pid].live_buffer
|
|
611
|
+
if has_unclosed_code_tag(self.pids[pid].live_buffer):
|
|
612
|
+
to_append += "\n```"
|
|
728
613
|
html = self.parser.parse(to_append)
|
|
729
614
|
escaped_chunk = json.dumps(html)
|
|
730
615
|
try:
|
|
@@ -744,7 +629,7 @@ class Renderer(BaseRenderer):
|
|
|
744
629
|
return
|
|
745
630
|
pid = self.get_or_create_pid(meta)
|
|
746
631
|
if not self.pids[pid].loaded:
|
|
747
|
-
js = "var element = document.getElementById('_append_live_');
|
|
632
|
+
js = "var element = document.getElementById('_append_live_');if (element) { element.innerHTML = ''; }"
|
|
748
633
|
else:
|
|
749
634
|
js = "clearLive();"
|
|
750
635
|
try:
|
|
@@ -795,19 +680,18 @@ class Renderer(BaseRenderer):
|
|
|
795
680
|
):
|
|
796
681
|
"""
|
|
797
682
|
Append text to output
|
|
798
|
-
|
|
683
|
+
|
|
799
684
|
:param pid: ctx pid
|
|
800
685
|
:param html: HTML code
|
|
801
686
|
:param flush: True if flush only
|
|
802
687
|
"""
|
|
803
|
-
|
|
804
|
-
if p.loaded and not p.use_buffer:
|
|
688
|
+
if self.pids[pid].loaded and not self.pids[pid].use_buffer:
|
|
805
689
|
self.clear_chunks(pid)
|
|
806
|
-
self.flush_output(pid, html)
|
|
807
|
-
|
|
690
|
+
self.flush_output(pid, html)
|
|
691
|
+
self.pids[pid].html = ""
|
|
808
692
|
else:
|
|
809
693
|
if not flush:
|
|
810
|
-
|
|
694
|
+
self.pids[pid].append_html(html)
|
|
811
695
|
|
|
812
696
|
def append_context_item(
|
|
813
697
|
self,
|
|
@@ -835,7 +719,7 @@ class Renderer(BaseRenderer):
|
|
|
835
719
|
flush=False,
|
|
836
720
|
prev_ctx=prev_ctx,
|
|
837
721
|
next_ctx=next_ctx
|
|
838
|
-
)
|
|
722
|
+
)
|
|
839
723
|
|
|
840
724
|
def append_extra(
|
|
841
725
|
self,
|
|
@@ -846,92 +730,87 @@ class Renderer(BaseRenderer):
|
|
|
846
730
|
) -> str:
|
|
847
731
|
"""
|
|
848
732
|
Append extra data (images, files, etc.) to output
|
|
849
|
-
|
|
733
|
+
|
|
850
734
|
:param meta: context meta
|
|
851
735
|
:param ctx: context item
|
|
852
736
|
:param footer: True if it is a footer
|
|
853
737
|
:param render: True if render, False if only return HTML
|
|
854
738
|
:return: HTML code
|
|
855
739
|
"""
|
|
856
|
-
self.tool_output_end()
|
|
857
|
-
|
|
740
|
+
self.tool_output_end()
|
|
741
|
+
|
|
858
742
|
pid = self.get_pid(meta)
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
# images
|
|
743
|
+
appended = set()
|
|
744
|
+
html_parts = []
|
|
745
|
+
|
|
863
746
|
c = len(ctx.images)
|
|
864
747
|
if c > 0:
|
|
865
748
|
n = 1
|
|
866
749
|
for image in ctx.images:
|
|
867
750
|
if image is None:
|
|
868
751
|
continue
|
|
869
|
-
|
|
870
|
-
# if image.startswith("http"):
|
|
871
|
-
# continue
|
|
872
|
-
if image in appended or image in p.images_appended:
|
|
752
|
+
if image in appended or image in self.pids[pid].images_appended:
|
|
873
753
|
continue
|
|
874
754
|
try:
|
|
875
|
-
appended.
|
|
876
|
-
|
|
877
|
-
|
|
755
|
+
appended.add(image)
|
|
756
|
+
html_parts.append(self.body.get_image_html(image, n, c))
|
|
757
|
+
self.pids[pid].images_appended.append(image)
|
|
878
758
|
n += 1
|
|
879
759
|
except Exception as e:
|
|
880
760
|
pass
|
|
881
761
|
|
|
882
|
-
# files and attachments, TODO check attachments
|
|
883
762
|
c = len(ctx.files)
|
|
884
763
|
if c > 0:
|
|
885
764
|
files_html = []
|
|
886
765
|
n = 1
|
|
887
766
|
for file in ctx.files:
|
|
888
|
-
if file in appended or file in
|
|
767
|
+
if file in appended or file in self.pids[pid].files_appended:
|
|
889
768
|
continue
|
|
890
769
|
try:
|
|
891
|
-
appended.
|
|
892
|
-
files_html.append(body.get_file_html(file, n, c))
|
|
893
|
-
|
|
770
|
+
appended.add(file)
|
|
771
|
+
files_html.append(self.body.get_file_html(file, n, c))
|
|
772
|
+
self.pids[pid].files_appended.append(file)
|
|
894
773
|
n += 1
|
|
895
774
|
except Exception as e:
|
|
896
775
|
pass
|
|
897
776
|
if files_html:
|
|
898
|
-
|
|
777
|
+
html_parts.append("<br/><br/>".join(files_html))
|
|
899
778
|
|
|
900
|
-
# urls
|
|
901
779
|
c = len(ctx.urls)
|
|
902
780
|
if c > 0:
|
|
903
781
|
urls_html = []
|
|
904
782
|
n = 1
|
|
905
783
|
for url in ctx.urls:
|
|
906
|
-
if url in appended or url in
|
|
784
|
+
if url in appended or url in self.pids[pid].urls_appended:
|
|
907
785
|
continue
|
|
908
786
|
try:
|
|
909
|
-
appended.
|
|
910
|
-
urls_html.append(body.get_url_html(url, n, c))
|
|
911
|
-
|
|
787
|
+
appended.add(url)
|
|
788
|
+
urls_html.append(self.body.get_url_html(url, n, c))
|
|
789
|
+
self.pids[pid].urls_appended.append(url)
|
|
912
790
|
n += 1
|
|
913
791
|
except Exception as e:
|
|
914
792
|
pass
|
|
915
793
|
if urls_html:
|
|
916
|
-
|
|
794
|
+
html_parts.append("<br/><br/>".join(urls_html))
|
|
917
795
|
|
|
918
|
-
# docs json
|
|
919
796
|
if self.window.core.config.get('ctx.sources'):
|
|
920
797
|
if ctx.doc_ids is not None and len(ctx.doc_ids) > 0:
|
|
921
798
|
try:
|
|
922
|
-
docs = body.get_docs_html(ctx.doc_ids)
|
|
923
|
-
|
|
799
|
+
docs = self.body.get_docs_html(ctx.doc_ids)
|
|
800
|
+
html_parts.append(docs)
|
|
924
801
|
except Exception as e:
|
|
925
802
|
pass
|
|
926
|
-
|
|
803
|
+
|
|
804
|
+
html = "".join(html_parts)
|
|
927
805
|
if render and html != "":
|
|
928
806
|
if footer:
|
|
929
|
-
# append to output
|
|
930
807
|
self.append(pid, html)
|
|
931
808
|
else:
|
|
932
|
-
# append to existing message box using JS
|
|
933
809
|
escaped_html = json.dumps(html)
|
|
934
|
-
|
|
810
|
+
try:
|
|
811
|
+
self.get_output_node(meta).page().runJavaScript(f"appendExtra('{ctx.id}',{escaped_html});")
|
|
812
|
+
except Exception as e:
|
|
813
|
+
pass
|
|
935
814
|
|
|
936
815
|
return html
|
|
937
816
|
|
|
@@ -958,7 +837,7 @@ class Renderer(BaseRenderer):
|
|
|
958
837
|
if timestamp is not None:
|
|
959
838
|
ts = datetime.fromtimestamp(timestamp)
|
|
960
839
|
hour = ts.strftime("%H:%M:%S")
|
|
961
|
-
text = '<span class="ts">{}: </span>{}'
|
|
840
|
+
text = f'<span class="ts">{hour}: </span>{text}'
|
|
962
841
|
return text
|
|
963
842
|
|
|
964
843
|
def reset(
|
|
@@ -971,16 +850,13 @@ class Renderer(BaseRenderer):
|
|
|
971
850
|
:param meta: Context meta
|
|
972
851
|
"""
|
|
973
852
|
pid = self.get_pid(meta)
|
|
974
|
-
if pid is not None and pid in self.pids:
|
|
853
|
+
if pid is not None and pid in self.pids:
|
|
975
854
|
self.reset_by_pid(pid)
|
|
976
855
|
else:
|
|
977
|
-
# there is no pid here if empty context so check for meta, and clear current
|
|
978
856
|
if meta is not None:
|
|
979
|
-
# create new PID using only meta
|
|
980
857
|
pid = self.get_or_create_pid(meta)
|
|
981
858
|
self.reset_by_pid(pid)
|
|
982
859
|
|
|
983
|
-
# clear live output
|
|
984
860
|
self.clear_live(meta, CtxItem())
|
|
985
861
|
|
|
986
862
|
def reset_by_pid(self, pid: Optional[int]):
|
|
@@ -989,15 +865,19 @@ class Renderer(BaseRenderer):
|
|
|
989
865
|
|
|
990
866
|
:param pid: context PID
|
|
991
867
|
"""
|
|
992
|
-
p = self.pids.get(pid)
|
|
993
868
|
self.parser.reset()
|
|
994
|
-
|
|
869
|
+
self.pids[pid].item = None
|
|
870
|
+
self.pids[pid].html = ""
|
|
995
871
|
self.clear_nodes(pid)
|
|
996
872
|
self.clear_chunks(pid)
|
|
997
|
-
self.
|
|
873
|
+
self.pids[pid].images_appended = []
|
|
874
|
+
self.pids[pid].urls_appended = []
|
|
875
|
+
self.pids[pid].files_appended = []
|
|
876
|
+
node = self.get_output_node_by_pid(pid)
|
|
877
|
+
if node is not None:
|
|
878
|
+
node.reset_current_content()
|
|
998
879
|
self.reset_names_by_pid(pid)
|
|
999
880
|
self.prev_chunk_replace = False
|
|
1000
|
-
self._stream_state.clear() # reset stream state
|
|
1001
881
|
|
|
1002
882
|
def clear_input(self):
|
|
1003
883
|
"""Clear input"""
|
|
@@ -1038,8 +918,7 @@ class Renderer(BaseRenderer):
|
|
|
1038
918
|
if pid is None:
|
|
1039
919
|
return
|
|
1040
920
|
if not self.pids[pid].loaded:
|
|
1041
|
-
js = "var element = document.getElementById('_append_input_');"
|
|
1042
|
-
js += "if (element) { element.innerHTML = ''; }"
|
|
921
|
+
js = "var element = document.getElementById('_append_input_');if (element) { element.innerHTML = ''; }"
|
|
1043
922
|
else:
|
|
1044
923
|
js = "clearInput();"
|
|
1045
924
|
try:
|
|
@@ -1058,8 +937,7 @@ class Renderer(BaseRenderer):
|
|
|
1058
937
|
"""
|
|
1059
938
|
self.prev_chunk_replace = False
|
|
1060
939
|
if not self.pids[pid].loaded:
|
|
1061
|
-
js = "var element = document.getElementById('_append_output_');"
|
|
1062
|
-
js += "if (element) { element.innerHTML = ''; }"
|
|
940
|
+
js = "var element = document.getElementById('_append_output_');if (element) { element.innerHTML = ''; }"
|
|
1063
941
|
else:
|
|
1064
942
|
js = "clearOutput();"
|
|
1065
943
|
try:
|
|
@@ -1077,8 +955,7 @@ class Renderer(BaseRenderer):
|
|
|
1077
955
|
:pid: context PID
|
|
1078
956
|
"""
|
|
1079
957
|
if not self.pids[pid].loaded:
|
|
1080
|
-
js = "var element = document.getElementById('_nodes_');"
|
|
1081
|
-
js += "if (element) { element.innerHTML = ''; }"
|
|
958
|
+
js = "var element = document.getElementById('_nodes_');if (element) { element.innerHTML = ''; }"
|
|
1082
959
|
else:
|
|
1083
960
|
js = "clearNodes();"
|
|
1084
961
|
try:
|
|
@@ -1097,7 +974,7 @@ class Renderer(BaseRenderer):
|
|
|
1097
974
|
) -> str:
|
|
1098
975
|
"""
|
|
1099
976
|
Prepare node HTML
|
|
1100
|
-
|
|
977
|
+
|
|
1101
978
|
:param meta: context meta
|
|
1102
979
|
:param ctx: CtxItem instance
|
|
1103
980
|
:param html: html text
|
|
@@ -1142,48 +1019,42 @@ class Renderer(BaseRenderer):
|
|
|
1142
1019
|
:param next_ctx: next context item
|
|
1143
1020
|
:return: prepared HTML
|
|
1144
1021
|
"""
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
post_format_text = helpers.post_format_text
|
|
1148
|
-
append_timestamp = self.append_timestamp
|
|
1149
|
-
append_debug = self.append_debug
|
|
1150
|
-
is_debug = self.is_debug
|
|
1151
|
-
pids = self.pids
|
|
1152
|
-
node_input = self.NODE_INPUT
|
|
1153
|
-
|
|
1154
|
-
msg_id = f"msg-user-{ctx.id}" if ctx is not None else ""
|
|
1155
|
-
|
|
1156
|
-
content = append_timestamp(
|
|
1022
|
+
msg_id = "msg-user-" + str(ctx.id) if ctx is not None else ""
|
|
1023
|
+
content = self.append_timestamp(
|
|
1157
1024
|
ctx,
|
|
1158
|
-
format_user_text(html),
|
|
1159
|
-
type=
|
|
1025
|
+
self.helpers.format_user_text(html),
|
|
1026
|
+
type=self.NODE_INPUT
|
|
1160
1027
|
)
|
|
1161
|
-
html =
|
|
1028
|
+
html = f"<p>{content}</p>"
|
|
1029
|
+
html = self.helpers.post_format_text(html)
|
|
1030
|
+
name = self.pids[pid].name_user
|
|
1162
1031
|
|
|
1163
|
-
name = pids[pid].name_user
|
|
1164
1032
|
if ctx.internal and ctx.input.startswith("[{"):
|
|
1165
1033
|
name = trans("msg.name.system")
|
|
1166
1034
|
if type(ctx.extra) is dict and "agent_evaluate" in ctx.extra:
|
|
1167
1035
|
name = trans("msg.name.evaluation")
|
|
1168
1036
|
|
|
1169
|
-
debug =
|
|
1037
|
+
debug = ""
|
|
1038
|
+
if self.is_debug():
|
|
1039
|
+
debug = self.append_debug(ctx, pid, "input")
|
|
1170
1040
|
|
|
1171
|
-
extra = ""
|
|
1172
1041
|
extra_style = ""
|
|
1042
|
+
extra = ""
|
|
1173
1043
|
if ctx.extra is not None and "footer" in ctx.extra:
|
|
1174
1044
|
extra = ctx.extra["footer"]
|
|
1175
1045
|
extra_style = "display:block;"
|
|
1046
|
+
html = (
|
|
1047
|
+
f'<div class="msg-box msg-user" id="{msg_id}">'
|
|
1048
|
+
f'<div class="name-header name-user">{name}</div>'
|
|
1049
|
+
f'<div class="msg">'
|
|
1050
|
+
f'{html}'
|
|
1051
|
+
f'<div class="msg-extra" style="{extra_style}">{extra}</div>'
|
|
1052
|
+
f'{debug}'
|
|
1053
|
+
f'</div>'
|
|
1054
|
+
f'</div>'
|
|
1055
|
+
)
|
|
1176
1056
|
|
|
1177
|
-
return
|
|
1178
|
-
f'<div class="msg-box msg-user" id="{msg_id}">',
|
|
1179
|
-
f'<div class="name-header name-user">{name}</div>',
|
|
1180
|
-
'<div class="msg">',
|
|
1181
|
-
html,
|
|
1182
|
-
f'<div class="msg-extra" style="{extra_style}">{extra}</div>',
|
|
1183
|
-
debug,
|
|
1184
|
-
'</div>',
|
|
1185
|
-
'</div>',
|
|
1186
|
-
))
|
|
1057
|
+
return html
|
|
1187
1058
|
|
|
1188
1059
|
def prepare_node_output(
|
|
1189
1060
|
self,
|
|
@@ -1203,97 +1074,86 @@ class Renderer(BaseRenderer):
|
|
|
1203
1074
|
:param next_ctx: next context item
|
|
1204
1075
|
:return: prepared HTML
|
|
1205
1076
|
"""
|
|
1206
|
-
helpers = self.helpers
|
|
1207
|
-
pre_format = helpers.pre_format_text
|
|
1208
|
-
post_format = helpers.post_format_text
|
|
1209
|
-
format_cmd = helpers.format_cmd_text
|
|
1210
|
-
parser = self.parser
|
|
1211
|
-
append_timestamp = self.append_timestamp
|
|
1212
|
-
append_extra = self.append_extra
|
|
1213
|
-
body = self.body
|
|
1214
|
-
prepare_action_icons = body.prepare_action_icons
|
|
1215
|
-
prepare_tool_extra = body.prepare_tool_extra
|
|
1216
|
-
get_name_header = self.get_name_header
|
|
1217
|
-
is_debug = self.is_debug
|
|
1218
|
-
append_debug = self.append_debug
|
|
1219
|
-
get_or_create_pid = self.get_or_create_pid
|
|
1220
|
-
node_output = self.NODE_OUTPUT
|
|
1221
|
-
trans_expand = trans('action.cmd.expand')
|
|
1222
|
-
|
|
1223
|
-
str_id = str(ctx.id)
|
|
1224
|
-
msg_id = f"msg-bot-{ctx.id}" if ctx is not None else ""
|
|
1225
|
-
cmds_len = len(ctx.cmds)
|
|
1226
|
-
extra_ctx_len = len(ctx.extra_ctx) if ctx.extra_ctx is not None else 0
|
|
1227
1077
|
is_cmd = (
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
)
|
|
1232
|
-
|
|
1233
|
-
pid = get_or_create_pid(meta)
|
|
1234
|
-
html = pre_format(html)
|
|
1235
|
-
html = parser.parse(html)
|
|
1236
|
-
html = append_timestamp(ctx, html, type=node_output)
|
|
1237
|
-
html = post_format(html)
|
|
1238
|
-
|
|
1239
|
-
extra = append_extra(meta, ctx, footer=True, render=False)
|
|
1240
|
-
footer = prepare_action_icons(ctx)
|
|
1241
|
-
|
|
1242
|
-
app_path = self.window.core.config.get_app_path()
|
|
1243
|
-
expand_icon_path = os.path.join(app_path, "data", "icons", "expand.svg")
|
|
1244
|
-
cmd_icon = (
|
|
1245
|
-
f'<img src="file://{expand_icon_path}" width="25" height="25" valign="middle">'
|
|
1246
|
-
)
|
|
1247
|
-
expand_btn = (
|
|
1248
|
-
f"<span class='toggle-cmd-output' onclick='toggleToolOutput({str_id});' "
|
|
1249
|
-
f"title='{trans_expand}' role='button'>{cmd_icon}</span>"
|
|
1078
|
+
next_ctx is not None and
|
|
1079
|
+
next_ctx.internal and
|
|
1080
|
+
(len(ctx.cmds) > 0 or (ctx.extra_ctx is not None and len(ctx.extra_ctx) > 0))
|
|
1250
1081
|
)
|
|
1082
|
+
pid = self.get_or_create_pid(meta)
|
|
1083
|
+
msg_id = f"msg-bot-{ctx.id}" if ctx is not None else ""
|
|
1084
|
+
html = self.helpers.pre_format_text(html)
|
|
1085
|
+
html = self.parser.parse(html)
|
|
1086
|
+
html = self.append_timestamp(ctx, html, type=self.NODE_OUTPUT)
|
|
1087
|
+
html = self.helpers.post_format_text(html)
|
|
1088
|
+
extra = self.append_extra(meta, ctx, footer=True, render=False)
|
|
1089
|
+
footer = self.body.prepare_action_icons(ctx)
|
|
1251
1090
|
|
|
1252
1091
|
tool_output = ""
|
|
1092
|
+
spinner = ""
|
|
1253
1093
|
output_class = "display:none"
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
and "agent_step" in ctx.extra
|
|
1094
|
+
cmd_icon = f'<img src="{self._file_prefix}{self._icon_expand}" width="25" height="25" valign="middle">'
|
|
1095
|
+
expand_btn = (
|
|
1096
|
+
f"<span class='toggle-cmd-output' onclick='toggleToolOutput({ctx.id});' title='{trans('action.cmd.expand')}' "
|
|
1097
|
+
f"role='button'>{cmd_icon}</span>"
|
|
1259
1098
|
)
|
|
1260
1099
|
|
|
1261
1100
|
if is_cmd:
|
|
1262
|
-
if
|
|
1263
|
-
|
|
1264
|
-
|
|
1101
|
+
if ctx.results is not None and len(ctx.results) > 0 \
|
|
1102
|
+
and isinstance(ctx.extra, dict) and "agent_step" in ctx.extra:
|
|
1103
|
+
tool_output = self.helpers.format_cmd_text(str(ctx.input))
|
|
1104
|
+
output_class = ""
|
|
1265
1105
|
else:
|
|
1266
|
-
tool_output =
|
|
1267
|
-
output_class = ""
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1106
|
+
tool_output = self.helpers.format_cmd_text(str(next_ctx.input))
|
|
1107
|
+
output_class = ""
|
|
1108
|
+
|
|
1109
|
+
elif ctx.results is not None and len(ctx.results) > 0 \
|
|
1110
|
+
and isinstance(ctx.extra, dict) and "agent_step" in ctx.extra:
|
|
1111
|
+
tool_output = self.helpers.format_cmd_text(str(ctx.input))
|
|
1112
|
+
else:
|
|
1113
|
+
out = (getattr(ctx, "output", "") or "")
|
|
1114
|
+
cmds = getattr(ctx, "cmds", ())
|
|
1115
|
+
if next_ctx is None and (
|
|
1116
|
+
cmds
|
|
1117
|
+
or out.startswith(('<tool>{"cmd"', '<tool>{"cmd"'))
|
|
1118
|
+
or out.rstrip().endswith(('}</tool>', '}</tool>'))
|
|
1119
|
+
):
|
|
1120
|
+
spinner_class = "" if ctx.live else "display:none"
|
|
1121
|
+
spinner = (
|
|
1122
|
+
f'<span class="spinner" style="{spinner_class}">'
|
|
1123
|
+
f'<img src="{self._file_prefix}{self._icon_sync}" width="30" height="30" '
|
|
1124
|
+
f'class="loading"></span>'
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
html_tools = (
|
|
1128
|
+
f'<div class="tool-output" style="{output_class}">' +
|
|
1129
|
+
expand_btn +
|
|
1130
|
+
'<div class="content" style="display:none">' +
|
|
1131
|
+
tool_output +
|
|
1132
|
+
'</div></div>'
|
|
1133
|
+
)
|
|
1134
|
+
tool_extra = self.body.prepare_tool_extra(ctx)
|
|
1135
|
+
|
|
1136
|
+
debug = ""
|
|
1137
|
+
if self.is_debug():
|
|
1138
|
+
debug = self.append_debug(ctx, pid, "output")
|
|
1139
|
+
|
|
1140
|
+
name_header = self.get_name_header(ctx)
|
|
1141
|
+
html = (
|
|
1142
|
+
f'<div class="msg-box msg-bot" id="{msg_id}">' +
|
|
1143
|
+
name_header +
|
|
1144
|
+
'<div class="msg">' +
|
|
1145
|
+
f'{html}' +
|
|
1146
|
+
f'{spinner}' +
|
|
1147
|
+
f'<div class="msg-tool-extra">{tool_extra}</div>' +
|
|
1148
|
+
f'{html_tools}' +
|
|
1149
|
+
f'<div class="msg-extra">{extra}</div>' +
|
|
1150
|
+
f'{footer}' +
|
|
1151
|
+
f'{debug}' +
|
|
1152
|
+
'</div>' +
|
|
1153
|
+
'</div>'
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
return html
|
|
1297
1157
|
|
|
1298
1158
|
def get_name_header(self, ctx: CtxItem) -> str:
|
|
1299
1159
|
"""
|
|
@@ -1323,11 +1183,7 @@ class Renderer(BaseRenderer):
|
|
|
1323
1183
|
avatars_dir = os.path.join(presets_dir, "avatars")
|
|
1324
1184
|
avatar_path = os.path.join(avatars_dir, preset.ai_avatar)
|
|
1325
1185
|
if os.path.exists(avatar_path):
|
|
1326
|
-
|
|
1327
|
-
prefix = 'file:///'
|
|
1328
|
-
else:
|
|
1329
|
-
prefix = 'file://'
|
|
1330
|
-
avatar_html = f"<img src=\"{prefix}{avatar_path}\" class=\"avatar\"> "
|
|
1186
|
+
avatar_html = f"<img src=\"{self._file_prefix}{avatar_path}\" class=\"avatar\"> "
|
|
1331
1187
|
|
|
1332
1188
|
if not output_name and not avatar_html:
|
|
1333
1189
|
return ""
|
|
@@ -1348,13 +1204,12 @@ class Renderer(BaseRenderer):
|
|
|
1348
1204
|
try:
|
|
1349
1205
|
node = self.get_output_node_by_pid(pid)
|
|
1350
1206
|
node.page().runJavaScript(f"if (typeof window.appendNode !== 'undefined') appendNode({escaped_html});")
|
|
1351
|
-
node.update_current_content()
|
|
1352
1207
|
except Exception as e:
|
|
1353
1208
|
pass
|
|
1354
1209
|
|
|
1355
1210
|
def reload(self):
|
|
1356
1211
|
"""Reload output, called externally only on theme change to redraw content"""
|
|
1357
|
-
self.window.controller.ctx.refresh_output()
|
|
1212
|
+
self.window.controller.ctx.refresh_output()
|
|
1358
1213
|
|
|
1359
1214
|
def flush(
|
|
1360
1215
|
self,
|
|
@@ -1367,11 +1222,12 @@ class Renderer(BaseRenderer):
|
|
|
1367
1222
|
:param pid: context PID
|
|
1368
1223
|
"""
|
|
1369
1224
|
if self.pids[pid].loaded:
|
|
1370
|
-
return
|
|
1225
|
+
return
|
|
1371
1226
|
|
|
1372
1227
|
html = self.body.get_html(pid)
|
|
1373
|
-
self.
|
|
1374
|
-
|
|
1228
|
+
node = self.get_output_node_by_pid(pid)
|
|
1229
|
+
if node is not None:
|
|
1230
|
+
node.setHtml(html, baseUrl="file://")
|
|
1375
1231
|
|
|
1376
1232
|
def fresh(
|
|
1377
1233
|
self,
|
|
@@ -1385,14 +1241,16 @@ class Renderer(BaseRenderer):
|
|
|
1385
1241
|
pid = self.get_or_create_pid(meta)
|
|
1386
1242
|
if pid is None:
|
|
1387
1243
|
return
|
|
1388
|
-
p = self.pids[pid]
|
|
1389
1244
|
html = self.body.get_html(pid)
|
|
1390
|
-
|
|
1391
|
-
p.loaded = False
|
|
1392
|
-
p.document = html
|
|
1245
|
+
self.pids[pid].loaded = False
|
|
1393
1246
|
node = self.get_output_node_by_pid(pid)
|
|
1394
|
-
node
|
|
1395
|
-
|
|
1247
|
+
if node is not None:
|
|
1248
|
+
# hard reset
|
|
1249
|
+
# old_view = node
|
|
1250
|
+
# new_view = old_view.hard_reset()
|
|
1251
|
+
# self.window.ui.nodes['output'][pid] = new_view
|
|
1252
|
+
node.resetPage()
|
|
1253
|
+
node.setHtml(html, baseUrl="file://")
|
|
1396
1254
|
|
|
1397
1255
|
def get_output_node(
|
|
1398
1256
|
self,
|
|
@@ -1426,23 +1284,6 @@ class Renderer(BaseRenderer):
|
|
|
1426
1284
|
"""
|
|
1427
1285
|
return self.window.ui.nodes['input']
|
|
1428
1286
|
|
|
1429
|
-
def get_document(
|
|
1430
|
-
self,
|
|
1431
|
-
plain: bool = False
|
|
1432
|
-
):
|
|
1433
|
-
"""
|
|
1434
|
-
Get document content (plain or HTML)
|
|
1435
|
-
|
|
1436
|
-
:param plain: True to convert to plain text
|
|
1437
|
-
:return: document content
|
|
1438
|
-
"""
|
|
1439
|
-
pid = self.window.core.ctx.container.get_active_pid()
|
|
1440
|
-
if pid is None:
|
|
1441
|
-
return ""
|
|
1442
|
-
if plain:
|
|
1443
|
-
return self.parser.to_plain_text(self.pids[pid].document.replace("<br>", "\n"))
|
|
1444
|
-
return self.pids[pid].document
|
|
1445
|
-
|
|
1446
1287
|
def remove_item(self, ctx: CtxItem):
|
|
1447
1288
|
"""
|
|
1448
1289
|
Remove item from output
|
|
@@ -1450,7 +1291,9 @@ class Renderer(BaseRenderer):
|
|
|
1450
1291
|
:param ctx: context item
|
|
1451
1292
|
"""
|
|
1452
1293
|
try:
|
|
1453
|
-
|
|
1294
|
+
_id = json.dumps(ctx.id)
|
|
1295
|
+
self.get_output_node(ctx.meta).page().runJavaScript(
|
|
1296
|
+
f"if (typeof window.removeNode !== 'undefined') removeNode({_id});")
|
|
1454
1297
|
except Exception as e:
|
|
1455
1298
|
pass
|
|
1456
1299
|
|
|
@@ -1461,7 +1304,9 @@ class Renderer(BaseRenderer):
|
|
|
1461
1304
|
:param ctx: context item
|
|
1462
1305
|
"""
|
|
1463
1306
|
try:
|
|
1464
|
-
|
|
1307
|
+
_id = json.dumps(ctx.id)
|
|
1308
|
+
self.get_output_node(ctx.meta).page().runJavaScript(
|
|
1309
|
+
f"if (typeof window.removeNodesFromId !== 'undefined') removeNodesFromId({_id});")
|
|
1465
1310
|
except Exception as e:
|
|
1466
1311
|
pass
|
|
1467
1312
|
|
|
@@ -1491,7 +1336,6 @@ class Renderer(BaseRenderer):
|
|
|
1491
1336
|
|
|
1492
1337
|
:param ctx: context item
|
|
1493
1338
|
"""
|
|
1494
|
-
# remove all items from ID
|
|
1495
1339
|
self.remove_items_from(ctx)
|
|
1496
1340
|
|
|
1497
1341
|
def on_edit_submit(self, ctx: CtxItem):
|
|
@@ -1500,7 +1344,6 @@ class Renderer(BaseRenderer):
|
|
|
1500
1344
|
|
|
1501
1345
|
:param ctx: context item
|
|
1502
1346
|
"""
|
|
1503
|
-
# remove all items from ID
|
|
1504
1347
|
self.remove_items_from(ctx)
|
|
1505
1348
|
|
|
1506
1349
|
def on_enable_edit(self, live: bool = True):
|
|
@@ -1512,7 +1355,8 @@ class Renderer(BaseRenderer):
|
|
|
1512
1355
|
if not live:
|
|
1513
1356
|
return
|
|
1514
1357
|
try:
|
|
1515
|
-
|
|
1358
|
+
nodes = self.get_all_nodes()
|
|
1359
|
+
for node in nodes:
|
|
1516
1360
|
node.page().runJavaScript("if (typeof window.enableEditIcons !== 'undefined') enableEditIcons();")
|
|
1517
1361
|
except Exception as e:
|
|
1518
1362
|
pass
|
|
@@ -1526,7 +1370,8 @@ class Renderer(BaseRenderer):
|
|
|
1526
1370
|
if not live:
|
|
1527
1371
|
return
|
|
1528
1372
|
try:
|
|
1529
|
-
|
|
1373
|
+
nodes = self.get_all_nodes()
|
|
1374
|
+
for node in nodes:
|
|
1530
1375
|
node.page().runJavaScript("if (typeof window.disableEditIcons !== 'undefined') disableEditIcons();")
|
|
1531
1376
|
except Exception as e:
|
|
1532
1377
|
pass
|
|
@@ -1540,7 +1385,8 @@ class Renderer(BaseRenderer):
|
|
|
1540
1385
|
if not live:
|
|
1541
1386
|
return
|
|
1542
1387
|
try:
|
|
1543
|
-
|
|
1388
|
+
nodes = self.get_all_nodes()
|
|
1389
|
+
for node in nodes:
|
|
1544
1390
|
node.page().runJavaScript("if (typeof window.enableTimestamp !== 'undefined') enableTimestamp();")
|
|
1545
1391
|
except Exception as e:
|
|
1546
1392
|
pass
|
|
@@ -1554,7 +1400,8 @@ class Renderer(BaseRenderer):
|
|
|
1554
1400
|
if not live:
|
|
1555
1401
|
return
|
|
1556
1402
|
try:
|
|
1557
|
-
|
|
1403
|
+
nodes = self.get_all_nodes()
|
|
1404
|
+
for node in nodes:
|
|
1558
1405
|
node.page().runJavaScript("if (typeof window.disableTimestamp !== 'undefined') disableTimestamp();")
|
|
1559
1406
|
except Exception as e:
|
|
1560
1407
|
pass
|
|
@@ -1566,7 +1413,7 @@ class Renderer(BaseRenderer):
|
|
|
1566
1413
|
):
|
|
1567
1414
|
"""
|
|
1568
1415
|
Update names
|
|
1569
|
-
|
|
1416
|
+
|
|
1570
1417
|
:param meta: context meta
|
|
1571
1418
|
:param ctx: context item
|
|
1572
1419
|
"""
|
|
@@ -1580,7 +1427,7 @@ class Renderer(BaseRenderer):
|
|
|
1580
1427
|
|
|
1581
1428
|
def clear_all(self):
|
|
1582
1429
|
"""Clear all"""
|
|
1583
|
-
for pid in self.pids
|
|
1430
|
+
for pid in self.pids:
|
|
1584
1431
|
self.clear_chunks(pid)
|
|
1585
1432
|
self.clear_nodes(pid)
|
|
1586
1433
|
self.pids[pid].html = ""
|
|
@@ -1614,15 +1461,18 @@ class Renderer(BaseRenderer):
|
|
|
1614
1461
|
def reload_css(self):
|
|
1615
1462
|
"""Reload CSS - all, global"""
|
|
1616
1463
|
to_json = json.dumps(self.body.prepare_styles())
|
|
1617
|
-
|
|
1464
|
+
nodes = self.get_all_nodes()
|
|
1465
|
+
for pid in self.pids:
|
|
1618
1466
|
if self.pids[pid].loaded:
|
|
1619
|
-
for node in
|
|
1467
|
+
for node in nodes:
|
|
1620
1468
|
try:
|
|
1621
|
-
node.page().runJavaScript(
|
|
1469
|
+
node.page().runJavaScript(
|
|
1470
|
+
f"if (typeof window.updateCSS !== 'undefined') updateCSS({to_json});")
|
|
1622
1471
|
if self.window.core.config.get('render.blocks'):
|
|
1623
1472
|
node.page().runJavaScript("if (typeof window.enableBlocks !== 'undefined') enableBlocks();")
|
|
1624
1473
|
else:
|
|
1625
|
-
node.page().runJavaScript(
|
|
1474
|
+
node.page().runJavaScript(
|
|
1475
|
+
"if (typeof window.disableBlocks !== 'undefined') disableBlocks();")
|
|
1626
1476
|
except Exception as e:
|
|
1627
1477
|
pass
|
|
1628
1478
|
return
|
|
@@ -1630,7 +1480,7 @@ class Renderer(BaseRenderer):
|
|
|
1630
1480
|
def on_theme_change(self):
|
|
1631
1481
|
"""On theme change"""
|
|
1632
1482
|
self.window.controller.theme.markdown.load()
|
|
1633
|
-
for pid in self.pids
|
|
1483
|
+
for pid in self.pids:
|
|
1634
1484
|
if self.pids[pid].loaded:
|
|
1635
1485
|
self.reload_css()
|
|
1636
1486
|
return
|
|
@@ -1648,7 +1498,8 @@ class Renderer(BaseRenderer):
|
|
|
1648
1498
|
"""
|
|
1649
1499
|
escaped_content = json.dumps(content)
|
|
1650
1500
|
try:
|
|
1651
|
-
self.get_output_node(meta).page().runJavaScript(
|
|
1501
|
+
self.get_output_node(meta).page().runJavaScript(
|
|
1502
|
+
f"if (typeof window.appendToolOutput !== 'undefined') appendToolOutput({escaped_content});")
|
|
1652
1503
|
except Exception as e:
|
|
1653
1504
|
pass
|
|
1654
1505
|
|
|
@@ -1665,7 +1516,8 @@ class Renderer(BaseRenderer):
|
|
|
1665
1516
|
"""
|
|
1666
1517
|
escaped_content = json.dumps(content)
|
|
1667
1518
|
try:
|
|
1668
|
-
self.get_output_node(meta).page().runJavaScript(
|
|
1519
|
+
self.get_output_node(meta).page().runJavaScript(
|
|
1520
|
+
f"if (typeof window.updateToolOutput !== 'undefined') updateToolOutput({escaped_content});")
|
|
1669
1521
|
except Exception as e:
|
|
1670
1522
|
pass
|
|
1671
1523
|
|
|
@@ -1676,7 +1528,8 @@ class Renderer(BaseRenderer):
|
|
|
1676
1528
|
:param meta: context meta
|
|
1677
1529
|
"""
|
|
1678
1530
|
try:
|
|
1679
|
-
self.get_output_node(meta).page().runJavaScript(
|
|
1531
|
+
self.get_output_node(meta).page().runJavaScript(
|
|
1532
|
+
f"if (typeof window.clearToolOutput !== 'undefined') clearToolOutput();")
|
|
1680
1533
|
except Exception as e:
|
|
1681
1534
|
pass
|
|
1682
1535
|
|
|
@@ -1687,14 +1540,16 @@ class Renderer(BaseRenderer):
|
|
|
1687
1540
|
:param meta: context meta
|
|
1688
1541
|
"""
|
|
1689
1542
|
try:
|
|
1690
|
-
self.get_output_node(meta).page().runJavaScript(
|
|
1543
|
+
self.get_output_node(meta).page().runJavaScript(
|
|
1544
|
+
f"if (typeof window.beginToolOutput !== 'undefined') beginToolOutput();")
|
|
1691
1545
|
except Exception as e:
|
|
1692
1546
|
pass
|
|
1693
1547
|
|
|
1694
1548
|
def tool_output_end(self):
|
|
1695
1549
|
"""End tool output"""
|
|
1696
1550
|
try:
|
|
1697
|
-
self.get_output_node().page().runJavaScript(
|
|
1551
|
+
self.get_output_node().page().runJavaScript(
|
|
1552
|
+
f"if (typeof window.endToolOutput !== 'undefined') endToolOutput();")
|
|
1698
1553
|
except Exception as e:
|
|
1699
1554
|
pass
|
|
1700
1555
|
|
|
@@ -1713,7 +1568,8 @@ class Renderer(BaseRenderer):
|
|
|
1713
1568
|
"""
|
|
1714
1569
|
if title is None:
|
|
1715
1570
|
title = "debug"
|
|
1716
|
-
|
|
1571
|
+
debug = "<b>" + title + ":</b> pid: " + str(pid) + ", ctx: " + str(ctx.to_dict())
|
|
1572
|
+
return "<div class='debug'>" + debug + "</div>"
|
|
1717
1573
|
|
|
1718
1574
|
def is_debug(self) -> bool:
|
|
1719
1575
|
"""
|
|
@@ -1728,4 +1584,4 @@ class Renderer(BaseRenderer):
|
|
|
1728
1584
|
Remove PID from renderer
|
|
1729
1585
|
"""
|
|
1730
1586
|
if pid in self.pids:
|
|
1731
|
-
del self.pids[pid]
|
|
1587
|
+
del self.pids[pid]
|