pygpt-net 2.6.60__py3-none-any.whl → 2.6.62__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.
Files changed (87) hide show
  1. pygpt_net/CHANGELOG.txt +14 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/chat/common.py +115 -6
  4. pygpt_net/controller/chat/input.py +4 -1
  5. pygpt_net/controller/chat/response.py +8 -2
  6. pygpt_net/controller/presets/presets.py +121 -6
  7. pygpt_net/controller/settings/editor.py +0 -15
  8. pygpt_net/controller/settings/profile.py +16 -4
  9. pygpt_net/controller/settings/workdir.py +30 -5
  10. pygpt_net/controller/theme/common.py +4 -2
  11. pygpt_net/controller/theme/markdown.py +4 -7
  12. pygpt_net/controller/theme/theme.py +2 -1
  13. pygpt_net/controller/ui/ui.py +32 -7
  14. pygpt_net/core/agents/custom/__init__.py +7 -1
  15. pygpt_net/core/agents/custom/llama_index/factory.py +17 -6
  16. pygpt_net/core/agents/custom/llama_index/runner.py +52 -4
  17. pygpt_net/core/agents/custom/llama_index/utils.py +12 -1
  18. pygpt_net/core/agents/custom/router.py +45 -6
  19. pygpt_net/core/agents/custom/runner.py +11 -5
  20. pygpt_net/core/agents/custom/schema.py +3 -1
  21. pygpt_net/core/agents/custom/utils.py +13 -1
  22. pygpt_net/core/agents/runners/llama_workflow.py +65 -5
  23. pygpt_net/core/agents/runners/openai_workflow.py +2 -1
  24. pygpt_net/core/db/viewer.py +11 -5
  25. pygpt_net/core/node_editor/graph.py +18 -9
  26. pygpt_net/core/node_editor/models.py +9 -2
  27. pygpt_net/core/node_editor/types.py +15 -1
  28. pygpt_net/core/presets/presets.py +216 -29
  29. pygpt_net/core/render/markdown/parser.py +0 -2
  30. pygpt_net/core/render/web/renderer.py +76 -11
  31. pygpt_net/data/config/config.json +5 -6
  32. pygpt_net/data/config/models.json +3 -3
  33. pygpt_net/data/config/settings.json +2 -38
  34. pygpt_net/data/css/style.dark.css +18 -0
  35. pygpt_net/data/css/style.light.css +20 -1
  36. pygpt_net/data/locale/locale.de.ini +66 -1
  37. pygpt_net/data/locale/locale.en.ini +64 -3
  38. pygpt_net/data/locale/locale.es.ini +66 -1
  39. pygpt_net/data/locale/locale.fr.ini +66 -1
  40. pygpt_net/data/locale/locale.it.ini +66 -1
  41. pygpt_net/data/locale/locale.pl.ini +67 -2
  42. pygpt_net/data/locale/locale.uk.ini +66 -1
  43. pygpt_net/data/locale/locale.zh.ini +66 -1
  44. pygpt_net/data/locale/plugin.cmd_system.en.ini +62 -66
  45. pygpt_net/item/ctx.py +23 -1
  46. pygpt_net/provider/agents/llama_index/flow_from_schema.py +2 -2
  47. pygpt_net/provider/agents/llama_index/workflow/codeact.py +9 -6
  48. pygpt_net/provider/agents/llama_index/workflow/openai.py +38 -11
  49. pygpt_net/provider/agents/llama_index/workflow/planner.py +36 -16
  50. pygpt_net/provider/agents/llama_index/workflow/supervisor.py +60 -10
  51. pygpt_net/provider/agents/openai/agent.py +3 -1
  52. pygpt_net/provider/agents/openai/agent_b2b.py +13 -9
  53. pygpt_net/provider/agents/openai/agent_planner.py +6 -2
  54. pygpt_net/provider/agents/openai/agent_with_experts.py +4 -1
  55. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -2
  56. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -2
  57. pygpt_net/provider/agents/openai/evolve.py +6 -2
  58. pygpt_net/provider/agents/openai/supervisor.py +3 -1
  59. pygpt_net/provider/api/openai/agents/response.py +1 -0
  60. pygpt_net/provider/core/config/patch.py +18 -1
  61. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +0 -6
  62. pygpt_net/tools/agent_builder/tool.py +48 -26
  63. pygpt_net/tools/agent_builder/ui/dialogs.py +36 -28
  64. pygpt_net/ui/__init__.py +2 -4
  65. pygpt_net/ui/dialog/about.py +58 -38
  66. pygpt_net/ui/dialog/db.py +142 -3
  67. pygpt_net/ui/dialog/preset.py +47 -8
  68. pygpt_net/ui/layout/toolbox/presets.py +64 -16
  69. pygpt_net/ui/main.py +2 -2
  70. pygpt_net/ui/widget/dialog/confirm.py +27 -3
  71. pygpt_net/ui/widget/dialog/db.py +0 -0
  72. pygpt_net/ui/widget/draw/painter.py +90 -1
  73. pygpt_net/ui/widget/lists/preset.py +908 -60
  74. pygpt_net/ui/widget/node_editor/command.py +10 -10
  75. pygpt_net/ui/widget/node_editor/config.py +157 -0
  76. pygpt_net/ui/widget/node_editor/editor.py +223 -153
  77. pygpt_net/ui/widget/node_editor/item.py +12 -11
  78. pygpt_net/ui/widget/node_editor/node.py +246 -13
  79. pygpt_net/ui/widget/node_editor/view.py +179 -63
  80. pygpt_net/ui/widget/tabs/output.py +1 -1
  81. pygpt_net/ui/widget/textarea/input.py +157 -23
  82. pygpt_net/utils.py +114 -2
  83. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/METADATA +26 -100
  84. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/RECORD +86 -85
  85. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/LICENSE +0 -0
  86. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/WHEEL +0 -0
  87. {pygpt_net-2.6.60.dist-info → pygpt_net-2.6.62.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.14 01:00:00 #
9
+ # Updated Date: 2025.09.26 15:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from __future__ import annotations
@@ -34,6 +34,7 @@ from llama_index.core.agent.workflow import (
34
34
  ToolCallResult,
35
35
  AgentStream,
36
36
  AgentOutput,
37
+ AgentInput, # ensure AgentInput propagation includes agent name
37
38
  )
38
39
 
39
40
  # v12/v13 compatibility imports
@@ -167,6 +168,9 @@ class OpenAIWorkflowAgent(Workflow):
167
168
  self._on_stop = on_stop
168
169
  self.verbose = verbose
169
170
 
171
+ # human-friendly display name propagated to UI via workflow events
172
+ self._display_agent_name: str = "FunctionAgent"
173
+
170
174
  # construct FunctionAgent once, will override tools/system_prompt/memory per run
171
175
  self._agent = FunctionAgent(
172
176
  name="OpenAIWorkflowAgent",
@@ -287,9 +291,13 @@ class OpenAIWorkflowAgent(Workflow):
287
291
  :param total: Total number of steps (optional)
288
292
  :param meta: Optional metadata dictionary for the step event
289
293
  """
294
+ # Always include agent_name so UI can set it before first token arrives.
295
+ m = dict(meta or {})
296
+ m.setdefault("agent_name", self._display_agent_name)
297
+
290
298
  try:
291
299
  if StepEvent is not None:
292
- ctx.write_event_to_stream(StepEvent(name=name, index=index, total=total, meta=meta or {}))
300
+ ctx.write_event_to_stream(StepEvent(name=name, index=index, total=total, meta=m))
293
301
  return
294
302
  except Exception:
295
303
  pass
@@ -300,9 +308,9 @@ class OpenAIWorkflowAgent(Workflow):
300
308
  AgentStream(
301
309
  delta="",
302
310
  response="",
303
- current_agent_name="OpenAIWorkflowAgent",
311
+ current_agent_name=self._display_agent_name,
304
312
  tool_calls=[],
305
- raw={"StepEvent": {"name": name, "index": index, "total": total, "meta": meta or {}}},
313
+ raw={"StepEvent": {"name": name, "index": index, "total": total, "meta": m}},
306
314
  )
307
315
  )
308
316
  except Exception:
@@ -499,14 +507,14 @@ class OpenAIWorkflowAgent(Workflow):
499
507
  self,
500
508
  ctx: Context,
501
509
  text: str,
502
- agent_name: str = "OpenAIWorkflowAgent"
510
+ agent_name: str = "FunctionAgent"
503
511
  ):
504
512
  """
505
513
  Emit text to the context stream, handling validation errors gracefully.
506
514
 
507
515
  :param ctx: Context for the workflow
508
516
  :param text: Text to emit to the stream
509
- :param agent_name: Name of the agent to set in the event (default: "OpenAIWorkflowAgent")
517
+ :param agent_name: Name of the agent to set in the event (default: "FunctionAgent")
510
518
  """
511
519
  try:
512
520
  ctx.write_event_to_stream(AgentStream(delta=text))
@@ -561,12 +569,31 @@ class OpenAIWorkflowAgent(Workflow):
561
569
  pass
562
570
  return last_answer
563
571
 
572
+ if isinstance(e, AgentInput):
573
+ # Ensure the input event also carries the display name for UI
574
+ try:
575
+ e.current_agent_name = self._display_agent_name
576
+ except Exception:
577
+ pass
578
+ ctx.write_event_to_stream(e)
579
+ continue
580
+
564
581
  if isinstance(e, AgentStream):
565
582
  if getattr(e, "delta", None):
566
583
  has_stream = True
567
- if not getattr(e, "current_agent_name", None):
584
+ # Always enforce agent name for consistency in UI
585
+ try:
586
+ e.current_agent_name = self._display_agent_name
587
+ except Exception:
588
+ # If immutable, rebuild a compatible event object
568
589
  try:
569
- e.current_agent_name = "OpenAIWorkflowAgent"
590
+ e = AgentStream(
591
+ delta=getattr(e, "delta", ""),
592
+ response=getattr(e, "response", ""),
593
+ current_agent_name=self._display_agent_name,
594
+ tool_calls=getattr(e, "tool_calls", []),
595
+ raw=getattr(e, "raw", {}),
596
+ )
570
597
  except Exception:
571
598
  pass
572
599
  ctx.write_event_to_stream(e)
@@ -581,9 +608,9 @@ class OpenAIWorkflowAgent(Workflow):
581
608
  AgentStream(
582
609
  delta=content,
583
610
  response=content,
584
- current_agent_name="OpenAIWorkflowAgent",
585
- tool_calls=e.tool_calls,
586
- raw=e.raw,
611
+ current_agent_name=self._display_agent_name,
612
+ tool_calls=getattr(e, "tool_calls", []),
613
+ raw=getattr(e, "raw", {}),
587
614
  )
588
615
  )
589
616
  continue
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.24 02:00:00 #
9
+ # Updated Date: 2025.09.26 15:20:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import List, Optional, Callable
@@ -134,6 +134,10 @@ class PlannerWorkflow(Workflow):
134
134
  self._memory_char_limit = memory_char_limit
135
135
  self._on_stop = on_stop
136
136
 
137
+ # Human-friendly display names propagated to UI via workflow events.
138
+ self._display_planner_name: str = "PlannerWorkflow"
139
+ self._display_executor_name: str = "FunctionAgent"
140
+
137
141
  self._executor = FunctionAgent(
138
142
  name="PlannerExecutor",
139
143
  description="Executes planner sub-tasks using available tools.",
@@ -184,9 +188,13 @@ class PlannerWorkflow(Workflow):
184
188
  :param total: The total number of steps (optional).
185
189
  :param meta: Additional metadata for the step (optional).
186
190
  """
191
+ # Always pass a friendly agent name; for 'subtask' we signal the executor agent to the UI.
192
+ m = dict(meta or {})
193
+ m.setdefault("agent_name", self._display_executor_name if name == "subtask" else self._display_planner_name)
194
+
187
195
  try:
188
196
  ctx.write_event_to_stream(
189
- StepEvent(name=name, index=index, total=total, meta=meta or {})
197
+ StepEvent(name=name, index=index, total=total, meta=m)
190
198
  )
191
199
  except Exception:
192
200
  # fallback for older versions of AgentStream
@@ -195,9 +203,9 @@ class PlannerWorkflow(Workflow):
195
203
  AgentStream(
196
204
  delta="",
197
205
  response="",
198
- current_agent_name="PlannerWorkflow",
206
+ current_agent_name=m.get("agent_name", self._display_planner_name),
199
207
  tool_calls=[],
200
- raw={"StepEvent": {"name": name, "index": index, "total": total, "meta": meta or {}}}
208
+ raw={"StepEvent": {"name": name, "index": index, "total": total, "meta": m}}
201
209
  )
202
210
  )
203
211
  except Exception:
@@ -308,9 +316,8 @@ class PlannerWorkflow(Workflow):
308
316
  :param text: The text message to emit.
309
317
  :param agent_name: The name of the agent emitting the text (default: "PlannerWorkflow").
310
318
  """
319
+ # Always try to include agent name; fall back to minimal event for older validators.
311
320
  try:
312
- ctx.write_event_to_stream(AgentStream(delta=text))
313
- except ValidationError:
314
321
  ctx.write_event_to_stream(
315
322
  AgentStream(
316
323
  delta=text,
@@ -320,6 +327,8 @@ class PlannerWorkflow(Workflow):
320
327
  raw={},
321
328
  )
322
329
  )
330
+ except ValidationError:
331
+ ctx.write_event_to_stream(AgentStream(delta=text))
323
332
 
324
333
  def _to_text(self, resp) -> str:
325
334
  """
@@ -443,9 +452,18 @@ class PlannerWorkflow(Workflow):
443
452
  if delta:
444
453
  has_stream = True
445
454
  stream_buf.append(str(delta))
446
- if not getattr(e, "current_agent_name", None):
455
+ # Always enforce a stable display name for executor events.
456
+ try:
457
+ e.current_agent_name = self._display_executor_name
458
+ except Exception:
447
459
  try:
448
- e.current_agent_name = self._executor.name
460
+ e = AgentStream(
461
+ delta=getattr(e, "delta", ""),
462
+ response=getattr(e, "response", ""),
463
+ current_agent_name=self._display_executor_name,
464
+ tool_calls=getattr(e, "tool_calls", []),
465
+ raw=getattr(e, "raw", {}),
466
+ )
449
467
  except Exception:
450
468
  pass
451
469
  ctx.write_event_to_stream(e)
@@ -460,7 +478,7 @@ class PlannerWorkflow(Workflow):
460
478
  AgentStream(
461
479
  delta=last_answer,
462
480
  response=last_answer,
463
- current_agent_name=f"{self._executor.name} (subtask)",
481
+ current_agent_name=self._display_executor_name,
464
482
  tool_calls=e.tool_calls,
465
483
  raw=e.raw,
466
484
  )
@@ -484,7 +502,7 @@ class PlannerWorkflow(Workflow):
484
502
  try:
485
503
  return await _stream()
486
504
  except Exception as ex:
487
- await self._emit_text(ctx, f"\n`Sub-task failed: {ex}`")
505
+ await self._emit_text(ctx, f"\n`Sub-task failed: {ex}`", agent_name=self._display_executor_name)
488
506
  return last_answer or ("".join(stream_buf).strip() if stream_buf else "")
489
507
 
490
508
  @step
@@ -520,7 +538,7 @@ class PlannerWorkflow(Workflow):
520
538
  f"Expected output: {st.expected_output}\n"
521
539
  f"Dependencies: {st.dependencies}\n\n"
522
540
  )
