waldiez 0.5.9__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 (109) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +113 -24
  3. waldiez/exporting/agent/exporter.py +9 -6
  4. waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
  5. waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
  6. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
  7. waldiez/exporting/agent/extras/handoffs/available.py +1 -0
  8. waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
  9. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
  10. waldiez/exporting/agent/extras/handoffs/target.py +1 -0
  11. waldiez/exporting/agent/termination.py +1 -0
  12. waldiez/exporting/chats/utils/common.py +25 -23
  13. waldiez/exporting/core/__init__.py +0 -2
  14. waldiez/exporting/core/constants.py +3 -1
  15. waldiez/exporting/core/context.py +13 -13
  16. waldiez/exporting/core/extras/serializer.py +12 -10
  17. waldiez/exporting/core/protocols.py +0 -141
  18. waldiez/exporting/core/result.py +5 -5
  19. waldiez/exporting/core/types.py +1 -0
  20. waldiez/exporting/core/utils/llm_config.py +2 -2
  21. waldiez/exporting/flow/execution_generator.py +1 -0
  22. waldiez/exporting/flow/merger.py +2 -2
  23. waldiez/exporting/flow/orchestrator.py +1 -0
  24. waldiez/exporting/flow/utils/common.py +3 -3
  25. waldiez/exporting/flow/utils/importing.py +1 -0
  26. waldiez/exporting/flow/utils/logging.py +7 -80
  27. waldiez/exporting/tools/exporter.py +5 -0
  28. waldiez/exporting/tools/factory.py +4 -0
  29. waldiez/exporting/tools/processor.py +5 -1
  30. waldiez/io/__init__.py +3 -1
  31. waldiez/io/_ws.py +15 -5
  32. waldiez/io/models/content/image.py +1 -0
  33. waldiez/io/models/user_input.py +4 -4
  34. waldiez/io/models/user_response.py +1 -0
  35. waldiez/io/mqtt.py +1 -1
  36. waldiez/io/structured.py +98 -45
  37. waldiez/io/utils.py +17 -11
  38. waldiez/io/ws.py +10 -12
  39. waldiez/logger.py +180 -63
  40. waldiez/models/agents/agent/agent.py +2 -1
  41. waldiez/models/agents/agent/update_system_message.py +0 -2
  42. waldiez/models/agents/doc_agent/doc_agent.py +8 -1
  43. waldiez/models/chat/chat.py +1 -0
  44. waldiez/models/chat/chat_data.py +0 -2
  45. waldiez/models/common/base.py +2 -0
  46. waldiez/models/common/dict_utils.py +169 -40
  47. waldiez/models/common/handoff.py +2 -0
  48. waldiez/models/common/method_utils.py +2 -0
  49. waldiez/models/flow/flow.py +6 -6
  50. waldiez/models/flow/info.py +5 -1
  51. waldiez/models/model/_llm.py +31 -14
  52. waldiez/models/model/model.py +4 -1
  53. waldiez/models/model/model_data.py +18 -5
  54. waldiez/models/tool/predefined/_config.py +5 -1
  55. waldiez/models/tool/predefined/_duckduckgo.py +4 -0
  56. waldiez/models/tool/predefined/_email.py +477 -0
  57. waldiez/models/tool/predefined/_google.py +4 -1
  58. waldiez/models/tool/predefined/_perplexity.py +4 -1
  59. waldiez/models/tool/predefined/_searxng.py +4 -1
  60. waldiez/models/tool/predefined/_tavily.py +4 -1
  61. waldiez/models/tool/predefined/_wikipedia.py +5 -2
  62. waldiez/models/tool/predefined/_youtube.py +4 -1
  63. waldiez/models/tool/predefined/protocol.py +3 -0
  64. waldiez/models/tool/tool.py +22 -4
  65. waldiez/models/waldiez.py +12 -0
  66. waldiez/runner.py +37 -54
  67. waldiez/running/__init__.py +6 -0
  68. waldiez/running/base_runner.py +381 -363
  69. waldiez/running/environment.py +1 -0
  70. waldiez/running/exceptions.py +9 -0
  71. waldiez/running/post_run.py +10 -4
  72. waldiez/running/pre_run.py +199 -66
  73. waldiez/running/protocol.py +21 -101
  74. waldiez/running/run_results.py +1 -1
  75. waldiez/running/standard_runner.py +83 -276
  76. waldiez/running/step_by_step/__init__.py +46 -0
  77. waldiez/running/step_by_step/breakpoints_mixin.py +512 -0
  78. waldiez/running/step_by_step/command_handler.py +151 -0
  79. waldiez/running/step_by_step/events_processor.py +199 -0
  80. waldiez/running/step_by_step/step_by_step_models.py +541 -0
  81. waldiez/running/step_by_step/step_by_step_runner.py +750 -0
  82. waldiez/running/subprocess_runner/__base__.py +279 -0
  83. waldiez/running/subprocess_runner/__init__.py +16 -0
  84. waldiez/running/subprocess_runner/_async_runner.py +362 -0
  85. waldiez/running/subprocess_runner/_sync_runner.py +456 -0
  86. waldiez/running/subprocess_runner/runner.py +570 -0
  87. waldiez/running/timeline_processor.py +1 -1
  88. waldiez/running/utils.py +492 -3
  89. waldiez/utils/version.py +2 -6
  90. waldiez/ws/__init__.py +71 -0
  91. waldiez/ws/__main__.py +15 -0
  92. waldiez/ws/_file_handler.py +199 -0
  93. waldiez/ws/_mock.py +74 -0
  94. waldiez/ws/cli.py +235 -0
  95. waldiez/ws/client_manager.py +851 -0
  96. waldiez/ws/errors.py +416 -0
  97. waldiez/ws/models.py +988 -0
  98. waldiez/ws/reloader.py +363 -0
  99. waldiez/ws/server.py +508 -0
  100. waldiez/ws/session_manager.py +393 -0
  101. waldiez/ws/session_stats.py +83 -0
  102. waldiez/ws/utils.py +410 -0
  103. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/METADATA +105 -96
  104. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/RECORD +108 -83
  105. waldiez/running/patch_io_stream.py +0 -210
  106. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
  107. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
  108. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
  109. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/NOTICE.md +0 -0
