waldiez 0.5.10__py3-none-any.whl → 0.6.0__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.

Potentially problematic release.


This version of waldiez might be problematic. Click here for more details.

Files changed (62) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +1 -0
  3. waldiez/exporting/agent/exporter.py +6 -6
  4. waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
  5. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
  6. waldiez/exporting/agent/extras/handoffs/available.py +1 -0
  7. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
  8. waldiez/exporting/agent/extras/handoffs/target.py +1 -0
  9. waldiez/exporting/agent/termination.py +1 -0
  10. waldiez/exporting/core/constants.py +3 -1
  11. waldiez/exporting/core/extras/serializer.py +12 -10
  12. waldiez/exporting/core/types.py +1 -0
  13. waldiez/exporting/core/utils/llm_config.py +2 -2
  14. waldiez/exporting/flow/execution_generator.py +1 -0
  15. waldiez/exporting/flow/utils/common.py +1 -1
  16. waldiez/exporting/flow/utils/importing.py +1 -1
  17. waldiez/exporting/flow/utils/logging.py +3 -75
  18. waldiez/io/__init__.py +3 -1
  19. waldiez/io/_ws.py +2 -0
  20. waldiez/io/structured.py +81 -28
  21. waldiez/io/utils.py +16 -10
  22. waldiez/io/ws.py +2 -2
  23. waldiez/models/agents/agent/agent.py +2 -1
  24. waldiez/models/chat/chat.py +1 -0
  25. waldiez/models/chat/chat_data.py +0 -2
  26. waldiez/models/common/base.py +2 -0
  27. waldiez/models/common/handoff.py +2 -0
  28. waldiez/models/common/method_utils.py +2 -0
  29. waldiez/models/model/_llm.py +3 -0
  30. waldiez/models/tool/predefined/_email.py +3 -0
  31. waldiez/models/tool/predefined/_perplexity.py +1 -1
  32. waldiez/models/tool/predefined/_searxng.py +1 -1
  33. waldiez/models/tool/predefined/_wikipedia.py +1 -1
  34. waldiez/running/base_runner.py +81 -20
  35. waldiez/running/post_run.py +6 -0
  36. waldiez/running/pre_run.py +167 -45
  37. waldiez/running/standard_runner.py +5 -5
  38. waldiez/running/step_by_step/breakpoints_mixin.py +368 -44
  39. waldiez/running/step_by_step/command_handler.py +151 -0
  40. waldiez/running/step_by_step/events_processor.py +199 -0
  41. waldiez/running/step_by_step/step_by_step_models.py +358 -41
  42. waldiez/running/step_by_step/step_by_step_runner.py +358 -353
  43. waldiez/running/subprocess_runner/__base__.py +4 -7
  44. waldiez/running/subprocess_runner/_async_runner.py +1 -1
  45. waldiez/running/subprocess_runner/_sync_runner.py +5 -4
  46. waldiez/running/subprocess_runner/runner.py +9 -0
  47. waldiez/running/utils.py +116 -2
  48. waldiez/ws/__init__.py +8 -7
  49. waldiez/ws/_file_handler.py +0 -2
  50. waldiez/ws/_mock.py +74 -0
  51. waldiez/ws/cli.py +27 -3
  52. waldiez/ws/client_manager.py +45 -29
  53. waldiez/ws/models.py +18 -1
  54. waldiez/ws/reloader.py +23 -2
  55. waldiez/ws/server.py +47 -8
  56. waldiez/ws/utils.py +29 -4
  57. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/METADATA +53 -44
  58. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/RECORD +62 -59
  59. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
  60. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
  61. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
  62. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/licenses/NOTICE.md +0 -0
waldiez/io/utils.py CHANGED
@@ -14,9 +14,16 @@ from autogen.agentchat.contrib.img_utils import get_pil_image # type: ignore
14
14
  from autogen.events import BaseEvent # type: ignore
15
15
  from autogen.messages import BaseMessage # type: ignore