523
- await self._emit_text(ctx, "\n".join(lines))
541
+ await self._emit_text(ctx, "\n".join(lines), agent_name=self._display_planner_name)
524
542
  return PlanReady(plan=plan, query=ev.query)
525
543
 
526
544
  @step
@@ -537,7 +555,7 @@ class PlannerWorkflow(Workflow):
537
555
  last_answer = ""
538
556
  completed: list[tuple[str, str]] = [] # (name, output)
539
557
 
540
- await self._emit_text(ctx, "\n\n`Executing plan...`")
558
+ await self._emit_text(ctx, "\n\n`Executing plan...`", agent_name=self._display_planner_name)
541
559
 
542
560
  for i, st in enumerate(plan_sub_tasks, 1):
543
561
  self._emit_step_event(
@@ -550,6 +568,8 @@ class PlannerWorkflow(Workflow):
550
568
  "expected_output": st.expected_output,
551
569
  "dependencies": st.dependencies,
552
570
  "input": st.input,
571
+ # Signal that the next stream will come from the executor.
572
+ "agent_name": self._display_executor_name,
553
573
  },
554
574
  )
555
575
 
@@ -561,10 +581,10 @@ class PlannerWorkflow(Workflow):
561
581
 
562
582
  # stop callback
563
583
  if self._stopped():
