waldiez 0.1.6__py3-none-any.whl → 0.1.8__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.
- waldiez/__init__.py +0 -2
- waldiez/_version.py +1 -1
- waldiez/cli.py +10 -6
- waldiez/exporting/agents/rag_user/chroma_utils.py +1 -2
- waldiez/exporting/chats/nested.py +2 -12
- waldiez/exporting/utils/logging_utils.py +1 -0
- waldiez/io/__init__.py +142 -0
- waldiez/models/agents/agent/nested_chat.py +4 -8
- waldiez/models/waldiez.py +18 -11
- waldiez/runner.py +24 -10
- waldiez-0.1.8.dist-info/METADATA +241 -0
- {waldiez-0.1.6.dist-info → waldiez-0.1.8.dist-info}/RECORD +15 -19
- {waldiez-0.1.6.dist-info → waldiez-0.1.8.dist-info}/WHEEL +1 -1
- waldiez/io_stream.py +0 -181
- waldiez/stream/__init__.py +0 -7
- waldiez/stream/consumer.py +0 -139
- waldiez/stream/provider.py +0 -339
- waldiez/stream/server.py +0 -412
- waldiez-0.1.6.dist-info/METADATA +0 -180
- {waldiez-0.1.6.dist-info → waldiez-0.1.8.dist-info}/entry_points.txt +0 -0
- {waldiez-0.1.6.dist-info → waldiez-0.1.8.dist-info}/licenses/LICENSE +0 -0
waldiez/__init__.py
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from ._version import __version__
|
|
4
4
|
from .exporter import WaldiezExporter
|
|
5
|
-
from .io_stream import WaldiezIOStream
|
|
6
5
|
from .models import Waldiez
|
|
7
6
|
from .runner import WaldiezRunner
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
9
|
"Waldiez",
|
|
11
10
|
"WaldiezExporter",
|
|
12
|
-
"WaldiezIOStream",
|
|
13
11
|
"WaldiezRunner",
|
|
14
12
|
"__version__",
|
|
15
13
|
]
|
waldiez/_version.py
CHANGED
waldiez/cli.py
CHANGED
|
@@ -6,14 +6,15 @@ import logging
|
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any, Dict, Optional
|
|
10
|
-
|
|
11
|
-
from autogen import ChatResult # type: ignore[import-untyped]
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
12
10
|
|
|
13
11
|
from . import Waldiez, __version__
|
|
14
12
|
from .exporter import WaldiezExporter
|
|
15
13
|
from .runner import WaldiezRunner
|
|
16
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from autogen import ChatResult # type: ignore[import-untyped]
|
|
17
|
+
|
|
17
18
|
|
|
18
19
|
def get_parser() -> argparse.ArgumentParser:
|
|
19
20
|
"""Get the argument parser for the Waldiez package.
|
|
@@ -28,7 +29,7 @@ def get_parser() -> argparse.ArgumentParser:
|
|
|
28
29
|
prog="waldiez",
|
|
29
30
|
)
|
|
30
31
|
parser.add_argument(
|
|
31
|
-
"
|
|
32
|
+
"file",
|
|
32
33
|
type=str,
|
|
33
34
|
help="Path to the Waldiez flow (*.waldiez) file.",
|
|
34
35
|
)
|
|
@@ -66,7 +67,7 @@ def get_parser() -> argparse.ArgumentParser:
|
|
|
66
67
|
return parser
|
|
67
68
|
|
|
68
69
|
|
|
69
|
-
def _log_result(result: ChatResult) -> None:
|
|
70
|
+
def _log_result(result: "ChatResult") -> None:
|
|
70
71
|
"""Log the result of the Waldiez flow."""
|
|
71
72
|
logger = logging.getLogger("waldiez::cli")
|
|
72
73
|
logger.info("Chat History:\n")
|
|
@@ -94,9 +95,12 @@ def _run(data: Dict[str, Any], output_path: Optional[str]) -> None:
|
|
|
94
95
|
def main() -> None:
|
|
95
96
|
"""Parse the command line arguments and run the Waldiez flow."""
|
|
96
97
|
parser = get_parser()
|
|
98
|
+
if len(sys.argv) == 1:
|
|
99
|
+
parser.print_help()
|
|
100
|
+
sys.exit(0)
|
|
97
101
|
args = parser.parse_args()
|
|
98
102
|
logger = _get_logger()
|
|
99
|
-
waldiez_file: str = args.
|
|
103
|
+
waldiez_file: str = args.file
|
|
100
104
|
if not os.path.exists(waldiez_file):
|
|
101
105
|
logger.error("File not found: %s", waldiez_file)
|
|
102
106
|
sys.exit(1)
|
|
@@ -109,8 +109,7 @@ def get_chroma_db_args(
|
|
|
109
109
|
f" embedding_function={embedding_function_arg},\n"
|
|
110
110
|
)
|
|
111
111
|
# The RAG example:
|
|
112
|
-
# https://
|
|
113
|
-
# notebooks/agentchat_groupchat_RAG
|
|
112
|
+
# https://ag2ai.github.io/ag2/docs/notebooks/agentchat_groupchat_RAG/
|
|
114
113
|
# raises `InvalidCollectionException`: Collection groupchat does not exist.
|
|
115
114
|
# https://github.com/chroma-core/chroma/issues/861
|
|
116
115
|
# https://github.com/microsoft/autogen/issues/3551#issuecomment-2366930994
|
|
@@ -14,7 +14,6 @@ from .helpers import escape_summary_args_quotes
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def get_nested_chat_trigger_agent_names(
|
|
17
|
-
all_chats: List[WaldiezChat],
|
|
18
17
|
nested_chat: WaldiezAgentNestedChat,
|
|
19
18
|
agent_names: Dict[str, str],
|
|
20
19
|
) -> str:
|
|
@@ -22,8 +21,6 @@ def get_nested_chat_trigger_agent_names(
|
|
|
22
21
|
|
|
23
22
|
Parameters
|
|
24
23
|
----------
|
|
25
|
-
all_chats : List[WaldiezChat]
|
|
26
|
-
All the chats in the flow.
|
|
27
24
|
nested_chat : WaldiezAgentNestedChat
|
|
28
25
|
The nested chat.
|
|
29
26
|
agent_names : Dict[str, str]
|
|
@@ -34,14 +31,7 @@ def get_nested_chat_trigger_agent_names(
|
|
|
34
31
|
str
|
|
35
32
|
The trigger agent names.
|
|
36
33
|
"""
|
|
37
|
-
|
|
38
|
-
for message in nested_chat.triggered_by:
|
|
39
|
-
waldiez_chat = next(chat for chat in all_chats if chat.id == message.id)
|
|
40
|
-
if message.is_reply:
|
|
41
|
-
trigger_agent_ids.append(waldiez_chat.target)
|
|
42
|
-
else:
|
|
43
|
-
trigger_agent_ids.append(waldiez_chat.source)
|
|
44
|
-
agents = [agent_names[agent_id] for agent_id in trigger_agent_ids]
|
|
34
|
+
agents = [agent_names[agent_id] for agent_id in nested_chat.triggered_by]
|
|
45
35
|
trigger_string = f'{[", ".join(agents)]}'
|
|
46
36
|
return trigger_string.replace("'", '"')
|
|
47
37
|
|
|
@@ -228,7 +218,7 @@ def export_nested_chat(
|
|
|
228
218
|
use_suffix = len(agent.data.nested_chats) > 1
|
|
229
219
|
for index, entry in enumerate(agent.data.nested_chats):
|
|
230
220
|
trigger_names = get_nested_chat_trigger_agent_names(
|
|
231
|
-
|
|
221
|
+
nested_chat=entry, agent_names=agent_names
|
|
232
222
|
)
|
|
233
223
|
chat_queue, extra_methods = get_nested_chat_queue(
|
|
234
224
|
nested_chat=entry,
|
|
@@ -14,6 +14,7 @@ get_sqlite_to_csv_call_string
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
# Check issue:
|
|
17
|
+
# Also check if in ag2 this still applies
|
|
17
18
|
# https://github.com/microsoft/autogen/issues/2286
|
|
18
19
|
# we cannot log new agents if they have code_execution enabled
|
|
19
20
|
# we get `Path` is not JSON serializable (on code_executor)
|
waldiez/io/__init__.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Custom IOStream class to use with autogen.
|
|
2
|
+
|
|
3
|
+
It is meant to be used when we want to use custom
|
|
4
|
+
`print` and `input`. For example, when a websocket
|
|
5
|
+
is used to trigger a UI element that requires user input.
|
|
6
|
+
and sends back the user's input to the websocket. In the same
|
|
7
|
+
way, we can use it to forward what is meant to be printed.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import threading
|
|
11
|
+
from typing import Any, Callable, Optional
|
|
12
|
+
|
|
13
|
+
from autogen.io import IOStream # type: ignore[import-untyped]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WaldiezIOStream(IOStream):
|
|
17
|
+
"""Custom IOStream class to handle `print` and `input` functions."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
input_timeout: float = 60.0,
|
|
22
|
+
print_function: Optional[Callable[..., None]] = None,
|
|
23
|
+
on_prompt_input: Optional[Callable[[str], None]] = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Initialize the IOStream.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
input_timeout : float, optional
|
|
31
|
+
The input timeout in seconds, by default 60.0.
|
|
32
|
+
print_function : Optional[Callable[..., None]], optional
|
|
33
|
+
The function to handle print operations, by default None.
|
|
34
|
+
on_prompt_input : Optional[Callable[[str], None]], optional
|
|
35
|
+
The function to call for processing input prompts, by default None.
|
|
36
|
+
|
|
37
|
+
Notes
|
|
38
|
+
-----
|
|
39
|
+
- on_prompt_input: It does not return a string (like 'input' does).
|
|
40
|
+
Instead, it is meant to be used to forward the prompt somewhere else
|
|
41
|
+
(e.g., a websocket). When we get the input, we can call
|
|
42
|
+
`waldiez_io_stream.set_input(input_data)` with the input data.
|
|
43
|
+
"""
|
|
44
|
+
self.input_timeout = input_timeout # Timeout for input
|
|
45
|
+
self.print_function = print_function # Custom print handler
|
|
46
|
+
self._on_prompt_input = on_prompt_input # Custom input prompt handler
|
|
47
|
+
self.current_input: Optional[str] = None # Store the current input
|
|
48
|
+
self._input_event = threading.Event() # Event to signal input readiness
|
|
49
|
+
self.allow_input = True # Flag to allow or block input setting
|
|
50
|
+
|
|
51
|
+
def print(
|
|
52
|
+
self,
|
|
53
|
+
*objects: Any,
|
|
54
|
+
sep: str = " ",
|
|
55
|
+
end: str = "\n",
|
|
56
|
+
flush: bool = False,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Mock the `print` function.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
objects : Any
|
|
64
|
+
The objects to print.
|
|
65
|
+
sep : str, optional
|
|
66
|
+
The separator, by default " ".
|
|
67
|
+
end : str, optional
|
|
68
|
+
The ending string, by default a new line.
|
|
69
|
+
flush : bool, optional
|
|
70
|
+
Whether to flush the output, by default False.
|
|
71
|
+
"""
|
|
72
|
+
print_function: Callable[..., None] = self.print_function or print
|
|
73
|
+
print_function(*objects, sep=sep, end=end, flush=flush)
|
|
74
|
+
|
|
75
|
+
def input(self, prompt: str = "", *, password: bool = False) -> str:
|
|
76
|
+
"""
|
|
77
|
+
Mock the `input` function with optional timeout handling.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
prompt : str, optional
|
|
82
|
+
The prompt to show, by default "".
|
|
83
|
+
password : bool, optional
|
|
84
|
+
Whether to hide the input as password (not used), by default False.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
str
|
|
89
|
+
The user's input or '\n' if timeout occurs.
|
|
90
|
+
"""
|
|
91
|
+
_prompt = prompt or "Your input:"
|
|
92
|
+
if _prompt in (">", "> "): # pragma: no cover
|
|
93
|
+
_prompt = "Your input:"
|
|
94
|
+
if prompt:
|
|
95
|
+
if self._on_prompt_input:
|
|
96
|
+
self._on_prompt_input(_prompt)
|
|
97
|
+
self.print(_prompt, end="")
|
|
98
|
+
|
|
99
|
+
# Only reset if no input is currently set
|
|
100
|
+
# e.g. handle the case when we call set_input(..) before input(..)
|
|
101
|
+
already_set = self._input_event.is_set()
|
|
102
|
+
if not self._input_event.is_set():
|
|
103
|
+
self.current_input = None # Reset previous input
|
|
104
|
+
self._input_event.clear() # Clear the event before waiting
|
|
105
|
+
|
|
106
|
+
# Wait for input or timeout
|
|
107
|
+
if not self._input_event.wait(self.input_timeout):
|
|
108
|
+
# Timeout occurred, return what we have so far
|
|
109
|
+
to_return = (
|
|
110
|
+
self.current_input if self.current_input is not None else "\n"
|
|
111
|
+
)
|
|
112
|
+
self.current_input = None
|
|
113
|
+
self._input_event.clear()
|
|
114
|
+
# if we had already set the input, return it
|
|
115
|
+
return to_return if already_set else "\n"
|
|
116
|
+
|
|
117
|
+
# Input is ready, return it
|
|
118
|
+
to_return = (
|
|
119
|
+
self.current_input if self.current_input is not None else "\n"
|
|
120
|
+
)
|
|
121
|
+
self.current_input = None
|
|
122
|
+
self._input_event.clear() # Clear the event after waiting
|
|
123
|
+
return to_return
|
|
124
|
+
|
|
125
|
+
def set_input(self, value: str) -> None:
|
|
126
|
+
"""
|
|
127
|
+
Set the input value and signal that input is ready.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
value : str
|
|
132
|
+
The value to set as input.
|
|
133
|
+
"""
|
|
134
|
+
if self.allow_input: # Respect the allow_input flag
|
|
135
|
+
self.current_input = value
|
|
136
|
+
self._input_event.set() # Signal that input is ready
|
|
137
|
+
|
|
138
|
+
def reset_state(self) -> None:
|
|
139
|
+
"""Reset the IOStream state for testing."""
|
|
140
|
+
self.current_input = None
|
|
141
|
+
self._input_event.clear()
|
|
142
|
+
self.allow_input = True # Re-enable input setting
|
|
@@ -40,22 +40,18 @@ class WaldiezAgentNestedChat(WaldiezBase):
|
|
|
40
40
|
|
|
41
41
|
Attributes
|
|
42
42
|
----------
|
|
43
|
-
triggered_by : List[
|
|
44
|
-
A list of
|
|
45
|
-
the triggering of the nested chat.
|
|
43
|
+
triggered_by : List[str]
|
|
44
|
+
A list of agent ids that trigger the nested chat.
|
|
46
45
|
messages : List[WaldiezAgentNestedChatMessage]
|
|
47
46
|
The list of messages (chat ids and 'is_reply'z)
|
|
48
47
|
to include the in the nested chat registration.
|
|
49
48
|
"""
|
|
50
49
|
|
|
51
50
|
triggered_by: Annotated[
|
|
52
|
-
List[
|
|
51
|
+
List[str],
|
|
53
52
|
Field(
|
|
54
53
|
title="Triggered By",
|
|
55
|
-
description=(
|
|
56
|
-
"A list of chats (id and is_reply) to determine"
|
|
57
|
-
"the triggering of the nested chat."
|
|
58
|
-
),
|
|
54
|
+
description=("A list of agent ids that trigger the nested chat."),
|
|
59
55
|
alias="triggeredBy",
|
|
60
56
|
default_factory=list,
|
|
61
57
|
),
|
waldiez/models/waldiez.py
CHANGED
|
@@ -7,12 +7,10 @@ definitions and their optional additional skills to be used.
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
+
from functools import cache
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
|
|
12
13
|
|
|
13
|
-
# let's be strict with autogen version
|
|
14
|
-
from autogen.version import __version__ as autogen_version # type: ignore
|
|
15
|
-
|
|
16
14
|
from .agents import WaldiezAgent
|
|
17
15
|
from .chat import WaldiezChat
|
|
18
16
|
from .flow import WaldiezFlow
|
|
@@ -203,21 +201,20 @@ class Waldiez:
|
|
|
203
201
|
@property
|
|
204
202
|
def requirements(self) -> List[str]:
|
|
205
203
|
"""Get the flow requirements."""
|
|
204
|
+
autogen_version = _get_autogen_version()
|
|
206
205
|
requirements_list = filter(
|
|
207
206
|
lambda requirement: not (
|
|
208
207
|
requirement.startswith("pyautogen")
|
|
209
|
-
or requirement.startswith("
|
|
208
|
+
or requirement.startswith("ag2")
|
|
210
209
|
),
|
|
211
210
|
self.flow.requirements,
|
|
212
211
|
)
|
|
213
212
|
requirements = set(requirements_list)
|
|
214
213
|
if self.has_rag_agents:
|
|
215
|
-
requirements.add(
|
|
216
|
-
f"autogen-agentchat[retrievechat]=={autogen_version}"
|
|
217
|
-
)
|
|
214
|
+
requirements.add(f"ag2[retrievechat]=={autogen_version}")
|
|
218
215
|
else:
|
|
219
|
-
requirements.add(f"
|
|
220
|
-
# ref: https://github.com/
|
|
216
|
+
requirements.add(f"ag2=={autogen_version}")
|
|
217
|
+
# ref: https://github.com/ag2ai/ag2/blob/main/setup.py
|
|
221
218
|
models_with_additional_requirements = [
|
|
222
219
|
"together",
|
|
223
220
|
"gemini",
|
|
@@ -230,8 +227,7 @@ class Waldiez:
|
|
|
230
227
|
for model in self.models:
|
|
231
228
|
if model.data.api_type in models_with_additional_requirements:
|
|
232
229
|
requirements.add(
|
|
233
|
-
f"
|
|
234
|
-
f"{autogen_version}"
|
|
230
|
+
f"ag2[{model.data.api_type}]==" f"{autogen_version}"
|
|
235
231
|
)
|
|
236
232
|
return list(requirements)
|
|
237
233
|
|
|
@@ -299,3 +295,14 @@ def _get_flow(
|
|
|
299
295
|
if "requirements" not in data:
|
|
300
296
|
data["requirements"] = []
|
|
301
297
|
return data
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@cache
|
|
301
|
+
def _get_autogen_version() -> str:
|
|
302
|
+
"""Get the autogen version."""
|
|
303
|
+
# pylint: disable=import-outside-toplevel
|
|
304
|
+
try:
|
|
305
|
+
from autogen.version import __version__ as atg_version # type: ignore
|
|
306
|
+
except ImportError: # pragma: no cover
|
|
307
|
+
atg_version = "0.0.0"
|
|
308
|
+
return atg_version
|
waldiez/runner.py
CHANGED
|
@@ -19,14 +19,25 @@ from contextlib import contextmanager
|
|
|
19
19
|
from contextvars import ContextVar
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from types import TracebackType
|
|
22
|
-
from typing import
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
from typing import (
|
|
23
|
+
TYPE_CHECKING,
|
|
24
|
+
Callable,
|
|
25
|
+
Dict,
|
|
26
|
+
Iterator,
|
|
27
|
+
List,
|
|
28
|
+
Optional,
|
|
29
|
+
Type,
|
|
30
|
+
Union,
|
|
31
|
+
)
|
|
25
32
|
|
|
26
33
|
from .exporter import WaldiezExporter
|
|
27
|
-
from .io_stream import WaldiezIOStream
|
|
28
34
|
from .models.waldiez import Waldiez
|
|
29
35
|
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from autogen import ChatResult # type: ignore
|
|
38
|
+
|
|
39
|
+
from .io import WaldiezIOStream
|
|
40
|
+
|
|
30
41
|
|
|
31
42
|
@contextmanager
|
|
32
43
|
def _chdir(to: Union[str, Path]) -> Iterator[None]:
|
|
@@ -60,7 +71,7 @@ class WaldiezRunner:
|
|
|
60
71
|
self._waldiez = waldiez
|
|
61
72
|
self._running = False
|
|
62
73
|
self._file_path = file_path
|
|
63
|
-
self._stream: ContextVar[Optional[WaldiezIOStream]] = ContextVar(
|
|
74
|
+
self._stream: ContextVar[Optional["WaldiezIOStream"]] = ContextVar(
|
|
64
75
|
"waldiez_stream", default=None
|
|
65
76
|
)
|
|
66
77
|
self._exporter = WaldiezExporter(waldiez)
|
|
@@ -225,9 +236,9 @@ class WaldiezRunner:
|
|
|
225
236
|
|
|
226
237
|
def _do_run(
|
|
227
238
|
self, output_path: Optional[Union[str, Path]]
|
|
228
|
-
) -> Union[ChatResult, List[ChatResult]]:
|
|
239
|
+
) -> Union["ChatResult", List["ChatResult"]]:
|
|
229
240
|
"""Run the Waldiez workflow."""
|
|
230
|
-
results: Union[ChatResult, List[ChatResult]] = []
|
|
241
|
+
results: Union["ChatResult", List["ChatResult"]] = []
|
|
231
242
|
temp_dir = Path(tempfile.mkdtemp())
|
|
232
243
|
file_name = "flow.py" if not output_path else Path(output_path).name
|
|
233
244
|
if file_name.endswith((".json", ".waldiez")):
|
|
@@ -259,19 +270,22 @@ class WaldiezRunner:
|
|
|
259
270
|
def _run(
|
|
260
271
|
self,
|
|
261
272
|
output_path: Optional[Union[str, Path]],
|
|
262
|
-
) -> Union[ChatResult, List[ChatResult]]:
|
|
273
|
+
) -> Union["ChatResult", List["ChatResult"]]:
|
|
263
274
|
self._install_requirements()
|
|
264
275
|
token = self._stream.get()
|
|
265
276
|
if token is not None:
|
|
277
|
+
# pylint: disable=import-outside-toplevel
|
|
278
|
+
from .io import WaldiezIOStream
|
|
279
|
+
|
|
266
280
|
with WaldiezIOStream.set_default(token):
|
|
267
281
|
return self._do_run(output_path)
|
|
268
282
|
return self._do_run(output_path)
|
|
269
283
|
|
|
270
284
|
def run(
|
|
271
285
|
self,
|
|
272
|
-
stream: Optional[WaldiezIOStream] = None,
|
|
286
|
+
stream: Optional["WaldiezIOStream"] = None,
|
|
273
287
|
output_path: Optional[Union[str, Path]] = None,
|
|
274
|
-
) -> Union[ChatResult, List[ChatResult]]:
|
|
288
|
+
) -> Union["ChatResult", List["ChatResult"]]:
|
|
275
289
|
"""Run the Waldiez workflow.
|
|
276
290
|
|
|
277
291
|
Parameters
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: waldiez
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: waldiez
|
|
5
|
+
Project-URL: homepage, https://waldiez.github.io/waldiez/
|
|
6
|
+
Project-URL: repository, https://github.com/waldiez/waldiez.git
|
|
7
|
+
Author-email: Panagiotis Kasnesis <pkasnesis@thingenious.io>, Lazaros Toumanidis <laztoum@protonmail.com>
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: <3.13,>=3.10
|
|
19
|
+
Requires-Dist: ag2==0.3.2
|
|
20
|
+
Requires-Dist: jupytext
|
|
21
|
+
Provides-Extra: ag2-extras
|
|
22
|
+
Requires-Dist: ag2[anthropic]==0.3.2; extra == 'ag2-extras'
|
|
23
|
+
Requires-Dist: ag2[bedrock]==0.3.2; extra == 'ag2-extras'
|
|
24
|
+
Requires-Dist: ag2[gemini]==0.3.2; extra == 'ag2-extras'
|
|
25
|
+
Requires-Dist: ag2[groq]==0.3.2; extra == 'ag2-extras'
|
|
26
|
+
Requires-Dist: ag2[mistral]==0.3.2; extra == 'ag2-extras'
|
|
27
|
+
Requires-Dist: ag2[retrievechat-mongodb]==0.3.2; extra == 'ag2-extras'
|
|
28
|
+
Requires-Dist: ag2[retrievechat-pgvector]==0.3.2; extra == 'ag2-extras'
|
|
29
|
+
Requires-Dist: ag2[retrievechat-qdrant]==0.3.2; extra == 'ag2-extras'
|
|
30
|
+
Requires-Dist: ag2[retrievechat]==0.3.2; extra == 'ag2-extras'
|
|
31
|
+
Requires-Dist: ag2[together]==0.3.2; extra == 'ag2-extras'
|
|
32
|
+
Requires-Dist: ag2[websurfer]==0.3.2; extra == 'ag2-extras'
|
|
33
|
+
Requires-Dist: chromadb==0.5.3; extra == 'ag2-extras'
|
|
34
|
+
Requires-Dist: fastembed==0.4.2; extra == 'ag2-extras'
|
|
35
|
+
Requires-Dist: pgvector>=0.2.5; extra == 'ag2-extras'
|
|
36
|
+
Requires-Dist: psycopg[binary]>=3.1.18; extra == 'ag2-extras'
|
|
37
|
+
Requires-Dist: pymongo==4.10.1; extra == 'ag2-extras'
|
|
38
|
+
Requires-Dist: qdrant-client==1.12.1; extra == 'ag2-extras'
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: autoflake==2.3.1; extra == 'dev'
|
|
41
|
+
Requires-Dist: bandit==1.7.10; extra == 'dev'
|
|
42
|
+
Requires-Dist: black[jupyter]==24.10.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: flake8==7.1.1; extra == 'dev'
|
|
44
|
+
Requires-Dist: isort==5.13.2; extra == 'dev'
|
|
45
|
+
Requires-Dist: mypy==1.13.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: pre-commit==4.0.1; extra == 'dev'
|
|
47
|
+
Requires-Dist: pydocstyle==6.3.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: pylint==3.3.1; extra == 'dev'
|
|
49
|
+
Requires-Dist: python-dotenv==1.0.1; extra == 'dev'
|
|
50
|
+
Requires-Dist: ruff==0.7.4; extra == 'dev'
|
|
51
|
+
Requires-Dist: types-pyyaml==6.0.12; extra == 'dev'
|
|
52
|
+
Requires-Dist: yamllint==1.35.1; extra == 'dev'
|
|
53
|
+
Provides-Extra: docs
|
|
54
|
+
Requires-Dist: mdx-include==1.4.2; extra == 'docs'
|
|
55
|
+
Requires-Dist: mdx-truly-sane-lists==1.3; extra == 'docs'
|
|
56
|
+
Requires-Dist: mkdocs-jupyter==0.25.1; extra == 'docs'
|
|
57
|
+
Requires-Dist: mkdocs-macros-plugin==1.3.7; extra == 'docs'
|
|
58
|
+
Requires-Dist: mkdocs-material==9.5.44; extra == 'docs'
|
|
59
|
+
Requires-Dist: mkdocs-minify-html-plugin==0.2.3; extra == 'docs'
|
|
60
|
+
Requires-Dist: mkdocs==1.6.1; extra == 'docs'
|
|
61
|
+
Requires-Dist: mkdocstrings-python==1.12.2; extra == 'docs'
|
|
62
|
+
Requires-Dist: mkdocstrings[crystal,python]==0.27.0; extra == 'docs'
|
|
63
|
+
Provides-Extra: test
|
|
64
|
+
Requires-Dist: pytest-cov==6.0.0; extra == 'test'
|
|
65
|
+
Requires-Dist: pytest-html==4.1.1; extra == 'test'
|
|
66
|
+
Requires-Dist: pytest-sugar==1.0.0; extra == 'test'
|
|
67
|
+
Requires-Dist: pytest-timeout==2.3.1; extra == 'test'
|
|
68
|
+
Requires-Dist: pytest-xdist==3.6.1; extra == 'test'
|
|
69
|
+
Requires-Dist: pytest==8.3.3; extra == 'test'
|
|
70
|
+
Description-Content-Type: text/markdown
|
|
71
|
+
|
|
72
|
+
# Waldiez
|
|
73
|
+
|
|
74
|
+
 [](https://coveralls.io/github/waldiez/waldiez) [](https://badge.fury.io/py/waldiez)
|
|
75
|
+
|
|
76
|
+
Translate a Waldiez flow:
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
To a python script or a jupyter notebook with the corresponding [ag2](https://github.com/ag2ai/ag2/) agents and chats.
|
|
81
|
+
|
|
82
|
+
## Features
|
|
83
|
+
|
|
84
|
+
- Export .waldiez flows to .py or .ipynb
|
|
85
|
+
- Run a .waldiez flow
|
|
86
|
+
- Include a `logs` folder with the logs of the flow in csv format
|
|
87
|
+
- Provide a custom [IOSStream](https://ag2ai.github.io/ag2/docs/reference/io/base#iostream) to handle input and output.
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
On PyPI:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python -m pip install waldiez
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
From the repository:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
python -m pip install git+https://github.com/waldiez/waldiez.git
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
### CLI
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Export a Waldiez flow to a python script or a jupyter notebook
|
|
109
|
+
waldiez --export /path/to/a/flow.waldiez --output /path/to/an/output/flow[.py|.ipynb]
|
|
110
|
+
# Export and run the script, optionally force generation if the output file already exists
|
|
111
|
+
waldiez /path/to/a/flow.waldiez --output /path/to/an/output/flow[.py] [--force]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Using docker/podman
|
|
115
|
+
|
|
116
|
+
```shell
|
|
117
|
+
CONTAINER_COMMAND=docker # or podman
|
|
118
|
+
# pull the image
|
|
119
|
+
$CONTAINER_COMMAND pull waldiez/waldiez
|
|
120
|
+
# Export a Waldiez flow to a python script or a jupyter notebook
|
|
121
|
+
$CONTAINER_COMMAND run \
|
|
122
|
+
--rm \
|
|
123
|
+
-v /path/to/a/flow.waldiez:/flow.waldiez \
|
|
124
|
+
-v /path/to/an/output:/output \
|
|
125
|
+
waldiez/waldiez --export /flow.waldiez --output /output/flow[.py|.ipynb]
|
|
126
|
+
|
|
127
|
+
# with selinux and/or podman, you might get permission (or file not found) errors, so you can try:
|
|
128
|
+
$CONTAINER_COMMAND run \
|
|
129
|
+
--rm \
|
|
130
|
+
-v /path/to/a/flow.waldiez:/flow.waldiez \
|
|
131
|
+
-v /path/to/an/output:/output \
|
|
132
|
+
--userns=keep-id \
|
|
133
|
+
--security-opt label=disable \
|
|
134
|
+
waldiez/waldiez --export /flow.waldiez --output /output/flow[.py|.ipynb]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```shell
|
|
138
|
+
# Export and run the script
|
|
139
|
+
$CONTAINER_COMMAND run --rm -v /path/to/a/flow.waldiez:/flow.waldiez -v /path/to/an/output:/output waldiez/waldiez /flow.waldiez --output /output/output[.py]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### As a library
|
|
143
|
+
|
|
144
|
+
#### Export a flow
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
# Export a Waldiez flow to a python script or a jupyter notebook
|
|
148
|
+
from waldiez import WaldiezExporter
|
|
149
|
+
flow_path = "/path/to/a/flow.waldiez"
|
|
150
|
+
output_path = "/path/to/an/output.py" # or .ipynb
|
|
151
|
+
exporter = WaldiezExporter.load(flow_path)
|
|
152
|
+
exporter.export(output_path)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Run a flow
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
# Run a flow
|
|
159
|
+
from waldiez import WaldiezRunner
|
|
160
|
+
flow_path = "/path/to/a/flow.waldiez"
|
|
161
|
+
output_path = "/path/to/an/output.py"
|
|
162
|
+
runner = WaldiezRunner.load(flow_path)
|
|
163
|
+
runner.run(output_path=output_path)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Run a flow with a custom IOStream
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# Run the flow with a custom IOStream
|
|
170
|
+
# In case the standard 'input' and 'print' functions cannot be used
|
|
171
|
+
import time
|
|
172
|
+
import threading
|
|
173
|
+
|
|
174
|
+
from typing import Any
|
|
175
|
+
|
|
176
|
+
from waldiez import WaldiezRunner
|
|
177
|
+
from waldiez.io import WaldiezIOStream
|
|
178
|
+
|
|
179
|
+
flow_path = "/path/to/a/flow.waldiez"
|
|
180
|
+
output_path = "/path/to/an/output.py"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def custom_print_function(*args: Any, sep: str = " ", **kwargs: Any) -> None:
|
|
184
|
+
"""Custom print function."""
|
|
185
|
+
print(*args, sep=sep, **kwargs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# Custom input handler
|
|
189
|
+
class InputProcessorWrapper:
|
|
190
|
+
"""Wrapper input processor.
|
|
191
|
+
|
|
192
|
+
To manage the interaction between the custom input processor and IOStream.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
def __init__(self):
|
|
196
|
+
self.stream = None # Placeholder for the WaldiezIOStream instance
|
|
197
|
+
self.lock = threading.Lock() # Ensure thread-safe operations
|
|
198
|
+
|
|
199
|
+
def custom_input_processor(self, prompt: str) -> None:
|
|
200
|
+
"""Simulate external input and send it back to the IOStream."""
|
|
201
|
+
def external_input_simulation():
|
|
202
|
+
with self.lock: # Ensure thread-safe access
|
|
203
|
+
time.sleep(2) # Simulate delay for network input
|
|
204
|
+
if self.stream:
|
|
205
|
+
self.stream.set_input("Simulated external input")
|
|
206
|
+
else:
|
|
207
|
+
raise RuntimeError("Stream reference not set!")
|
|
208
|
+
|
|
209
|
+
threading.Thread(target=external_input_simulation, daemon=True).start()
|
|
210
|
+
|
|
211
|
+
def set_stream(self, stream: "WaldiezIOStream"):
|
|
212
|
+
"""Set the WaldiezIOStream instance."""
|
|
213
|
+
with self.lock: # Ensure thread-safe setting of the stream
|
|
214
|
+
self.stream = stream
|
|
215
|
+
|
|
216
|
+
processor_wrapper = InputProcessorWrapper()
|
|
217
|
+
|
|
218
|
+
stream = WaldiezIOStream(
|
|
219
|
+
input_timeout=30,
|
|
220
|
+
print_function=
|
|
221
|
+
on_prompt_input=processor_wrapper.custom_input_processor,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Link the processor wrapper to the WaldiezIOStream instance
|
|
225
|
+
processor_wrapper.set_stream(custom_stream)
|
|
226
|
+
|
|
227
|
+
with WaldiezIOStream.set_default(io_stream):
|
|
228
|
+
runner = WaldiezRunner.load(flow_path)
|
|
229
|
+
runner.run(stream=io_stream, output_path=output_path)
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Tools
|
|
234
|
+
|
|
235
|
+
- [ag2 (formerly AutoGen)](https://github.com/ag2ai/ag2)
|
|
236
|
+
- [juptytext](https://github.com/mwouts/jupytext)
|
|
237
|
+
- [pydantic](https://github.com/pydantic/pydantic)
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/waldiez/waldiez/blob/main/LICENSE) file for details.
|