16
16
 
17
+ DEBUG_INPUT_PROMPT = (
18
+ "[Step] (c)ontinue, (r)un, (q)uit, (i)nfo, (h)elp, (st)ats: "
19
+ )
20
+ START_CHAT_PROMPT = "Enter your message to start the conversation: "
21
+
17
22
  MessageType = Literal[
18
23
  "input_request",
19
24
  "input_response",
25
+ "debug_input_request",
26
+ "debug_input_response",
20
27
  "print",
21
28
  "input",
22
29
  ]
@@ -131,26 +138,25 @@ def get_image(
131
138
  return image_data
132
139
 
133
140
 
134
- def is_json_dumped(value: str) -> bool:
135
- """Check if a string is JSON-dumped.
141
+ def is_json_dumped(value: Any) -> tuple[bool, Any]:
142
+ """Check if a value is JSON-dumped.
136
143
 
137
144
  Parameters
138
145
  ----------
139
- value : str
140
- The string to check.
146
+ value : Any
147
+ The value to check.
141
148
 
142
149
  Returns
143
150
  -------
144
151
  bool
145
- True if the string is JSON-dumped, False otherwise.
152
+ True if the value is JSON-dumped, False otherwise.
146
153
  """
154
+ to_check = value.strip() if isinstance(value, str) else value
147
155
  try:
148
- parsed = json.loads(value)
149
- # If we can parse it as JSON and it's not a string,
150
- # we consider it JSON-dumped
151
- return not isinstance(parsed, str)
156
+ parsed = json.loads(to_check)
157
+ return True, parsed
152
158
  except json.JSONDecodeError:
153
- return False
159
+ return False, value
154
160
 
155
161
 
156
162
  def try_parse_maybe_serialized(value: str) -> Any:
waldiez/io/ws.py CHANGED
@@ -118,8 +118,8 @@ class AsyncWebsocketsIOStream(IOStream):
118
118
  end = kwargs.get("end", "\n")
119
119
  msg = sep.join(str(arg) for arg in args)
120
120
 
121
- is_dumped = is_json_dumped(msg)
122
- if is_dumped and end.endswith("\n"): # pragma: no cover
121
+ is_dumped, msg = is_json_dumped(msg)
122
+ if is_dumped: # pragma: no cover
123
123
  msg = json.loads(msg)
124
124
  else:
125
125
  msg = f"{msg}{end}"
@@ -21,10 +21,11 @@ from .code_execution import WaldiezAgentCodeExecutionConfig
21
21
  from .nested_chat import WaldiezAgentNestedChat, WaldiezAgentNestedChatMessage
22
22
 
23
23
  if TYPE_CHECKING:
24
+ # noinspection PyUnusedImports
24
25
  from ...chat import WaldiezChat
25
26
 
26
27
 
27
- # noinspection PyUnresolvedReferences
28
+ # noinspection PyUnresolvedReferences,PyNestedDecorators
28
29
  class WaldiezAgent(WaldiezBase):
29
30
  """Waldiez Agent to be inherited by all other agents.
30
31
 
@@ -194,6 +194,7 @@ class WaldiezChat(WaldiezBase):
194
194
  WaldiezHandoff
195
195
  The handoff representation of the chat.
196
196
  """
197
+ # noinspection PyTypeHints
197
198
  target: WaldiezTransitionTarget = WaldiezAgentTarget(
198
199
  target_type="AgentTarget",
199
200
  value=[self.target],
@@ -55,8 +55,6 @@ class WaldiezChatData(WaldiezBase):
55
55
  The maximum number of turns for the chat, by default None (no limit).
56
56
  silent : bool, optional
57
57
  Whether to run the chat silently, by default False (not silent).
58
- summary_args : Optional[dict[str, Any]]
59
- The summary args to use in autogen.
60
58
  real_source : Optional[str]
61
59
  The real source of the chat (overrides the source).
62
60
  real_target : Optional[str]
@@ -58,6 +58,7 @@ class WaldiezBase(BaseModel):
58
58
  mode = "json"
59
59
  if by_alias is None: # pragma: no branch
60
60
  by_alias = True
61
+ # noinspection PyUnreachableCode
61
62
  if not isinstance(by_alias, bool):
62
63
  by_alias = True
63
64
  return super().model_dump(by_alias=by_alias, mode=mode, **kwargs)
@@ -92,6 +93,7 @@ class WaldiezBase(BaseModel):
92
93
  by_alias = kwargs.pop("by_alias", None)
93
94
  if by_alias is None:
94
95
  by_alias = True
96
+ # noinspection PyUnreachableCode
95
97
  if not isinstance(by_alias, bool):
96
98
  by_alias = True
97
99
  return super().model_dump_json(by_alias=by_alias, **kwargs)
@@ -343,6 +343,7 @@ class WaldiezTransitionAvailability(WaldiezBase):
343
343
  value: str = ""
344
344
 
345
345
 
346
+ # noinspection PyTypeHints
346
347
  class WaldiezLLMBasedTransition(WaldiezBase):
347
348
  """Condition wrapper for LLM conditions."""
348
349
 
@@ -351,6 +352,7 @@ class WaldiezLLMBasedTransition(WaldiezBase):
351
352
  available: WaldiezTransitionAvailability
352
353
 
353
354
 
355
+ # noinspection PyTypeHints
354
356
  class WaldiezContextBasedTransition(WaldiezBase):
355
357
  """Condition wrapper for context conditions."""
356
358
 
@@ -123,11 +123,13 @@ def _extract_imports_from_ast(code_string: str) -> tuple[list[str], list[str]]:
123
123
 
124
124
  for node in ast.walk(tree):
125
125
  if isinstance(node, (ast.Import, ast.ImportFrom)):
126
+ # noinspection PyTypeChecker
126
127
  full_import_statement = ast.get_source_segment(code_string, node)
127
128
  if not full_import_statement: # pragma: no cover
128
129
  continue
129
130
  full_import_statement = full_import_statement.strip()
130
131
 
132
+ # noinspection PyTypeChecker
131
133
  module_name = _extract_module_name(node)
132
134
  if not module_name: # pragma: no cover
133
135
  continue
@@ -31,6 +31,7 @@ if TYPE_CHECKING:
31
31
  from .model import WaldiezModel
32
32
 
33
33
 
34
+ # noinspection PyUnusedLocal
34
35
  def get_llm_requirements(
35
36
  model: "WaldiezModel",
36
37
  ag2_version: str, # pylint: disable=unused-argument
@@ -154,6 +155,7 @@ def get_llm_imports(model: "WaldiezModel") -> set[str]:
154
155
  case "other":
155
156
  return {"from llama_index.llms.openai_like import OpenAILike"}
156
157
  case _: # pragma: no cover
158
+ # noinspection PyUnreachableCode
157
159
  raise ValueError(f"Unsupported API type: {model.data.api_type}")
158
160
 
159
161
 
@@ -201,6 +203,7 @@ def get_llm_arg(model: "WaldiezModel") -> tuple[str, str]:
201
203
  case "other":
202
204
  return do_other_llm(model)
203
205
  case _: # pragma: no cover
206
+ # noinspection PyUnreachableCode
204
207
  raise ValueError(f"Unsupported API type: {model.data.api_type}")
205
208
 
206
209
 
@@ -11,6 +11,7 @@ from ._config import PredefinedToolConfig
11
11
  from .protocol import PredefinedTool
12
12
 
13
13
 
14
+ # noinspection PyBroadException,TryExceptPass
14
15
  class SendEmailToolImpl(PredefinedTool):
15
16
  """Predefined tool for sending emails."""
16
17
 
@@ -139,6 +140,7 @@ class SendEmailToolImpl(PredefinedTool):
139
140
  A list of validation error messages, if any.
140
141
  """
141
142
  updated = dict(self._kwargs)
143
+ # noinspection DuplicatedCode
142
144
  for key, value in kwargs.items():
143
145
  if key in self.kwarg_types:
144
146
  typ = self.kwarg_types[key]
@@ -161,6 +163,7 @@ class SendEmailToolImpl(PredefinedTool):
161
163
  ) -> dict[str, Any]:
