pygpt-net 2.6.62__py3-none-any.whl → 2.6.64__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +11 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/attachment/attachment.py +17 -8
- pygpt_net/controller/camera/camera.py +4 -4
- pygpt_net/controller/lang/custom.py +2 -2
- pygpt_net/controller/presets/editor.py +65 -1
- pygpt_net/controller/ui/mode.py +18 -3
- pygpt_net/core/agents/custom/llama_index/runner.py +15 -52
- pygpt_net/core/agents/custom/runner.py +194 -76
- pygpt_net/core/agents/runners/llama_workflow.py +60 -10
- pygpt_net/core/render/web/renderer.py +11 -0
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/presets/agent_openai_b2b.json +1 -15
- pygpt_net/data/config/presets/agent_openai_coder.json +0 -0
- pygpt_net/data/config/presets/agent_openai_evolve.json +1 -23
- pygpt_net/data/config/presets/agent_openai_planner.json +1 -21
- pygpt_net/data/config/presets/agent_openai_researcher.json +1 -21
- pygpt_net/data/config/presets/agent_openai_supervisor.json +1 -13
- pygpt_net/data/config/presets/agent_openai_writer.json +1 -15
- pygpt_net/data/config/presets/agent_supervisor.json +1 -11
- pygpt_net/data/js/app/runtime.js +10 -0
- pygpt_net/data/js/app/scroll.js +14 -0
- pygpt_net/data/js/app.min.js +6 -4
- pygpt_net/data/locale/locale.de.ini +32 -0
- pygpt_net/data/locale/locale.en.ini +37 -0
- pygpt_net/data/locale/locale.es.ini +32 -0
- pygpt_net/data/locale/locale.fr.ini +32 -0
- pygpt_net/data/locale/locale.it.ini +32 -0
- pygpt_net/data/locale/locale.pl.ini +34 -2
- pygpt_net/data/locale/locale.uk.ini +32 -0
- pygpt_net/data/locale/locale.zh.ini +32 -0
- pygpt_net/js_rc.py +7571 -7499
- pygpt_net/provider/agents/base.py +0 -0
- pygpt_net/provider/agents/llama_index/flow_from_schema.py +0 -0
- pygpt_net/provider/agents/llama_index/planner_workflow.py +15 -3
- pygpt_net/provider/agents/llama_index/workflow/codeact.py +0 -0
- pygpt_net/provider/agents/llama_index/workflow/planner.py +272 -44
- pygpt_net/provider/agents/llama_index/workflow/supervisor.py +0 -0
- pygpt_net/provider/agents/openai/agent.py +0 -0
- pygpt_net/provider/agents/openai/agent_b2b.py +4 -4
- pygpt_net/provider/agents/openai/agent_planner.py +631 -254
- pygpt_net/provider/agents/openai/agent_with_experts.py +0 -0
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -4
- pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -4
- pygpt_net/provider/agents/openai/evolve.py +6 -9
- pygpt_net/provider/agents/openai/flow_from_schema.py +0 -0
- pygpt_net/provider/agents/openai/supervisor.py +290 -37
- pygpt_net/provider/api/google/__init__.py +9 -3
- pygpt_net/provider/api/google/image.py +11 -1
- pygpt_net/provider/api/google/music.py +375 -0
- pygpt_net/provider/api/x_ai/__init__.py +0 -0
- pygpt_net/provider/core/agent/__init__.py +0 -0
- pygpt_net/provider/core/agent/base.py +0 -0
- pygpt_net/provider/core/agent/json_file.py +0 -0
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -0
- pygpt_net/provider/llms/base.py +0 -0
- pygpt_net/provider/llms/deepseek_api.py +0 -0
- pygpt_net/provider/llms/google.py +0 -0
- pygpt_net/provider/llms/hugging_face_api.py +0 -0
- pygpt_net/provider/llms/hugging_face_router.py +0 -0
- pygpt_net/provider/llms/mistral.py +0 -0
- pygpt_net/provider/llms/perplexity.py +0 -0
- pygpt_net/provider/llms/x_ai.py +0 -0
- pygpt_net/ui/widget/dialog/confirm.py +34 -8
- pygpt_net/ui/widget/option/combo.py +149 -11
- pygpt_net/ui/widget/textarea/input.py +1 -1
- pygpt_net/ui/widget/textarea/web.py +1 -1
- pygpt_net/ui/widget/vision/camera.py +135 -12
- {pygpt_net-2.6.62.dist-info → pygpt_net-2.6.64.dist-info}/METADATA +13 -2
- {pygpt_net-2.6.62.dist-info → pygpt_net-2.6.64.dist-info}/RECORD +53 -52
- {pygpt_net-2.6.62.dist-info → pygpt_net-2.6.64.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.62.dist-info → pygpt_net-2.6.64.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.62.dist-info → pygpt_net-2.6.64.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
2.6.64 (2025-09-27)
|
|
2
|
+
|
|
3
|
+
- Added translations to agent headers.
|
|
4
|
+
- Improved presets tabs.
|
|
5
|
+
- Added support for music (Lyria) in both image and video modes (beta).
|
|
6
|
+
|
|
7
|
+
2.6.63 (2025-09-27)
|
|
8
|
+
|
|
9
|
+
- Improved agents' workflows.
|
|
10
|
+
- Enhanced the display of agents' steps in the UI.
|
|
11
|
+
|
|
1
12
|
2.6.62 (2025-09-26)
|
|
2
13
|
|
|
3
14
|
- Enhanced agent workflow execution.
|
pygpt_net/__init__.py
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.27 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
__author__ = "Marcin Szczygliński"
|
|
13
13
|
__copyright__ = "Copyright 2025, Marcin Szczygliński"
|
|
14
14
|
__credits__ = ["Marcin Szczygliński"]
|
|
15
15
|
__license__ = "MIT"
|
|
16
|
-
__version__ = "2.6.
|
|
17
|
-
__build__ = "2025-09-
|
|
16
|
+
__version__ = "2.6.64"
|
|
17
|
+
__build__ = "2025-09-27"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
20
20
|
__report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
|
|
@@ -579,19 +579,28 @@ class Attachment:
|
|
|
579
579
|
if not os.path.exists(url):
|
|
580
580
|
return
|
|
581
581
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
582
|
+
is_image = False
|
|
583
|
+
image_ext = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff']
|
|
584
|
+
ext = os.path.splitext(url)[1].lower()
|
|
585
|
+
if ext in image_ext:
|
|
586
|
+
is_image = True
|
|
587
|
+
|
|
588
|
+
if not all and not is_image:
|
|
589
|
+
return
|
|
590
|
+
|
|
591
|
+
if is_image:
|
|
592
|
+
title = "attachments.paste.img"
|
|
593
|
+
status = "painter.capture.manual.captured.success"
|
|
594
|
+
else:
|
|
595
|
+
title = "attachments.paste.file"
|
|
596
|
+
status = "attachments.paste.success"
|
|
587
597
|
|
|
588
598
|
mode = self.window.core.config.get('mode')
|
|
589
|
-
title
|
|
590
|
-
self.window.core.attachments.new(mode, title, url, False)
|
|
599
|
+
self.window.core.attachments.new(mode, trans(title), url, False)
|
|
591
600
|
self.window.core.attachments.save()
|
|
592
601
|
self.window.controller.attachment.update()
|
|
593
602
|
event = KernelEvent(KernelEvent.STATUS, {
|
|
594
|
-
'status': trans(
|
|
603
|
+
'status': trans(status) + ' ' + os.path.basename(url),
|
|
595
604
|
})
|
|
596
605
|
self.window.dispatch(event)
|
|
597
606
|
|
|
@@ -67,9 +67,9 @@ class Camera(QObject):
|
|
|
67
67
|
|
|
68
68
|
# update label
|
|
69
69
|
if not self.window.core.config.get('vision.capture.auto'):
|
|
70
|
-
self.window.ui.nodes['video.preview'].
|
|
70
|
+
self.window.ui.nodes['video.preview'].video.setToolTip(trans("vision.capture.label"))
|
|
71
71
|
else:
|
|
72
|
-
self.window.ui.nodes['video.preview'].
|
|
72
|
+
self.window.ui.nodes['video.preview'].video.setToolTip(trans("vision.capture.auto.label"))
|
|
73
73
|
|
|
74
74
|
def update(self):
|
|
75
75
|
"""Update camera frame"""
|
|
@@ -381,7 +381,7 @@ class Camera(QObject):
|
|
|
381
381
|
{'value': True}
|
|
382
382
|
)
|
|
383
383
|
"""
|
|
384
|
-
self.window.ui.nodes['video.preview'].
|
|
384
|
+
self.window.ui.nodes['video.preview'].video.setToolTip(trans("vision.capture.auto.label"))
|
|
385
385
|
|
|
386
386
|
if not self.window.core.config.get('vision.capture.enabled'):
|
|
387
387
|
self.enable_capture()
|
|
@@ -403,7 +403,7 @@ class Camera(QObject):
|
|
|
403
403
|
{'value': False}
|
|
404
404
|
)
|
|
405
405
|
"""
|
|
406
|
-
self.window.ui.nodes['video.preview'].
|
|
406
|
+
self.window.ui.nodes['video.preview'].video.setToolTip(trans("vision.capture.label"))
|
|
407
407
|
|
|
408
408
|
def toggle_auto(self, state: bool):
|
|
409
409
|
"""
|
|
@@ -68,9 +68,9 @@ class Custom:
|
|
|
68
68
|
|
|
69
69
|
# camera capture
|
|
70
70
|
if not self.window.core.config.get('vision.capture.auto'):
|
|
71
|
-
self.window.ui.nodes['video.preview'].
|
|
71
|
+
self.window.ui.nodes['video.preview'].video.setToolTip(trans("vision.capture.label"))
|
|
72
72
|
else:
|
|
73
|
-
self.window.ui.nodes['video.preview'].
|
|
73
|
+
self.window.ui.nodes['video.preview'].video.setToolTip(trans("vision.capture.auto.label"))
|
|
74
74
|
|
|
75
75
|
# files / indexes
|
|
76
76
|
self.window.ui.nodes['output_files'].btn_upload.setText(trans('files.local.upload'))
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.27 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -393,6 +393,9 @@ class Editor:
|
|
|
393
393
|
value=extra_options[key].get('default', None),
|
|
394
394
|
)
|
|
395
395
|
|
|
396
|
+
# ensure combo defaults are effectively applied for this tab (only empty values are updated)
|
|
397
|
+
self._apply_combo_defaults_for_group(option_key, extra_options)
|
|
398
|
+
|
|
396
399
|
def load_extra_defaults(self):
|
|
397
400
|
"""Load extra options defaults for preset editor"""
|
|
398
401
|
if not self.tab_options_idx:
|
|
@@ -423,6 +426,8 @@ class Editor:
|
|
|
423
426
|
option=extra_options[key],
|
|
424
427
|
value=value,
|
|
425
428
|
)
|
|
429
|
+
# ensure combo defaults are effectively applied for this tab (only empty values are updated)
|
|
430
|
+
self._apply_combo_defaults_for_group(option_key, extra_options)
|
|
426
431
|
|
|
427
432
|
def load_extra_defaults_current(self):
|
|
428
433
|
"""Load extra options defaults on mode change"""
|
|
@@ -479,6 +484,8 @@ class Editor:
|
|
|
479
484
|
option=extra_options[key],
|
|
480
485
|
value=value,
|
|
481
486
|
)
|
|
487
|
+
# ensure combo defaults are effectively applied for this tab (only empty values are updated)
|
|
488
|
+
self._apply_combo_defaults_for_group(option_key, extra_options)
|
|
482
489
|
|
|
483
490
|
def append_extra_options(self, preset: PresetItem):
|
|
484
491
|
"""
|
|
@@ -785,6 +792,9 @@ class Editor:
|
|
|
785
792
|
value=opt_schema.get('default'),
|
|
786
793
|
)
|
|
787
794
|
|
|
795
|
+
# ensure combo defaults are effectively applied for this tab (only empty values are updated)
|
|
796
|
+
self._apply_combo_defaults_for_group(config_id, schema_options)
|
|
797
|
+
|
|
788
798
|
# 4) Recompute mapping fully based on actual tabs and their 'agent_id' properties.
|
|
789
799
|
self._rebuild_tab_index_mapping()
|
|
790
800
|
|
|
@@ -1521,6 +1531,9 @@ class Editor:
|
|
|
1521
1531
|
value=opt_schema.get('default'),
|
|
1522
1532
|
)
|
|
1523
1533
|
|
|
1534
|
+
# ensure combo defaults are effectively applied for this tab (only empty values are updated)
|
|
1535
|
+
self._apply_combo_defaults_for_group(config_id, schema_options)
|
|
1536
|
+
|
|
1524
1537
|
# 7) Recompute the index mapping strictly from the QTabWidget
|
|
1525
1538
|
self._rebuild_tab_index_mapping()
|
|
1526
1539
|
|
|
@@ -1529,3 +1542,54 @@ class Editor:
|
|
|
1529
1542
|
|
|
1530
1543
|
finally:
|
|
1531
1544
|
tabs.setUpdatesEnabled(True)
|
|
1545
|
+
|
|
1546
|
+
# ---------- Helpers for reliable combo defaults in agent extra options ----------
|
|
1547
|
+
|
|
1548
|
+
def _apply_combo_defaults_for_group(self, parent_id: str, schema_options: Dict[str, Any]) -> None:
|
|
1549
|
+
"""
|
|
1550
|
+
Ensure that combo-type inputs inside a given UI config group have their default values applied
|
|
1551
|
+
when the current value is empty ("", None or "_"). This avoids the situation where combo boxes
|
|
1552
|
+
remain uninitialized while other field types receive defaults correctly.
|
|
1553
|
+
|
|
1554
|
+
This function never overrides a non-empty value set by the user or loaded from a preset.
|
|
1555
|
+
"""
|
|
1556
|
+
if not schema_options:
|
|
1557
|
+
return
|
|
1558
|
+
|
|
1559
|
+
get_value = self.window.controller.config.get_value
|
|
1560
|
+
apply_value = self.window.controller.config.apply_value
|
|
1561
|
+
|
|
1562
|
+
for key, opt_schema in schema_options.items():
|
|
1563
|
+
if not isinstance(opt_schema, dict):
|
|
1564
|
+
continue
|
|
1565
|
+
if opt_schema.get('type') != 'combo':
|
|
1566
|
+
continue
|
|
1567
|
+
|
|
1568
|
+
default_val = opt_schema.get('default', None)
|
|
1569
|
+
if default_val is None:
|
|
1570
|
+
continue
|
|
1571
|
+
|
|
1572
|
+
current_val = get_value(
|
|
1573
|
+
parent_id=parent_id,
|
|
1574
|
+
key=key,
|
|
1575
|
+
option=opt_schema,
|
|
1576
|
+
)
|
|
1577
|
+
|
|
1578
|
+
# Treat "_", "", None as empty and safe to replace with default
|
|
1579
|
+
if current_val in (None, "", "_"):
|
|
1580
|
+
# First try apply_value (standard path)
|
|
1581
|
+
apply_value(
|
|
1582
|
+
parent_id=parent_id,
|
|
1583
|
+
key=key,
|
|
1584
|
+
option=opt_schema,
|
|
1585
|
+
value=default_val,
|
|
1586
|
+
)
|
|
1587
|
+
# Additionally set directly on widget if accessible to guard against timing of key population
|
|
1588
|
+
try:
|
|
1589
|
+
widget_group = self.window.ui.config.get(parent_id, {})
|
|
1590
|
+
widget = widget_group.get(key)
|
|
1591
|
+
if widget and hasattr(widget, "set_value"):
|
|
1592
|
+
widget.set_value(default_val)
|
|
1593
|
+
except Exception:
|
|
1594
|
+
# Silent fallback; apply_value above should already handle most cases
|
|
1595
|
+
pass
|
pygpt_net/controller/ui/mode.py
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.27 15:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.types import (
|
|
@@ -58,6 +58,19 @@ class Mode:
|
|
|
58
58
|
is_completion = mode == MODE_COMPLETION
|
|
59
59
|
is_audio = mode == MODE_AUDIO
|
|
60
60
|
|
|
61
|
+
# enable/disable system prompt edit - disable in agents (prompts are defined per agent in presets)
|
|
62
|
+
if not is_agent_openai and not is_agent_llama:
|
|
63
|
+
presets_editor.toggle_tab("personalize", True)
|
|
64
|
+
if 'preset.prompt' in ui_nodes and ui_nodes['preset.prompt'].isReadOnly():
|
|
65
|
+
ui_nodes['preset.prompt'].setReadOnly(False)
|
|
66
|
+
ui_nodes['preset.prompt'].setPlaceholderText("")
|
|
67
|
+
else:
|
|
68
|
+
presets_editor.toggle_tab("personalize", False)
|
|
69
|
+
if 'preset.prompt' in ui_nodes and not ui_nodes['preset.prompt'].isReadOnly():
|
|
70
|
+
ui_nodes['preset.prompt'].setReadOnly(True)
|
|
71
|
+
ui_nodes['preset.prompt'].setPlaceholderText(trans("toolbox.agent.preset.placeholder"))
|
|
72
|
+
|
|
73
|
+
# audio options visibility
|
|
61
74
|
if not is_audio:
|
|
62
75
|
ui_nodes['audio.auto_turn'].setVisible(False)
|
|
63
76
|
ui_nodes["audio.loop"].setVisible(False)
|
|
@@ -71,6 +84,7 @@ class Mode:
|
|
|
71
84
|
else:
|
|
72
85
|
ctrl.audio.toggle_output_icon(False)
|
|
73
86
|
|
|
87
|
+
# presets/assistants visibility
|
|
74
88
|
if not is_assistant:
|
|
75
89
|
ui_nodes['presets.widget'].setVisible(True)
|
|
76
90
|
else:
|
|
@@ -81,6 +95,7 @@ class Mode:
|
|
|
81
95
|
else:
|
|
82
96
|
ui_nodes['env.widget'].setVisible(True)
|
|
83
97
|
|
|
98
|
+
# agents/experts/presets label visibility
|
|
84
99
|
show_agents_label = is_agent or is_agent_llama or is_agent_openai
|
|
85
100
|
if show_agents_label:
|
|
86
101
|
ui_nodes['preset.agents.label'].setVisible(True)
|
|
@@ -112,6 +127,7 @@ class Mode:
|
|
|
112
127
|
else:
|
|
113
128
|
ui_nodes['preset.editor.agent_provider_openai'].setVisible(False)
|
|
114
129
|
|
|
130
|
+
# prompt editor toolbox visibility
|
|
115
131
|
if is_agent:
|
|
116
132
|
presets_editor.toggle_tab("experts", True)
|
|
117
133
|
ui_nodes['preset.editor.temperature'].setVisible(True)
|
|
@@ -145,6 +161,7 @@ class Mode:
|
|
|
145
161
|
ui_nodes['preset.editor.modes'].setVisible(True)
|
|
146
162
|
ui_tabs['preset.editor.extra'].setTabText(0, trans("preset.prompt"))
|
|
147
163
|
|
|
164
|
+
# image options visibility
|
|
148
165
|
if is_image:
|
|
149
166
|
ui_nodes['media.raw'].setVisible(True)
|
|
150
167
|
if ctrl.media.is_video_model():
|
|
@@ -198,10 +215,8 @@ class Mode:
|
|
|
198
215
|
# remote tools icon visibility
|
|
199
216
|
if not is_image and not is_completion:
|
|
200
217
|
self.window.ui.nodes['input'].set_icon_visible("web", True)
|
|
201
|
-
# ui_nodes['icon.remote_tool.web'].setVisible(True)
|
|
202
218
|
else:
|
|
203
219
|
self.window.ui.nodes['input'].set_icon_visible("web", False)
|
|
204
|
-
# ui_nodes['icon.remote_tool.web'].setVisible(False)
|
|
205
220
|
|
|
206
221
|
ui_tabs['input'].setTabVisible(2, is_assistant)
|
|
207
222
|
ui_tabs['input'].setTabVisible(3, (not is_assistant) and (not is_image))
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# core/agents/runners/llama_workflow.py
|
|
2
|
+
|
|
1
3
|
#!/usr/bin/env python3
|
|
2
4
|
# -*- coding: utf-8 -*-
|
|
3
5
|
# ================================================== #
|
|
@@ -6,7 +8,7 @@
|
|
|
6
8
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
9
|
# MIT License #
|
|
8
10
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
11
|
+
# Updated Date: 2025.09.27 06:00:00 #
|
|
10
12
|
# ================================================== #
|
|
11
13
|
|
|
12
14
|
from __future__ import annotations
|
|
@@ -207,37 +209,6 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
207
209
|
return False
|
|
208
210
|
return False
|
|
209
211
|
|
|
210
|
-
def _friendly_map(self) -> Dict[str, str]:
|
|
211
|
-
return {aid: a.name or aid for aid, a in self.fs.agents.items()}
|
|
212
|
-
|
|
213
|
-
def _friendly_map_for_routes(self, route_ids: List[str]) -> Dict[str, Any]:
|
|
214
|
-
"""
|
|
215
|
-
Build a friendly map for the given route ids:
|
|
216
|
-
- Always include a human-friendly name.
|
|
217
|
-
- Include role only if provided in preset options or schema and non-empty.
|
|
218
|
-
"""
|
|
219
|
-
out: Dict[str, Any] = {}
|
|
220
|
-
for rid in route_ids or []:
|
|
221
|
-
a = self.fs.agents.get(rid)
|
|
222
|
-
name = (a.name if a and a.name else rid)
|
|
223
|
-
# Prefer preset option, then schema role
|
|
224
|
-
role_opt = None
|
|
225
|
-
try:
|
|
226
|
-
role_opt = self.option_get(rid, "role", None)
|
|
227
|
-
except Exception:
|
|
228
|
-
role_opt = None
|
|
229
|
-
role_schema = getattr(a, "role", None) if a is not None else None
|
|
230
|
-
role_val = None
|
|
231
|
-
if isinstance(role_opt, str) and role_opt.strip():
|
|
232
|
-
role_val = role_opt.strip()
|
|
233
|
-
elif isinstance(role_schema, str) and role_schema.strip():
|
|
234
|
-
role_val = role_schema.strip()
|
|
235
|
-
item = {"name": name}
|
|
236
|
-
if role_val:
|
|
237
|
-
item["role"] = role_val
|
|
238
|
-
out[rid] = item
|
|
239
|
-
return out
|
|
240
|
-
|
|
241
212
|
async def _emit(self, ctx: Context, ev: Any):
|
|
242
213
|
if self.dbg.event_echo:
|
|
243
214
|
self.logger.debug(f"[event] emit {ev.__class__.__name__}")
|
|
@@ -245,8 +216,8 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
245
216
|
|
|
246
217
|
async def _emit_agent_text(self, ctx: Context, text: str, agent_name: str = "Agent"):
|
|
247
218
|
"""
|
|
248
|
-
Emit AgentStream(delta=text) robustly. If
|
|
249
|
-
fall back to extended AgentStream
|
|
219
|
+
Emit AgentStream(delta=text) robustly. If env requires extra fields,
|
|
220
|
+
fall back to extended AgentStream.
|
|
250
221
|
"""
|
|
251
222
|
try:
|
|
252
223
|
if self.dbg.event_echo:
|
|
@@ -266,16 +237,11 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
266
237
|
)
|
|
267
238
|
|
|
268
239
|
async def _emit_header(self, ctx: Context, name: str):
|
|
269
|
-
|
|
270
|
-
self.logger.debug(f"[event] header emit begin name='{name}'")
|
|
240
|
+
# Lightweight header to ensure agent name is known before tokens.
|
|
271
241
|
await self._emit_agent_text(ctx, "", agent_name=name)
|
|
272
|
-
# await self._emit_agent_text(ctx, f"\n\n**{name}**\n\n", agent_name=name)
|
|
273
|
-
if self.dbg.event_echo:
|
|
274
|
-
self.logger.debug("[event] header emit done")
|
|
275
242
|
|
|
276
243
|
async def _emit_step_sep(self, ctx: Context, node_id: str):
|
|
277
244
|
try:
|
|
278
|
-
# Include human-friendly agent name in StepEvent meta for downstream ctx propagation.
|
|
279
245
|
a = self.fs.agents.get(node_id)
|
|
280
246
|
friendly_name = (a.name if a and a.name else node_id)
|
|
281
247
|
await self._emit(
|
|
@@ -350,6 +316,9 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
350
316
|
return user_msg, [], "no-mem:last_output"
|
|
351
317
|
|
|
352
318
|
async def _update_memory_after_step(self, node_id: str, user_msg_text: str, display_text: str):
|
|
319
|
+
"""
|
|
320
|
+
Update per-node memory after a step, storing baton user message and assistant output.
|
|
321
|
+
"""
|
|
353
322
|
mem_id = self.g.agent_to_memory.get(node_id)
|
|
354
323
|
mem_state = self.mem.get(mem_id) if mem_id else None
|
|
355
324
|
if not mem_state:
|
|
@@ -374,7 +343,7 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
374
343
|
# ============== Workflow steps ==============
|
|
375
344
|
|
|
376
345
|
def run(self, query: str, ctx: Optional[Context] = None, memory: Any = None, verbose: bool = False, on_stop=None):
|
|
377
|
-
"""Entry point used by
|
|
346
|
+
"""Entry point used by LlamaWorkflow runner."""
|
|
378
347
|
self._on_stop = on_stop
|
|
379
348
|
|
|
380
349
|
# Build initial chat once
|
|
@@ -444,8 +413,9 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
444
413
|
return FlowTickEvent() if self._current_ids else FlowStopEvent(final_answer=self._last_plain_output or "")
|
|
445
414
|
|
|
446
415
|
node: AgentNode = self.fs.agents[current_id]
|
|
447
|
-
|
|
448
|
-
|
|
416
|
+
|
|
417
|
+
# IMPORTANT: emit StepEvent also for the very first agent step.
|
|
418
|
+
await self._emit_step_sep(ctx, current_id)
|
|
449
419
|
await self._emit_header(ctx, node.name or current_id)
|
|
450
420
|
|
|
451
421
|
# Resolve runtime + per-node LLM/tools
|
|
@@ -474,11 +444,10 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
474
444
|
f"user='{ellipsize(user_msg_text, self.dbg.preview_chars)}'"
|
|
475
445
|
)
|
|
476
446
|
|
|
477
|
-
#
|
|
447
|
+
# Build agent
|
|
478
448
|
allowed_routes_now = list(node.outputs or [])
|
|
479
|
-
friendly_map = self.
|
|
449
|
+
friendly_map = {rid: self.fs.agents.get(rid).name or rid for rid in allowed_routes_now if rid in self.fs.agents}
|
|
480
450
|
|
|
481
|
-
# Build agent (chat_history/max_iterations in ctor – best practice)
|
|
482
451
|
built = self.factory.build(
|
|
483
452
|
node=node,
|
|
484
453
|
node_runtime=node_rt,
|
|
@@ -528,9 +497,6 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
528
497
|
display_text = decision.content or ""
|
|
529
498
|
if display_text:
|
|
530
499
|
await self._emit_agent_text(ctx, display_text, agent_name=(node.name or current_id))
|
|
531
|
-
if self.dbg.log_memory_dump:
|
|
532
|
-
self.logger.debug(f"[mem.prep] node={current_id} save user='{ellipsize(user_msg_text, self.dbg.preview_chars)}' "
|
|
533
|
-
f"assist='{ellipsize(display_text, self.dbg.preview_chars)}'")
|
|
534
500
|
await self._update_memory_after_step(current_id, user_msg_text, display_text)
|
|
535
501
|
next_id = decision.route if decision.valid else (allowed_routes[0] if allowed_routes else None)
|
|
536
502
|
if self.dbg.log_routes:
|
|
@@ -541,9 +507,6 @@ class DynamicFlowWorkflowLI(Workflow):
|
|
|
541
507
|
display_text = raw_text_clean or ""
|
|
542
508
|
if display_text:
|
|
543
509
|
await self._emit_agent_text(ctx, display_text, agent_name=(node.name or current_id))
|
|
544
|
-
if self.dbg.log_memory_dump:
|
|
545
|
-
self.logger.debug(f"[mem.prep] node={current_id} save user='{ellipsize(user_msg_text, self.dbg.preview_chars)}' "
|
|
546
|
-
f"assist='{ellipsize(display_text, self.dbg.preview_chars)}'")
|
|
547
510
|
await self._update_memory_after_step(current_id, user_msg_text, display_text)
|
|
548
511
|
outs = self.g.get_next(current_id)
|
|
549
512
|
next_id = outs[0] if outs else self.g.first_connected_end(current_id)
|