waldiez 0.6.0__py3-none-any.whl → 0.6.1__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 (188) hide show
  1. waldiez/__init__.py +1 -1
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +18 -7
  4. waldiez/cli_extras/jupyter.py +3 -0
  5. waldiez/cli_extras/runner.py +3 -1
  6. waldiez/cli_extras/studio.py +3 -1
  7. waldiez/exporter.py +9 -3
  8. waldiez/exporting/agent/exporter.py +9 -10
  9. waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
  10. waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
  11. waldiez/exporting/agent/extras/group_manager_agent_extas.py +34 -23
  12. waldiez/exporting/agent/extras/group_member_extras.py +6 -5
  13. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -1
  14. waldiez/exporting/agent/extras/handoffs/available.py +1 -1
  15. waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
  16. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -1
  17. waldiez/exporting/agent/extras/handoffs/target.py +6 -4
  18. waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
  19. waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
  20. waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
  21. waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
  22. waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
  23. waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
  24. waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
  25. waldiez/exporting/chats/exporter.py +4 -4
  26. waldiez/exporting/chats/processor.py +1 -2
  27. waldiez/exporting/chats/utils/common.py +89 -48
  28. waldiez/exporting/chats/utils/group.py +9 -9
  29. waldiez/exporting/chats/utils/nested.py +7 -7
  30. waldiez/exporting/chats/utils/sequential.py +1 -1
  31. waldiez/exporting/chats/utils/single.py +2 -2
  32. waldiez/exporting/core/content.py +7 -7
  33. waldiez/exporting/core/context.py +5 -3
  34. waldiez/exporting/core/exporter.py +5 -3
  35. waldiez/exporting/core/exporters.py +2 -2
  36. waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
  37. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
  38. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
  39. waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
  40. waldiez/exporting/core/extras/base.py +7 -5
  41. waldiez/exporting/core/extras/flow_extras.py +4 -5
  42. waldiez/exporting/core/extras/model_extras.py +2 -2
  43. waldiez/exporting/core/extras/path_resolver.py +1 -2
  44. waldiez/exporting/core/extras/serializer.py +2 -2
  45. waldiez/exporting/core/protocols.py +6 -5
  46. waldiez/exporting/core/result.py +25 -28
  47. waldiez/exporting/core/types.py +10 -10
  48. waldiez/exporting/core/utils/llm_config.py +2 -2
  49. waldiez/exporting/core/validation.py +10 -11
  50. waldiez/exporting/flow/execution_generator.py +98 -10
  51. waldiez/exporting/flow/exporter.py +2 -2
  52. waldiez/exporting/flow/factory.py +2 -2
  53. waldiez/exporting/flow/file_generator.py +4 -2
  54. waldiez/exporting/flow/merger.py +5 -3
  55. waldiez/exporting/flow/orchestrator.py +72 -2
  56. waldiez/exporting/flow/utils/common.py +5 -5
  57. waldiez/exporting/flow/utils/importing.py +6 -7
  58. waldiez/exporting/flow/utils/linting.py +25 -9
  59. waldiez/exporting/flow/utils/logging.py +2 -2
  60. waldiez/exporting/models/exporter.py +8 -8
  61. waldiez/exporting/models/processor.py +5 -5
  62. waldiez/exporting/tools/exporter.py +2 -2
  63. waldiez/exporting/tools/processor.py +7 -4
  64. waldiez/io/__init__.py +8 -4
  65. waldiez/io/_ws.py +10 -6
  66. waldiez/io/models/constants.py +10 -10
  67. waldiez/io/models/content/audio.py +1 -0
  68. waldiez/io/models/content/base.py +20 -18
  69. waldiez/io/models/content/file.py +1 -0
  70. waldiez/io/models/content/image.py +1 -0
  71. waldiez/io/models/content/text.py +1 -0
  72. waldiez/io/models/content/video.py +1 -0
  73. waldiez/io/models/user_input.py +10 -5
  74. waldiez/io/models/user_response.py +17 -16
  75. waldiez/io/mqtt.py +18 -31
  76. waldiez/io/redis.py +18 -22
  77. waldiez/io/structured.py +52 -53
  78. waldiez/io/utils.py +3 -0
  79. waldiez/io/ws.py +5 -1
  80. waldiez/logger.py +16 -3
  81. waldiez/models/agents/__init__.py +3 -0
  82. waldiez/models/agents/agent/agent.py +23 -16
  83. waldiez/models/agents/agent/agent_data.py +25 -22
  84. waldiez/models/agents/agent/code_execution.py +9 -11
  85. waldiez/models/agents/agent/termination_message.py +10 -12
  86. waldiez/models/agents/agent/update_system_message.py +2 -4
  87. waldiez/models/agents/agents.py +8 -8
  88. waldiez/models/agents/assistant/assistant.py +6 -3
  89. waldiez/models/agents/assistant/assistant_data.py +2 -2
  90. waldiez/models/agents/captain/captain_agent.py +7 -4
  91. waldiez/models/agents/captain/captain_agent_data.py +5 -7
  92. waldiez/models/agents/doc_agent/doc_agent.py +7 -4
  93. waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
  94. waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
  95. waldiez/models/agents/extra_requirements.py +3 -3
  96. waldiez/models/agents/group_manager/group_manager.py +12 -7
  97. waldiez/models/agents/group_manager/group_manager_data.py +13 -12
  98. waldiez/models/agents/group_manager/speakers.py +17 -19
  99. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
  100. waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
  101. waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
  102. waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
  103. waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
  104. waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
  105. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
  106. waldiez/models/agents/user_proxy/user_proxy.py +6 -3
  107. waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
  108. waldiez/models/chat/chat.py +27 -20
  109. waldiez/models/chat/chat_data.py +22 -19
  110. waldiez/models/chat/chat_message.py +9 -9
  111. waldiez/models/chat/chat_nested.py +9 -9
  112. waldiez/models/chat/chat_summary.py +6 -6
  113. waldiez/models/common/__init__.py +2 -0
  114. waldiez/models/common/ag2_version.py +2 -0
  115. waldiez/models/common/dict_utils.py +8 -6
  116. waldiez/models/common/handoff.py +18 -17
  117. waldiez/models/common/method_utils.py +7 -7
  118. waldiez/models/common/naming.py +49 -0
  119. waldiez/models/flow/flow.py +11 -6
  120. waldiez/models/flow/flow_data.py +23 -17
  121. waldiez/models/flow/info.py +3 -3
  122. waldiez/models/flow/naming.py +2 -1
  123. waldiez/models/model/_aws.py +11 -13
  124. waldiez/models/model/_llm.py +5 -0
  125. waldiez/models/model/_price.py +2 -4
  126. waldiez/models/model/extra_requirements.py +1 -3
  127. waldiez/models/model/model.py +2 -2
  128. waldiez/models/model/model_data.py +21 -21
  129. waldiez/models/tool/extra_requirements.py +2 -4
  130. waldiez/models/tool/predefined/_duckduckgo.py +1 -0
  131. waldiez/models/tool/predefined/_email.py +1 -0
  132. waldiez/models/tool/predefined/_google.py +1 -0
  133. waldiez/models/tool/predefined/_perplexity.py +1 -0
  134. waldiez/models/tool/predefined/_searxng.py +1 -0
  135. waldiez/models/tool/predefined/_tavily.py +1 -0
  136. waldiez/models/tool/predefined/_wikipedia.py +1 -0
  137. waldiez/models/tool/predefined/_youtube.py +1 -0
  138. waldiez/models/tool/tool.py +8 -5
  139. waldiez/models/tool/tool_data.py +2 -2
  140. waldiez/models/waldiez.py +152 -4
  141. waldiez/runner.py +11 -5
  142. waldiez/running/async_utils.py +192 -0
  143. waldiez/running/base_runner.py +117 -264
  144. waldiez/running/dir_utils.py +52 -0
  145. waldiez/running/environment.py +10 -44
  146. waldiez/running/events_mixin.py +252 -0
  147. waldiez/running/exceptions.py +20 -0
  148. waldiez/running/gen_seq_diagram.py +18 -15
  149. waldiez/running/io_utils.py +216 -0
  150. waldiez/running/protocol.py +11 -5
  151. waldiez/running/requirements_mixin.py +65 -0
  152. waldiez/running/results_mixin.py +926 -0
  153. waldiez/running/standard_runner.py +22 -25
  154. waldiez/running/step_by_step/breakpoints_mixin.py +192 -60
  155. waldiez/running/step_by_step/command_handler.py +3 -0
  156. waldiez/running/step_by_step/events_processor.py +194 -14
  157. waldiez/running/step_by_step/step_by_step_models.py +110 -43
  158. waldiez/running/step_by_step/step_by_step_runner.py +107 -57
  159. waldiez/running/subprocess_runner/__base__.py +9 -1
  160. waldiez/running/subprocess_runner/_async_runner.py +5 -3
  161. waldiez/running/subprocess_runner/_sync_runner.py +6 -2
  162. waldiez/running/subprocess_runner/runner.py +39 -23
  163. waldiez/running/timeline_processor.py +1 -1
  164. waldiez/utils/__init__.py +2 -0
  165. waldiez/utils/conflict_checker.py +4 -4
  166. waldiez/utils/python_manager.py +415 -0
  167. waldiez/ws/_file_handler.py +18 -18
  168. waldiez/ws/_mock.py +2 -1
  169. waldiez/ws/cli.py +36 -12
  170. waldiez/ws/client_manager.py +35 -27
  171. waldiez/ws/errors.py +3 -0
  172. waldiez/ws/models.py +43 -52
  173. waldiez/ws/reloader.py +12 -4
  174. waldiez/ws/server.py +85 -55
  175. waldiez/ws/session_manager.py +8 -9
  176. waldiez/ws/session_stats.py +1 -1
  177. waldiez/ws/utils.py +4 -1
  178. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/METADATA +82 -93
  179. waldiez-0.6.1.dist-info/RECORD +254 -0
  180. waldiez/running/post_run.py +0 -186
  181. waldiez/running/pre_run.py +0 -281
  182. waldiez/running/run_results.py +0 -14
  183. waldiez/running/utils.py +0 -625
  184. waldiez-0.6.0.dist-info/RECORD +0 -251
  185. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
  186. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
  187. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
  188. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