564
- await self._emit_text(ctx, "\n`Plan execution stopped.`")
584
+ await self._emit_text(ctx, "\n`Plan execution stopped.`", agent_name=self._display_planner_name)
565
585
  return FinalEvent(result=last_answer or "Plan execution stopped.")
566
586
 
567
- await self._emit_text(ctx, header)
587
+ await self._emit_text(ctx, header, agent_name=self._display_planner_name)
568
588
 
569
589
  # build context for sub-task
570
590
  ctx_text = self._build_context_for_subtask(
@@ -588,7 +608,7 @@ class PlannerWorkflow(Workflow):
588
608
  sub_answer = await self._run_subtask(ctx, composed_prompt)
589
609
  sub_answer = (sub_answer or "").strip()
590
610
 
591
- await self._emit_text(ctx, f"\n\n`Finished Sub Task {i}/{total}: {st.name}`")
611
+ await self._emit_text(ctx, f"\n\n`Finished Sub Task {i}/{total}: {st.name}`", agent_name=self._display_planner_name)
592
612
 
593
613
  # save completed sub-task
594
614
  completed.append((st.name, sub_answer))
@@ -597,5 +617,5 @@ class PlannerWorkflow(Workflow):
597
617
 
598
618
  # TODO: refine plan if needed
599
619
 
600
- await self._emit_text(ctx, "\n\n`Plan execution finished.`")
620
+ await self._emit_text(ctx, "\n\n`Plan execution finished.`", agent_name=self._display_planner_name)
601
621
  return FinalEvent(result=last_answer or "Plan finished.")
@@ -1,3 +1,5 @@
1
+ # workflow/supervisor.py
2
+
1
3
  #!/usr/bin/env python3
2
4
  # -*- coding: utf-8 -*-
3
5
  # ================================================== #
@@ -6,11 +8,11 @@
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.08.17 02:00:00 #
11
+ # Updated Date: 2025.09.26 22:25:00 #
10
12
  # ================================================== #
11
13
 
12
14
  import re
13
- from typing import Optional, Literal, List
15
+ from typing import Optional, Literal, List, Callable, Any
14
16
  from pydantic import BaseModel, ValidationError
15
17
  from llama_index.core.workflow import Workflow, Context, StartEvent, StopEvent, Event, step
16
18
  from llama_index.core.agent.workflow import FunctionAgent, AgentStream
@@ -173,8 +175,6 @@ class SupervisorWorkflow(Workflow):
173
175
  :param agent_name: The name of the agent emitting the text (default: "PlannerWorkflow").
174
176
  """
175
177
  try:
176
- ctx.write_event_to_stream(AgentStream(delta=text))
177
- except ValidationError:
178
178
  ctx.write_event_to_stream(
179
179
  AgentStream(
180
180
  delta=text,
@@ -184,11 +184,47 @@ class SupervisorWorkflow(Workflow):
184
184
  raw={},
185
185
  )
186
186
  )
187
+ except ValidationError:
188
+ ctx.write_event_to_stream(AgentStream(delta=text))
189
+
190
+ async def _emit_step(self, ctx: Context, agent_name: str, index: int, total: int, meta: Optional[dict] = None):
191
+ """
192
+ Emit a StepEvent that your runner uses to split UI into blocks.
193
+ Mirrors the behavior used by the schema-driven workflow.
194
+ """
195
+ from pygpt_net.provider.agents.llama_index.workflow.events import StepEvent
196
+ try:
197
+ ctx.write_event_to_stream(
198
+ StepEvent(
199
+ name="next",
200
+ index=index,
201
+ total=total,
202
+ meta={"agent_name": agent_name, **(meta or {})},
203
+ )
204
+ )
205
+ except Exception:
206
+ pass
207
+
208
+ async def _run_muted(self, ctx: Context, awaitable) -> Any:
209
+ """
210
+ Execute an agent call while muting all events sent to ctx.
211
+ Matches schema-style emission: we control all UI events ourselves.
212
+ """
213
+ orig_write = ctx.write_event_to_stream
214
+
215
+ def _noop(ev: Any) -> None:
216
+ return None
217
+
218
+ ctx.write_event_to_stream = _noop
219
+ try:
220
+ return await awaitable
221
+ finally:
222
+ ctx.write_event_to_stream = orig_write
187
223
 
188
224
  @step
189
225
  async def supervisor_step(self, ctx: Context, ev: InputEvent) -> ExecuteEvent | OutputEvent:
190
226
  """
191
- Supervisor step to process the user's input and generate an instruction for the Worker.
227
+ Supervisor step: run Supervisor silently, then emit exactly one UI block like schema.
192
228
 
193
229
  :param ctx: Context for the workflow
194
230
  :param ev: InputEvent containing the user's message and context.
@@ -206,21 +242,33 @@ class SupervisorWorkflow(Workflow):
206
242
  "Return ONE JSON following the schema.\n</control>"
207
243
  )
