waldiez 0.4.8__py3-none-any.whl → 0.4.11__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 (40) hide show
  1. waldiez/__init__.py +1 -2
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +90 -96
  4. waldiez/exporter.py +64 -9
  5. waldiez/exporting/agent/extras/rag/chroma_extras.py +21 -9
  6. waldiez/exporting/core/context.py +12 -0
  7. waldiez/exporting/core/extras/flow_extras.py +2 -14
  8. waldiez/exporting/core/types.py +21 -0
  9. waldiez/exporting/flow/exporter.py +4 -0
  10. waldiez/exporting/flow/factory.py +16 -0
  11. waldiez/exporting/flow/orchestrator.py +12 -0
  12. waldiez/exporting/flow/utils/__init__.py +2 -0
  13. waldiez/exporting/flow/utils/common.py +96 -2
  14. waldiez/exporting/flow/utils/logging.py +5 -6
  15. waldiez/io/mqtt.py +7 -3
  16. waldiez/io/structured.py +26 -2
  17. waldiez/models/common/method_utils.py +1 -1
  18. waldiez/models/tool/tool.py +2 -1
  19. waldiez/runner.py +402 -332
  20. waldiez/running/__init__.py +6 -28
  21. waldiez/running/base_runner.py +907 -0
  22. waldiez/running/environment.py +74 -0
  23. waldiez/running/import_runner.py +424 -0
  24. waldiez/running/patch_io_stream.py +208 -0
  25. waldiez/running/post_run.py +121 -0
  26. waldiez/running/pre_run.py +105 -0
  27. waldiez/running/protocol.py +281 -0
  28. waldiez/running/run_results.py +22 -0
  29. waldiez/running/subprocess_runner.py +100 -0
  30. waldiez/running/utils.py +134 -0
  31. waldiez/utils/__init__.py +2 -2
  32. waldiez/utils/version.py +49 -0
  33. {waldiez-0.4.8.dist-info → waldiez-0.4.11.dist-info}/METADATA +11 -11
  34. {waldiez-0.4.8.dist-info → waldiez-0.4.11.dist-info}/RECORD +38 -30
  35. waldiez/running/running.py +0 -388
  36. waldiez/utils/flaml_warnings.py +0 -17
  37. {waldiez-0.4.8.dist-info → waldiez-0.4.11.dist-info}/WHEEL +0 -0
  38. {waldiez-0.4.8.dist-info → waldiez-0.4.11.dist-info}/entry_points.txt +0 -0
  39. {waldiez-0.4.8.dist-info → waldiez-0.4.11.dist-info}/licenses/LICENSE +0 -0
  40. {waldiez-0.4.8.dist-info → waldiez-0.4.11.dist-info}/licenses/NOTICE.md +0 -0
@@ -23,6 +23,7 @@ from .utils import (
23
23
  generate_header,
24
24
  get_after_run_content,
25
25
  get_np_no_nep50_handle,
26
+ get_set_io_stream,
26
27
  get_sqlite_out,
27
28
  get_start_logging,
28
29
  get_stop_logging,
@@ -182,6 +183,17 @@ class ExportOrchestrator:
182
183
  order=ContentOrder.CLEANUP.value + 1, # after imports
183
184
  skip_strip=True, # keep newlines
184
185
  )
