waldiez 0.1.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 (94) hide show
  1. waldiez/__init__.py +15 -0
  2. waldiez/__main__.py +6 -0
  3. waldiez/_version.py +3 -0
  4. waldiez/cli.py +162 -0
  5. waldiez/exporter.py +293 -0
  6. waldiez/exporting/__init__.py +14 -0
  7. waldiez/exporting/agents/__init__.py +5 -0
  8. waldiez/exporting/agents/agent.py +229 -0
  9. waldiez/exporting/agents/agent_skills.py +67 -0
  10. waldiez/exporting/agents/code_execution.py +67 -0
  11. waldiez/exporting/agents/group_manager.py +209 -0
  12. waldiez/exporting/agents/llm_config.py +53 -0
  13. waldiez/exporting/agents/rag_user/__init__.py +5 -0
  14. waldiez/exporting/agents/rag_user/chroma_utils.py +134 -0
  15. waldiez/exporting/agents/rag_user/mongo_utils.py +83 -0
  16. waldiez/exporting/agents/rag_user/pgvector_utils.py +93 -0
  17. waldiez/exporting/agents/rag_user/qdrant_utils.py +112 -0
  18. waldiez/exporting/agents/rag_user/rag_user.py +165 -0
  19. waldiez/exporting/agents/rag_user/vector_db.py +119 -0
  20. waldiez/exporting/agents/teachability.py +37 -0
  21. waldiez/exporting/agents/termination_message.py +45 -0
  22. waldiez/exporting/chats/__init__.py +14 -0
  23. waldiez/exporting/chats/chats.py +46 -0
  24. waldiez/exporting/chats/helpers.py +395 -0
  25. waldiez/exporting/chats/nested.py +264 -0
  26. waldiez/exporting/flow/__init__.py +5 -0
  27. waldiez/exporting/flow/def_main.py +37 -0
  28. waldiez/exporting/flow/flow.py +185 -0
  29. waldiez/exporting/models/__init__.py +193 -0
  30. waldiez/exporting/skills/__init__.py +128 -0
  31. waldiez/exporting/utils/__init__.py +34 -0
  32. waldiez/exporting/utils/comments.py +136 -0
  33. waldiez/exporting/utils/importing.py +267 -0
  34. waldiez/exporting/utils/logging_utils.py +203 -0
  35. waldiez/exporting/utils/method_utils.py +35 -0
  36. waldiez/exporting/utils/naming.py +127 -0
  37. waldiez/exporting/utils/object_string.py +81 -0
  38. waldiez/io_stream.py +181 -0
  39. waldiez/models/__init__.py +107 -0
  40. waldiez/models/agents/__init__.py +65 -0
  41. waldiez/models/agents/agent/__init__.py +21 -0
  42. waldiez/models/agents/agent/agent.py +190 -0
  43. waldiez/models/agents/agent/agent_data.py +162 -0
  44. waldiez/models/agents/agent/code_execution.py +71 -0
  45. waldiez/models/agents/agent/linked_skill.py +30 -0
  46. waldiez/models/agents/agent/nested_chat.py +73 -0
  47. waldiez/models/agents/agent/teachability.py +68 -0
  48. waldiez/models/agents/agent/termination_message.py +167 -0
  49. waldiez/models/agents/agents.py +129 -0
  50. waldiez/models/agents/assistant/__init__.py +6 -0
  51. waldiez/models/agents/assistant/assistant.py +41 -0
  52. waldiez/models/agents/assistant/assistant_data.py +29 -0
  53. waldiez/models/agents/group_manager/__init__.py +19 -0
  54. waldiez/models/agents/group_manager/group_manager.py +87 -0
  55. waldiez/models/agents/group_manager/group_manager_data.py +91 -0
  56. waldiez/models/agents/group_manager/speakers.py +211 -0
  57. waldiez/models/agents/rag_user/__init__.py +26 -0
  58. waldiez/models/agents/rag_user/rag_user.py +58 -0
  59. waldiez/models/agents/rag_user/rag_user_data.py +32 -0
  60. waldiez/models/agents/rag_user/retrieve_config.py +592 -0
  61. waldiez/models/agents/rag_user/vector_db_config.py +162 -0
  62. waldiez/models/agents/user_proxy/__init__.py +6 -0
  63. waldiez/models/agents/user_proxy/user_proxy.py +41 -0
  64. waldiez/models/agents/user_proxy/user_proxy_data.py +30 -0
  65. waldiez/models/chat/__init__.py +22 -0
  66. waldiez/models/chat/chat.py +129 -0
  67. waldiez/models/chat/chat_data.py +326 -0
  68. waldiez/models/chat/chat_message.py +304 -0
  69. waldiez/models/chat/chat_nested.py +160 -0
  70. waldiez/models/chat/chat_summary.py +110 -0
  71. waldiez/models/common/__init__.py +38 -0
  72. waldiez/models/common/base.py +63 -0
  73. waldiez/models/common/method_utils.py +165 -0
  74. waldiez/models/flow/__init__.py +9 -0
  75. waldiez/models/flow/flow.py +302 -0
  76. waldiez/models/flow/flow_data.py +87 -0
  77. waldiez/models/model/__init__.py +11 -0
  78. waldiez/models/model/model.py +169 -0
  79. waldiez/models/model/model_data.py +86 -0
  80. waldiez/models/skill/__init__.py +9 -0
  81. waldiez/models/skill/skill.py +129 -0
  82. waldiez/models/skill/skill_data.py +37 -0
  83. waldiez/models/waldiez.py +301 -0
  84. waldiez/py.typed +0 -0
  85. waldiez/runner.py +304 -0
  86. waldiez/stream/__init__.py +7 -0
  87. waldiez/stream/consumer.py +139 -0
  88. waldiez/stream/provider.py +339 -0
  89. waldiez/stream/server.py +412 -0
  90. waldiez-0.1.0.dist-info/METADATA +181 -0
  91. waldiez-0.1.0.dist-info/RECORD +94 -0
  92. waldiez-0.1.0.dist-info/WHEEL +4 -0
  93. waldiez-0.1.0.dist-info/entry_points.txt +2 -0
  94. waldiez-0.1.0.dist-info/licenses/LICENSE +21 -0
