pygpt-net 2.6.12__py3-none-any.whl → 2.6.14__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 +12 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/controller/chat/output.py +2 -2
- pygpt_net/controller/chat/response.py +6 -1
- pygpt_net/controller/presets/editor.py +1 -1
- pygpt_net/core/agents/observer/evaluation.py +46 -17
- pygpt_net/core/agents/runner.py +4 -4
- pygpt_net/core/agents/runners/llama_workflow.py +13 -4
- pygpt_net/core/experts/experts.py +1 -5
- pygpt_net/core/presets/presets.py +1 -2
- pygpt_net/core/render/web/pid.py +1 -0
- pygpt_net/core/render/web/renderer.py +280 -80
- pygpt_net/data/config/config.json +2 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/ui/layout/toolbox/presets.py +0 -4
- pygpt_net/ui/widget/textarea/web.py +0 -2
- {pygpt_net-2.6.12.dist-info → pygpt_net-2.6.14.dist-info}/METADATA +14 -2
- {pygpt_net-2.6.12.dist-info → pygpt_net-2.6.14.dist-info}/RECORD +21 -21
- {pygpt_net-2.6.12.dist-info → pygpt_net-2.6.14.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.12.dist-info → pygpt_net-2.6.14.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.12.dist-info → pygpt_net-2.6.14.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
2.6.14 (2025-08-19)
|
|
2
|
+
|
|
3
|
+
- Fixed: Agent evaluation tool runs even if tools are disabled.
|
|
4
|
+
- Extended agent response evaluation by providing the full context of the output.
|
|
5
|
+
|
|
6
|
+
2.6.13 (2025-08-19)
|
|
7
|
+
|
|
8
|
+
- Fix: Do not load the index in experts if it is not provided.
|
|
9
|
+
- Fix: Load remote images in the webview.
|
|
10
|
+
- Fix: Presets list refresh.
|
|
11
|
+
- Optimize context items reload.
|
|
12
|
+
|
|
1
13
|
2.6.12 (2025-08-19)
|
|
2
14
|
|
|
3
15
|
- Optimized web renderer memory cleanup.
|
pygpt_net/__init__.py
CHANGED
|
@@ -13,7 +13,7 @@ __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.
|
|
16
|
+
__version__ = "2.6.14"
|
|
17
17
|
__build__ = "2025-08-19"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
@@ -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
|
|
|
@@ -265,7 +265,12 @@ 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
|
|
@@ -132,17 +132,37 @@ class Evaluation:
|
|
|
132
132
|
:param force_prev: force to use previous input
|
|
133
133
|
:return: last user input
|
|
134
134
|
"""
|
|
135
|
-
|
|
135
|
+
last_input = ""
|
|
136
136
|
use_prev = self.window.core.config.get("agent.llama.append_eval", False)
|
|
137
137
|
if force_prev:
|
|
138
138
|
use_prev = True
|
|
139
139
|
for ctx in history:
|
|
140
|
-
if ctx
|
|
140
|
+
if self.is_input(ctx): # ensure ctx is input
|
|
141
141
|
if not use_prev and "agent_evaluate" in ctx.extra: # exclude evaluation inputs
|
|
142
142
|
continue
|
|
143
143
|
if ctx.input:
|
|
144
|
-
|
|
145
|
-
return
|
|
144
|
+
last_input = ctx.input
|
|
145
|
+
return last_input
|
|
146
|
+
|
|
147
|
+
def is_input(self, ctx: CtxItem) -> bool:
|
|
148
|
+
"""
|
|
149
|
+
Check if the context item is an input
|
|
150
|
+
|
|
151
|
+
:param ctx: context item
|
|
152
|
+
:return: True if input, False otherwise
|
|
153
|
+
"""
|
|
154
|
+
return ctx.extra is not None and "agent_input" in ctx.extra
|
|
155
|
+
|
|
156
|
+
def is_output(self, ctx: CtxItem) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
Check if the context item is an output
|
|
159
|
+
|
|
160
|
+
:param ctx: context item
|
|
161
|
+
:return: True if output, False otherwise
|
|
162
|
+
"""
|
|
163
|
+
return (ctx.extra is not None
|
|
164
|
+
and ("agent_output" in ctx.extra or "agent_finish" in ctx.extra)
|
|
165
|
+
and "agent_finish_evaluate" not in ctx.extra)
|
|
146
166
|
|
|
147
167
|
def get_main_task(self, history: List[CtxItem]) -> str:
|
|
148
168
|
"""
|
|
@@ -151,10 +171,12 @@ class Evaluation:
|
|
|
151
171
|
:param history: ctx items
|
|
152
172
|
:return: main task
|
|
153
173
|
"""
|
|
154
|
-
first = history[0]
|
|
155
174
|
task = ""
|
|
156
|
-
|
|
157
|
-
|
|
175
|
+
for ctx in history:
|
|
176
|
+
if self.is_input(ctx):
|
|
177
|
+
if ctx.input:
|
|
178
|
+
task = ctx.input
|
|
179
|
+
break
|
|
158
180
|
return task
|
|
159
181
|
|
|
160
182
|
def get_final_response(self, history: List[CtxItem]) -> str:
|
|
@@ -164,19 +186,28 @@ class Evaluation:
|
|
|
164
186
|
:param history: ctx items
|
|
165
187
|
:return: final response from agent
|
|
166
188
|
"""
|
|
167
|
-
|
|
189
|
+
outputs = []
|
|
168
190
|
for ctx in history:
|
|
169
|
-
if
|
|
191
|
+
if self.is_output(ctx):
|
|
170
192
|
if ctx.output:
|
|
171
|
-
|
|
193
|
+
outputs.append(ctx.output)
|
|
194
|
+
|
|
195
|
+
# if next input then clear outputs - use only output after last user input
|
|
196
|
+
if self.is_input(ctx):
|
|
197
|
+
outputs.clear()
|
|
172
198
|
|
|
173
199
|
# feedback for OpenAI agents
|
|
174
|
-
if
|
|
200
|
+
if len(outputs) == 0:
|
|
175
201
|
for ctx in history:
|
|
176
|
-
if
|
|
202
|
+
if self.is_output(ctx):
|
|
177
203
|
if ctx.output:
|
|
178
|
-
|
|
179
|
-
|
|
204
|
+
outputs.append(ctx.output)
|
|
205
|
+
|
|
206
|
+
# if next input then clear outputs - use only output after last user input
|
|
207
|
+
if self.is_input(ctx):
|
|
208
|
+
outputs.clear()
|
|
209
|
+
|
|
210
|
+
return "\n\n".join(outputs) if outputs else ""
|
|
180
211
|
|
|
181
212
|
def get_prompt_score(self, history: List[CtxItem]) -> str:
|
|
182
213
|
"""
|
|
@@ -224,9 +255,7 @@ class Evaluation:
|
|
|
224
255
|
return "OK. Feedback has been sent."
|
|
225
256
|
|
|
226
257
|
tool = FunctionTool.from_defaults(fn=send_feedback)
|
|
227
|
-
|
|
228
|
-
tools.append(tool)
|
|
229
|
-
return tools
|
|
258
|
+
return [tool]
|
|
230
259
|
|
|
231
260
|
def handle_evaluation(
|
|
232
261
|
self,
|
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)
|
|
@@ -250,16 +251,14 @@ class Runner:
|
|
|
250
251
|
tools = extra["agent_tools"] # use tools from extra if provided
|
|
251
252
|
else:
|
|
252
253
|
tools = self.window.core.agents.tools.prepare(context, extra, force=True)
|
|
254
|
+
if not is_cmd:
|
|
255
|
+
tools = [] # disable tools if cmd is not enabled, force agent tools
|
|
253
256
|
|
|
254
257
|
if "agent_history" in extra:
|
|
255
258
|
history = extra["agent_history"]
|
|
256
259
|
else:
|
|
257
260
|
history = self.window.core.agents.memory.prepare(context)
|
|
258
261
|
|
|
259
|
-
# disable tools if cmd is not enabled
|
|
260
|
-
if not is_cmd:
|
|
261
|
-
tools = []
|
|
262
|
-
|
|
263
262
|
# append system prompt
|
|
264
263
|
if agent_id in self.APPEND_SYSTEM_PROMPT_TO_MSG:
|
|
265
264
|
if system_prompt:
|
|
@@ -301,6 +300,7 @@ class Runner:
|
|
|
301
300
|
"verbose": verbose,
|
|
302
301
|
"history": history,
|
|
303
302
|
"llm": llm,
|
|
303
|
+
"is_expert_call": is_expert_call,
|
|
304
304
|
}
|
|
305
305
|
# TODO: add support for other modes
|
|
306
306
|
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):
|
|
@@ -651,7 +651,7 @@ class ExpertWorker(QRunnable):
|
|
|
651
651
|
|
|
652
652
|
# index to use
|
|
653
653
|
use_index = False
|
|
654
|
-
if db_idx:
|
|
654
|
+
if db_idx and db_idx != '_':
|
|
655
655
|
use_index = True
|
|
656
656
|
if use_index:
|
|
657
657
|
index, llm = self.window.core.idx.chat.get_index(db_idx, model_data, stream=False)
|
|
@@ -694,10 +694,6 @@ class ExpertWorker(QRunnable):
|
|
|
694
694
|
tools = self.window.core.agents.tools.prepare(
|
|
695
695
|
bridge_context, extra, verbose=False, force=True)
|
|
696
696
|
|
|
697
|
-
# disable tools if cmd is not enabled
|
|
698
|
-
if not self.window.core.command.is_cmd(inline=False):
|
|
699
|
-
tools = []
|
|
700
|
-
|
|
701
697
|
# remove expert_call tool from tools
|
|
702
698
|
for tool in list(tools):
|
|
703
699
|
if tool.metadata.name == "expert_call":
|
|
@@ -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
|
"""
|
pygpt_net/core/render/web/pid.py
CHANGED
|
@@ -108,6 +108,7 @@ class Renderer(BaseRenderer):
|
|
|
108
108
|
pid = tab.pid
|
|
109
109
|
if pid is None or pid not in self.pids:
|
|
110
110
|
return
|
|
111
|
+
|
|
111
112
|
self.pids[pid].loaded = True
|
|
112
113
|
if self.pids[pid].html != "" and not self.pids[pid].use_buffer:
|
|
113
114
|
self.clear_chunks_input(pid)
|
|
@@ -189,7 +190,8 @@ class Renderer(BaseRenderer):
|
|
|
189
190
|
node = self.get_output_node_by_pid(pid)
|
|
190
191
|
try:
|
|
191
192
|
node.page().runJavaScript(
|
|
192
|
-
"if (typeof window.showLoading !== 'undefined') showLoading();"
|
|
193
|
+
"if (typeof window.showLoading !== 'undefined') showLoading();"
|
|
194
|
+
)
|
|
193
195
|
except Exception as e:
|
|
194
196
|
pass
|
|
195
197
|
|
|
@@ -199,7 +201,8 @@ class Renderer(BaseRenderer):
|
|
|
199
201
|
if node is not None:
|
|
200
202
|
try:
|
|
201
203
|
node.page().runJavaScript(
|
|
202
|
-
"if (typeof window.hideLoading !== 'undefined') hideLoading();"
|
|
204
|
+
"if (typeof window.hideLoading !== 'undefined') hideLoading();"
|
|
205
|
+
)
|
|
203
206
|
except Exception as e:
|
|
204
207
|
pass
|
|
205
208
|
|
|
@@ -209,7 +212,8 @@ class Renderer(BaseRenderer):
|
|
|
209
212
|
if node is not None:
|
|
210
213
|
try:
|
|
211
214
|
node.page().runJavaScript(
|
|
212
|
-
"if (typeof window.hideLoading !== 'undefined') hideLoading();"
|
|
215
|
+
"if (typeof window.hideLoading !== 'undefined') hideLoading();"
|
|
216
|
+
)
|
|
213
217
|
except Exception as e:
|
|
214
218
|
pass
|
|
215
219
|
|
|
@@ -251,6 +255,8 @@ class Renderer(BaseRenderer):
|
|
|
251
255
|
if self.pids[pid].item is not None and stream:
|
|
252
256
|
self.append_context_item(meta, self.pids[pid].item)
|
|
253
257
|
self.pids[pid].item = None
|
|
258
|
+
else:
|
|
259
|
+
self.reload()
|
|
254
260
|
self.pids[pid].clear()
|
|
255
261
|
|
|
256
262
|
def end_extra(
|
|
@@ -320,7 +326,27 @@ class Renderer(BaseRenderer):
|
|
|
320
326
|
clear: bool = True
|
|
321
327
|
):
|
|
322
328
|
"""
|
|
323
|
-
Append all context to output
|
|
329
|
+
Append all context items to output
|
|
330
|
+
|
|
331
|
+
:param meta: Context meta
|
|
332
|
+
:param items: context items
|
|
333
|
+
:param clear: True if clear all output before append
|
|
334
|
+
"""
|
|
335
|
+
self.tool_output_end()
|
|
336
|
+
self.append_context_all(
|
|
337
|
+
meta,
|
|
338
|
+
items,
|
|
339
|
+
clear=clear,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def append_context_partial(
|
|
343
|
+
self,
|
|
344
|
+
meta: CtxMeta,
|
|
345
|
+
items: List[CtxItem],
|
|
346
|
+
clear: bool = True
|
|
347
|
+
):
|
|
348
|
+
"""
|
|
349
|
+
Append all context items to output (part by part)
|
|
324
350
|
|
|
325
351
|
:param meta: Context meta
|
|
326
352
|
:param items: context items
|
|
@@ -351,7 +377,7 @@ class Renderer(BaseRenderer):
|
|
|
351
377
|
meta,
|
|
352
378
|
item,
|
|
353
379
|
prev_ctx=prev_ctx,
|
|
354
|
-
next_ctx=next_item
|
|
380
|
+
next_ctx=next_item,
|
|
355
381
|
)
|
|
356
382
|
prev_ctx = item
|
|
357
383
|
|
|
@@ -362,35 +388,126 @@ class Renderer(BaseRenderer):
|
|
|
362
388
|
self.append(
|
|
363
389
|
pid,
|
|
364
390
|
self.pids[pid].html,
|
|
365
|
-
flush=True
|
|
391
|
+
flush=True,
|
|
366
392
|
)
|
|
367
393
|
self.parser.reset()
|
|
368
394
|
|
|
369
|
-
def
|
|
395
|
+
def append_context_all(
|
|
396
|
+
self,
|
|
397
|
+
meta: CtxMeta,
|
|
398
|
+
items: List[CtxItem],
|
|
399
|
+
clear: bool = True
|
|
400
|
+
):
|
|
401
|
+
"""
|
|
402
|
+
Append all context items to output (whole context at once)
|
|
403
|
+
|
|
404
|
+
:param meta: Context meta
|
|
405
|
+
:param items: context items
|
|
406
|
+
:param clear: True if clear all output before append
|
|
407
|
+
"""
|
|
408
|
+
if len(items) == 0:
|
|
409
|
+
if meta is None:
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
pid = self.get_or_create_pid(meta)
|
|
413
|
+
self.init(pid)
|
|
414
|
+
|
|
415
|
+
if clear:
|
|
416
|
+
self.reset(meta)
|
|
417
|
+
|
|
418
|
+
self.pids[pid].use_buffer = True
|
|
419
|
+
self.pids[pid].html = ""
|
|
420
|
+
prev_ctx = None
|
|
421
|
+
next_ctx = None
|
|
422
|
+
total = len(items)
|
|
423
|
+
html_parts = []
|
|
424
|
+
for i, item in enumerate(items):
|
|
425
|
+
self.update_names(meta, item)
|
|
426
|
+
item.idx = i
|
|
427
|
+
if i == 0:
|
|
428
|
+
item.first = True
|
|
429
|
+
next_ctx = items[i + 1] if i + 1 < total else None
|
|
430
|
+
|
|
431
|
+
# ignore hidden items
|
|
432
|
+
if item.hidden:
|
|
433
|
+
prev_ctx = item
|
|
434
|
+
continue
|
|
435
|
+
|
|
436
|
+
# input node
|
|
437
|
+
data = self.prepare_input(meta, item, flush=False)
|
|
438
|
+
if data:
|
|
439
|
+
html = self.prepare_node(
|
|
440
|
+
meta=meta,
|
|
441
|
+
ctx=item,
|
|
442
|
+
html=data,
|
|
443
|
+
type=self.NODE_INPUT,
|
|
444
|
+
prev_ctx=prev_ctx,
|
|
445
|
+
next_ctx=next_ctx,
|
|
446
|
+
)
|
|
447
|
+
if html:
|
|
448
|
+
html_parts.append(html)
|
|
449
|
+
|
|
450
|
+
# output node
|
|
451
|
+
data = self.prepare_output(
|
|
452
|
+
meta,
|
|
453
|
+
item,
|
|
454
|
+
flush=False,
|
|
455
|
+
prev_ctx=prev_ctx,
|
|
456
|
+
next_ctx=next_ctx,
|
|
457
|
+
)
|
|
458
|
+
if data:
|
|
459
|
+
html = self.prepare_node(
|
|
460
|
+
meta=meta,
|
|
461
|
+
ctx=item,
|
|
462
|
+
html=data,
|
|
463
|
+
type=self.NODE_OUTPUT,
|
|
464
|
+
prev_ctx=prev_ctx,
|
|
465
|
+
next_ctx=next_ctx,
|
|
466
|
+
)
|
|
467
|
+
if html:
|
|
468
|
+
html_parts.append(html)
|
|
469
|
+
|
|
470
|
+
prev_ctx = item
|
|
471
|
+
|
|
472
|
+
# flush all nodes at once
|
|
473
|
+
if html_parts:
|
|
474
|
+
self.append(
|
|
475
|
+
pid,
|
|
476
|
+
"".join(html_parts)
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
html_parts.clear()
|
|
480
|
+
html_parts = None
|
|
481
|
+
prev_ctx = None
|
|
482
|
+
next_ctx = None
|
|
483
|
+
self.pids[pid].use_buffer = False
|
|
484
|
+
if self.pids[pid].html != "":
|
|
485
|
+
self.append(
|
|
486
|
+
pid,
|
|
487
|
+
self.pids[pid].html,
|
|
488
|
+
flush=True,
|
|
489
|
+
)
|
|
490
|
+
self.parser.reset()
|
|
491
|
+
|
|
492
|
+
def prepare_input(
|
|
370
493
|
self, meta: CtxMeta,
|
|
371
494
|
ctx: CtxItem,
|
|
372
495
|
flush: bool = True,
|
|
373
496
|
append: bool = False
|
|
374
|
-
):
|
|
497
|
+
) -> Optional[str]:
|
|
375
498
|
"""
|
|
376
|
-
|
|
499
|
+
Prepare text input
|
|
377
500
|
|
|
378
501
|
:param meta: context meta
|
|
379
502
|
:param ctx: context item
|
|
380
503
|
:param flush: flush HTML
|
|
381
504
|
:param append: True if force append node
|
|
505
|
+
:return: Prepared input text or None if internal or empty input
|
|
382
506
|
"""
|
|
383
|
-
self.tool_output_end()
|
|
384
|
-
pid = self.get_or_create_pid(meta)
|
|
385
|
-
if not flush:
|
|
386
|
-
self.clear_chunks_input(pid)
|
|
387
|
-
|
|
388
|
-
self.update_names(meta, ctx)
|
|
389
507
|
if ctx.input is None or ctx.input == "":
|
|
390
508
|
return
|
|
391
509
|
|
|
392
510
|
text = ctx.input
|
|
393
|
-
|
|
394
511
|
if isinstance(ctx.extra, dict) and "sub_reply" in ctx.extra and ctx.extra["sub_reply"]:
|
|
395
512
|
try:
|
|
396
513
|
json_encoded = json.loads(text)
|
|
@@ -410,32 +527,60 @@ class Renderer(BaseRenderer):
|
|
|
410
527
|
if ctx.internal and ctx.input.startswith("user: "):
|
|
411
528
|
text = re.sub(r'^user: ', '> ', ctx.input)
|
|
412
529
|
|
|
413
|
-
|
|
414
|
-
if self.is_stream() and not append:
|
|
415
|
-
content = self.prepare_node(meta, ctx, text.strip(), self.NODE_INPUT)
|
|
416
|
-
self.append_chunk_input(meta, ctx, content, False)
|
|
417
|
-
return
|
|
530
|
+
return text.strip()
|
|
418
531
|
|
|
419
|
-
|
|
532
|
+
def append_input(
|
|
533
|
+
self, meta: CtxMeta,
|
|
534
|
+
ctx: CtxItem,
|
|
535
|
+
flush: bool = True,
|
|
536
|
+
append: bool = False
|
|
537
|
+
):
|
|
538
|
+
"""
|
|
539
|
+
Append text input to output
|
|
420
540
|
|
|
421
|
-
|
|
541
|
+
:param meta: context meta
|
|
542
|
+
:param ctx: context item
|
|
543
|
+
:param flush: flush HTML
|
|
544
|
+
:param append: True if force append node
|
|
545
|
+
"""
|
|
546
|
+
self.tool_output_end()
|
|
547
|
+
pid = self.get_or_create_pid(meta)
|
|
548
|
+
if not flush:
|
|
549
|
+
self.clear_chunks_input(pid)
|
|
550
|
+
|
|
551
|
+
self.update_names(meta, ctx)
|
|
552
|
+
text = self.prepare_input(meta, ctx, flush, append)
|
|
553
|
+
if text:
|
|
554
|
+
if flush:
|
|
555
|
+
if self.is_stream() and not append:
|
|
556
|
+
content = self.prepare_node(meta, ctx, text, self.NODE_INPUT)
|
|
557
|
+
self.append_chunk_input(meta, ctx, content, begin=False)
|
|
558
|
+
return
|
|
559
|
+
self.append_node(
|
|
560
|
+
meta=meta,
|
|
561
|
+
ctx=ctx,
|
|
562
|
+
html=text,
|
|
563
|
+
type=self.NODE_INPUT,
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
def prepare_output(
|
|
422
567
|
self,
|
|
423
568
|
meta: CtxMeta,
|
|
424
569
|
ctx: CtxItem,
|
|
425
570
|
flush: bool = True,
|
|
426
571
|
prev_ctx: Optional[CtxItem] = None,
|
|
427
572
|
next_ctx: Optional[CtxItem] = None
|
|
428
|
-
):
|
|
573
|
+
) -> Optional[str]:
|
|
429
574
|
"""
|
|
430
|
-
|
|
575
|
+
Prepare text output
|
|
431
576
|
|
|
432
577
|
:param meta: context meta
|
|
433
578
|
:param ctx: context item
|
|
434
579
|
:param flush: flush HTML
|
|
435
580
|
:param prev_ctx: previous context
|
|
436
581
|
:param next_ctx: next context
|
|
582
|
+
:return: Prepared output text or None if empty output
|
|
437
583
|
"""
|
|
438
|
-
self.tool_output_end()
|
|
439
584
|
output = ctx.output
|
|
440
585
|
if isinstance(ctx.extra, dict) and ctx.extra.get("output"):
|
|
441
586
|
if self.window.core.config.get("llama.idx.chat.agent.render.all", False):
|
|
@@ -445,14 +590,42 @@ class Renderer(BaseRenderer):
|
|
|
445
590
|
else:
|
|
446
591
|
if not output:
|
|
447
592
|
return
|
|
448
|
-
|
|
593
|
+
return output.strip()
|
|
594
|
+
|
|
595
|
+
def append_output(
|
|
596
|
+
self,
|
|
597
|
+
meta: CtxMeta,
|
|
598
|
+
ctx: CtxItem,
|
|
599
|
+
flush: bool = True,
|
|
600
|
+
prev_ctx: Optional[CtxItem] = None,
|
|
601
|
+
next_ctx: Optional[CtxItem] = None
|
|
602
|
+
):
|
|
603
|
+
"""
|
|
604
|
+
Append text output to output
|
|
605
|
+
|
|
606
|
+
:param meta: context meta
|
|
607
|
+
:param ctx: context item
|
|
608
|
+
:param flush: flush HTML
|
|
609
|
+
:param prev_ctx: previous context
|
|
610
|
+
:param next_ctx: next context
|
|
611
|
+
"""
|
|
612
|
+
self.tool_output_end()
|
|
613
|
+
output = self.prepare_output(
|
|
449
614
|
meta=meta,
|
|
450
615
|
ctx=ctx,
|
|
451
|
-
|
|
452
|
-
type=self.NODE_OUTPUT,
|
|
616
|
+
flush=flush,
|
|
453
617
|
prev_ctx=prev_ctx,
|
|
454
|
-
next_ctx=next_ctx
|
|
618
|
+
next_ctx=next_ctx,
|
|
455
619
|
)
|
|
620
|
+
if output:
|
|
621
|
+
self.append_node(
|
|
622
|
+
meta=meta,
|
|
623
|
+
ctx=ctx,
|
|
624
|
+
html=output,
|
|
625
|
+
type=self.NODE_OUTPUT,
|
|
626
|
+
prev_ctx=prev_ctx,
|
|
627
|
+
next_ctx=next_ctx,
|
|
628
|
+
)
|
|
456
629
|
|
|
457
630
|
def append_chunk(
|
|
458
631
|
self,
|
|
@@ -479,8 +652,11 @@ class Renderer(BaseRenderer):
|
|
|
479
652
|
self._throttle_reset(pid)
|
|
480
653
|
return
|
|
481
654
|
|
|
482
|
-
|
|
483
|
-
|
|
655
|
+
if begin: # prepare name and avatar header only at the beginning to avoid unnecessary checks
|
|
656
|
+
pctx.header = self.get_name_header(ctx)
|
|
657
|
+
self.update_names(meta, ctx)
|
|
658
|
+
|
|
659
|
+
name_header_str = pctx.header
|
|
484
660
|
text_chunk = text_chunk if isinstance(text_chunk, str) else str(text_chunk)
|
|
485
661
|
text_chunk = text_chunk.translate({ord('<'): '<', ord('>'): '>'})
|
|
486
662
|
|
|
@@ -540,7 +716,7 @@ class Renderer(BaseRenderer):
|
|
|
540
716
|
except Exception:
|
|
541
717
|
pass
|
|
542
718
|
|
|
543
|
-
self._throttle_queue(pid, name_header_str or "", html, text_chunk, replace,
|
|
719
|
+
self._throttle_queue(pid, name_header_str or "", html, text_chunk, replace, is_code_block)
|
|
544
720
|
self._throttle_emit(pid, force=False)
|
|
545
721
|
|
|
546
722
|
def next_chunk(
|
|
@@ -623,6 +799,7 @@ class Renderer(BaseRenderer):
|
|
|
623
799
|
if begin:
|
|
624
800
|
self.pids[pid].live_buffer = ""
|
|
625
801
|
return
|
|
802
|
+
|
|
626
803
|
self.update_names(meta, ctx)
|
|
627
804
|
raw_chunk = str(text_chunk).translate({ord('<'): '<', ord('>'): '>'})
|
|
628
805
|
if begin:
|
|
@@ -648,7 +825,6 @@ class Renderer(BaseRenderer):
|
|
|
648
825
|
)});"""
|
|
649
826
|
)
|
|
650
827
|
except Exception as e:
|
|
651
|
-
print(e)
|
|
652
828
|
pass
|
|
653
829
|
|
|
654
830
|
def clear_live(self, meta: CtxMeta, ctx: CtxItem):
|
|
@@ -701,7 +877,7 @@ class Renderer(BaseRenderer):
|
|
|
701
877
|
html=html,
|
|
702
878
|
type=type,
|
|
703
879
|
prev_ctx=prev_ctx,
|
|
704
|
-
next_ctx=next_ctx
|
|
880
|
+
next_ctx=next_ctx,
|
|
705
881
|
)
|
|
706
882
|
)
|
|
707
883
|
|
|
@@ -745,14 +921,14 @@ class Renderer(BaseRenderer):
|
|
|
745
921
|
self.append_input(
|
|
746
922
|
meta,
|
|
747
923
|
ctx,
|
|
748
|
-
flush=False
|
|
924
|
+
flush=False,
|
|
749
925
|
)
|
|
750
926
|
self.append_output(
|
|
751
927
|
meta,
|
|
752
928
|
ctx,
|
|
753
929
|
flush=False,
|
|
754
930
|
prev_ctx=prev_ctx,
|
|
755
|
-
next_ctx=next_ctx
|
|
931
|
+
next_ctx=next_ctx,
|
|
756
932
|
)
|
|
757
933
|
|
|
758
934
|
def append_extra(
|
|
@@ -1029,7 +1205,7 @@ class Renderer(BaseRenderer):
|
|
|
1029
1205
|
ctx=ctx,
|
|
1030
1206
|
html=html,
|
|
1031
1207
|
prev_ctx=prev_ctx,
|
|
1032
|
-
next_ctx=next_ctx
|
|
1208
|
+
next_ctx=next_ctx,
|
|
1033
1209
|
)
|
|
1034
1210
|
elif type == self.NODE_INPUT:
|
|
1035
1211
|
return self.prepare_node_input(
|
|
@@ -1037,7 +1213,7 @@ class Renderer(BaseRenderer):
|
|
|
1037
1213
|
ctx=ctx,
|
|
1038
1214
|
html=html,
|
|
1039
1215
|
prev_ctx=prev_ctx,
|
|
1040
|
-
next_ctx=next_ctx
|
|
1216
|
+
next_ctx=next_ctx,
|
|
1041
1217
|
)
|
|
1042
1218
|
|
|
1043
1219
|
def prepare_node_input(
|
|
@@ -1141,8 +1317,10 @@ class Renderer(BaseRenderer):
|
|
|
1141
1317
|
or out.startswith(('<tool>{"cmd"', '<tool>{"cmd"'))
|
|
1142
1318
|
or out.rstrip().endswith(('}</tool>', '}</tool>'))
|
|
1143
1319
|
):
|
|
1144
|
-
spinner_class = ""
|
|
1145
|
-
spinner =
|
|
1320
|
+
spinner_class = ""
|
|
1321
|
+
spinner = ""
|
|
1322
|
+
# spinner_class = "" if ctx.live else "display:none"
|
|
1323
|
+
# spinner = f"<span class=\"spinner\" style=\"{spinner_class}\"><img src=\"{self._file_prefix}{self._icon_sync}\" width=\"30\" height=\"30\" class=\"loading\"></span>"
|
|
1146
1324
|
|
|
1147
1325
|
tool_extra = self.body.prepare_tool_extra(ctx)
|
|
1148
1326
|
debug = self.append_debug(ctx, pid, "output") if self.is_debug() else ""
|
|
@@ -1240,6 +1418,7 @@ class Renderer(BaseRenderer):
|
|
|
1240
1418
|
node = self.get_output_node_by_pid(pid)
|
|
1241
1419
|
if node is not None:
|
|
1242
1420
|
node.resetPage()
|
|
1421
|
+
|
|
1243
1422
|
self._throttle_reset(pid)
|
|
1244
1423
|
|
|
1245
1424
|
def get_output_node(
|
|
@@ -1282,7 +1461,8 @@ class Renderer(BaseRenderer):
|
|
|
1282
1461
|
"""
|
|
1283
1462
|
try:
|
|
1284
1463
|
self.get_output_node(ctx.meta).page().runJavaScript(
|
|
1285
|
-
f"if (typeof window.removeNode !== 'undefined') removeNode({self.to_json(ctx.id)});"
|
|
1464
|
+
f"if (typeof window.removeNode !== 'undefined') removeNode({self.to_json(ctx.id)});"
|
|
1465
|
+
)
|
|
1286
1466
|
except Exception:
|
|
1287
1467
|
pass
|
|
1288
1468
|
|
|
@@ -1294,7 +1474,8 @@ class Renderer(BaseRenderer):
|
|
|
1294
1474
|
"""
|
|
1295
1475
|
try:
|
|
1296
1476
|
self.get_output_node(ctx.meta).page().runJavaScript(
|
|
1297
|
-
f"if (typeof window.removeNodesFromId !== 'undefined') removeNodesFromId({self.to_json(ctx.id)});"
|
|
1477
|
+
f"if (typeof window.removeNodesFromId !== 'undefined') removeNodesFromId({self.to_json(ctx.id)});"
|
|
1478
|
+
)
|
|
1298
1479
|
except Exception:
|
|
1299
1480
|
pass
|
|
1300
1481
|
|
|
@@ -1456,12 +1637,16 @@ class Renderer(BaseRenderer):
|
|
|
1456
1637
|
for node in nodes:
|
|
1457
1638
|
try:
|
|
1458
1639
|
node.page().runJavaScript(
|
|
1459
|
-
f"if (typeof window.updateCSS !== 'undefined') updateCSS({to_json});"
|
|
1640
|
+
f"if (typeof window.updateCSS !== 'undefined') updateCSS({to_json});"
|
|
1641
|
+
)
|
|
1460
1642
|
if self.window.core.config.get('render.blocks'):
|
|
1461
|
-
node.page().runJavaScript(
|
|
1643
|
+
node.page().runJavaScript(
|
|
1644
|
+
"if (typeof window.enableBlocks !== 'undefined') enableBlocks();"
|
|
1645
|
+
)
|
|
1462
1646
|
else:
|
|
1463
1647
|
node.page().runJavaScript(
|
|
1464
|
-
"if (typeof window.disableBlocks !== 'undefined') disableBlocks();"
|
|
1648
|
+
"if (typeof window.disableBlocks !== 'undefined') disableBlocks();"
|
|
1649
|
+
)
|
|
1465
1650
|
except Exception as e:
|
|
1466
1651
|
pass
|
|
1467
1652
|
return
|
|
@@ -1636,60 +1821,62 @@ class Renderer(BaseRenderer):
|
|
|
1636
1821
|
is_code_block: bool
|
|
1637
1822
|
):
|
|
1638
1823
|
"""
|
|
1639
|
-
Queue chunk for throttled
|
|
1824
|
+
Queue text chunk for throttled output
|
|
1640
1825
|
|
|
1641
1826
|
:param pid: context PID
|
|
1642
|
-
:param name: name
|
|
1643
|
-
:param html: HTML content
|
|
1644
|
-
:param text_chunk:
|
|
1645
|
-
:param replace:
|
|
1646
|
-
:param is_code_block:
|
|
1827
|
+
:param name: name header string
|
|
1828
|
+
:param html: HTML content to replace or append
|
|
1829
|
+
:param text_chunk: text chunk to append
|
|
1830
|
+
:param replace: True if the chunk should replace existing content
|
|
1831
|
+
:param is_code_block: True if the chunk is a code block
|
|
1647
1832
|
"""
|
|
1648
1833
|
thr = self._throttle_get(pid)
|
|
1649
1834
|
if name:
|
|
1650
1835
|
thr["name"] = name
|
|
1836
|
+
|
|
1651
1837
|
if replace:
|
|
1652
1838
|
thr["op"] = 1
|
|
1653
1839
|
thr["replace_html"] = html
|
|
1654
1840
|
thr["append"].clear()
|
|
1655
1841
|
thr["code"] = bool(is_code_block)
|
|
1656
1842
|
else:
|
|
1657
|
-
if thr["op"]
|
|
1658
|
-
thr["
|
|
1843
|
+
if thr["op"] == 1:
|
|
1844
|
+
thr["replace_html"] = html
|
|
1845
|
+
thr["code"] = bool(is_code_block)
|
|
1846
|
+
return
|
|
1847
|
+
thr["op"] = 2
|
|
1659
1848
|
thr["append"].append(text_chunk)
|
|
1660
1849
|
thr["code"] = bool(is_code_block)
|
|
1661
1850
|
|
|
1662
1851
|
def _throttle_emit(self, pid: int, force: bool = False):
|
|
1663
1852
|
"""
|
|
1664
|
-
Emit
|
|
1853
|
+
Emit throttled output to the node
|
|
1665
1854
|
|
|
1666
1855
|
:param pid: context PID
|
|
1667
|
-
:param force:
|
|
1856
|
+
:param force: Force emit even if throttle interval has not passed
|
|
1668
1857
|
"""
|
|
1669
1858
|
thr = self._throttle_get(pid)
|
|
1670
1859
|
now = monotonic()
|
|
1671
1860
|
if not force and (now - thr["last"] < self._throttle_interval):
|
|
1672
1861
|
return
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
node = self.get_output_node_by_pid(pid)
|
|
1692
|
-
if node is not None:
|
|
1862
|
+
|
|
1863
|
+
node = self.get_output_node_by_pid(pid)
|
|
1864
|
+
if node is None:
|
|
1865
|
+
return
|
|
1866
|
+
|
|
1867
|
+
try:
|
|
1868
|
+
if thr["op"] == 1:
|
|
1869
|
+
node.page().bridge.chunk.emit(
|
|
1870
|
+
thr["name"],
|
|
1871
|
+
self.sanitize_html(thr["replace_html"]),
|
|
1872
|
+
"",
|
|
1873
|
+
True,
|
|
1874
|
+
bool(thr["code"]),
|
|
1875
|
+
)
|
|
1876
|
+
thr["last"] = now
|
|
1877
|
+
|
|
1878
|
+
if thr["append"]:
|
|
1879
|
+
append_str = "".join(thr["append"])
|
|
1693
1880
|
node.page().bridge.chunk.emit(
|
|
1694
1881
|
thr["name"],
|
|
1695
1882
|
"",
|
|
@@ -1697,7 +1884,20 @@ class Renderer(BaseRenderer):
|
|
|
1697
1884
|
False,
|
|
1698
1885
|
bool(thr["code"]),
|
|
1699
1886
|
)
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1887
|
+
thr["last"] = now
|
|
1888
|
+
|
|
1889
|
+
self._throttle_reset(pid)
|
|
1890
|
+
|
|
1891
|
+
elif thr["op"] == 2 and thr["append"]:
|
|
1892
|
+
append_str = "".join(thr["append"])
|
|
1893
|
+
node.page().bridge.chunk.emit(
|
|
1894
|
+
thr["name"],
|
|
1895
|
+
"",
|
|
1896
|
+
self.sanitize_html(append_str),
|
|
1897
|
+
False,
|
|
1898
|
+
bool(thr["code"]),
|
|
1899
|
+
)
|
|
1900
|
+
thr["last"] = now
|
|
1901
|
+
self._throttle_reset(pid)
|
|
1902
|
+
except Exception:
|
|
1903
|
+
pass
|
|
@@ -118,8 +118,6 @@ class Presets:
|
|
|
118
118
|
self.window.ui.models[self.id] = model
|
|
119
119
|
view.setModel(model)
|
|
120
120
|
|
|
121
|
-
blocker = QtCore.QSignalBlocker(model)
|
|
122
|
-
|
|
123
121
|
rc = model.rowCount()
|
|
124
122
|
if rc:
|
|
125
123
|
model.removeRows(0, rc)
|
|
@@ -144,6 +142,4 @@ class Presets:
|
|
|
144
142
|
model.setData(index, name, QtCore.Qt.DisplayRole)
|
|
145
143
|
model.setData(index, tooltip, QtCore.Qt.ToolTipRole)
|
|
146
144
|
|
|
147
|
-
del blocker
|
|
148
|
-
|
|
149
145
|
view.restore_selection()
|
|
@@ -124,7 +124,6 @@ class ChatWebOutput(QWebEngineView):
|
|
|
124
124
|
"""
|
|
125
125
|
p = QWebEngineProfile(parent or self)
|
|
126
126
|
try:
|
|
127
|
-
p.setHttpCacheType(QWebEngineProfile.NoCache)
|
|
128
127
|
p.setHttpCacheMaximumSize(0)
|
|
129
128
|
p.setPersistentCookiesPolicy(QWebEngineProfile.NoPersistentCookies)
|
|
130
129
|
p.setSpellCheckEnabled(False)
|
|
@@ -193,7 +192,6 @@ class ChatWebOutput(QWebEngineView):
|
|
|
193
192
|
p.runJavaScript(
|
|
194
193
|
f"""clean();"""
|
|
195
194
|
)
|
|
196
|
-
p.profile().clearHttpCache()
|
|
197
195
|
try:
|
|
198
196
|
p.history().clear()
|
|
199
197
|
except Exception:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.14
|
|
4
4
|
Summary: Desktop AI Assistant powered by: OpenAI GPT-5, o1, o3, GPT-4, Gemini, Claude, Grok, DeepSeek, and other models supported by Llama Index, and Ollama. Chatbot, agents, completion, image generation, vision analysis, speech-to-text, plugins, internet access, file handling, command execution and more.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: py_gpt,py-gpt,pygpt,desktop,app,o1,o3,gpt-5,gpt,gpt4,gpt-4o,gpt-4v,gpt3.5,gpt-4,gpt-4-vision,gpt-3.5,llama3,mistral,gemini,grok,deepseek,bielik,claude,tts,whisper,vision,chatgpt,dall-e,chat,chatbot,assistant,text completion,image generation,ai,api,openai,api key,langchain,llama-index,ollama,presets,ui,qt,pyside
|
|
@@ -108,7 +108,7 @@ Description-Content-Type: text/markdown
|
|
|
108
108
|
|
|
109
109
|
[](https://snapcraft.io/pygpt)
|
|
110
110
|
|
|
111
|
-
Release: **2.6.
|
|
111
|
+
Release: **2.6.14** | build: **2025-08-19** | Python: **>=3.10, <3.14**
|
|
112
112
|
|
|
113
113
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
114
114
|
>
|
|
@@ -4566,6 +4566,18 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
4566
4566
|
|
|
4567
4567
|
## Recent changes:
|
|
4568
4568
|
|
|
4569
|
+
**2.6.14 (2025-08-19)**
|
|
4570
|
+
|
|
4571
|
+
- Fixed: Agent evaluation tool runs even if tools are disabled.
|
|
4572
|
+
- Extended agent response evaluation by providing the full context of the output.
|
|
4573
|
+
|
|
4574
|
+
**2.6.13 (2025-08-19)**
|
|
4575
|
+
|
|
4576
|
+
- Fix: Do not load the index in experts if it is not provided.
|
|
4577
|
+
- Fix: Load remote images in the webview.
|
|
4578
|
+
- Fix: Presets list refresh.
|
|
4579
|
+
- Optimize context items reload.
|
|
4580
|
+
|
|
4569
4581
|
**2.6.12 (2025-08-19)**
|
|
4570
4582
|
|
|
4571
4583
|
- Optimized web renderer memory cleanup.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
pygpt_net/CHANGELOG.txt,sha256=
|
|
1
|
+
pygpt_net/CHANGELOG.txt,sha256=MteKitJVszN9R1u3hfVWk11KFOxt9a4NIkyIVhWVffE,99967
|
|
2
2
|
pygpt_net/LICENSE,sha256=dz9sfFgYahvu2NZbx4C1xCsVn9GVer2wXcMkFRBvqzY,1146
|
|
3
|
-
pygpt_net/__init__.py,sha256=
|
|
3
|
+
pygpt_net/__init__.py,sha256=qxhiFAYY-bI6byswkH6rOhEXbQF7ZpcYWH2apkXFhaM,1373
|
|
4
4
|
pygpt_net/app.py,sha256=2IXjjYJ0tm-iFn3pHu3-JGoFAnN9YvmXGHPmeOhpU3Y,20999
|
|
5
5
|
pygpt_net/config.py,sha256=LCKrqQfePVNrAvH3EY_1oZx1Go754sDoyUneJ0iGWFI,16660
|
|
6
6
|
pygpt_net/container.py,sha256=NsMSHURaEC_eW8vrCNdztwqkxB7jui3yVlzUOMYvCHg,4124
|
|
@@ -40,9 +40,9 @@ pygpt_net/controller/chat/common.py,sha256=mOHvXqj3pPCzNPU82i7NXSmglW8jh7G5cCKDe
|
|
|
40
40
|
pygpt_net/controller/chat/files.py,sha256=QZAi1Io57EU7htKt9M5I9OoGAFX51OH2V5-NsJktOto,2838
|
|
41
41
|
pygpt_net/controller/chat/image.py,sha256=yPX26gsz0fLnyXR88lpVyvvHnKA-yZwfXJ4paUDYkeM,8579
|
|
42
42
|
pygpt_net/controller/chat/input.py,sha256=EPA90r6GqHIlu4JJbr0cuvKIEYSs6LVkimxrWHAyyX0,12390
|
|
43
|
-
pygpt_net/controller/chat/output.py,sha256=
|
|
43
|
+
pygpt_net/controller/chat/output.py,sha256=aSgZ8E8IaP4Jjd0ab6eXhebM_YIRPW7pRJUesYCgDQg,10864
|
|
44
44
|
pygpt_net/controller/chat/render.py,sha256=YIKWMZIUXD3f_11p9mXjgg-TtudZTjJ26iHXTn553Qc,20401
|
|
45
|
-
pygpt_net/controller/chat/response.py,sha256=
|
|
45
|
+
pygpt_net/controller/chat/response.py,sha256=tacJOG_iG7IjEFiZ4iDggEVVS9mbL0e92JBC--7bnCY,12543
|
|
46
46
|
pygpt_net/controller/chat/stream.py,sha256=zmDGI_Z9Rn8IYv6vEIVBMTOGjjY0zlfmM3qJMddRGRI,21994
|
|
47
47
|
pygpt_net/controller/chat/text.py,sha256=ktluNw9ItG4g9p3OpOSmgALhFf1Gnonhl3J9kLzdTqU,10743
|
|
48
48
|
pygpt_net/controller/chat/vision.py,sha256=LsFc0TZZwY8dVtJH6Q5iha8rUQCf5HhOMuRXMtnLzZU,3578
|
|
@@ -112,7 +112,7 @@ pygpt_net/controller/plugins/plugins.py,sha256=jO5KcC0jsIcwF2U6eSZqicGKUr2MzC0F7
|
|
|
112
112
|
pygpt_net/controller/plugins/presets.py,sha256=8EsEwpU2MjWMQu1kcY4JTcyqqN8pjBrcxA2uW2tFU_A,11674
|
|
113
113
|
pygpt_net/controller/plugins/settings.py,sha256=pa0iJvpb1SXWD8hqYoC_BuSogx2-5u6nwcFF3TH5sWw,5732
|
|
114
114
|
pygpt_net/controller/presets/__init__.py,sha256=Bb9_aAvGxQcKCW2fvG5CAJ6ZUwNYN3GaCf3BXB9eGfI,511
|
|
115
|
-
pygpt_net/controller/presets/editor.py,sha256
|
|
115
|
+
pygpt_net/controller/presets/editor.py,sha256=-fFDkIqDwhYwAaSRuK6IayjX8RriqFoc0_LKblSOi-Q,39449
|
|
116
116
|
pygpt_net/controller/presets/experts.py,sha256=dfPKmAPO-7gaUD2ILs3lR005ir32G5vV-Sa5TGEHwOU,5820
|
|
117
117
|
pygpt_net/controller/presets/presets.py,sha256=Tq9AIgr042ALu6hEQunBxnUZTzLdzmp4IStWprzmyjg,21918
|
|
118
118
|
pygpt_net/controller/settings/__init__.py,sha256=hn5n_Hti6byJQdQCs4Ld2EbPoZF7dHVMwqaBPscePQ8,512
|
|
@@ -146,16 +146,16 @@ pygpt_net/core/agents/bridge.py,sha256=KhCbMTZNigNlgOhXEMN1kqWGNUhkEdjFdiHBBVTAr
|
|
|
146
146
|
pygpt_net/core/agents/legacy.py,sha256=DdlyIpFjmeAC4XUGtq3F5_1BLGZLPOej0RZ6x9ycFjM,1731
|
|
147
147
|
pygpt_net/core/agents/memory.py,sha256=9Jz9kT-xT8QPpGeXEpWopJUGBLLHu6Ys_-fRrg6BWDg,5210
|
|
148
148
|
pygpt_net/core/agents/observer/__init__.py,sha256=qVIBJKpGbc0k7PTESAwAR7SbN-pbkBMJUTzeliCAaJU,651
|
|
149
|
-
pygpt_net/core/agents/observer/evaluation.py,sha256=
|
|
149
|
+
pygpt_net/core/agents/observer/evaluation.py,sha256=r-zIRXZpOgo8TJ0qbNPL2Qm_9zmPac9AEWwIbnIW4T8,8491
|
|
150
150
|
pygpt_net/core/agents/provider.py,sha256=rjxnuqzRxv2Z1d9i_wKpREwJBTeTgtyBDYtyHuwcSPA,2440
|
|
151
|
-
pygpt_net/core/agents/runner.py,sha256=
|
|
151
|
+
pygpt_net/core/agents/runner.py,sha256=C3BQkpHLrd92EuM55mR4PnCJMVvZ4nV0kDhed4xfl9o,12186
|
|
152
152
|
pygpt_net/core/agents/runners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
153
153
|
pygpt_net/core/agents/runners/base.py,sha256=XjheBYhBZan51r3vkUh5uf00sYiRj8btZUeUP1jpqlA,5687
|
|
154
154
|
pygpt_net/core/agents/runners/helpers.py,sha256=0iQQlSg_pJfxY_gQEWImnyAVkTUQYYSW67Z9DS7oUYw,8305
|
|
155
155
|
pygpt_net/core/agents/runners/llama_assistant.py,sha256=a_Abkc8u1S8vr6lUIDRrzTM9sQnEvyZA8nZxXaYp05w,2492
|
|
156
156
|
pygpt_net/core/agents/runners/llama_plan.py,sha256=CC3WPG9KUxd_dRjPZROOrmPQrWQ_u8C0nRx0TCzi9bE,13391
|
|
157
157
|
pygpt_net/core/agents/runners/llama_steps.py,sha256=1SBLp5t4TUsxpYIUtSSnBy5Sd2AxheDlv2AXimls-Vg,7328
|
|
158
|
-
pygpt_net/core/agents/runners/llama_workflow.py,sha256=
|
|
158
|
+
pygpt_net/core/agents/runners/llama_workflow.py,sha256=yDy1YDnG0A2OIs1FnJArXql8-BPnT7qpEywh3HqhR8c,12086
|
|
159
159
|
pygpt_net/core/agents/runners/loop.py,sha256=opcVGx8WFjJesLlmMzoCBgP06Ajh6j_Taat4zCTumHg,6022
|
|
160
160
|
pygpt_net/core/agents/runners/openai_workflow.py,sha256=J47INptxu8Uc40UfAWNRRiHRYL6ZM6lPojoqeHsC-mc,7989
|
|
161
161
|
pygpt_net/core/agents/tools.py,sha256=6V2IjSF0m8cNY0HI7vW9bTLjKzR1KtcP0XTcHBvrPjU,21727
|
|
@@ -232,7 +232,7 @@ pygpt_net/core/events/event.py,sha256=uxNdPGaV5KDBaosPM6uxio7dPig091wAoz-OdOPCmx
|
|
|
232
232
|
pygpt_net/core/events/kernel.py,sha256=Y5zQ-YCex04OQNMRMC7Q8g55A-imyj9XQ0Jkuyk2eCU,2074
|
|
233
233
|
pygpt_net/core/events/render.py,sha256=Xfncp6ESvjPyBLtFq6KiNxckAFDN0DsUOJ_mrowjfnM,2525
|
|
234
234
|
pygpt_net/core/experts/__init__.py,sha256=oscAmAEsCZclyHU7k3z5JzYqilwIO7J90e6oTa29tZ0,511
|
|
235
|
-
pygpt_net/core/experts/experts.py,sha256=
|
|
235
|
+
pygpt_net/core/experts/experts.py,sha256=Bq7-xsXK-UBs7ZbjC6kPDULOIwndDcAJSHlV1vXIMug,30287
|
|
236
236
|
pygpt_net/core/filesystem/__init__.py,sha256=KZLS3s_otd3Md9eDA6FN-b4CtOCWl_fplUlM9V6hTy8,514
|
|
237
237
|
pygpt_net/core/filesystem/actions.py,sha256=2lRVF_MpIxCwbH8DkugP0K6pY6FymLeH6LKVR2rtQGQ,4152
|
|
238
238
|
pygpt_net/core/filesystem/editor.py,sha256=or7cT2xhZfDwjX47reyXQCt-_1c4h_xPJDddYi1auNw,4284
|
|
@@ -280,7 +280,7 @@ pygpt_net/core/platforms/platforms.py,sha256=599XwekDL1cNs5p8i89rcUO05Dl4t1XFBPf
|
|
|
280
280
|
pygpt_net/core/plugins/__init__.py,sha256=NOKL-CNsF4rrKTBpsN-ue92H4pTUGKlgDCwr1iA0geY,511
|
|
281
281
|
pygpt_net/core/plugins/plugins.py,sha256=2tARWlrBbjarWINp-woHMqB4jzp2SvzVVZVrLzFhgxg,14832
|
|
282
282
|
pygpt_net/core/presets/__init__.py,sha256=NZjBxjGv4fgEX6Hp8FznsWK5QqD1Tl7zyp2Ir3ufXv4,511
|
|
283
|
-
pygpt_net/core/presets/presets.py,sha256=
|
|
283
|
+
pygpt_net/core/presets/presets.py,sha256=rdumbBggdXJCZ45o4aFOBswzW4GDGXrWBAAEZneJRig,12961
|
|
284
284
|
pygpt_net/core/profile/__init__.py,sha256=ovh37AKXCbEz1owutNltCXRkmhZj097wcdZLSzkJUvk,511
|
|
285
285
|
pygpt_net/core/profile/profile.py,sha256=ZpXKTwbViskCUDBn8JvxSk7wRFdfrwMfs7Gb3O7N_48,7840
|
|
286
286
|
pygpt_net/core/prompt/__init__.py,sha256=4GH-2XlmPjySjMTEWWb_gZDDetT8lZaoUc-P6FzECus,510
|
|
@@ -306,8 +306,8 @@ pygpt_net/core/render/web/__init__.py,sha256=istp5dsn6EkLEP7lOBeDb8RjodUcWZqjcEv
|
|
|
306
306
|
pygpt_net/core/render/web/body.py,sha256=rybg76GiLWowR-qEvM0Y64woSsO4KSBnww4f8BU7GgI,54872
|
|
307
307
|
pygpt_net/core/render/web/helpers.py,sha256=ivrXrCqRIUWHDmu3INu-i6XUlB2W9IOO8iYyqpbnSRU,5438
|
|
308
308
|
pygpt_net/core/render/web/parser.py,sha256=pDFc9Tf8P-jvrDilXyT1fukcQHbixHRJ9Dn9hF10Gko,12892
|
|
309
|
-
pygpt_net/core/render/web/pid.py,sha256=
|
|
310
|
-
pygpt_net/core/render/web/renderer.py,sha256=
|
|
309
|
+
pygpt_net/core/render/web/pid.py,sha256=pXBdPb8hw_aZS2Rtz3pLBpuybpXrzoqwYAFWBal9bLE,3685
|
|
310
|
+
pygpt_net/core/render/web/renderer.py,sha256=b7nKnICWSrqZqdd6VBN-JPbm_x5WIkTQ7auhF72Hm-Y,56128
|
|
311
311
|
pygpt_net/core/render/web/syntax_highlight.py,sha256=QSLGF5cJL_Xeqej7_TYwY_5C2w9enXV_cMEuaJ3C43U,2005
|
|
312
312
|
pygpt_net/core/settings/__init__.py,sha256=GQ6_gJ2jf_Chm7ZuZLvkcvEh_sfMDVMBieeoJi2iPI4,512
|
|
313
313
|
pygpt_net/core/settings/settings.py,sha256=onqwNiICm2VhHfmXLvp1MiEJ14m2jzeeI2pjUiaUwtY,7787
|
|
@@ -343,8 +343,8 @@ pygpt_net/css_rc.py,sha256=i13kX7irhbYCWZ5yJbcMmnkFp_UfS4PYnvRFSPF7XXo,11349
|
|
|
343
343
|
pygpt_net/data/audio/click_off.mp3,sha256=aNiRDP1pt-Jy7ija4YKCNFBwvGWbzU460F4pZWZDS90,65201
|
|
344
344
|
pygpt_net/data/audio/click_on.mp3,sha256=qfdsSnthAEHVXzeyN4LlC0OvXuyW8p7stb7VXtlvZ1k,65201
|
|
345
345
|
pygpt_net/data/audio/ok.mp3,sha256=LTiV32pEBkpUGBkKkcOdOFB7Eyt_QoP2Nv6c5AaXftk,32256
|
|
346
|
-
pygpt_net/data/config/config.json,sha256=
|
|
347
|
-
pygpt_net/data/config/models.json,sha256=
|
|
346
|
+
pygpt_net/data/config/config.json,sha256=pYWdDpJvFUZg2YhkGAe2sfqhzyb8nC5ZmZMMrZN0c8U,24924
|
|
347
|
+
pygpt_net/data/config/models.json,sha256=eD8uuC55qsr7lljL0b-JdwL6ieTBjvKGqUnrZ3wN8Uw,109650
|
|
348
348
|
pygpt_net/data/config/modes.json,sha256=M882iiqX_R2sNQl9cqZ3k-uneEvO9wpARtHRMLx_LHw,2265
|
|
349
349
|
pygpt_net/data/config/presets/agent_code_act.json,sha256=GYHqhxtKFLUCvRI3IJAJ7Qe1k8yD9wGGNwManldWzlI,754
|
|
350
350
|
pygpt_net/data/config/presets/agent_openai.json,sha256=bpDJgLRey_effQkzFRoOEGd4aHUrmzeODSDdNzrf62I,730
|
|
@@ -2308,7 +2308,7 @@ pygpt_net/ui/layout/toolbox/image.py,sha256=QiMDZmsLklIEVDibq1FRraD30NJll_bn4HVp
|
|
|
2308
2308
|
pygpt_net/ui/layout/toolbox/indexes.py,sha256=fPRuMS96HX3VJdvbGVg0Z7N6P0LTbrYM0BYjXZOUUic,8502
|
|
2309
2309
|
pygpt_net/ui/layout/toolbox/mode.py,sha256=EoXDzzhh3XRE5i42sWmiOCLT_m_sUELYXfk_vpDNoSs,1772
|
|
2310
2310
|
pygpt_net/ui/layout/toolbox/model.py,sha256=9JHW8yzTHUh6TJmOAylriwlDsyrB8J9wDpJpHR__z9I,1789
|
|
2311
|
-
pygpt_net/ui/layout/toolbox/presets.py,sha256=
|
|
2311
|
+
pygpt_net/ui/layout/toolbox/presets.py,sha256=praww59pJLD7l5TITJh--B5H7V_OH1_8t3KSw2yvgFw,5565
|
|
2312
2312
|
pygpt_net/ui/layout/toolbox/prompt.py,sha256=jebF-q1S1Et6ISa9vI0_nM4sb7liDesAXJHtZ5Ll7ZI,4006
|
|
2313
2313
|
pygpt_net/ui/layout/toolbox/toolbox.py,sha256=zEZr_XDz9QbPKL0u0KMSt1b8yOG-ao1gmZPvWWVpuVs,3392
|
|
2314
2314
|
pygpt_net/ui/layout/toolbox/vision.py,sha256=GZY-N2z8re1LN1ntsy-3Ius8OY4DujmJpyJ1qP2ZRxs,2447
|
|
@@ -2433,12 +2433,12 @@ pygpt_net/ui/widget/textarea/output.py,sha256=8T2spzqVYHKopSB83p1ULazGZ14nFJhXLB
|
|
|
2433
2433
|
pygpt_net/ui/widget/textarea/rename.py,sha256=NwuGRIeWMo7WfsMguAFpTqdOz1eTiXbxrDXGsbWF_TY,1358
|
|
2434
2434
|
pygpt_net/ui/widget/textarea/search_input.py,sha256=phEXf50VcfCRBen0p2iEAzuX2zmrSE3nWVRfWmtHKpo,5228
|
|
2435
2435
|
pygpt_net/ui/widget/textarea/url.py,sha256=xbNQxoM5fYI1ZWbvybQkPmNPrIq3yhtNPBOSOWftZCg,1337
|
|
2436
|
-
pygpt_net/ui/widget/textarea/web.py,sha256=
|
|
2436
|
+
pygpt_net/ui/widget/textarea/web.py,sha256=3XA7Ue8PtxYkVbZRaOPIGjpC5Iko37RlxfO358UEdrk,19425
|
|
2437
2437
|
pygpt_net/ui/widget/vision/__init__.py,sha256=8HT4tQFqQogEEpGYTv2RplKBthlsFKcl5egnv4lzzEw,488
|
|
2438
2438
|
pygpt_net/ui/widget/vision/camera.py,sha256=T8b5cmK6uhf_WSSxzPt_Qod8JgMnst6q8sQqRvgQiSA,2584
|
|
2439
2439
|
pygpt_net/utils.py,sha256=gGbw-lBTodGg_uBx6zKEwa58GaVNZN1I9zY_ZDyJ9xg,8872
|
|
2440
|
-
pygpt_net-2.6.
|
|
2441
|
-
pygpt_net-2.6.
|
|
2442
|
-
pygpt_net-2.6.
|
|
2443
|
-
pygpt_net-2.6.
|
|
2444
|
-
pygpt_net-2.6.
|
|
2440
|
+
pygpt_net-2.6.14.dist-info/LICENSE,sha256=rbPqNB_xxANH8hKayJyIcTwD4bj4Y2G-Mcm85r1OImM,1126
|
|
2441
|
+
pygpt_net-2.6.14.dist-info/METADATA,sha256=unXw9fPsGAEbt_rxZ0LGD0J5d4z9T1y-9SxBWKSBjs0,189682
|
|
2442
|
+
pygpt_net-2.6.14.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
2443
|
+
pygpt_net-2.6.14.dist-info/entry_points.txt,sha256=qvpII6UHIt8XfokmQWnCYQrTgty8FeJ9hJvOuUFCN-8,43
|
|
2444
|
+
pygpt_net-2.6.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|