162
164
  """Get effective keyword arguments."""
163
165
  effective = dict(self._kwargs)
166
+ # noinspection DuplicatedCode
164
167
  if runtime_kwargs:
165
168
  # cast only known keys, same rules as validate_kwargs
166
169
  for k, v in runtime_kwargs.items():
@@ -94,7 +94,7 @@ class PerplexitySearchToolImpl(PredefinedTool):
94
94
  if key in kwargs: # pragma: no branch
95
95
  type_of = self.kwarg_types.get(key, str)
96
96
  # pylint: disable=broad-exception-caught
97
- # noinspection PyBroadException
97
+ # noinspection PyBroadException,TryExceptPass
98
98
  try:
99
99
  casted = type_of(value)
100
100
  if key in self.kwargs: # pragma: no branch
@@ -86,7 +86,7 @@ class SearxNGSearchToolImpl(PredefinedTool):
86
86
  if key in kwargs: # pragma: no branch
87
87
  type_of = self.kwarg_types.get(key, str)
88
88
  # pylint: disable=broad-exception-caught
89
- # noinspection PyBroadException
89
+ # noinspection PyBroadException,TryExceptPass
90
90
  try:
91
91
  casted = type_of(value)
92
92
  if key in self.kwargs: # pragma: no branch
@@ -91,7 +91,7 @@ class WikipediaSearchToolImpl(PredefinedTool):
91
91
  if key in kwargs:
92
92
  type_of = self.kwargs_types.get(key, str)
93
93
  # pylint: disable=broad-exception-caught
94
- # noinspection PyBroadException
94
+ # noinspection PyBroadException,TryExceptPass
95
95
  try:
96
96
  casted = type_of(value)
97
97
  if key in self.kwargs:
@@ -7,6 +7,7 @@
7
7
 
8
8
  import importlib.util
9
9
  import inspect
10
+ import json
10
11
  import shutil
11
12
  import sys
12
13
  import tempfile
@@ -26,7 +27,7 @@ from waldiez.models import Waldiez
26
27
  from .environment import refresh_environment, reset_env_vars, set_env_vars
27
28
  from .exceptions import StopRunningException
28
29
  from .post_run import after_run
29
- from .pre_run import RequirementsMixin
30
+ from .pre_run import RequirementsMixin, dump_waldiez
30
31
  from .protocol import WaldiezRunnerProtocol
31
32
  from .utils import (
32
33
  a_chdir,
@@ -38,8 +39,13 @@ from .utils import (
38
39
  )
39
40
 
40
41
  if TYPE_CHECKING:
41
- from autogen.events import BaseEvent # type: ignore[import-untyped]
42
- from autogen.messages import BaseMessage # type: ignore[import-untyped]
42
+ # pylint: disable=line-too-long
43
+ from autogen.events import ( # type: ignore[import-untyped,import-not-found,unused-ignore] # noqa: E501
44
+ BaseEvent,
45
+ )
46
+ from autogen.messages import ( # type: ignore[import-untyped,import-not-found,unused-ignore] # noqa: E501
47
+ BaseMessage,
48
+ )
43
49
 
44
50
 
45
51
  # pylint: disable=too-many-public-methods
@@ -51,7 +57,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
51
57
  - output_path: Path to save the output file.
52
58
  - uploads_root: Root directory for uploads.
53
59
  - structured_io: Whether to use structured I/O.
54
- - skip_patch_io: Whether to skip patching I/O functions.
55
60
  - dot_env: Path to a .env file for environment variables.
56
61
 
57
62
  Methods to possibly override:
@@ -69,9 +74,9 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
69
74
  _output_path: str | Path | None
70
75
  _uploads_root: str | Path | None
71
76
  _dot_env_path: str | Path | None
72
- _skip_patch_io: bool
73
77
  _running: bool
74
78
  _is_async: bool
79
+ _waldiez_file: Path
75
80
  _input: Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
76
81
  _print: Callable[..., None]
77
82
  _send: Callable[[Union["BaseEvent", "BaseMessage"]], None]
@@ -82,7 +87,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
82
87
  output_path: str | Path | None,
83
88
  uploads_root: str | Path | None,
84
89
  structured_io: bool,
85
- skip_patch_io: bool = False,
86
90
  dot_env: str | Path | None = None,
87
91
  **kwargs: Any,
88
92
  ) -> None:
@@ -91,7 +95,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
91
95
  WaldiezBaseRunner._structured_io = structured_io
92
96
  WaldiezBaseRunner._output_path = output_path
93
97
  WaldiezBaseRunner._uploads_root = uploads_root
94
- WaldiezBaseRunner._skip_patch_io = skip_patch_io
95
98
  WaldiezBaseRunner._dot_env_path = dot_env
96
99
  WaldiezBaseRunner._input = input
97
100
  WaldiezBaseRunner._print = print
@@ -110,6 +113,33 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
110
113
  self._logger = logger
111
114
  else:
112
115
  self._logger = get_logger()
116
+ waldiez_file = kwargs.get("waldiez_file", "")
117
+ if isinstance(waldiez_file, str) and waldiez_file:
118
+ waldiez_file_path = Path(waldiez_file).resolve()
119
+ elif isinstance(waldiez_file, Path):
120
+ waldiez_file_path = waldiez_file.resolve()
121
+ else:
122
+ waldiez_file_path = dump_waldiez(waldiez, output_path=output_path)
123
+ if not waldiez_file_path or not waldiez_file_path.is_file():
124
+ raise ValueError("Could not resolve a waldiez file path")
125
+ WaldiezBaseRunner._waldiez_file = waldiez_file_path
126
+
127
+ @staticmethod
128
+ def print(*args: Any, **kwargs: Any) -> None:
129
+ """Print a message to the console.
130
+
131
+ Parameters
132
+ ----------
133
+ *args : Any
134
+ Positional arguments to print.
135
+ **kwargs : Any
136
+ Keyword arguments to print.
137
+ """
138
+ if len(args) == 1 and isinstance(args[0], dict):
139
+ arg = json.dumps(args[0], default=str, ensure_ascii=False)
140
+ WaldiezBaseRunner._print(arg, **kwargs)
141
+ else:
142
+ WaldiezBaseRunner._print(*args, **kwargs)
113
143
 
114
144
  def is_running(self) -> bool:
115
145
  """Check if the workflow is currently running.
@@ -143,7 +173,9 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
143
173
  return input_sync
144
174
 
145
175
  @staticmethod
146
- async def a_get_user_input(prompt: str, *, password: bool = False) -> str:
176
+ async def a_get_user_input(
177
+ prompt: str, *, password: bool = False, **kwargs: Any
178
+ ) -> str:
147
179
  """Get user input with an optional password prompt.
148
180
 
149
181
  Parameters
@@ -152,6 +184,8 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
152
184
  The prompt to display to the user.
153
185
  password : bool, optional
154
186
  If True, the input will be hidden (default is False).
187
+ **kwargs : Any
188
+ Additional keyword arguments to pass to the input function.
155
189
 
156
190
  Returns
157
191
  -------
@@ -164,12 +198,13 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
164
198
  result = await input_function( # type: ignore
165
199
  prompt,
166
200
  password=password,
201
+ **kwargs,
167
202
  )
168
203
  except TypeError:
169
204
  result = await input_function(prompt) # type: ignore
170
205
  else:
171
206
  try:
172
- result = input_function(prompt, password=password)
207
+ result = input_function(prompt, password=password, **kwargs)
173
208
  except TypeError:
174
209
  result = input_function(prompt)
175
210
  return result # pyright: ignore
@@ -179,6 +214,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
179
214
  prompt: str,
180
215
  *,
181
216
  password: bool = False,
217
+ **kwargs: Any,
182
218
  ) -> str:
183
219
  """Get user input with an optional password prompt.
184
220
 
@@ -188,6 +224,8 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
188
224
  The prompt to display to the user.
189
225
  password : bool, optional
190
226
  If True, the input will be hidden (default is False).
227
+ **kwargs : Any
228
+ Additional keyword arguments to pass to the input function.
191
229
 
192
230
  Returns
193
231
  -------
@@ -197,11 +235,13 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
197
235
  input_function = WaldiezBaseRunner.get_input_function()
198
236
  if inspect.iscoroutinefunction(input_function):
199
237
  try:
200
- return syncify(input_function)(prompt, password=password)
238
+ return syncify(input_function)(
239
+ prompt, password=password, **kwargs
240
+ )
201
241
  except TypeError:
202
242
  return syncify(input_function)(prompt)
203
243
  try:
204
- return str(input_function(prompt, password=password))
244
+ return str(input_function(prompt, password=password, **kwargs))
205
245
  except TypeError:
206
246
  return str(input_function(prompt))
207
247
 
@@ -301,6 +341,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
301
341
  self,
302
342
  results: list[dict[str, Any]],
303
343
  output_file: Path,
344
+ waldiez_file: Path,
304
345
  uploads_root: Path | None,
305
346
  temp_dir: Path,
306
347
  skip_mmd: bool,
@@ -318,6 +359,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
318
359
  temp_dir=temp_dir,
319
360
  output_file=output_file,
320
361
  flow_name=self._waldiez.name,
362
+ waldiez_file=waldiez_file,
321
363
  uploads_root=uploads_root,
322
364
  skip_mmd=skip_mmd,
323
365
  skip_timeline=skip_timeline,
@@ -330,6 +372,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
330
372
  self,
331
373
  results: list[dict[str, Any]],
332
374
  output_file: Path,
375
+ waldiez_file: Path,
333
376
  uploads_root: Path | None,
334
377
  temp_dir: Path,
335
378
  skip_mmd: bool,
@@ -343,6 +386,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
343
386
  temp_dir=temp_dir,
344
387
  skip_mmd=skip_mmd,
345
388
  skip_timeline=skip_timeline,
389
+ waldiez_file=waldiez_file,
346
390
  )
347
391
 
348
392
  @staticmethod
@@ -365,13 +409,18 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
365
409
  return output_file, uploads_root_path
366
410
 
367
411
  @staticmethod
368
- async def a_process_event(event: Union["BaseEvent", "BaseMessage"]) -> None:
412
+ async def a_process_event(
413
+ event: Union["BaseEvent", "BaseMessage"],
414
+ skip_send: bool = False,
415
+ ) -> None:
369
416
  """Process an event or message asynchronously.
370
417
 
371
418
  Parameters
372
419
  ----------
373
420
  event : Union[BaseEvent, BaseMessage]
374
421
  The event or message to process.
422
+ skip_send : bool
423
+ Skip sending the event.
375
424
  """
376
425
  if hasattr(event, "type"): # pragma: no branch
377
426
  if event.type == "input_request":
@@ -387,17 +436,22 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
387
436
  prompt, password=password
388
437
  )
389
438
  await event.content.respond(user_input)
390
- else:
439
+ elif not skip_send:
391
440
  WaldiezBaseRunner._send(event)
392
441
 
393
442
  @staticmethod
394
- def process_event(event: Union["BaseEvent", "BaseMessage"]) -> None:
443
+ def process_event(
444
+ event: Union["BaseEvent", "BaseMessage"],
445
+ skip_send: bool = False,
446
+ ) -> None:
395
447
  """Process an event or message synchronously.
396
448
 
397
449
  Parameters
398
450
  ----------