@@ -18,10 +18,12 @@ class ToolProcessingResult:
18
18
  """Result from processing tools."""
19
19
 
20
20
  content: str = ""
21
- builtin_imports: list[str] = field(default_factory=list[str])
22
- third_party_imports: list[str] = field(default_factory=list[str])
21
+ builtin_imports: list[str] = field(default_factory=list)
22
+ third_party_imports: list[str] = field(
23
+ default_factory=list,
24
+ )
23
25
  environment_variables: list[EnvironmentVariable] = field(
24
- default_factory=list[EnvironmentVariable]
26
+ default_factory=list,
25
27
  )
26
28
 
27
29
 
@@ -198,10 +200,11 @@ ag2_{tool_name} = ag2_{tool_name}_interop.convert_tool(
198
200
  for key, value in tool.secrets.items():
199
201
  # f.write(f'os.environ["{key}"] = "{value}"\n')
200
202
  # check first if the key already exists in os.environ
201
- f.write(
203
+ to_write = (
202
204
  f'os.environ["{key}"] = '
203
205
  f'os.environ.get("{key}", "{value}")\n'
204
206
  )
207
+ f.write(to_write)
205
208
  except Exception as exc: # pragma: no cover
206
209
  raise ExporterContentError(
207
210
  f"Failed to write secrets file for tool '{tool_name}': {exc}"
waldiez/io/__init__.py CHANGED
@@ -2,7 +2,8 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
 
4
4
  # flake8: noqa: F401
5
- # pylint: disable=unused-import,too-few-public-methods
5
+ # pylint: disable=unused-import,unused-argument,too-few-public-methods
6
+ # pyright: reportAssignmentType=false
6
7
 
7
8
  """IOSTream extensions.
8
9
 
@@ -49,10 +50,11 @@ except ImportError: # pragma: no cover
49
50
  kwargs : dict
50
51
  Keyword arguments.
51
52
  """
52
- raise ImportError(
53
+ msg = (
53
54
  "RedisIOStream is not available. "
54
55
  "Please install the required package."
55
56
  )
57
+ raise ImportError(msg)
56
58
 
57
59
 
58
60
  try:
@@ -74,10 +76,11 @@ except ImportError: # pragma: no cover
74
76
  kwargs : dict
75
77
  Keyword arguments.
76
78
  """
77
- raise ImportError(
79
+ msg = (
78
80
  "AsyncWebsocketsIOStream is not available. "
79
81
  "Please install the required package."
80
82
  )
83
+ raise ImportError(msg)
81
84
 
82
85
 
83
86
  try:
@@ -97,10 +100,11 @@ except ImportError: # pragma: no cover
97
100
  kwargs : dict
98
101
  Keyword arguments.
99
102
  """
100
- raise ImportError(
103
+ msg = (
101
104
  "MqttIOStream is not available. "
102
105
  "Please install the required package."
103
106
  )
107
+ raise ImportError(msg)
104
108
 
105
109
 
106
110
  __all__ = [
waldiez/io/_ws.py CHANGED
@@ -1,9 +1,12 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # flake8: noqa: E501
4
- # pylint: disable=line-too-long
4
+ # pylint: disable=line-too-long,invalid-name
5
5
  # pyright: reportUnknownMemberType=false,reportUnknownParameterType=false
6
6
  # pyright: reportUnknownVariableType=false,reportUnknownArgumentType=false
7
+ # pyright: reportConstantRedefinition=false,reportUnnecessaryIsInstance=false
8
+ # pyright: reportMissingImports=false
9
+
7
10
  """WebSocket IOStream implementation for AsyncIO."""
8
11
 
9
12
  import asyncio
@@ -15,14 +18,14 @@ HAS_WS_LIB = False
15
18
  try:
16
19
  from starlette.websockets import WebSocket # type: ignore[unused-ignore, unused-import, import-not-found, import-untyped] # noqa
17
20
 
18
- HAS_WS_LIB = True # pyright: ignore
21
+ HAS_WS_LIB = True
19
22
  except ImportError: # pragma: no cover
20
23
  pass
21
24
 
22
25
  try:
23
26
  import websockets # type: ignore[unused-ignore, unused-import, import-not-found, import-untyped] # noqa
24
27
 
25
- HAS_WS_LIB = True # pyright: ignore
28
+ HAS_WS_LIB = True
26
29
  except ImportError: # pragma: no cover
27
30
  pass
28
31
 
@@ -42,7 +45,7 @@ class WebSocketConnection(Protocol):
42
45
  async def receive_message(
43
46
  self,
44
47
  timeout: float = 120,
45
- ) -> str: # pyright: ignore
48
+ ) -> str: # pyright: ignore[reportReturnType]
46
49
  """Receive a message from the WebSocket connection.
47
50
 
48
51
  Parameters
@@ -103,7 +106,7 @@ class WebSocketsAdapter:
103
106
  self.websocket.recv(), timeout=timeout
104
107
  )
105
108
  if isinstance(response, bytes):
106
- return response.decode("utf-8")
109
+ return response.decode("utf-8", errors="replace")
107
110
  # noinspection PyUnreachableCode
108
111
  return response if isinstance(response, str) else str(response)
109
112
  except asyncio.TimeoutError:
@@ -192,12 +195,13 @@ def create_websocket_adapter(websocket: Any) -> WebSocketConnection:
192
195
  if hasattr(websocket, "send") and hasattr(websocket, "recv"):
193
196
  return WebSocketsAdapter(websocket)
194
197
 
195
- raise ValueError(
198
+ msg = (
196
199
  "Unsupported WebSocket type. "
197
200
  "Must be either websockets.ServerConnection "
198
201
  "or starlette.websockets.WebSocket. "
199
202
  f"Received: {type(websocket)}"
200
203
  )
204
+ raise ValueError(msg)
201
205
 
202
206
 
203
207
  def is_websocket_available() -> bool:
@@ -3,7 +3,7 @@
3
3
 
4
4
  """Constants and type mappings for content models."""
5
5
 
6
- from typing import Type, TypedDict, Union
6
+ from typing import TypedDict
7
7
 
8
8
  from typing_extensions import Literal
9
9
 
@@ -13,14 +13,14 @@ from .content.image import ImageMediaContent, ImageUrlMediaContent
13
13
  from .content.text import TextMediaContent
14
14
  from .content.video import VideoMediaContent
15
15
 
16
- MediaContent = Union[
17
- TextMediaContent,
18
- ImageMediaContent,
19
- ImageUrlMediaContent,
20
- VideoMediaContent,
21
- AudioMediaContent,
22
- FileMediaContent,
23
- ]
16
+ MediaContent = (
17
+ TextMediaContent
18
+ | ImageMediaContent
19
+ | ImageUrlMediaContent
20
+ | VideoMediaContent
21
+ | AudioMediaContent
22
+ | FileMediaContent
23
+ )
24
24
 
25
25
  ContentTypeKey = Literal["text", "image", "image_url", "video", "audio", "file"]
26
26
  """Possible content types for the mapping."""
@@ -40,7 +40,7 @@ class ContentMappingEntry(TypedDict):
40
40
  """
41
41
 
42
42
  fields: list[str]
43
- cls: Type[MediaContent]
43
+ cls: type[MediaContent]
44
44
  required_field: str
45
45
 
46
46
 
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=unused-argument
4
+ # pyright: reportUnusedParameter=false
4
5
  """Audio media content models."""
5
6
 
6
7
  from pathlib import Path
@@ -1,8 +1,10 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+
3
5
  """Base content models for media types."""
4
6
 
5
- from typing import Any, Optional
7
+ from typing import Any
6
8
 
7
9
  from pydantic import BaseModel
8
10
 
@@ -10,36 +12,36 @@ from pydantic import BaseModel
10
12
  class ImageContent(BaseModel):
11
13
  """Image content model."""
12
14
 
13
- url: Optional[str] = None
14
- file: Optional[Any] = None # File type not directly mappable in Python
15
- alt: Optional[str] = None
15
+ url: str | None = None
16
+ file: Any | None = None # File type not directly mappable in Python
17
+ alt: str | None = None
16
18
 
17
19
 
18
20
  class VideoContent(BaseModel):
19
21
  """Video content model."""
20
22
 
21
- url: Optional[str] = None
22
- file: Optional[Any] = None
23
- duration: Optional[int] = None
24
- thumbnailUrl: Optional[str] = None
25
- mimeType: Optional[str] = None
23
+ url: str | None = None
24
+ file: Any | None = None
25
+ duration: int | None = None
26
+ thumbnailUrl: str | None = None
27
+ mimeType: str | None = None
26
28
 
27
29
 
28
30
  class AudioContent(BaseModel):
29
31
  """Audio content model."""
30
32
 
31
- url: Optional[str] = None
32
- file: Optional[Any] = None
33
- duration: Optional[int] = None
34
- transcript: Optional[str] = None
33
+ url: str | None = None
34
+ file: Any | None = None
35
+ duration: int | None = None
36
+ transcript: str | None = None
35
37
 
36
38
 
37
39
  class FileContent(BaseModel):
38
40
  """File content model."""
39
41
 
40
- url: Optional[str] = None
41
- file: Optional[Any] = None
42
+ url: str | None = None
43
+ file: Any | None = None
42
44
  name: str
43
- size: Optional[int] = None
44
- type: Optional[str] = None
45
- previewUrl: Optional[str] = None
45
+ size: int | None = None
46
+ type: str | None = None
47
+ previewUrl: str | None = None
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=unused-argument
4
+ # pyright: reportUnusedParameter=false
4
5
  """File media content models."""
5
6
 
6
7
  from pathlib import Path
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=unused-argument
4
+ # pyright: reportUnusedParameter=false
4
5
  """Image media content models."""
5
6
 
6
7
  from pathlib import Path
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=unused-argument
4
+ # pyright: reportUnusedParameter=false
4
5
  """Text media content models."""
5
6
 
6
7
  from pathlib import Path
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=unused-argument
4
+ # pyright: reportUnusedParameter=false
4
5
  """Video and audio media content models."""
5
6
 
6
7
  from pathlib import Path
@@ -1,12 +1,16 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
 
4
+ # pyright: reportUnnecessaryIsInstance=false,reportUnknownVariableType=false
5
+ # pyright: reportUnknownArgumentType=false,reportArgumentType=false
6
+ # pyright: reportReturnType=false
7
+
4
8
  """User input data models and validation."""
5
9
 
6
10
  import json
7
11
  import os
8
12
  from pathlib import Path
9
- from typing import Any, Union
13
+ from typing import Any
10
14
  from urllib.parse import urlparse
11
15
 
12
16
  from pydantic import BaseModel, Field, field_validator
@@ -31,7 +35,7 @@ class UserInputData(BaseModel):
31
35
  """User's input data model."""
32
36
 
33
37
  content: Annotated[
34
- Union[MediaContent, list[MediaContent]],
38
+ MediaContent | list[MediaContent],
35
39
  Field(
36
40
  description="The content of the input data.",
37
41
  title="Content",
@@ -68,7 +72,7 @@ class UserInputData(BaseModel):
68
72
  if isinstance(self.content, list):
69
73
  return " ".join(
70
74
  [
71
- item.to_string(uploads_root, base_name) # pyright: ignore
75
+ item.to_string(uploads_root, base_name)
72
76
  for item in self.content
73
77
  ]
74
78
  )
@@ -161,10 +165,11 @@ class UserInputData(BaseModel):
161
165
  **{mapping["required_field"]: converted} # type: ignore
162
166
  )
163
167
 
164
- raise ValueError(
168
+ msg = (
165
169
  "Missing required field for content type "
166
170
  f"'{content_type}' in: {value}"
167
171
  )
172
+ raise ValueError(msg)
168
173
 
169
174
  @staticmethod
170
175
  def _convert_simple_content(
@@ -226,7 +231,7 @@ class UserInputData(BaseModel):
226
231
 
227
232
  # If it's a dictionary, check if it has a 'type' field
228
233
  if isinstance(v, dict):
229
- return cls.content_from_dict(v) # pyright: ignore
234
+ return cls.content_from_dict(v)
230
235
 
231
236
  # If it's a list
232
237
  if isinstance(v, list):
@@ -1,10 +1,14 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ # pyright: reportUnnecessaryIsInstance=false,reportUnknownVariableType=false
5
+ # pyright: reportUnknownArgumentType=false,reportArgumentType=false
6
+
3
7
  """User response model and validation."""
4
8
 
5
9
  import json
6
10
  from pathlib import Path
7
- from typing import Any, Callable, Optional, Union
11
+ from typing import Any, Callable
8
12
 
9
13
  from pydantic import ValidationError, field_validator
10
14
 
@@ -26,7 +30,7 @@ class UserResponse(StructuredBase):
26
30
  @classmethod
27
31
  def validate_data(
28
32
  cls, value: Any
29
- ) -> Optional[Union[str, UserInputData, list[UserInputData]]]:
33
+ ) -> str | UserInputData | list[UserInputData] | None:
30
34
  """Validate the data field in UserResponse.
31
35
 
32
36
  Parameters
@@ -51,16 +55,16 @@ class UserResponse(StructuredBase):
51
55
 
52
56
  handlers: dict[
53
57
  type,
54
- Callable[[Any], Union[str, UserInputData, list[UserInputData]]],
58
+ Callable[[Any], str | UserInputData | list[UserInputData]],
55
59
  ] = {
56
60
  str: cls._handle_string,
57
61
  dict: cls._handle_dict,
58
62
  list: cls._handle_list,
59
63
  }
60
64
 
61
- value_type = type(value) # pyright: ignore
65
+ value_type = type(value)
62
66
  handler = handlers.get(
63
- value_type, # pyright: ignore
67
+ value_type,
64
68
  cls._handle_default,
65
69
  )
66
70
  result = handler(value)
@@ -71,16 +75,13 @@ class UserResponse(StructuredBase):
71
75
  """Check if value is already a valid type."""
72
76
  return isinstance(value, UserInputData) or (
73
77
  isinstance(value, list)
74
- and all(
75
- isinstance(item, UserInputData)
76
- for item in value # pyright: ignore
77
- )
78
+ and all(isinstance(item, UserInputData) for item in value)
78
79
  )
79
80
 
80
81
  @classmethod
81
82
  def _handle_string(
82
83
  cls, value: str
83
- ) -> Union[str, UserInputData, list[UserInputData]]:
84
+ ) -> str | UserInputData | list[UserInputData]:
84
85
  """Handle string input.
85
86
 
86
87
  Parameters
@@ -97,9 +98,9 @@ class UserResponse(StructuredBase):
97
98
  try:
98
99
  parsed_value = json.loads(value)
99
100
  if isinstance(parsed_value, dict):
100
- return cls._handle_dict(parsed_value) # pyright: ignore
101
+ return cls._handle_dict(parsed_value)
101
102
  if isinstance(parsed_value, list):
102
- return cls._handle_list(parsed_value) # pyright: ignore
103
+ return cls._handle_list(parsed_value)
103
104
  return cls._create_text_input(str(parsed_value))
104
105
  except json.JSONDecodeError:
105
106
  return cls._create_text_input(value)
@@ -127,18 +128,18 @@ class UserResponse(StructuredBase):
127
128
  @classmethod
128
129
  def _handle_list(
129
130
  cls, value: list[Any]
130
- ) -> Union[UserInputData, list[UserInputData]]:
131
+ ) -> UserInputData | list[UserInputData]:
131
132
  result: list[UserInputData] = []
132
133
 
133
134
  for item in value:
134
135
  if isinstance(item, UserInputData):
135
136
  result.append(item)
136
137
  elif isinstance(item, dict):
137
- result.append(cls._handle_dict(item)) # pyright: ignore
138
+ result.append(cls._handle_dict(item))
138
139
  elif isinstance(item, str):
139
140
  result.append(cls._create_text_input(item))
140
141
  elif isinstance(item, list):
141
- nested_result = cls._handle_list(item) # pyright: ignore
142
+ nested_result = cls._handle_list(item)
142
143
  if isinstance(nested_result, list):
143
144
  result.extend(nested_result)
144
145
  else:
@@ -207,7 +208,7 @@ class UserResponse(StructuredBase):
207
208
  uploads_root=uploads_root, base_name=base_name
208
209
  ).strip()
209
210
  # we have probably returned sth till here
210
- if isinstance(self.data, str): # pyright: ignore # pragma: no cover
211
+ if isinstance(self.data, str): # pragma: no cover
211
212
  return self.data
212
213
  # noinspection PyUnreachableCode
213
214
  return ( # pragma: no cover
waldiez/io/mqtt.py CHANGED
@@ -7,6 +7,9 @@
7
7
  # pylint: disable=too-many-arguments,too-many-positional-arguments
8
8
  # pylint: disable=too-many-locals,too-many-instance-attributes
9
9
 
10
+ # pyright: reportMissingTypeStubs=false,reportUnknownMemberType=false
11
+ # pyright: reportUnusedParameter=false
12
+
10
13
  """An MQTT I/O stream for handling print and input messages."""
11
14
 
12
15
  import json
@@ -17,12 +20,7 @@ import uuid
17
20
  from pathlib import Path
18
21
  from threading import Event, Lock
19
22
  from types import TracebackType
20
- from typing import (
21
- Any,
22
- Callable,
23
- Optional,
24
- Type,
25
- )
23
+ from typing import Any, Callable
26
24
 
27
25
  try:
28
26
  from paho.mqtt import client as mqtt
@@ -44,7 +42,7 @@ from .models import (
44
42
  UserInputRequest,
45
43
  UserResponse,
46
44
  )
47
- from .utils import gen_id, now
45
+ from .utils import gen_id, get_message_dump, now
48
46
 
49
47
  LOG = logging.getLogger(__name__)
50
48
 
@@ -62,8 +60,8 @@ class MqttIOStream(IOStream):
62
60
  client: mqtt.Client
63
61
  task_id: str
64
62
  input_timeout: int
65
- on_input_request: Optional[Callable[[str, str, str], None]]
66
- on_input_received: Optional[Callable[[str, str], None]]
63
+ on_input_request: Callable[[str, str, str], None] | None
64
+ on_input_response: Callable[[str, str], None] | None
67
65
  max_retain_messages: int
68
66
  output_topic: str
69
67
  input_request_topic: str
@@ -87,8 +85,8 @@ class MqttIOStream(IOStream):
87
85
  input_timeout: int = 120,
88
86
  connect_timeout: int = 10,
89
87
  max_retain_messages: int = 1000,
90
- on_input_request: Optional[Callable[[str, str, str], None]] = None,
91
- on_input_response: Optional[Callable[[str, str], None]] = None,
88
+ on_input_request: Callable[[str, str, str], None] | None = None,
89
+ on_input_response: Callable[[str, str], None] | None = None,
92
90
  mqtt_client_kwargs: dict[str, Any] | None = None,
93
91
  uploads_root: Path | str | None = None,
94
92
  username: str | None = None,
@@ -173,9 +171,9 @@ class MqttIOStream(IOStream):
173
171
  # Set up TLS
174
172
  if use_tls:
175
173
  if ca_cert_path:
176
- self.client.tls_set(ca_cert_path) # pyright: ignore
174
+ self.client.tls_set(ca_cert_path)
177
175
  else: # pragma: no cover
178
- self.client.tls_set() # pyright: ignore
176
+ self.client.tls_set()
179
177
 
180
178
  # Set up callbacks
181
179
  self.client.on_connect = self._on_connect
@@ -320,11 +318,13 @@ class MqttIOStream(IOStream):
320
318
  LOG.debug(
321
319
  "Received message on topic %s: %s",
322
320
  msg.topic,
323
- msg.payload.decode(),
321
+ msg.payload.decode("utf-8", errors="replace"),
324
322
  )
325
323
 
326
324
  if msg.topic == self.input_response_topic: # pragma: no branch
327
- self._handle_input_response(msg.payload.decode())
325
+ self._handle_input_response(
326
+ msg.payload.decode("utf-8", errors="replace")
327
+ )
328
328
 
329
329
  except Exception as e: # pragma: no cover
330
330
  LOG.error("Error handling message: %s", e)
@@ -396,7 +396,7 @@ class MqttIOStream(IOStream):
396
396
 
397
397
  def __exit__(
398
398
  self,
399
- exc_type: Type[Exception] | None,
399
+ exc_type: type[Exception] | None,
400
400
  exc_value: Exception | None,
401
401
  traceback: TracebackType | None,
402
402
  ) -> None:
@@ -487,23 +487,10 @@ class MqttIOStream(IOStream):
487
487
  message : BaseEvent | BaseMessage
488
488
  The message or event to send.
489
489
  """
490
- try:
491
- message_dump = message.model_dump(mode="json")
492
- except Exception:
493
- try:
494
- message_dump = message.model_dump(
495
- serialize_as_any=True, mode="json", fallback=str
496
- )
497
- except Exception as e:
498
- message_dump = {
499
- "error": str(e),
500
- "type": message.__class__.__name__,
501
- }
502
-
490
+ message_dump = get_message_dump(message)
503
491
  message_type = message_dump.get("type", None)
504
492
  if not message_type: # pragma: no cover
505
493
  message_type = message.__class__.__name__
506
-
507
494
  self._print(
508
495
  {
509
496
  "type": message_type,
@@ -652,7 +639,7 @@ class MqttIOStream(IOStream):
652
639
  @staticmethod
653
640
  def _create_user_response(
654
641
  message_data: dict[str, Any],
655
- ) -> Optional["UserResponse"]:
642
+ ) -> UserResponse | None:
656
643
  """Create UserResponse object from validated data."""
657
644
  try:
658
645
  # Handle nested JSON in 'data' field