208
244
  sup_input = "\n".join(parts)
209
- sup_resp = await self._supervisor.run(user_msg=sup_input, memory=self._supervisor_memory)
245
+
246
+ # Run Supervisor with stream muted to avoid extra blocks/finishes.
247
+ sup_resp = await self._run_muted(ctx, self._supervisor.run(user_msg=sup_input, memory=self._supervisor_memory))
210
248
  directive = parse_supervisor_json(str(sup_resp))
211
249
 
250
+ # Final/ask_user/max_rounds -> emit single Supervisor block and stop (schema-like).
212
251
  if directive.action == "final":
252
+ await self._emit_step(ctx, agent_name=self._supervisor.name, index=ev.round_idx + 1, total=ev.max_rounds)
213
253
  await self._emit_text(ctx, f"\n\n{directive.final_answer or str(sup_resp)}", agent_name=self._supervisor.name)
214
254
  return OutputEvent(status="final", final_answer=directive.final_answer or str(sup_resp), rounds_used=ev.round_idx)
255
+
215
256
  if directive.action == "ask_user" and ev.stop_on_ask_user:
257
+ await self._emit_step(ctx, agent_name=self._supervisor.name, index=ev.round_idx + 1, total=ev.max_rounds)
216
258
  q = directive.question or "I need more information, please clarify."
