waldiez 0.2.2__py3-none-any.whl → 0.3.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 (138) hide show
  1. waldiez/__init__.py +2 -0
  2. waldiez/__main__.py +2 -0
  3. waldiez/_version.py +3 -1
  4. waldiez/cli.py +13 -3
  5. waldiez/cli_extras.py +4 -3
  6. waldiez/conflict_checker.py +4 -3
  7. waldiez/exporter.py +28 -105
  8. waldiez/exporting/__init__.py +8 -9
  9. waldiez/exporting/agent/__init__.py +7 -0
  10. waldiez/exporting/agent/agent_exporter.py +279 -0
  11. waldiez/exporting/agent/utils/__init__.py +23 -0
  12. waldiez/exporting/agent/utils/agent_class_name.py +34 -0
  13. waldiez/exporting/agent/utils/agent_imports.py +50 -0
  14. waldiez/exporting/{agents → agent/utils}/code_execution.py +9 -11
  15. waldiez/exporting/{agents → agent/utils}/group_manager.py +47 -35
  16. waldiez/exporting/{agents → agent/utils}/rag_user/__init__.py +2 -0
  17. waldiez/exporting/{agents → agent/utils}/rag_user/chroma_utils.py +22 -17
  18. waldiez/exporting/{agents → agent/utils}/rag_user/mongo_utils.py +14 -10
  19. waldiez/exporting/{agents → agent/utils}/rag_user/pgvector_utils.py +12 -8
  20. waldiez/exporting/{agents → agent/utils}/rag_user/qdrant_utils.py +11 -8
  21. waldiez/exporting/{agents → agent/utils}/rag_user/rag_user.py +78 -55
  22. waldiez/exporting/{agents → agent/utils}/rag_user/vector_db.py +10 -8
  23. waldiez/exporting/agent/utils/swarm_agent.py +463 -0
  24. waldiez/exporting/{agents → agent/utils}/teachability.py +10 -6
  25. waldiez/exporting/{agents → agent/utils}/termination_message.py +7 -8
  26. waldiez/exporting/base/__init__.py +25 -0
  27. waldiez/exporting/base/agent_position.py +75 -0
  28. waldiez/exporting/base/base_exporter.py +118 -0
  29. waldiez/exporting/base/export_position.py +48 -0
  30. waldiez/exporting/base/import_position.py +23 -0
  31. waldiez/exporting/base/mixin.py +134 -0
  32. waldiez/exporting/base/utils/__init__.py +18 -0
  33. waldiez/exporting/{utils → base/utils}/comments.py +12 -55
  34. waldiez/exporting/{utils → base/utils}/naming.py +14 -4
  35. waldiez/exporting/base/utils/path_check.py +68 -0
  36. waldiez/exporting/{utils/object_string.py → base/utils/to_string.py} +21 -20
  37. waldiez/exporting/chats/__init__.py +5 -12
  38. waldiez/exporting/chats/chats_exporter.py +240 -0
  39. waldiez/exporting/chats/utils/__init__.py +15 -0
  40. waldiez/exporting/chats/utils/common.py +81 -0
  41. waldiez/exporting/chats/{nested.py → utils/nested.py} +125 -86
  42. waldiez/exporting/chats/utils/sequential.py +244 -0
  43. waldiez/exporting/chats/utils/single_chat.py +313 -0
  44. waldiez/exporting/chats/utils/swarm.py +207 -0
  45. waldiez/exporting/flow/__init__.py +5 -3
  46. waldiez/exporting/flow/flow_exporter.py +503 -0
  47. waldiez/exporting/flow/utils/__init__.py +47 -0
  48. waldiez/exporting/flow/utils/agent_utils.py +204 -0
  49. waldiez/exporting/flow/utils/chat_utils.py +71 -0
  50. waldiez/exporting/flow/utils/def_main.py +62 -0
  51. waldiez/exporting/flow/utils/flow_content.py +112 -0
  52. waldiez/exporting/flow/utils/flow_names.py +115 -0
  53. waldiez/exporting/flow/utils/importing_utils.py +179 -0
  54. waldiez/exporting/{utils → flow/utils}/logging_utils.py +34 -31
  55. waldiez/exporting/models/__init__.py +7 -242
  56. waldiez/exporting/models/models_exporter.py +192 -0
  57. waldiez/exporting/models/utils.py +166 -0
  58. waldiez/exporting/skills/__init__.py +7 -161
  59. waldiez/exporting/skills/skills_exporter.py +169 -0
  60. waldiez/exporting/skills/utils.py +281 -0
  61. waldiez/models/__init__.py +25 -7
  62. waldiez/models/agents/__init__.py +70 -0
  63. waldiez/models/agents/agent/__init__.py +11 -1
  64. waldiez/models/agents/agent/agent.py +9 -4
  65. waldiez/models/agents/agent/agent_data.py +3 -1
  66. waldiez/models/agents/agent/code_execution.py +2 -0
  67. waldiez/models/agents/agent/linked_skill.py +2 -0
  68. waldiez/models/agents/agent/nested_chat.py +2 -0
  69. waldiez/models/agents/agent/teachability.py +2 -0
  70. waldiez/models/agents/agent/termination_message.py +49 -13
  71. waldiez/models/agents/agents.py +15 -3
  72. waldiez/models/agents/assistant/__init__.py +2 -0
  73. waldiez/models/agents/assistant/assistant.py +2 -0
  74. waldiez/models/agents/assistant/assistant_data.py +2 -0
  75. waldiez/models/agents/group_manager/__init__.py +9 -1
  76. waldiez/models/agents/group_manager/group_manager.py +2 -0
  77. waldiez/models/agents/group_manager/group_manager_data.py +2 -0
  78. waldiez/models/agents/group_manager/speakers.py +49 -13
  79. waldiez/models/agents/rag_user/__init__.py +21 -4
  80. waldiez/models/agents/rag_user/rag_user.py +3 -1
  81. waldiez/models/agents/rag_user/rag_user_data.py +2 -0
  82. waldiez/models/agents/rag_user/retrieve_config.py +268 -17
  83. waldiez/models/agents/rag_user/vector_db_config.py +5 -3
  84. waldiez/models/agents/swarm_agent/__init__.py +49 -0
  85. waldiez/models/agents/swarm_agent/after_work.py +178 -0
  86. waldiez/models/agents/swarm_agent/on_condition.py +103 -0
  87. waldiez/models/agents/swarm_agent/on_condition_available.py +140 -0
  88. waldiez/models/agents/swarm_agent/on_condition_target.py +40 -0
  89. waldiez/models/agents/swarm_agent/swarm_agent.py +107 -0
  90. waldiez/models/agents/swarm_agent/swarm_agent_data.py +125 -0
  91. waldiez/models/agents/swarm_agent/update_system_message.py +144 -0
  92. waldiez/models/agents/user_proxy/__init__.py +2 -0
  93. waldiez/models/agents/user_proxy/user_proxy.py +2 -0
  94. waldiez/models/agents/user_proxy/user_proxy_data.py +2 -0
  95. waldiez/models/chat/__init__.py +21 -3
  96. waldiez/models/chat/chat.py +241 -7
  97. waldiez/models/chat/chat_data.py +192 -48
  98. waldiez/models/chat/chat_message.py +153 -144
  99. waldiez/models/chat/chat_nested.py +33 -53
  100. waldiez/models/chat/chat_summary.py +2 -0
  101. waldiez/models/common/__init__.py +6 -6
  102. waldiez/models/common/base.py +4 -1
  103. waldiez/models/common/method_utils.py +163 -83
  104. waldiez/models/flow/__init__.py +2 -0
  105. waldiez/models/flow/flow.py +176 -40
  106. waldiez/models/flow/flow_data.py +63 -2
  107. waldiez/models/flow/utils.py +172 -0
  108. waldiez/models/model/__init__.py +2 -0
  109. waldiez/models/model/model.py +25 -6
  110. waldiez/models/model/model_data.py +3 -1
  111. waldiez/models/skill/__init__.py +4 -1
  112. waldiez/models/skill/skill.py +30 -2
  113. waldiez/models/skill/skill_data.py +2 -0
  114. waldiez/models/waldiez.py +28 -4
  115. waldiez/runner.py +142 -228
  116. waldiez/running/__init__.py +33 -0
  117. waldiez/running/environment.py +83 -0
  118. waldiez/running/gen_seq_diagram.py +185 -0
  119. waldiez/running/running.py +300 -0
  120. {waldiez-0.2.2.dist-info → waldiez-0.3.0.dist-info}/METADATA +32 -26
  121. waldiez-0.3.0.dist-info/RECORD +125 -0
  122. waldiez-0.3.0.dist-info/licenses/LICENSE +201 -0
  123. waldiez/exporting/agents/__init__.py +0 -5
  124. waldiez/exporting/agents/agent.py +0 -236
  125. waldiez/exporting/agents/agent_skills.py +0 -67
  126. waldiez/exporting/agents/llm_config.py +0 -53
  127. waldiez/exporting/chats/chats.py +0 -46
  128. waldiez/exporting/chats/helpers.py +0 -420
  129. waldiez/exporting/flow/def_main.py +0 -32
  130. waldiez/exporting/flow/flow.py +0 -189
  131. waldiez/exporting/utils/__init__.py +0 -36
  132. waldiez/exporting/utils/importing.py +0 -265
  133. waldiez/exporting/utils/method_utils.py +0 -35
  134. waldiez/exporting/utils/path_check.py +0 -51
  135. waldiez-0.2.2.dist-info/RECORD +0 -92
  136. waldiez-0.2.2.dist-info/licenses/LICENSE +0 -21
  137. {waldiez-0.2.2.dist-info → waldiez-0.3.0.dist-info}/WHEEL +0 -0
  138. {waldiez-0.2.2.dist-info → waldiez-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,178 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Swarm after work model.
4
+
5
+ Handles the next step in the conversation when an
6
+ agent doesn't suggest a tool call or a handoff.
7
+
8
+ """
9
+
10
+ # pylint: disable=line-too-long
11
+
12
+ from typing import Dict, Optional, Tuple
13
+
14
+ from pydantic import Field, model_validator
15
+ from typing_extensions import Annotated, Literal, Self
16
+
17
+ from ...common import WaldiezBase, check_function, generate_function
18
+
19
+ WaldiezSwarmAfterWorkRecipientType = Literal["agent", "option", "callable"]
20
+ WaldiezSwarmAfterWorkOption = Literal[
21
+ "TERMINATE", "REVERT_TO_USER", "STAY", "SWARM_MANAGER"
22
+ ]
23
+
24
+
25
+ CUSTOM_AFTER_WORK = "custom_after_work"
26
+ CUSTOM_AFTER_WORK_ARGS = ["last_speaker", "messages", "groupchat"]
27
+ CUSTOM_AFTER_WORK_TYPES = (
28
+ ["SwarmAgent", "List[Dict[str, Any]]", "GroupChat"],
29
+ "Union[AfterWorkOption, SwarmAgent, str]",
30
+ )
31
+
32
+
33
+ class WaldiezSwarmAfterWork(WaldiezBase):
34
+ """Swarm after work.
35
+
36
+
37
+ Attributes
38
+ ----------
39
+ recipient : str
40
+ The agent_id to hand off to, an AfterWork option,
41
+ or the custom after work method.
42
+ If it is an AfterWork option, it can be one of
43
+ ('TERMINATE', 'REVERT_TO_USER', 'STAY', 'SWARM_MANAGER').
44
+
45
+ recipient_type : WaldiezSwarmAfterWorkRecipientType
46
+ The type of recipient.
47
+ Can be 'agent', 'option', or 'callable'.
48
+ If 'agent', the recipient is a SwarmAgent.
49
+ If 'option', the recipient is an AfterWorkOption :
50
+ ('TERMINATE', 'REVERT_TO_USER', 'STAY', 'SWARM_MANAGER').
51
+ If 'callable', it should have the signature:
52
+ def custom_after_work(
53
+ last_speaker: SwarmAgent,
54
+ messages: List[dict],
55
+ groupchat: GroupChat,
56
+ ) -> Union[AfterWorkOption, SwarmAgent, str]:
57
+
58
+ """
59
+
60
+ recipient: Annotated[
61
+ str,
62
+ Field(
63
+ "TERMINATE",
64
+ title="Recipient",
65
+ description=(
66
+ "The agent_id to hand off to, an AfterWork option, "
67
+ "or the custom after work method. "
68
+ "If it is an AfterWork option, it can be one of "
69
+ "('TERMINATE', 'REVERT_TO_USER', 'STAY')"
70
+ ),
71
+ ),
72
+ ]
73
+ recipient_type: Annotated[
74
+ WaldiezSwarmAfterWorkRecipientType,
75
+ Field(
76
+ "option",
77
+ alias="recipientType",
78
+ title="Recipient Type",
79
+ description=(
80
+ "The type of recipient. "
81
+ "Can be 'agent', 'option', or 'callable'. "
82
+ "If 'agent', the recipient is a SwarmAgent. "
83
+ "If 'option', the recipient is an AfterWorkOption :"
84
+ " ('TERMINATE', 'REVERT_TO_USER', 'STAY', 'SWARM_MANAGER'). "
85
+ "If 'callable', it should have the signature: "
86
+ "def custom_after_work("
87
+ " last_speaker: SwarmAgent,"
88
+ " messages: List[Dict[str, Any]],"
89
+ " groupchat: GroupChat,"
90
+ ") -> Union[AfterWorkOption, SwarmAgent, str]:"
91
+ ),
92
+ ),
93
+ ]
94
+
95
+ _recipient_string: str = ""
96
+
97
+ def get_recipient(
98
+ self,
99
+ agent_names: Dict[str, str],
100
+ name_prefix: Optional[str] = None,
101
+ name_suffix: Optional[str] = None,
102
+ ) -> Tuple[str, str]:
103
+ """Get the recipient string.
104
+
105
+ Parameters
106
+ ----------
107
+ agent_names : Dict[str, str]
108
+ A mapping of agent id to agent name.
109
+ name_prefix : Optional[str], optional
110
+ The prefix for the function name, by default None.
111
+ name_suffix : Optional[str], optional
112
+ The suffix for the function name, by default None.
113
+
114
+ Returns
115
+ -------
116
+ Tuple[str, str]
117
+ The recipient string and the function content if applicable.
118
+ """
119
+ if self.recipient_type == "option":
120
+ return f"AFTER_WORK(AfterWorkOption.{self.recipient})", ""
121
+ if self.recipient_type == "agent":
122
+ # the the recipient is passed as the agent name
123
+ # (and not its id), care should be taken to ensure
124
+ # the all the agents in the flow have unique names
125
+ agent_instance = agent_names.get(self.recipient, self.recipient)
126
+ return f"AFTER_WORK({agent_instance})", ""
127
+
128
+ function_name = CUSTOM_AFTER_WORK
129
+ if name_prefix:
130
+ function_name = f"{name_prefix}_{function_name}"
131
+ if name_suffix:
132
+ function_name = f"{function_name}_{name_suffix}"
133
+ return (
134
+ f"AFTER_WORK({function_name})",
135
+ generate_function(
136
+ function_name=function_name,
137
+ function_args=CUSTOM_AFTER_WORK_ARGS,
138
+ function_body=self._recipient_string,
139
+ function_types=CUSTOM_AFTER_WORK_TYPES,
140
+ ),
141
+ )
142
+
143
+ @model_validator(mode="after")
144
+ def validate_recipient(self) -> Self:
145
+ """Validate the recipient.
146
+
147
+ Returns
148
+ -------
149
+ WaldiezSwarmAfterWork
150
+ The validated after work model.
151
+
152
+ Raises
153
+ ------
154
+ ValueError
155
+ If the validation fails.
156
+ """
157
+ self._recipient_string = self.recipient
158
+ if self.recipient_type == "callable":
159
+ is_valid, error_or_body = check_function(
160
+ code_string=self.recipient,
161
+ function_name=CUSTOM_AFTER_WORK,
162
+ function_args=CUSTOM_AFTER_WORK_ARGS,
163
+ )
164
+ if not is_valid or not error_or_body:
165
+ # pylint: disable=inconsistent-quotes
166
+ raise ValueError(
167
+ f"Invalid custom method: {error_or_body or 'no content'}"
168
+ )
169
+ self._recipient_string = error_or_body
170
+ elif self.recipient_type == "option":
171
+ if self.recipient not in [
172
+ "TERMINATE",
173
+ "REVERT_TO_USER",
174
+ "STAY",
175
+ "SWARM_MANAGER",
176
+ ]:
177
+ raise ValueError("Invalid option.")
178
+ return self
@@ -0,0 +1,103 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Swarm condition model for handoff."""
4
+
5
+ from typing import Optional, Tuple
6
+
7
+ from pydantic import Field
8
+ from typing_extensions import Annotated, Literal
9
+
10
+ from ...common import WaldiezBase
11
+ from .on_condition_available import WaldiezSwarmOnConditionAvailable
12
+ from .on_condition_target import WaldiezSwarmOnConditionTarget
13
+
14
+ WaldiezSwarmOnConditionTargetType = Literal["agent", "nested_chat"]
15
+
16
+
17
+ class WaldiezSwarmOnCondition(WaldiezBase):
18
+ """Swarm condition to handle handoff.
19
+
20
+ Attributes
21
+ ----------
22
+ target : WaldiezSwarmOnConditionTarget
23
+ The agent or nested chat configuration to hand off to.
24
+
25
+ target_type: Literal["agent", "nested_chat"]
26
+ The type of the target. Can be either 'agent' or 'nested_chat'.
27
+ Default is 'agent'.
28
+
29
+ condition : str
30
+ The condition for transitioning to the target agent
31
+
32
+ available: str, optional
33
+ Optional condition to determine if this ON_CONDITION is available.
34
+ Can be a Callable or a string. If a string, it will look up the
35
+ value of the context variable with that name, which should be a bool.
36
+
37
+ available_check_type : Literal["string", "callable", "none"]
38
+ The type of the `available` property to check. Default is "none".
39
+ """
40
+
41
+ target: Annotated[
42
+ WaldiezSwarmOnConditionTarget,
43
+ Field(
44
+ title="Target",
45
+ description=(
46
+ "The agent or nested chat configuration to hand off to."
47
+ ),
48
+ ),
49
+ ]
50
+ target_type: Annotated[
51
+ WaldiezSwarmOnConditionTargetType,
52
+ Field(
53
+ "agent",
54
+ alias="targetType",
55
+ title="Target Type",
56
+ description=(
57
+ "The type of the target. "
58
+ "Can be either 'agent' or 'nested_chat'.Default is 'agent'."
59
+ ),
60
+ ),
61
+ ] = "agent"
62
+ condition: Annotated[
63
+ str,
64
+ Field(
65
+ ...,
66
+ title="Condition",
67
+ description="The condition for transitioning to the target agent",
68
+ ),
69
+ ]
70
+ available: Annotated[
71
+ WaldiezSwarmOnConditionAvailable,
72
+ Field(
73
+ default_factory=WaldiezSwarmOnConditionAvailable,
74
+ title="Available",
75
+ description=(
76
+ "Optional condition to determine if this ON_CONDITION "
77
+ "is available."
78
+ ),
79
+ ),
80
+ ]
81
+
82
+ def get_available(
83
+ self,
84
+ name_prefix: Optional[str] = None,
85
+ name_suffix: Optional[str] = None,
86
+ ) -> Tuple[str, str]:
87
+ """Get the available string.
88
+
89
+ Parameters
90
+ ----------
91
+ name_prefix : str, optional
92
+ The prefix to add to the function name. Default is None.
93
+ name_suffix : str, optional
94
+ The suffix to add to the function name. Default is None.
95
+ Returns
96
+ -------
97
+ Tuple[str, str]
98
+ The available string or function name and code if available.
99
+ """
100
+ return self.available.get_available(
101
+ name_prefix,
102
+ name_suffix,
103
+ )
@@ -0,0 +1,140 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Waldiez Swarm Agent ON_CONDITION Available Model."""
4
+
5
+ from typing import Optional, Tuple
6
+
7
+ from pydantic import Field, model_validator
8
+ from typing_extensions import Annotated, Literal, Self
9
+
10
+ from ...common import WaldiezBase, check_function, generate_function
11
+
12
+ WaldiezSwarmOnConditionAvailableCheckType = Literal[
13
+ "string", "callable", "none"
14
+ ]
15
+
16
+ CUSTOM_ON_CONDITION_AVAILABLE = "custom_on_condition_available"
17
+ CUSTOM_ON_CONDITION_AVAILABLE_ARGS = ["agent", "message"]
18
+ CUSTOM_ON_CONDITION_AVAILABLE_TYPES = (
19
+ ["ConversableAgent", "Dict[str, Any]"],
20
+ "bool",
21
+ )
22
+
23
+ # In ag2 it is used as:
24
+ #
25
+ # if on_condition.available is not None:
26
+ # if isinstance(on_condition.available, Callable):
27
+ # is_available = on_condition.available(
28
+ # agent, next(iter(agent.chat_messages.values()))
29
+ # )
30
+ # elif isinstance(on_condition.available, str):
31
+ # is_available = agent.get_context(on_condition.available) or False
32
+
33
+
34
+ class WaldiezSwarmOnConditionAvailable(WaldiezBase):
35
+ """Swarm condition availability check."""
36
+
37
+ type: Annotated[
38
+ WaldiezSwarmOnConditionAvailableCheckType,
39
+ Field(
40
+ "none",
41
+ alias="availableCheckType",
42
+ title="Available Check Type",
43
+ description=("The type of the `available` property to check. "),
44
+ ),
45
+ ] = "none"
46
+ value: Annotated[
47
+ Optional[str],
48
+ Field(
49
+ None,
50
+ title="Available",
51
+ description=(
52
+ "Optional condition to determine if this ON_CONDITION "
53
+ "is available. Can be a Callable or a string. If a string, "
54
+ " it will look up the value of the context variable with that "
55
+ "name, which should be a bool."
56
+ ),
57
+ ),
58
+ ]
59
+
60
+ _available_string: str = ""
61
+
62
+ @property
63
+ def available_string(self) -> str:
64
+ """Get the available string.
65
+
66
+ Returns
67
+ -------
68
+ str
69
+ The available string.
70
+ """
71
+ return self._available_string
72
+
73
+ def get_available(
74
+ self,
75
+ name_prefix: Optional[str] = None,
76
+ name_suffix: Optional[str] = None,
77
+ ) -> Tuple[str, str]:
78
+ """Get the available string.
79
+
80
+ Parameters
81
+ ----------
82
+ name_prefix : str, optional
83
+ The prefix to add to the function name. Default is None.
84
+ name_suffix : str, optional
85
+ The suffix to add to the function name. Default is None.
86
+ Returns
87
+ -------
88
+ Tuple[str, str]
89
+ The available string or function name and code if available.
90
+ """
91
+ if self.type == "none" or not self.value:
92
+ return "", ""
93
+ if self.type == "string":
94
+ return self._available_string, ""
95
+ function_name = CUSTOM_ON_CONDITION_AVAILABLE
96
+ if name_prefix:
97
+ function_name = f"{name_prefix}_{function_name}"
98
+ if name_suffix:
99
+ function_name = f"{function_name}_{name_suffix}"
100
+ return function_name, generate_function(
101
+ function_name=function_name,
102
+ function_args=CUSTOM_ON_CONDITION_AVAILABLE_ARGS,
103
+ function_types=CUSTOM_ON_CONDITION_AVAILABLE_TYPES,
104
+ function_body=self._available_string,
105
+ )
106
+
107
+ @model_validator(mode="after")
108
+ def validate_available(self) -> Self:
109
+ """Validate the available check.
110
+
111
+ Returns
112
+ -------
113
+ Self
114
+ The swarm agent's on condition available model.
115
+
116
+ Raises
117
+ ------
118
+ ValueError
119
+ If the available check fails.
120
+ """
121
+ if self.type == "callable":
122
+ if not self.value:
123
+ raise ValueError(
124
+ "A callable is expected, but no value was provided."
125
+ )
126
+ is_valid, error_or_body = check_function(
127
+ code_string=self.value,
128
+ function_name=CUSTOM_ON_CONDITION_AVAILABLE,
129
+ function_args=CUSTOM_ON_CONDITION_AVAILABLE_ARGS,
130
+ )
131
+ if not is_valid:
132
+ raise ValueError(error_or_body)
133
+ self._available_string = error_or_body
134
+ if self.type == "string":
135
+ if not self.value:
136
+ raise ValueError(
137
+ "A string is expected, but no value was provided."
138
+ )
139
+ self._available_string = self.value
140
+ return self
@@ -0,0 +1,40 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Swarm condition model for handoff."""
4
+
5
+ from pydantic import Field
6
+ from typing_extensions import Annotated
7
+
8
+ from ...common import WaldiezBase
9
+
10
+
11
+ class WaldiezSwarmOnConditionTarget(WaldiezBase):
12
+ """Swarm condition target.
13
+
14
+ If the condition's target is "agent", the id refers to the agent's ID.
15
+ If the condition's target is "nested_chat", the id refers to the edge's ID.
16
+
17
+ Attributes
18
+ ----------
19
+ id : str
20
+ The ID of the target agent or edge.
21
+ order : int
22
+ The order of the target agent or edge.
23
+ """
24
+
25
+ id: Annotated[
26
+ str,
27
+ Field(
28
+ ...,
29
+ title="ID",
30
+ description="The ID of the target agent or edge.",
31
+ ),
32
+ ]
33
+ order: Annotated[
34
+ int,
35
+ Field(
36
+ ...,
37
+ title="Order",
38
+ description="The order of the target agent or edge.",
39
+ ),
40
+ ]
@@ -0,0 +1,107 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Swarm agent."""
4
+
5
+ from typing import List, Union
6
+
7
+ from pydantic import Field
8
+ from typing_extensions import Annotated, Literal
9
+
10
+ from ..agent import WaldiezAgent, WaldiezAgentNestedChat
11
+ from .after_work import WaldiezSwarmAfterWork
12
+ from .on_condition import WaldiezSwarmOnCondition
13
+ from .swarm_agent_data import WaldiezSwarmAgentData
14
+ from .update_system_message import WaldiezSwarmUpdateSystemMessage
15
+
16
+
17
+ class WaldiezSwarmAgent(WaldiezAgent):
18
+ """Swarm agent.
19
+
20
+ It extends a user agent and has swarm related parameters.
21
+
22
+ Attributes
23
+ ----------
24
+ agent_type : Literal["swarm"]
25
+ The agent type: 'swarm' for a swarm agent.
26
+ data : WaldiezSwarmAgentData
27
+ The swarm agent's data.
28
+ See `WaldiezSwarmAgentData` for more info.
29
+ """
30
+
31
+ agent_type: Annotated[
32
+ Literal["swarm"],
33
+ Field(
34
+ "swarm",
35
+ title="Agent type",
36
+ description="The agent type: 'swarm' for a swarm agent.",
37
+ alias="agentType",
38
+ ),
39
+ ]
40
+
41
+ data: Annotated[
42
+ WaldiezSwarmAgentData,
43
+ Field(
44
+ title="Data",
45
+ description="The swarm agent's data",
46
+ default_factory=WaldiezSwarmAgentData,
47
+ ),
48
+ ]
49
+
50
+ @property
51
+ def functions(self) -> List[str]:
52
+ """Get the functions that the agent can use.
53
+
54
+ Returns
55
+ -------
56
+ List[str]
57
+ The functions that the agent can use.
58
+ """
59
+ return self.data.functions
60
+
61
+ @property
62
+ def update_agent_state_before_reply(
63
+ self,
64
+ ) -> List[Union[str, WaldiezSwarmUpdateSystemMessage]]:
65
+ """Get the functions that update the agent's state before it replies.
66
+
67
+ Returns
68
+ -------
69
+ List[str]
70
+ The functions that update the agent's state before it replies.
71
+ """
72
+ return self.data.update_agent_state_before_reply
73
+
74
+ @property
75
+ def handoffs(
76
+ self,
77
+ ) -> List[Union[WaldiezSwarmOnCondition, WaldiezSwarmAfterWork]]:
78
+ """Get the hand offs to register.
79
+
80
+ Returns
81
+ -------
82
+ List[str]
83
+ The hand offs to register.
84
+ """
85
+ return self.data.handoffs
86
+
87
+ @property
88
+ def nested_chats(self) -> List[WaldiezAgentNestedChat]:
89
+ """Get the nested chats.
90
+
91
+ Returns
92
+ -------
93
+ List[WaldiezChat]
94
+ The nested chats.
95
+ """
96
+ return self.data.nested_chats
97
+
98
+ @property
99
+ def is_initial(self) -> bool:
100
+ """Check if the agent is the initial agent.
101
+
102
+ Returns
103
+ -------
104
+ bool
105
+ Whether the agent is the initial agent.
106
+ """
107
+ return self.data.is_initial
@@ -0,0 +1,125 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ # https://docs.ag2.ai/docs/reference/agentchat/contrib/swarm_agent
4
+ """Swarm agent data."""
5
+
6
+ from typing import List, Union
7
+
8
+ from pydantic import Field, model_validator
9
+ from typing_extensions import Annotated, Self
10
+
11
+ from ..agent import WaldiezAgentData
12
+ from .after_work import WaldiezSwarmAfterWork
13
+ from .on_condition import WaldiezSwarmOnCondition
14
+ from .update_system_message import WaldiezSwarmUpdateSystemMessage
15
+
16
+
17
+ # flake8: noqa: E501
18
+ # pylint: disable=line-too-long
19
+ class WaldiezSwarmAgentData(WaldiezAgentData):
20
+ """Swarm agent data.
21
+
22
+ Attributes
23
+ ----------
24
+ is_initial: bool
25
+ Whether the agent is the initial agent.
26
+ functions : List[str]
27
+ A list of functions (skill ids) to register with the agent.
28
+
29
+ update_agent_state_before_reply : List[str]
30
+ A list of functions, including `UPDATE_SYSTEM_MESSAGE`,
31
+ called to update the agent's state before it replies. Each function
32
+ is called when the agent is selected and before it speaks.
33
+
34
+ handoffs : List[Union[WaldiezSwarmOnCondition, WaldiezSwarmAfterWork]]
35
+ A list of hand offs to register.
36
+
37
+ Notes
38
+ -----
39
+ Each agent should have at most one `AfterWork` and (if any) it should be
40
+ at the end the list of hand offs.
41
+ """
42
+
43
+ is_initial: Annotated[
44
+ bool,
45
+ Field(
46
+ False,
47
+ title="Is Initial",
48
+ alias="isInitial",
49
+ description=("Whether the agent is the initial agent."),
50
+ ),
51
+ ]
52
+ functions: Annotated[
53
+ List[str],
54
+ Field(
55
+ title="Functions",
56
+ description=(
57
+ "A list of functions (skill ids) to register with the agent."
58
+ ),
59
+ default_factory=list,
60
+ ),
61
+ ]
62
+ update_agent_state_before_reply: Annotated[
63
+ List[Union[str, WaldiezSwarmUpdateSystemMessage]],
64
+ Field(
65
+ title="Update Agent State Before Reply",
66
+ alias="updateAgentStateBeforeReply",
67
+ description=(
68
+ "A list of functions, including UPDATE_SYSTEM_MESSAGEs,"
69
+ "called to update the agent's state before it replies. "
70
+ " Each function is called when the agent is selected "
71
+ "and before it speaks. If not an UPDATE_SYSTEM_MESSAGE, "
72
+ "it should be a skill id."
73
+ ),
74
+ default_factory=list,
75
+ ),
76
+ ]
77
+ handoffs: Annotated[
78
+ List[Union[WaldiezSwarmOnCondition, WaldiezSwarmAfterWork]],
79
+ Field(
80
+ title="Handoffs",
81
+ description=(
82
+ "A list of hand offs to register. "
83
+ "There should only be at most one `AfterWork` per agent"
84
+ "And (if any) it should be at the end of the list."
85
+ ),
86
+ default_factory=list,
87
+ ),
88
+ ]
89
+
90
+ @model_validator(mode="after")
91
+ def validate_handoffs(self) -> Self:
92
+ """Validate the hand offs.
93
+
94
+ Returns
95
+ -------
96
+ Self
97
+ The swarm agent data.
98
+
99
+ Raises
100
+ ------
101
+ ValueError
102
+ If there are more than one `AfterWork`s.
103
+ """
104
+ after_works: List[WaldiezSwarmAfterWork] = [
105
+ hand_off
106
+ for hand_off in self.handoffs
107
+ if isinstance(hand_off, WaldiezSwarmAfterWork)
108
+ ]
109
+ if len(after_works) > 1:
110
+ raise ValueError(
111
+ "Each agent should have at most one `AfterWork` "
112
+ "and (if any) it should be at the end of the list."
113
+ )
114
+ on_conditions: List[WaldiezSwarmOnCondition] = [
115
+ hand_off
116
+ for hand_off in self.handoffs
117
+ if isinstance(hand_off, WaldiezSwarmOnCondition)
118
+ ]
119
+ on_conditions = sorted(on_conditions, key=lambda x: x.target.order)
120
+ handoffs = on_conditions + after_works
121
+ if after_works and after_works[0] != handoffs[-1]:
122
+ handoffs.remove(after_works[0])
123
+ handoffs.append(after_works[0])
124
+ self.handoffs = handoffs
125
+ return self