@@ -10,7 +10,7 @@ from typing import Any, Optional
10
10
  from ..protocols import Serializer
11
11
 
12
12
 
13
- # pylint: disable=too-few-public-methods
13
+ # pylint: disable=too-few-public-methods,no-self-use
14
14
  class DefaultSerializer(Serializer):
15
15
  """Default serializer for Waldiez items."""
16
16
 
@@ -37,7 +37,7 @@ class DefaultSerializer(Serializer):
37
37
  def serialize_item(
38
38
  item: Any,
39
39
  tabs: int = 1,
40
- _visited: Optional[set[int]] = None,
40
+ visited: Optional[set[int]] = None,
41
41
  ) -> str:
42
42
  """Convert an item to a formatted string with given indentation.
43
43
 
@@ -47,6 +47,8 @@ def serialize_item(
47
47
  The item to convert.
48
48
  tabs : int, optional
49
49
  The number of tabs, by default 1.
50
+ visited : set[int], optional
51
+ A set of visited IDs, by default None
50
52
 
51
53
  Returns
52
54
  -------
@@ -81,8 +83,8 @@ def serialize_item(
81
83
  }
82
84
  ```
83
85
  """
84
- if _visited is None:
85
- _visited = set()
86
+ if visited is None:
87
+ visited = set()
86
88
 
87
89
  if callable(item):
88
90
  return item.__name__