399
451
  event : Union[BaseEvent, BaseMessage]
400
452
  The event or message to process.
453
+ skip_send : bool
454
+ Skip sending the event.
401
455
  """
402
456
  if hasattr(event, "type"): # pragma: no branch
403
457
  if event.type == "input_request":
@@ -413,7 +467,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
413
467
  prompt, password=password
414
468
  )
415
469
  event.content.respond(user_input)
416
- else:
470
+ elif not skip_send:
417
471
  WaldiezBaseRunner._send(event)
418
472
 
419
473
  def before_run(
@@ -708,6 +762,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
708
762
  results=results,
709
763
  output_file=output_file,
710
764
  uploads_root=uploads_root_path,
765
+ waldiez_file=WaldiezBaseRunner._waldiez_file,
711
766
  temp_dir=temp_dir,
712
767
  skip_mmd=skip_mmd,
713
768
  skip_timeline=skip_timeline,
@@ -745,6 +800,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
745
800
  self._after_run(
746
801
  results=results,
747
802
  output_file=output_file,
803
+ waldiez_file=WaldiezBaseRunner._waldiez_file,
748
804
  uploads_root=uploads_root,
749
805
  temp_dir=temp_dir,
750
806
  skip_mmd=skip_mmd,
@@ -784,6 +840,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
784
840
  temp_dir=temp_dir,
785
841
  skip_mmd=skip_mmd,
786
842
  skip_timeline=skip_timeline,
843
+ waldiez_file=WaldiezBaseRunner._waldiez_file,
787
844
  )
788
845
 
789
846
  @property
@@ -791,6 +848,11 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
791
848
  """Get the Waldiez instance."""
792
849
  return self._waldiez
793
850
 
851
+ @property
852
+ def waldiez_file(self) -> Path:
853
+ """Get the path to the waldiez file."""
854
+ return self._waldiez_file
855
+
794
856
  @property
795
857
  def is_async(self) -> bool:
796
858
  """Check if the workflow is async."""
@@ -826,11 +888,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
826
888
  """Get the uploads root path for the runner."""
827
889
  return WaldiezBaseRunner._uploads_root
828
890
 
829
- @property
830
- def skip_patch_io(self) -> bool:
831
- """Check if the runner is skipping patching IO."""
832
- return WaldiezBaseRunner._skip_patch_io
833
-
834
891
  @classmethod
835
892
  def load(
836
893
  cls,
@@ -909,6 +966,10 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
909
966
  """
910
967
  return self._stop_requested.is_set()
911
968
 
969
+ def set_stop_requested(self) -> None:
970
+ """Set the stop requested flag."""
971
+ self._stop_requested.set()
972
+
912
973
  def __enter__(self) -> Self:
913
974
  """Enter the context manager."""
914
975
  return self
@@ -20,6 +20,7 @@ def after_run(
20
20
  temp_dir: Path,
21
21
  output_file: Optional[Union[str, Path]],
22
22
  flow_name: str,
23
+ waldiez_file: Path,
23
24
  uploads_root: Optional[Path] = None,
24
25
  skip_mmd: bool = False,
25
26
  skip_timeline: bool = False,
@@ -34,6 +35,8 @@ def after_run(
34
35
  The output file.
35
36
  flow_name : str
36
37
  The flow name.
38
+ waldiez_file : Path
39
+ The path of the waldiez file used (or dumped) for the run.
37
40
  uploads_root : Optional[Path], optional
38
41
  The runtime uploads root, by default None
39
42
  skip_mmd : bool, optional
@@ -69,6 +72,9 @@ def after_run(
69
72
  output_file=output_file,
70
73
  destination_dir=destination_dir,
71
74
  )
75
+ dst_waldiez = destination_dir / waldiez_file.name
76
+ if not dst_waldiez.exists() and waldiez_file.is_file():
77
+ shutil.copyfile(waldiez_file, dst_waldiez)
72
78
  shutil.rmtree(temp_dir)
73
79
 
74
80