186
+ if not self.config.skip_patch_io:
187
+ merged_result.add_content(
188
+ get_set_io_stream(
189
+ use_structured_io=self.config.structured_io,
190
+ is_async=self.waldiez.is_async,
191
+ uploads_root=self.config.uploads_root,
192
+ ),
193
+ position=ExportPosition.IMPORTS, # after imports, before models
194
+ order=ContentOrder.CLEANUP.value + 2, # after start logging
195
+ )
196
+ # merged_result.add_content
185
197
  merged_result.add_content(
186
198
  get_sqlite_out(is_async=self.waldiez.is_async),
187
199
  position=ExportPosition.AGENTS,
@@ -6,6 +6,7 @@ from .common import (
6
6
  generate_header,
7
7
  get_after_run_content,
8
8
  get_np_no_nep50_handle,
9
+ get_set_io_stream,
9
10
  )
10
11
  from .importing import get_sorted_imports, get_the_imports_string
11
12
  from .logging import get_sqlite_out, get_start_logging, get_stop_logging
@@ -19,4 +20,5 @@ __all__ = [
19
20
  "get_start_logging",
20
21
  "get_stop_logging",
21
22
  "get_sqlite_out",
23
+ "get_set_io_stream",
22
24
  ]
@@ -4,6 +4,8 @@
4
4
  # flake8: noqa: E501
5
5
  """Common utils for the final generatio."""
6
6
 
7
+ from pathlib import Path
8
+
7
9
  from waldiez.models import Waldiez
8
10
 
9
11
  from ...core import FILE_HEADER
@@ -151,7 +153,7 @@ def get_after_run_content(
151
153
  if agent.is_reasoning:
152
154
  agent_name = agent_names[agent.id]
153
155
  content += f"""
154
- {space}# pylint: disable=broad-except,too-many-try-statements
156
+ {space}# pylint: disable=broad-exception-caught,too-many-try-statements
155
157
  {space}try:
156
158
  {space}{tab}{agent_name}.visualize_tree()
157
159
  {space}{tab}if os.path.exists("tree_of_thoughts.png"):
@@ -160,8 +162,9 @@ def get_after_run_content(
160
162
  {space}except BaseException:
161
163
  {space}{tab}pass
162
164
  {space}# save the tree to json
165
+ {space}# pylint: disable=protected-access
163
166
  {space}try:
164
- {space}{tab}data = {agent_name}._root.to_dict() # pylint: disable=protected-access # pyright: ignore
167
+ {space}{tab}data = {agent_name}._root.to_dict() # pyright: ignore
165
168
  {space}{tab}with open("{agent_name}_reasoning_tree.json", "w", encoding="utf-8") as f:
166
169
  {space}{tab}{tab}json.dump(data, f)
167
170
  {space}except BaseException:
@@ -204,3 +207,94 @@ if not hasattr(np, "_no_pep50_warning"):
204
207
  setattr(np, "_no_pep50_warning", _np_no_nep50_warning) # noqa
205
208
  '''
206
209
  return content
210
+
211
+
212
+ def get_set_io_stream(
213
+ use_structured_io: bool,
214
+ is_async: bool,
215
+ uploads_root: Path | None,
216
+ ) -> str:
217
+ """Get the content to set structured IO.
218
+
219
+ Parameters
220
+ ----------
221
+ use_structured_io : bool
222
+ Whether to use structured IO or not.
223
+ is_async : bool
224
+ Whether the flow is async or not.
225
+ uploads_root : Path | None
226
+ The uploads root, to get user-uploaded files, by default None.
227
+
228
+ Returns
229
+ -------
230
+ str
231
+ The content to set structured IO.
232
+ """
233
+ if use_structured_io:
234
+ return get_set_structured_io_stream(
235
+ is_async=is_async,
236
+ uploads_root=uploads_root,
237
+ )
238
+ return get_patch_default_io_stream(is_async)
239
+
240
+
241
+ def get_set_structured_io_stream(
242
+ is_async: bool,
243
+ uploads_root: Path | None,
244
+ ) -> str:
245
+ """Get the content to set structured IO.
246
+
247
+ Parameters
248
+ ----------
249
+ is_async : bool
250
+ Whether the flow is async or not.
251
+ uploads_root : Path | None
252
+ The uploads root, to get user-uploaded files, by default None.
253
+
254
+ Returns
255
+ -------
256
+ str
257
+ The content to set structured IO.
258
+ """
259
+ upload_root_arg = f'r"{uploads_root}"' if uploads_root else "None"
260
+ return f"""
261
+ # set structured IO
262
+ try:
263
+ # pylint: disable=import-outside-toplevel
264
+ from autogen.io import IOStream
265
+ from waldiez.io import StructuredIOStream
266
+ stream = StructuredIOStream(
267
+ is_async={is_async},
268
+ uploads_root={upload_root_arg}
269
+ )
270
+ IOStream.set_default(stream)
271
+ except BaseException: # pylint: disable=broad-exception-caught
272
+ # allow running the flow without structured IO
273
+ pass
274
+ """
275
+
276
+
277
+ def get_patch_default_io_stream(is_async: bool) -> str:
278
+ """Get the content to patch the default IO stream.
279
+
280
+ Parameters
281
+ ----------
282
+ is_async : bool
283
+ Whether the flow is async or not.
284
+
285
+ Returns
286
+ -------
287
+ str
288
+ The content to patch the default IO stream.
289
+ """
290
+ # copy from waldiez/running/patch_io_stream.py
291
+ return f"""
292
+ # patch the default IOStream
293
+ try:
294
+ # pylint: disable=import-outside-toplevel
295
+ from waldiez.running.patch_io_stream import patch_io_stream
296
+ patch_io_stream(is_async={is_async})
297
+ except BaseException: # pylint: disable=broad-exception-caught
298
+ # allow running the flow without patching the IOStream
299
+ pass
300
+ """
@@ -193,7 +193,7 @@ def get_async_sqlite_out() -> str:
193
193
  query = f"SELECT * FROM {table}" # nosec
194
194
  try:
195
195
  cursor = await conn.execute(query)
196
- except BaseException: # pylint: disable=broad-except
196
+ except BaseException: # pylint: disable=broad-exception-caught
197
197
  await conn.close()
198
198
  return
199
199
  rows = await cursor.fetchall()
@@ -210,6 +210,7 @@ def get_async_sqlite_out() -> str:
210
210
  await file.write(json.dumps(data, indent=4, ensure_ascii=False)
211
211
  ```
212
212
  """
213
+ # fmt: off
213
214
  content = "\n\n"
214
215
  content += "async def get_sqlite_out(dbname: str, table: str, csv_file: str) -> None:\n"
215
216
  content += ' """Convert a sqlite table to csv and json files.\n\n'
@@ -226,7 +227,7 @@ def get_async_sqlite_out() -> str:
226
227
  content += ' query = f"SELECT * FROM {table}" # nosec\n'
227
228
  content += " try:\n"
228
229
  content += " cursor = await conn.execute(query)\n"
229
- content += " except BaseException: # pylint: disable=broad-except\n"
230
+ content += " except BaseException: # pylint: disable=broad-exception-caught\n"
230
231
  content += " await conn.close()\n"
231
232
  content += " return\n"
232
233
  content += " rows = await cursor.fetchall()\n"
@@ -235,10 +236,7 @@ def get_async_sqlite_out() -> str:
235
236
  content += " data = [dict(zip(column_names, row)) for row in rows]\n"
236
237
  content += " await cursor.close()\n"
237
238
  content += " await conn.close()\n"
238
- content += (
239
- ' async with aiofiles.open(csv_file, "w", newline="", '
240
- 'encoding="utf-8") as file:\n'
241
- )
239
+ content += ' async with aiofiles.open(csv_file, "w", newline="", encoding="utf-8") as file:\n'
242
240
  content += ' csv_writer = AsyncDictWriter(file, fieldnames=column_names, dialect="unix")\n'
243
241
  content += " await csv_writer.writeheader()\n"
244
242
  content += " await csv_writer.writerows(data)\n"
@@ -246,6 +244,7 @@ def get_async_sqlite_out() -> str:
246
244
  content += ' async with aiofiles.open(json_file, "w", encoding="utf-8") as file:\n'
247
245
  content += " await file.write(json.dumps(data, indent=4, ensure_ascii=False))\n"
248
246
  content += "\n"
247
+ # fmt: on
249
248
  return content
250
249
 
251
250
 
waldiez/io/mqtt.py CHANGED
@@ -4,7 +4,7 @@
4
4
  # flake8: noqa: E501
5
5
  # pylint: disable=too-many-try-statements,broad-exception-caught,
6
6
  # pylint: disable=line-too-long,unused-argument,too-many-instance-attributes
7
- # pylint: disable=too-many-arguments,too-many-positional-arguments
7
+ # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
8
8
 
9
9
  """An MQTT I/O stream for handling print and input messages."""
10
10
 
@@ -82,6 +82,7 @@ class MqttIOStream(IOStream):
82
82
  broker_port: int = 1883,
83
83
  task_id: str | None = None,
84
84
  input_timeout: int = 120,
85
+ connect_timeout: int = 10,
85
86
  max_retain_messages: int = 1000,
86
87
  on_input_request: Optional[Callable[[str, str, str], None]] = None,
87
88
  on_input_response: Optional[Callable[[str, str], None]] = None,
@@ -104,6 +105,8 @@ class MqttIOStream(IOStream):
104
105
  An ID to use for the topics. If not provided, a random UUID will be generated.
105
106
  input_timeout : int, optional
106
107
  The time to wait for user input in seconds, by default 120.
108
+ connect_timeout : int, optional
109
+ The time to wait for MQTT connection in seconds, by default 10.
107
110
  on_input_request : Optional[Callable[[str, str, str], None]], optional
108
111
  Callback for input request, by default None
109
112
  parameters: prompt, request_id, task_id
@@ -129,6 +132,7 @@ class MqttIOStream(IOStream):
129
132
  self.broker_port = broker_port
130
133
  self.task_id = task_id or uuid.uuid4().hex
131
134
  self.input_timeout = input_timeout
135
+ self.connect_timeout = connect_timeout
132
136
  self.on_input_request = on_input_request
133
137
  self.on_input_response = on_input_response
134
138
  self.max_retain_messages = max_retain_messages
@@ -191,7 +195,7 @@ class MqttIOStream(IOStream):
191
195
  self.client.loop_start()
192
196
 
193
197
  # Wait for connection
194
- timeout = 10 # seconds
198
+ timeout = self.connect_timeout # seconds
195
199
  start_time = time.time()
196
200
  while (
197
201
  not self.client.is_connected()
@@ -280,7 +284,7 @@ class MqttIOStream(IOStream):
280
284
  while reconnect_count < MQTT_MAX_RECONNECT_COUNT:
281
285
  LOG.info("Reconnecting in %d seconds...", reconnect_delay)
282
286
  time.sleep(reconnect_delay)
283
- # pylint: disable=broad-except
287
+ # pylint: disable=broad-exception-caught
284
288
  try:
285
289
  client.reconnect()
286
290
  except Exception as err:
waldiez/io/structured.py CHANGED
@@ -36,9 +36,13 @@ class StructuredIOStream(IOStream):
36
36
  uploads_root: Path | None = None
37
37
 
38
38
  def __init__(
39
- self, timeout: float = 120, uploads_root: Path | str | None = None
39
+ self,
40
+ timeout: float = 120,
41
+ uploads_root: Path | str | None = None,
42
+ is_async: bool = False,
40
43
  ) -> None:
41
44
  self.timeout = timeout
45
+ self.is_async = is_async
42
46
  if uploads_root is not None:
43
47
  self.uploads_root = Path(uploads_root).resolve()
44
48
  if not self.uploads_root.exists():
@@ -97,7 +101,11 @@ class StructuredIOStream(IOStream):
97
101
  The line read from the input stream.
98
102
  """
99
103
  request_id = uuid4().hex
100
- prompt = prompt or "> "
104
+ prompt = prompt or ">"
105
+ if not prompt or prompt in [">", "> "]:
106
+ # if the prompt is just ">" or "> ",
107
+ # let's use a more descriptive one
108
+ prompt = "Enter your message to start the conversation: "
101
109
 
102
110
  self._send_input_request(prompt, request_id, password)
103
111
  user_input_raw = self._read_user_input(prompt, password, request_id)
@@ -106,6 +114,22 @@ class StructuredIOStream(IOStream):
106
114
  uploads_root=self.uploads_root,
107
115
  base_name=request_id,
108
116
  )
117
+ # let's keep an eye here:
118
+ # https://github.com/ag2ai/ag2/blob/main/autogen/agentchat/conversable_agent.py#L2973
119
+ # reply = await iostream.input(prompt) ???? (await???)
120
+ if self.is_async:
121
+ # let's make a coroutine to just return the user response
122
+ async def async_input() -> str:
123
+ """Asynchronous input method.
124
+
125
+ Returns
126
+ -------
127
+ str
128
+ The line read from the input stream.
129
+ """
130
+ return user_response
131
+
132
+ return async_input() # type: ignore
109
133
  return user_response
110
134
 
111
135
  # noinspection PyMethodMayBeStatic
@@ -73,7 +73,7 @@ def parse_code_string(
73
73
  If valid, None and the ast module.
74
74
  If invalid, the error message and None.
75
75
  """
76
- # pylint: disable=broad-except
76
+ # pylint: disable=broad-exception-caught
77
77
  try:
78
78
  tree = ast.parse(code_string)
79
79
  except SyntaxError as e:
@@ -137,9 +137,10 @@ class WaldiezTool(WaldiezBase):
137
137
  raise FileNotFoundError(f"File not found: {resolved}")
138
138
  with resolved.open("r", encoding="utf-8") as file:
139
139
  data_string = file.read()
140
+ # pylint: disable=broad-exception-caught
140
141
  try:
141
142
  data_dict = json.loads(data_string)
142
- except BaseException as exc: # pylint: disable=broad-except
143
+ except BaseException as exc:
143
144
  raise ValueError(f"Invalid WaldiezTool/JSON: {exc}") from exc
144
145
  return WaldiezTool.model_validate(data_dict)
145
146