@@ -92,37 +94,37 @@ def serialize_item(
92
94
  return _format_primitive(item)
93
95
 
94
96
  # Handle circular references in containers
95
- if isinstance(item, (dict, list, tuple, set)) and id(item) in _visited:
97
+ if isinstance(item, (dict, list, tuple, set)) and id(item) in visited:
96
98
  return '"<circular reference>"'
97
99
 
98
100
  next_indent = " " * 4 * (tabs + 1)
99
- _visited.add(id(item))
101
+ visited.add(id(item))
100
102
 
101
103
  if isinstance(item, dict):
102
104
  items: list[str] = []
103
105
  for key, value in item.items():
104
106
  key_str = f'{next_indent}"{key}"'
105
- value_str = serialize_item(value, tabs + 1, _visited)
107
+ value_str = serialize_item(value, tabs + 1, visited)
106
108
  items.append(f"{key_str}: {value_str}")
107
109
  return _format_container(items, "{", "}", tabs)
108
110
 
109
111
  if isinstance(item, list):
110
112
  items = [
111
- f"{next_indent}{serialize_item(sub_item, tabs + 1, _visited)}"
113
+ f"{next_indent}{serialize_item(sub_item, tabs + 1, visited)}"
112
114
  for sub_item in item
113
115
  ]
114
116
  return _format_container(items, "[", "]", tabs)
115
117
 
116
118
  if isinstance(item, tuple):
117
119
  items = [
118
- f"{next_indent}{serialize_item(sub_item, tabs + 1, _visited)}"
120
+ f"{next_indent}{serialize_item(sub_item, tabs + 1, visited)}"
119
121
  for sub_item in item
120
122
  ]
121
123
  return _format_container(items, "(", ")", tabs)
122
124
 
123
125
  if isinstance(item, set):
124
126
  items = [
125
- f"{next_indent}{serialize_item(sub_item, tabs + 1, _visited)}"
127
+ f"{next_indent}{serialize_item(sub_item, tabs + 1, visited)}"
126
128
  for sub_item in item
127
129
  ]
128
130
  return _format_container(items, "{", "}", tabs)
@@ -139,144 +139,3 @@ class ContentGenerator(Protocol):
139
139
  ExporterContentError
140
140
  If exporting fails.
141
141
  """
142
-
143
-
144
- @runtime_checkable
145
- class ExportingLogger(Protocol):
146
- """Protocol for logging during exporting."""
147
-
148
- def log(
149
- self, message: Any, *args: Any, level: str = "info", **kwargs: Any
150
- ) -> None:
151
- """Log a message with the specified level.
152
-
153
- Parameters
154
- ----------
155
- message : Any
156
- The message to log or message template for formatting.
157
- level : str, optional
158
- The logging level to use (e.g., "debug", "info", "warning", "error",
159
- "critical"). Defaults to "info".
160
- *args : Any
161
- Arguments to format into the message using % formatting.
162
- **kwargs : Any
163
- Keyword arguments to format into the message using str.format().
164
- """
165
-
166
- def error(self, message: Any, *args: Any, **kwargs: Any) -> None:
167
- """Log an error message.
168
-
169
- Parameters
170
- ----------
171
- message : Any
172
- The error message to log or message template.
173
- *args : Any
174
- Arguments to format into the message.
175
- **kwargs : Any
176
- Keyword arguments to format into the message using str.format().
177
- """
178
-
179
- def warning(self, message: Any, *args: Any, **kwargs: Any) -> None:
180
- """Log a warning message.
181
-
182
- Parameters
183
- ----------
184
- message : Any
185
- The warning message to log or message template.
186
- *args : Any
187
- Arguments to format into the message.
188
- **kwargs : Any
189
- Keyword arguments to format into the message using str.format().
190
- """
191
-
192
- def info(self, message: Any, *args: Any, **kwargs: Any) -> None:
193
- """Log an informational message.
194
-
195
- Parameters
196
- ----------
197
- message : Any
198
- The informational message to log or message template.
199
- *args : Any
200
- Arguments to format into the message.
201
- **kwargs : Any
202
- Keyword arguments to format into the message using str.format().
203
- """
204
-
205
- def success(self, message: Any, *args: Any, **kwargs: Any) -> None:
206
- """Log a success message.
207
-
208
- Parameters
209
- ----------
210
- message : Any
211
- The success message to log or message template.
212
- *args : Any
213
- Arguments to format into the message.
214
- **kwargs : Any
215
- Keyword arguments to format into the message using str.format().
216
- """
217
-
218
- def debug(self, message: Any, *args: Any, **kwargs: Any) -> None:
219
- """Log a debug message.
220
-
221
- Parameters
222
- ----------
223
- message : Any
224
- The debug message to log or message template.
225
- *args : Any
226
- Arguments to format into the message.
227
- **kwargs : Any
228
- Keyword arguments to format into the message using str.format().
229
- """
230
- self.log(message, *args, level="debug", **kwargs)
231
-
232
- def critical(self, message: Any, *args: Any, **kwargs: Any) -> None:
233
- """Log a critical error message.
234
-
235
- Parameters
236
- ----------
237
- message : Any
238
- The critical error message to log or message template.
239
- *args : Any
240
- Arguments to format into the message.
241
- **kwargs : Any
242
- Keyword arguments to format into the message using str.format().
243
- """
244
- self.log(message, *args, level="critical", **kwargs)
245
-
246
- def exception(self, message: Any, *args: Any, **kwargs: Any) -> None:
247
- """Log an exception message.
248
-
249
- Parameters
250
- ----------
251
- message : Any
252
- The exception message to log or message template.
253
- *args : Any
254
- Arguments to format into the message.
255
- **kwargs : Any
256
- Keyword arguments to format into the message using str.format().
257
- """
258
- self.log(message, *args, level="exception", **kwargs)
259
-
260
- def set_level(self, level: str) -> None:
261
- """Set the logging level.
262
-
263
- Parameters
264
- ----------
265
- level : str
266
- The logging level to set
267
- (e.g., "debug", "info", "warning", "error", "critical").
268
-
269
- Raises
270
- ------
271
- ValueError
272
- If the provided level is invalid.
273
- """
274
-
275
- def get_level(self) -> str: # pyright: ignore
276
- """Get the current logging level.
277
-
278
- Returns
279
- -------
280
- str
281
- The current logging level.
282
- """
@@ -59,7 +59,7 @@ class ExportResult:
59
59
  position : ImportPosition, optional
60
60
  The position of the import, by default THIRD_PARTY
61
61
  """
62
- if statement and statement.strip():
62
+ if statement and statement.strip(): # pragma: no branch
63
63
  self.imports.add(
64
64
  ImportStatement(
65
65
  statement=statement.strip(),
@@ -123,7 +123,7 @@ class ExportResult:
123
123
  comment : Optional[str], optional
124
124
  Optional comment for the argument, by default None
125
125
  """
126
- if name and value is not None:
126
+ if name and value is not None: # pragma: no branch
127
127
  arg = InstanceArgument(
128
128
  instance_id=instance_id,
129
129
  name=name,
@@ -173,7 +173,7 @@ class ExportResult:
173
173
  position : ExportPosition, optional
174
174
  The position for the merged content, by default AGENTS
175
175
  """
176
- if other.main_content:
176
+ if other.main_content: # pragma: no branch
177
177
  self.main_content = (
178
178
  (self.main_content or "") + "\n" + other.main_content
179
179
  ).strip()
@@ -241,7 +241,7 @@ class ExportResult:
241
241
  Additional metadata for the content, by default None
242
242
  """
243
243
  order_value = order.value if isinstance(order, ContentOrder) else order
244
- if content and content.strip():
244
+ if content and content.strip(): # pragma: no branch
245
245
  positioned = PositionedContent(
246
246
  content=content.strip() if not skip_strip else content,
247
247
  position=position,
@@ -273,7 +273,7 @@ class ExportResult:
273
273
  required : bool, optional
274
274
  Whether the variable is required, by default True
275
275
  """
276
- if name and value:
276
+ if name and value: # pragma: no branch
277
277
  env_var = EnvironmentVariable(
278
278
  name=name,
279
279
  value=value,
@@ -314,6 +314,7 @@ class ExportConfig:
314
314
  if for_notebook:
315
315
  output_extension = "ipynb"
316
316
  cache_seed = kwargs.pop("cache_seed", None)
317
+ # noinspection PyUnreachableCode
317
318
  if cache_seed is not None and not isinstance(cache_seed, int):
318
319
  cache_seed = None
319
320
  return cls(
@@ -97,7 +97,7 @@ def _get_agent_llm_config_arg_as_dict(
97
97
  ) -> str:
98
98
  tab = " " * tab_leng * tabs if tabs > 0 else ""
99
99
  if not agent.data.model_ids:
100
- return f'{tab}"llm_config": False' + "\n"
100
+ return f'{tab}"llm_config": False,' + "\n"
101
101
  content = f'{tab}"llm_config": autogen.LLMConfig(' + "\n"
102
102
  content += f"{tab} config_list=["
103
103
  got_at_least_one_model = False
@@ -110,7 +110,7 @@ def _get_agent_llm_config_arg_as_dict(
110
110
  content += "\n" + f"{tab} {model_name}_llm_config,"
111
111
  got_at_least_one_model = True
112
112
  if not got_at_least_one_model: # pragma: no cover
113
- return f'{tab}"llm_config": False' + "\n"
113
+ return f'{tab}"llm_config": False,' + "\n"
114
114
  content += "\n" + f"{tab} ]," + "\n"
115
115
  content += f"{tab} cache_seed={cache_seed}," + "\n"
116
116
  if temperature is not None:
@@ -125,6 +125,7 @@ class ExecutionGenerator:
125
125
  flow_content += " result_dicts: list[dict[str, Any]] = []\n"
126
126
  space = " "
127
127
  if cache_seed is not None:
128
+ # noinspection SqlDialectInspection
128
129
  flow_content += (
129
130
  f" with Cache.disk(cache_seed={cache_seed}"
130
131
  ") as cache: # pyright: ignore\n"
@@ -167,7 +167,7 @@ class ContentMerger:
167
167
  # Log conflicts but continue
168
168
  for conflict in conflicts:
169
169
  self.context.get_logger().warning(
170
- f"Import conflict: {conflict}"
170
+ "Import conflict: %s", conflict
171
171
  )
172
172
 
173
173
  return set(grouped.values())
@@ -309,7 +309,7 @@ class ContentMerger:
309
309
  # Log conflicts but continue (non-fatal)
310
310
  for conflict in conflicts:
311
311
  self.context.get_logger().warning(
312
- f"Environment variable conflict: {conflict}"
312
+ "Environment variable conflict: %s", conflict
313
313
  )
314
314
 
315
315
  return list(env_vars.values())
@@ -249,6 +249,7 @@ class ExportOrchestrator:
249
249
  agent_names=self.agent_names,
250
250
  tools=self.tools,
251
251
  tool_names=self.tool_names,
252
+ is_async=self.waldiez.is_async,
252
253
  output_dir=self.config.output_directory,
253
254
  context=self.context,
254
255
  )
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=line-too-long
4
4
  # flake8: noqa: E501
5
- """Common utils for the final generatio."""
5
+ """Common utils for the final flow generation."""
6
6
 
7
7
  from waldiez.models import Waldiez
8
8
 
@@ -120,8 +120,8 @@ def main_doc_string() -> str:
120
120
 
121
121
  Raises
122
122
  ------
123
- RuntimeError
124
- If the chat session fails.
123
+ SystemExit
124
+ If the user interrupts the chat session.
125
125
  """'''
126
126
 
127
127
 
@@ -7,6 +7,7 @@ from typing import Optional
7
7
  from waldiez.exporting.core import ImportPosition
8
8
 
9
9
  BUILTIN_IMPORTS = [
10
+ "import asyncio",
10
11
  "import csv",
11
12
  "import importlib",
12
13
  "import json",
@@ -82,47 +82,13 @@ def get_start_logging(is_async: bool, for_notebook: bool) -> str:
82
82
  '''
83
83
 
84
84
 
85
- # pylint: disable=differing-param-doc,differing-type-doc
86
- # noinspection PyUnresolvedReferences
87
85
  def get_sync_sqlite_out() -> str:
88
- r"""Get the sqlite to csv and json conversion code string.
86
+ """Get the sqlite to csv and json conversion code string.
89
87
 
90
88
  Returns
91
89
  -------
92
90
  str
93
91
  The sqlite to csv and json conversion code string.
94
-
95
- Example
96
- -------
97
- ```python
98
- >>> get_sqlite_outputs()
99
- def get_sqlite_out(dbname: str, table: str, csv_file: str) -> None:
100
- \"\"\"Convert a sqlite table to csv and json files.
101
-
102
- Parameters
103
- ----------
104
- dbname : str
105
- The sqlite database name.
106
- table : str
107
- The table name.
108
- csv_file : str
109
- The csv file name.
110
- \"\"\"
111
- conn = sqlite3.connect(dbname)
112
- query = f"SELECT * FROM {table}" # nosec
113
- cursor = conn.execute(query)
114
- rows = cursor.fetchall()
115
- column_names = [description[0] for description in cursor.description]
116
- data = [dict(zip(column_names, row)) for row in rows]
117
- conn.close()
118
- with open(csv_file, "w", newline="", encoding="utf-8") as file:
119
- csv_writer = csv.DictWriter(file, fieldnames=column_names)
120
- csv_writer.writeheader()
121
- csv_writer.writerows(data)
122
- json_file = csv_file.replace(".csv", ".json")
123
- with open(json_file, "w", encoding="utf-8") as file:
124
- json.dump(data, file, indent=4, ensure_ascii=False)
125
- ```
126
92
  """
127
93
  content = "\n\n"
128
94
  content += (
@@ -148,7 +114,8 @@ def get_sync_sqlite_out() -> str:
148
114
  content += " rows = cursor.fetchall()\n"
149
115
  content += " column_names = [description[0] for description "
150
116
  content += "in cursor.description]\n"
151
- content += " data = [dict(zip(column_names, row)) for row in rows]\n"
117
+ # pylint: disable=line-too-long
118
+ content += " data = [dict(zip(column_names, row, strict=True)) for row in rows]\n"
152
119
  content += " conn.close()\n"
153
120
  content += (
154
121
  ' with open(csv_file, "w", newline="", encoding="utf-8") as file:\n'
@@ -165,52 +132,14 @@ def get_sync_sqlite_out() -> str:
165
132
  return content
166
133
 
167
134
 
168
- # pylint: disable=differing-param-doc,differing-type-doc,line-too-long
169
- # noinspection PyUnresolvedReferences
135
+ # pylint: disable=line-too-long
170
136
  def get_async_sqlite_out() -> str:
171
- r"""Get the sqlite to csv and json conversion code string.
137
+ """Get the sqlite to csv and json conversion code string.
172
138
 
173
139
  Returns
174
140
  -------
175
141
  str
176
142
  The sqlite to csv and json conversion code string.
177
-
178
- Example
179
- -------
180
- ```python
181
- >>> get_sqlite_outputs()
182
- async def get_sqlite_out(dbname: str, table: str, csv_file: str) -> None:
183
- \"\"\"Convert a sqlite table to csv and json files.
184
-
185
- Parameters
186
- ----------
187
- dbname : str
188
- The sqlite database name.
189
- table : str
190
- The table name.
191
- csv_file : str
192
- The csv file name.
193
- \"\"\"
194
- conn = await aiosqlite.connect(dbname)
195
- query = f"SELECT * FROM {table}" # nosec
196
- try:
197
- cursor = await conn.execute(query)
198
- except BaseException: # pylint: disable=broad-exception-caught
199
- await conn.close()
200
- return
201
- rows = await cursor.fetchall()
202
- column_names = [description[0] for description in cursor.description]
203
- data = [dict(zip(column_names, row)) for row in rows]
204
- await cursor.close()
205
- await conn.close()
206
- async with aiofiles.open(csv_file, "w", newline="", encoding="utf-8") as file:
207
- csv_writer = csv.DictWriter(file, fieldnames=column_names)
208
- csv_writer.writeheader()
209
- csv_writer.writerows(data)
210
- json_file = csv_file.replace(".csv", ".json")
211
- async with aiofiles.open(json_file, "w", encoding="utf-8") as file:
212
- await file.write(json.dumps(data, indent=4, ensure_ascii=False)
213
- ```
214
143
  """
215
144
  # fmt: off
216
145
  content = "\n\n"
@@ -235,7 +164,7 @@ def get_async_sqlite_out() -> str:
235
164
  content += " rows = await cursor.fetchall()\n"
236
165
  content += " column_names = [description[0] for description "
237
166
  content += "in cursor.description]\n"
238
- content += " data = [dict(zip(column_names, row)) for row in rows]\n"
167
+ content += " data = [dict(zip(column_names, row, strict=True)) for row in rows]\n"
239
168
  content += " await cursor.close()\n"
240
169
  content += " await conn.close()\n"
241
170
  content += ' async with aiofiles.open(csv_file, "w", newline="", encoding="utf-8") as file:\n'
@@ -369,9 +298,7 @@ def get_stop_logging(is_async: bool, tabs: int = 0) -> str:
369
298
  content += "def stop_logging() -> None:\n"
370
299
  content += ' """Stop logging."""\n'
371
300
  if is_async:
372
- content += f"{tab} # pylint: disable=import-outside-toplevel\n"
373
- content += f"{tab} from asyncer import asyncify\n\n"
374
- content += f"{tab} await asyncify(runtime_logging.stop)()\n"
301
+ content += f"{tab} await asyncio.to_thread(runtime_logging.stop)\n"
375
302
  else:
376
303
  content += f"{tab} runtime_logging.stop()\n"
377
304
  content += get_sqlite_out_call(tabs + 1, is_async)
@@ -32,6 +32,7 @@ class ToolsExporter(Exporter[ToolExtras]):
32
32
  agent_names: dict[str, str],
33
33
  tools: list[WaldiezTool],
34
34
  tool_names: dict[str, str],
35
+ is_async: bool,
35
36
  output_dir: str | Path | None = None,
36
37
  context: ExporterContext | None = None,
37
38
  **kwargs: Any,
@@ -50,6 +51,8 @@ class ToolsExporter(Exporter[ToolExtras]):
50
51
  The tools to export.
51
52
  tool_names : dict[str, str]
52
53
  Mapping of tool IDs to names.
54
+ is_async : bool
55
+ Whether the flow is asynchronous.
53
56
  output_dir : str | Path | None
54
57
  Output directory for generated files, by default None
55
58
  context : ExporterContext | None
@@ -64,6 +67,7 @@ class ToolsExporter(Exporter[ToolExtras]):
64
67
  self.agent_names = agent_names
65
68
  self.tools = tools
66
69
  self.tool_names = tool_names
70
+ self.is_async = is_async
67
71
  self.output_dir = Path(output_dir) if output_dir else None
68
72
 
69
73
  # Initialize extras with processed tool content
@@ -83,6 +87,7 @@ class ToolsExporter(Exporter[ToolExtras]):
83
87
  flow_name=self.flow_name,
84
88
  tools=self.tools,
85
89
  tool_names=self.tool_names,
90
+ is_async=self.is_async,
86
91
  output_dir=self.output_dir,
87
92
  )
88
93
 
@@ -16,6 +16,7 @@ def create_tools_exporter(
16
16
  agent_names: dict[str, str],
17
17
  tools: list[WaldiezTool],
18
18
  tool_names: dict[str, str],
19
+ is_async: bool,
19
20
  output_dir: str | Path | None = None,
20
21
  context: ExporterContext | None = None,
21
22
  ) -> ToolsExporter:
@@ -33,6 +34,8 @@ def create_tools_exporter(
33
34
  The tools to export.
34
35
  tool_names : dict[str, str]
35
36
  Mapping of tool IDs to names.
37
+ is_async : bool
38
+ Whether the flow is asynchronous.
36
39
  output_dir : str | Path | None, optional
37
40
  Output directory for generated files, by default None
38
41
  context : ExporterContext | None, optional
@@ -52,5 +55,6 @@ def create_tools_exporter(
52
55
  tools=tools,
53
56
  tool_names=tool_names,
54
57
  output_dir=output_dir,
58
+ is_async=is_async,
55
59
  context=context,
56
60
  )
@@ -33,6 +33,7 @@ class ToolProcessor:
33
33
  flow_name: str,
34
34
  tools: list[WaldiezTool],
35
35
  tool_names: dict[str, str],
36
+ is_async: bool,
36
37
  output_dir: Path | None = None,
37
38
  ):
38
39
  """Initialize the tool processor.
@@ -52,6 +53,7 @@ class ToolProcessor:
52
53
  self.tools = tools
53
54
  self.tool_names = tool_names
54
55
  self.output_dir = output_dir
56
+ self.is_async = is_async
55
57
 
56
58
  def process(self) -> ToolProcessingResult:
57
59
  """Process all tools and return consolidated result.
@@ -94,7 +96,9 @@ class ToolProcessor:
94
96
  The result to add processed content to.
95
97
  """
96
98
  # Get tool content
97
- tool_content = tool.get_content()
99
+ tool_content = tool.get_content(
100
+ runtime_kwargs={"is_async": self.is_async}
101
+ )
98
102
  if tool_content: # pragma: no branch
99
103
  # Add interop conversion if needed
100
104
  if tool.is_interop:
waldiez/io/__init__.py CHANGED
@@ -30,7 +30,7 @@ from .models import (
30
30
  VideoMediaContent,
31
31
  )
32
32
  from .structured import StructuredIOStream
33
- from .utils import MediaType, MessageType
33
+ from .utils import DEBUG_INPUT_PROMPT, START_CHAT_PROMPT, MediaType, MessageType
34
34
 
35
35
  try:
36
36
  from .redis import RedisIOStream # type: ignore[no-redef,unused-ignore]
@@ -125,4 +125,6 @@ __all__ = [
125
125
  "AudioContent",
126
126
  "VideoMediaContent",
127
127
  "VideoContent",
128
+ "DEBUG_INPUT_PROMPT",
129
+ "START_CHAT_PROMPT",
128
130
  ]
waldiez/io/_ws.py CHANGED
@@ -2,9 +2,12 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # flake8: noqa: E501
4
4
  # pylint: disable=line-too-long
5
+ # pyright: reportUnknownMemberType=false,reportUnknownParameterType=false
6
+ # pyright: reportUnknownVariableType=false,reportUnknownArgumentType=false
5
7
  """WebSocket IOStream implementation for AsyncIO."""
6
8
 
7
9
  import asyncio
10
+ import logging
8
11
  from typing import Any, Protocol
9
12
 
10
13
  HAS_WS_LIB = False
@@ -13,7 +16,7 @@ try:
13
16
  from starlette.websockets import WebSocket # type: ignore[unused-ignore, unused-import, import-not-found, import-untyped] # noqa
14
17
 
15
18
  HAS_WS_LIB = True # pyright: ignore
16
- except ImportError:
19
+ except ImportError: # pragma: no cover
17
20
  pass
18
21
 
19
22
  try:
@@ -68,6 +71,7 @@ class WebSocketsAdapter:
68
71
  The websockets library connection.
69
72
  """
70
73
  self.websocket = websocket
74
+ self.log = logging.getLogger(__name__)
71
75
 
72
76
  async def send_message(self, message: str) -> None:
73
77
  """Send a message using websockets library.
@@ -100,11 +104,14 @@ class WebSocketsAdapter:
100
104
  )
101
105
  if isinstance(response, bytes):
102
106
  return response.decode("utf-8")
107
+ # noinspection PyUnreachableCode
103
108
  return response if isinstance(response, str) else str(response)
104
109
  except asyncio.TimeoutError:
105
110
  return ""
106
- except BaseException: # pylint: disable=broad-exception-caught
107
- # Handle other exceptions that may occur during receive
111
+ except BaseException as exc: # pylint: disable=broad-exception-caught
112
+ self.log.error(
113
+ "WebsocketsAdapter: Error receiving message: %s", exc
114
+ ) # pragma: no cover
108
115
  return ""
109
116
 
110
117
 
@@ -120,6 +127,7 @@ class StarletteAdapter:
120
127
  The Starlette/FastAPI WebSocket connection.
121
128
  """
122
129
  self.websocket = websocket
130
+ self.log = logging.getLogger(__name__)
123
131
 
124
132
  async def send_message(self, message: str) -> None:
125
133
  """Send a message using Starlette WebSocket.
@@ -151,8 +159,10 @@ class StarletteAdapter:
151
159
  )
152
160
  except asyncio.TimeoutError:
153
161
  return ""
154
- except BaseException: # pylint: disable=broad-exception-caught
155
- # Handle other exceptions that may occur during receive
162
+ except BaseException as exc: # pylint: disable=broad-exception-caught
163
+ self.log.error(
164
+ "StarletteAdapter: Error receiving message: %s", exc
165
+ ) # pragma: no cover
156
166
  return ""
157
167
 
158
168
 
@@ -61,6 +61,7 @@ class ImageUrlMediaContent(BaseModel):
61
61
  type: Literal["image_url"] = "image_url"
62
62
  image_url: ImageContent
63
63
 
64
+ # noinspection DuplicatedCode
64
65
  def to_string(
65
66
  self,
66
67
  uploads_root: Path | None,