217
259
  await self._emit_text(ctx, f"\n\n{q}", agent_name=self._supervisor.name)
218
260
  return OutputEvent(status="ask_user", final_answer=q, rounds_used=ev.round_idx)
261
+
219
262
  if ev.round_idx >= ev.max_rounds:
263
+ await self._emit_step(ctx, agent_name=self._supervisor.name, index=ev.round_idx + 1, total=ev.max_rounds)
220
264
  await self._emit_text(ctx, "\n\nMax rounds exceeded.", agent_name=self._supervisor.name)
221
265
  return OutputEvent(status="max_rounds", final_answer="Exceeded maximum number of iterations.", rounds_used=ev.round_idx)
222
266
 
267
+ # Emit exactly one Supervisor block with the instruction (no JSON leakage, no duplicates).
223
268
  instruction = (directive.instruction or "").strip() or "Perform a step that gets closest to fulfilling the DoD."
269
+ await self._emit_step(ctx, agent_name=self._supervisor.name, index=ev.round_idx + 1, total=ev.max_rounds)
270
+ await self._emit_text(ctx, f"\n\n{instruction}", agent_name=self._supervisor.name)
271
+
224
272
  return ExecuteEvent(
225
273
  instruction=instruction,
226
274
  round_idx=ev.round_idx,
@@ -232,17 +280,19 @@ class SupervisorWorkflow(Workflow):
232
280
  @step
233
281
  async def worker_step(self, ctx: Context, ev: ExecuteEvent) -> InputEvent:
234
282
  """
235
- Worker step to execute the Supervisor's instruction.
283
+ Worker step: run Worker silently and emit exactly one UI block like schema.
236
284
 
237
285
  :param ctx: Context for the workflow
238
286
  :param ev: ExecuteEvent containing the instruction and context.
239
287
  :return: InputEvent for the next round or final output.
240
288
  """
289
+ # Run Worker with stream muted; we will emit a single block with the final text.
241
290
  worker_input = f"Instruction from Supervisor:\n{ev.instruction}\n"
242
- await self._emit_text(ctx, f"\n\n**Supervisor:** {ev.instruction}", agent_name=self._worker.name)
291
+ worker_resp = await self._run_muted(ctx, self._worker.run(user_msg=worker_input, memory=self._worker_memory))
243
292
 
244
- worker_resp = await self._worker.run(user_msg=worker_input, memory=self._worker_memory)
245
- await self._emit_text(ctx, f"\n\n**Worker:** {worker_resp}", agent_name=self._worker.name)
293
+ # Emit exactly one Worker block (schema-style: one AgentStream per node).
294
+ await self._emit_step(ctx, agent_name=self._worker.name, index=ev.round_idx + 1, total=ev.max_rounds)
295
+ await self._emit_text(ctx, f"\n\n{str(worker_resp)}", agent_name=self._worker.name)
246
296
 
247
297
  return InputEvent(
248
298
  user_msg="",
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.24 03:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any, Tuple, Optional
@@ -133,6 +133,8 @@ class Agent(BaseAgent):
133
133
  "preset": preset,
134
134
  "is_expert_call": False,
135
135
  }
136
+
137
+ ctx.set_agent_name(agent.name)
136
138
  # call computer agent if computer tool is enabled
137
139
  if is_computer_tool(**tool_kwargs):
138
140
  computer = LocalComputer(window)
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.26 01:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -265,6 +265,7 @@ class Agent(BaseAgent):
265
265
  break
266
266
 
267
267
  kwargs = self.prepare_model(model, window, previous_response_id, kwargs)
268
+ ctx.set_agent_name(bot_1.name)
268
269
  result = await Runner.run(
269
270
  bot_1,
270
271
  **kwargs
@@ -296,6 +297,7 @@ class Agent(BaseAgent):
296
297
  # -------- bot 2 --------
297
298
  kwargs["input"] = input_items
298
299
  kwargs = self.prepare_model(model_2, window, previous_response_id, kwargs)
300
+ ctx.set_agent_name(bot_2.name)
299
301
  result = await Runner.run(
300
302
  bot_2,
301
303
  **kwargs
@@ -329,6 +331,7 @@ class Agent(BaseAgent):
329
331
  # -------- bot 1 --------
330
332
  kwargs["input"] = input_items
331
333
  kwargs = self.prepare_model(model, window, previous_response_id, kwargs)
334
+ ctx.set_agent_name(bot_1.name)
332
335
  result = Runner.run_streamed(
333
336
  bot_1,
334
337
  **kwargs
@@ -337,13 +340,13 @@ class Agent(BaseAgent):
337
340
  handler.reset()
338
341
 
339
342
  # bot 1 title
340
- title = f"\n\n**{bot_1_name}**\n\n"
341
- ctx.stream = title
343
+ # title = f"\n\n**{bot_1_name}**\n\n"
344
+ # ctx.stream = title
342
345
  bridge.on_step(ctx, begin)
343
346
  begin = False
344
347
  handler.begin = begin
345
- if not use_partial_ctx:
346
- handler.to_buffer(title)
348
+ # if not use_partial_ctx:
349
+ # handler.to_buffer(title)
347
350
  async for event in result.stream_events():
348
351
  if bridge.stopped():
349
352
  result.cancel()
@@ -376,6 +379,7 @@ class Agent(BaseAgent):
376
379
  # -------- bot 2 --------
377
380
  kwargs["input"] = input_items
378
381
  kwargs = self.prepare_model(model_2, window, previous_response_id, kwargs)
382
+ ctx.set_agent_name(bot_2.name)
379
383
  result = Runner.run_streamed(
380
384
  bot_2,
381
385
  **kwargs
@@ -383,11 +387,11 @@ class Agent(BaseAgent):
383
387
  handler.reset()
384
388
 
385
389
  # bot 2 title
386
- title = f"\n\n**{bot_2_name}**\n\n"
387
- ctx.stream = title
390
+ # title = f"\n\n**{bot_2_name}**\n\n"
391
+ # ctx.stream = title
388
392
  bridge.on_step(ctx, False)
389
- if not use_partial_ctx:
390
- handler.to_buffer(title)
393
+ # if not use_partial_ctx:
394
+ # handler.to_buffer(title)
391
395
  async for event in result.stream_events():
392
396
  if bridge.stopped():
393
397
  result.cancel()
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.26 01:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from dataclasses import dataclass
@@ -153,7 +153,7 @@ class Agent(BaseAgent):
153
153
  :return: Agent provider instance
154
154
  """
155
155
  kwargs = {
156
- "name": "evaluator",
156
+ "name": "Evaluator",
157
157
  "instructions": instructions,
158
158
  "model": window.core.agents.provider.get_openai_model(model),
159
159
  "output_type": EvaluationFeedback,
@@ -313,6 +313,7 @@ class Agent(BaseAgent):
313
313
  if not stream:
314
314
  while True:
315
315
  kwargs["input"] = input_items
316
+ ctx.set_agent_name(agent.name)
316
317
  if bridge.stopped():
317
318
  bridge.on_stop(ctx)
318
319
  break
@@ -333,6 +334,7 @@ class Agent(BaseAgent):
333
334
  break
334
335
 
335
336
  evaluator_result = await Runner.run(evaluator, input_items)
337
+ ctx.set_agent_name(evaluator.name)
336
338
  result: EvaluationFeedback = evaluator_result.final_output
337
339
 
338
340
  print(f"Evaluator score: {result.score}")
@@ -366,6 +368,7 @@ class Agent(BaseAgent):
366
368
  handler = StreamHandler(window, bridge, final_output)
367
369
  while True:
368
370
  kwargs["input"] = input_items
371
+ ctx.set_agent_name(agent.name)
369
372
  result = Runner.run_streamed(
370
373
  agent,
371
374
  **kwargs
@@ -386,6 +389,7 @@ class Agent(BaseAgent):
386
389
  break
387
390
 
388
391
  input_items = result.to_input_list()
392
+ ctx.set_agent_name(evaluator.name)
389
393
  evaluator_result = await Runner.run(evaluator, input_items)
390
394
  result: EvaluationFeedback = evaluator_result.final_output
391
395
 
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.24 03:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any, Tuple, Optional
@@ -123,6 +123,7 @@ class Agent(BaseAgent):
123
123
  agent_kwargs["handoffs"] = experts
124
124
 
125
125
  agent = self.get_agent(window, agent_kwargs)
126
+ ctx.set_agent_name(agent.name)
126
127
 
127
128
  kwargs = {
128
129
  "input": messages,
@@ -137,6 +138,7 @@ class Agent(BaseAgent):
137
138
  agent,
138
139
  **kwargs
139
140
  )
141
+ ctx.set_agent_name(agent.name)
140
142
  final_output, last_response_id = window.core.api.openai.responses.unpack_agent_response(result, ctx)
141
143
  response_id = result.last_response_id
142
144
  if verbose:
@@ -152,6 +154,7 @@ class Agent(BaseAgent):
152
154
  result.cancel()
153
155
  bridge.on_stop(ctx)
154
156
  break
157
+ ctx.set_agent_name(agent.name)
155
158
  final_output, response_id = handler.handle(event, ctx)
156
159
 
157
160
  return ctx, final_output, response_id
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.26 01:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from dataclasses import dataclass
@@ -119,7 +119,7 @@ class Agent(BaseAgent):
119
119
  :return: Agent provider instance
120
120
  """
121
121
  kwargs = {
122
- "name": "evaluator",
122
+ "name": "Evaluator",
123
123
  "instructions": instructions,
124
124
  "model": window.core.agents.provider.get_openai_model(model),
125
125
  "output_type": EvaluationFeedback,
@@ -212,6 +212,7 @@ class Agent(BaseAgent):
212
212
  bridge.on_stop(ctx)
213
213
  break
214
214
 
215
+ ctx.set_agent_name(agent.name)
215
216
  result = await Runner.run(
216
217
  agent,
217
218
  **kwargs
@@ -259,6 +260,7 @@ class Agent(BaseAgent):
259
260
  handler = StreamHandler(window, bridge)
260
261
  while True:
261
262
  kwargs["input"] = input_items
263
+ ctx.set_agent_name(agent.name)
262
264
  result = Runner.run_streamed(
263
265
  agent,
264
266
  **kwargs
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.26 01:00:00 #
9
+ # Updated Date: 2025.09.26 17:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from dataclasses import dataclass
@@ -119,7 +119,7 @@ class Agent(BaseAgent):
119
119
  :return: Agent provider instance
120
120
  """
121
121
  kwargs = {
122
- "name": "evaluator",
122
+ "name": "Evaluator",
123
123
  "instructions": instructions,
124
124
  "model": window.core.agents.provider.get_openai_model(model),
125
125
  "output_type": EvaluationFeedback,
@@ -212,6 +212,7 @@ class Agent(BaseAgent):
212
212
  bridge.on_stop(ctx)
213
213
  break
214
214
 
215
+ ctx.set_agent_name(agent.name)
215
216
  result = await Runner.run(
216
217
  agent,
217
218
  **kwargs
@@ -259,6 +260,7 @@ class Agent(BaseAgent):
259
260
  handler = StreamHandler(window, bridge)
260
261
  while True:
261
262
  kwargs["input"] = input_items
263
+ ctx.set_agent_name(agent.name)
262
264
  result = Runner.run_streamed(
263
265
  agent,
264
266
  **kwargs