waldiez/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """Waldiez package."""
2
+
3
+ from ._version import __version__
4
+ from .exporter import WaldiezExporter
5
+ from .io_stream import WaldiezIOStream
6
+ from .models import Waldiez
7
+ from .runner import WaldiezRunner
8
+
9
+ __all__ = [
10
+ "Waldiez",
11
+ "WaldiezExporter",
12
+ "WaldiezIOStream",
13
+ "WaldiezRunner",
14
+ "__version__",
15
+ ]
waldiez/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Waldiez entrypoint when called as a module."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
waldiez/_version.py ADDED
@@ -0,0 +1,3 @@
1
+ """Version information for Waldiez."""
2
+
3
+ __version__ = "0.1.0"
waldiez/cli.py ADDED
@@ -0,0 +1,162 @@
1
+ """Command line interface to convert or run a waldiez file."""
2
+
3
+ import argparse
4
+ import json
5
+ import logging
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Any, Dict, Optional
10
+
11
+ from autogen import ChatResult # type: ignore[import-untyped]
12
+
13
+ from . import Waldiez, __version__
14
+ from .exporter import WaldiezExporter
15
+ from .runner import WaldiezRunner
16
+
17
+
18
+ def get_parser() -> argparse.ArgumentParser:
19
+ """Get the argument parser for the Waldiez package.
20
+
21
+ Returns
22
+ -------
23
+ argparse.ArgumentParser
24
+ The argument parser.
25
+ """
26
+ parser = argparse.ArgumentParser(
27
+ description="Run or export a Waldiez flow.",
28
+ prog="waldiez",
29
+ )
30
+ parser.add_argument(
31
+ "waldiez",
32
+ type=str,
33
+ help="Path to the Waldiez flow (*.waldiez) file.",
34
+ )
35
+ parser.add_argument(
36
+ "-e",
37
+ "--export",
38
+ action="store_true",
39
+ help=(
40
+ "Export the Waldiez flow to a Python script or a jupyter notebook."
41
+ ),
42
+ )
43
+ parser.add_argument(
44
+ "-o",
45
+ "--output",
46
+ type=str,
47
+ help=(
48
+ "Path to the output file. "
49
+ "If exporting, the file extension determines the output format. "
50
+ "If running, the output's directory will contain "
51
+ "the generated flow (.py) and any additional generated files."
52
+ ),
53
+ )
54
+ parser.add_argument(
55
+ "-f",
56
+ "--force",
57
+ action="store_true",
58
+ help=("Override the output file if it already exists. "),
59
+ )
60
+ parser.add_argument(
61
+ "-v",
62
+ "--version",
63
+ action="version",
64
+ version=f"waldiez version: {__version__}",
65
+ )
66
+ return parser
67
+
68
+
69
+ def _log_result(result: ChatResult) -> None:
70
+ """Log the result of the Waldiez flow."""
71
+ logger = logging.getLogger("waldiez::cli")
72
+ logger.info("Chat History:\n")
73
+ logger.info(result.chat_history)
74
+ logger.info("Summary:\n")
75
+ logger.info(result.summary)
76
+ logger.info("Cost:\n")
77
+ logger.info(result.cost)
78
+
79
+
80
+ def _run(data: Dict[str, Any], output_path: Optional[str]) -> None:
81
+ """Run the Waldiez flow."""
82
+ waldiez = Waldiez.from_dict(data)
83
+ runner = WaldiezRunner(waldiez)
84
+ results = runner.run(stream=None, output_path=output_path)
85
+ if isinstance(results, list):
86
+ for result in results:
87
+ _log_result(result)
88
+ sep = "-" * 80
89
+ print(f"\n{sep}\n")
90
+ else:
91
+ _log_result(results)
92
+
93
+
94
+ def main() -> None:
95
+ """Parse the command line arguments and run the Waldiez flow."""
96
+ parser = get_parser()
97
+ args = parser.parse_args()
98
+ logger = _get_logger()
99
+ waldiez_file: str = args.waldiez
100
+ if not os.path.exists(waldiez_file):
101
+ logger.error("File not found: %s", waldiez_file)
102
+ sys.exit(1)
103
+ if not waldiez_file.endswith((".json", ".waldiez")):
104
+ logger.error("Only .json or .waldiez files are supported.")
105
+ sys.exit(1)
106
+ with open(waldiez_file, "r", encoding="utf-8") as file:
107
+ try:
108
+ data = json.load(file)
109
+ except json.decoder.JSONDecodeError:
110
+ logger.error("Invalid .waldiez file: %s. Not a valid json?", file)
111
+ return
112
+ if args.export is True:
113
+ if args.output is None:
114
+ logger.error("Please provide an output file.")
115
+ sys.exit(1)
116
+ if not args.output.endswith((".py", ".ipynb", ".json", ".waldiez")):
117
+ logger.error(
118
+ "Only Python scripts, Jupyter notebooks "
119
+ "and JSON/Waldiez files are supported."
120
+ )
121
+ sys.exit(1)
122
+ output_file = Path(args.output).resolve()
123
+ waldiez = Waldiez.from_dict(data)
124
+ exporter = WaldiezExporter(waldiez)
125
+ exporter.export(output_file, force=args.force)
126
+ generated = str(output_file).replace(os.getcwd(), ".")
127
+ logger.info("Generated: %s", generated)
128
+ else:
129
+ _run(data, args.output)
130
+
131
+
132
+ def _get_logger(level: int = logging.INFO) -> logging.Logger:
133
+ """Get the logger for the Waldiez package.
134
+
135
+ Parameters
136
+ ----------
137
+ level : int or str, optional
138
+ The logging level. Default is logging.INFO.
139
+
140
+ Returns
141
+ -------
142
+ logging.Logger
143
+ The logger.
144
+ """
145
+ # check if we already have setup a config
146
+
147
+ if not logging.getLogger().handlers:
148
+ logging.basicConfig(
149
+ level=level,
150
+ format="%(levelname)s %(message)s",
151
+ stream=sys.stderr,
152
+ force=True,
153
+ )
154
+ logger = logging.getLogger("waldiez::cli")
155
+ current_level = logger.getEffectiveLevel()
156
+ if current_level != level:
157
+ logger.setLevel(level)
158
+ return logger
159
+
160
+
161
+ if __name__ == "__main__":
162
+ main()
waldiez/exporter.py ADDED
@@ -0,0 +1,293 @@
1
+ """Waldiez exporter.
2
+
3
+ The role of the exporter is to export the model's data
4
+ to an autogen's flow with one or more chats.
5
+
6
+ The resulting file(s): a `flow.py` file with one `main()` function
7
+ to trigger the chat(s).
8
+ If additional tools/skills are used,
9
+ they are exported as their `skill_name` in the same directory with
10
+ the `flow.py` file. So the `flow.py` could have entries like:
11
+ `form {skill1_name} import {skill1_name}`
12
+ `form {skill2_name} import {skill2_name}`
13
+ """
14
+
15
+ # pylint: disable=inconsistent-quotes
16
+
17
+ import os
18
+ import shutil
19
+ import subprocess
20
+ import sys
21
+ from pathlib import Path
22
+ from typing import Dict, List, Optional, Union
23
+
24
+ from .exporting import comment, export_flow, get_valid_instance_name
25
+ from .models import (
26
+ Waldiez,
27
+ WaldiezAgent,
28
+ WaldiezChat,
29
+ WaldiezModel,
30
+ WaldiezSkill,
31
+ )
32
+
33
+
34
+ class WaldiezExporter:
35
+ """Waldiez exporter.
36
+
37
+ Attributes:
38
+ waldiez (Waldiez): The Waldiez instance.
39
+ """
40
+
41
+ _agent_names: Dict[str, str]
42
+ _model_names: Dict[str, str]
43
+ _skill_names: Dict[str, str]
44
+ _chat_names: Dict[str, str]
45
+ _chats: List[WaldiezChat]
46
+ _skills: List[WaldiezSkill]
47
+ _models: List[WaldiezModel]
48
+ _agents: List[WaldiezAgent]
49
+
50
+ def __init__(self, waldiez: Waldiez) -> None:
51
+ """Initialize the Waldiez exporter.
52
+
53
+ Parameters:
54
+ waldiez (Waldiez): The Waldiez instance.
55
+ """
56
+ self.waldiez = waldiez
57
+ self._initialize()
58
+
59
+ @classmethod
60
+ def load(cls, file_path: Path) -> "WaldiezExporter":
61
+ """Load the Waldiez instance from a file.
62
+
63
+ Parameters
64
+ ----------
65
+ file_path : Path
66
+ The file path.
67
+
68
+ Returns
69
+ -------
70
+ WaldiezExporter
71
+ The Waldiez exporter.
72
+ """
73
+ waldiez = Waldiez.load(file_path)
74
+ return cls(waldiez)
75
+
76
+ def _initialize(
77
+ self,
78
+ ) -> None:
79
+ """Get all the names in the flow.
80
+
81
+ We need to make sure that no duplicate names are used,
82
+ and that the names can be used as python variables.
83
+ """
84
+ all_names: Dict[str, str] = {}
85
+ agent_names: Dict[str, str] = {}
86
+ model_names: Dict[str, str] = {}
87
+ skill_names: Dict[str, str] = {}
88
+ chat_names: Dict[str, str] = {}
89
+ chats: List[WaldiezChat] = []
90
+ skills: List[WaldiezSkill] = []
91
+ models: List[WaldiezModel] = []
92
+ agents: List[WaldiezAgent] = []
93
+ for agent in self.waldiez.agents:
94
+ all_names = get_valid_instance_name(
95
+ (agent.id, agent.name), all_names, prefix="wa"
96
+ )
97
+ agent_names[agent.id] = all_names[agent.id]
98
+ agents.append(agent)
99
+ for model in self.waldiez.models:
100
+ all_names = get_valid_instance_name(
101
+ (model.id, model.name), all_names, prefix="wm"
102
+ )
103
+ model_names[model.id] = all_names[model.id]
104
+ models.append(model)
105
+ for skill in self.waldiez.skills:
106
+ all_names = get_valid_instance_name(
107
+ (skill.id, skill.name), all_names, prefix="ws"
108
+ )
109
+ skill_names[skill.id] = all_names[skill.id]
110
+ skills.append(skill)
111
+ for chat in self.waldiez.flow.data.chats:
112
+ all_names = get_valid_instance_name(
113
+ (chat.id, chat.name), all_names, prefix="wc"
114
+ )
115
+ chat_names[chat.id] = all_names[chat.id]
116
+ chats.append(chat)
117
+ self._agent_names = agent_names
118
+ self._model_names = model_names
119
+ self._skill_names = skill_names
120
+ self._chat_names = chat_names
121
+ self._chats = chats
122
+ self._skills = skills
123
+ self._models = models
124
+ self._agents = agents
125
+
126
+ def export(self, path: Union[str, Path], force: bool = False) -> None:
127
+ """Export the Waldiez instance.
128
+
129
+ Parameters
130
+ ----------
131
+ path : Union[str, Path]
132
+ The path to export to.
133
+ force : bool, optional
134
+ Override the output file if it already exists, by default False.
135
+
136
+ Raises
137
+ ------
138
+ FileExistsError
139
+ If the file already exists and force is False.
140
+ IsADirectoryError
141
+ If the output is a directory.
142
+ ValueError
143
+ If the file extension is invalid.
144
+ """
145
+ if not isinstance(path, Path):
146
+ path = Path(path)
147
+ path = path.resolve()
148
+ if path.is_dir():
149
+ raise IsADirectoryError(f"Output is a directory: {path}")
150
+ if path.exists():
151
+ if force is False:
152
+ raise FileExistsError(f"File already exists: {path}")
153
+ path.unlink(missing_ok=True)
154
+ path.parent.mkdir(parents=True, exist_ok=True)
155
+ extension = path.suffix
156
+ if extension == ".waldiez":
157
+ self.to_waldiez(path)
158
+ elif extension == ".py":
159
+ self.to_py(path)
160
+ elif extension == ".ipynb":
161
+ self.to_ipynb(path)
162
+ else:
163
+ raise ValueError(f"Invalid extension: {extension}")
164
+
165
+ def to_ipynb(self, path: Path) -> None:
166
+ """Export flow to jupyter notebook.
167
+
168
+ Parameters
169
+ ----------
170
+ path : Path
171
+ The path to export to.
172
+
173
+ Raises
174
+ ------
175
+ RuntimeError
176
+ If the notebook could not be generated.
177
+ """
178
+ content = f"{comment(True)}{self.waldiez.name}" + "\n\n"
179
+ content += f"{comment(True, 2)}Dependencies" + "\n\n"
180
+ content += "import sys\n"
181
+ requirements = " ".join(self.waldiez.requirements)
182
+ if requirements:
183
+ content += (
184
+ f"# !{{sys.executable}} -m pip install -q {requirements}" + "\n"
185
+ )
186
+ content += export_flow(
187
+ waldiez=self.waldiez,
188
+ agents=(self._agents, self._agent_names),
189
+ chats=(self._chats, self._chat_names),
190
+ models=(self._models, self._model_names),
191
+ skills=(self._skills, self._skill_names),
192
+ output_dir=path.parent,
193
+ notebook=True,
194
+ )
195
+ # we first create a .py file with the content
196
+ # and then convert it to a notebook using jupytext
197
+ py_path = path.with_suffix(".tmp.py")
198
+ with open(py_path, "w", encoding="utf-8") as f:
199
+ f.write(content)
200
+ if not shutil.which("jupytext"): # pragma: no cover
201
+ run_command(
202
+ [sys.executable, "-m", "pip", "install", "jupytext"],
203
+ allow_error=False,
204
+ )
205
+ run_command(
206
+ ["jupytext", "--to", "notebook", str(py_path)],
207
+ allow_error=False,
208
+ )
209
+ ipynb_path = str(py_path).replace(".tmp.py", ".tmp.ipynb")
210
+ if not os.path.exists(ipynb_path): # pragma: no cover
211
+ raise RuntimeError("Could not generate notebook")
212
+ Path(ipynb_path).rename(ipynb_path.replace(".tmp.ipynb", ".ipynb"))
213
+ py_path.unlink(missing_ok=True)
214
+
215
+ def to_py(self, path: Path) -> None:
216
+ """Export waldiez flow to python script.
217
+
218
+ Parameters
219
+ ----------
220
+ path : Path
221
+ The path to export to.
222
+ """
223
+ content = "#!/usr/bin/env python\n"
224
+ content += f'"""{self.waldiez.name}\n\n'
225
+ content += f"{self.waldiez.description}\n\n"
226
+ content += f"Tags: {', '.join(self.waldiez.tags)}\n\n"
227
+ content += f"Requirements: {', '.join(self.waldiez.requirements)}\n\n"
228
+ content += '"""\n\n'
229
+ content += "# cspell: disable\n"
230
+ content += "# flake8: noqa\n\n"
231
+ content += export_flow(
232
+ waldiez=self.waldiez,
233
+ agents=(self._agents, self._agent_names),
234
+ chats=(self._chats, self._chat_names),
235
+ models=(self._models, self._model_names),
236
+ skills=(self._skills, self._skill_names),
237
+ output_dir=path.parent,
238
+ notebook=False,
239
+ )
240
+ content += '\n\nif __name__ == "__main__":\n'
241
+ content += " print(main())\n"
242
+ with open(path, "w", encoding="utf-8") as file:
243
+ file.write(content)
244
+
245
+ def to_waldiez(self, file_path: Path) -> None:
246
+ """Export the Waldiez instance.
247
+
248
+ Parameters
249
+ ----------
250
+ file_path : Path
251
+ The file path.
252
+ """
253
+ with open(file_path, "w", encoding="utf-8") as file:
254
+ file.write(self.waldiez.model_dump_json())
255
+
256
+
257
+ def run_command(
258
+ cmd: List[str],
259
+ cwd: Optional[Path] = None,
260
+ allow_error: bool = True,
261
+ ) -> None:
262
+ """Run a command.
263
+
264
+ Parameters
265
+ ----------
266
+ cmd : List[str]
267
+ The command to run.
268
+ cwd : Path, optional
269
+ The working directory, by default None (current working directory).
270
+ allow_error : bool, optional
271
+ Whether to allow errors, by default True.
272
+
273
+ Raises
274
+ ------
275
+ RuntimeError
276
+ If the command fails and allow_error is False.
277
+ """
278
+ if not cwd:
279
+ cwd = Path.cwd()
280
+ # pylint: disable=broad-except
281
+ try:
282
+ subprocess.run(
283
+ cmd,
284
+ check=True,
285
+ cwd=cwd,
286
+ env=os.environ,
287
+ stdout=subprocess.PIPE,
288
+ stderr=subprocess.PIPE,
289
+ ) # nosemgrep # nosec
290
+ except BaseException as error: # pragma: no cover
291
+ if allow_error:
292
+ return
293
+ raise RuntimeError(f"Error running command: {error}") from error
@@ -0,0 +1,14 @@
1
+ """Tools for exporting agents, models, skills and chats to strings."""
2
+
3
+ from .flow import export_flow
4
+ from .models import export_models
5
+ from .skills import export_skills
6
+ from .utils import comment, get_valid_instance_name
7
+
8
+ __all__ = [
9
+ "export_flow",
10
+ "comment",
11
+ "get_valid_instance_name",
12
+ "export_models",
13
+ "export_skills",
14
+ ]
@@ -0,0 +1,5 @@
1
+ """Agent related string generation functions."""
2
+
3
+ from .agent import export_agent
4
+
5
+ __all__ = ["export_agent"]