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.
- waldiez/__init__.py +15 -0
- waldiez/__main__.py +6 -0
- waldiez/_version.py +3 -0
- waldiez/cli.py +162 -0
- waldiez/exporter.py +293 -0
- waldiez/exporting/__init__.py +14 -0
- waldiez/exporting/agents/__init__.py +5 -0
- waldiez/exporting/agents/agent.py +229 -0
- waldiez/exporting/agents/agent_skills.py +67 -0
- waldiez/exporting/agents/code_execution.py +67 -0
- waldiez/exporting/agents/group_manager.py +209 -0
- waldiez/exporting/agents/llm_config.py +53 -0
- waldiez/exporting/agents/rag_user/__init__.py +5 -0
- waldiez/exporting/agents/rag_user/chroma_utils.py +134 -0
- waldiez/exporting/agents/rag_user/mongo_utils.py +83 -0
- waldiez/exporting/agents/rag_user/pgvector_utils.py +93 -0
- waldiez/exporting/agents/rag_user/qdrant_utils.py +112 -0
- waldiez/exporting/agents/rag_user/rag_user.py +165 -0
- waldiez/exporting/agents/rag_user/vector_db.py +119 -0
- waldiez/exporting/agents/teachability.py +37 -0
- waldiez/exporting/agents/termination_message.py +45 -0
- waldiez/exporting/chats/__init__.py +14 -0
- waldiez/exporting/chats/chats.py +46 -0
- waldiez/exporting/chats/helpers.py +395 -0
- waldiez/exporting/chats/nested.py +264 -0
- waldiez/exporting/flow/__init__.py +5 -0
- waldiez/exporting/flow/def_main.py +37 -0
- waldiez/exporting/flow/flow.py +185 -0
- waldiez/exporting/models/__init__.py +193 -0
- waldiez/exporting/skills/__init__.py +128 -0
- waldiez/exporting/utils/__init__.py +34 -0
- waldiez/exporting/utils/comments.py +136 -0
- waldiez/exporting/utils/importing.py +267 -0
- waldiez/exporting/utils/logging_utils.py +203 -0
- waldiez/exporting/utils/method_utils.py +35 -0
- waldiez/exporting/utils/naming.py +127 -0
- waldiez/exporting/utils/object_string.py +81 -0
- waldiez/io_stream.py +181 -0
- waldiez/models/__init__.py +107 -0
- waldiez/models/agents/__init__.py +65 -0
- waldiez/models/agents/agent/__init__.py +21 -0
- waldiez/models/agents/agent/agent.py +190 -0
- waldiez/models/agents/agent/agent_data.py +162 -0
- waldiez/models/agents/agent/code_execution.py +71 -0
- waldiez/models/agents/agent/linked_skill.py +30 -0
- waldiez/models/agents/agent/nested_chat.py +73 -0
- waldiez/models/agents/agent/teachability.py +68 -0
- waldiez/models/agents/agent/termination_message.py +167 -0
- waldiez/models/agents/agents.py +129 -0
- waldiez/models/agents/assistant/__init__.py +6 -0
- waldiez/models/agents/assistant/assistant.py +41 -0
- waldiez/models/agents/assistant/assistant_data.py +29 -0
- waldiez/models/agents/group_manager/__init__.py +19 -0
- waldiez/models/agents/group_manager/group_manager.py +87 -0
- waldiez/models/agents/group_manager/group_manager_data.py +91 -0
- waldiez/models/agents/group_manager/speakers.py +211 -0
- waldiez/models/agents/rag_user/__init__.py +26 -0
- waldiez/models/agents/rag_user/rag_user.py +58 -0
- waldiez/models/agents/rag_user/rag_user_data.py +32 -0
- waldiez/models/agents/rag_user/retrieve_config.py +592 -0
- waldiez/models/agents/rag_user/vector_db_config.py +162 -0
- waldiez/models/agents/user_proxy/__init__.py +6 -0
- waldiez/models/agents/user_proxy/user_proxy.py +41 -0
- waldiez/models/agents/user_proxy/user_proxy_data.py +30 -0
- waldiez/models/chat/__init__.py +22 -0
- waldiez/models/chat/chat.py +129 -0
- waldiez/models/chat/chat_data.py +326 -0
- waldiez/models/chat/chat_message.py +304 -0
- waldiez/models/chat/chat_nested.py +160 -0
- waldiez/models/chat/chat_summary.py +110 -0
- waldiez/models/common/__init__.py +38 -0
- waldiez/models/common/base.py +63 -0
- waldiez/models/common/method_utils.py +165 -0
- waldiez/models/flow/__init__.py +9 -0
- waldiez/models/flow/flow.py +302 -0
- waldiez/models/flow/flow_data.py +87 -0
- waldiez/models/model/__init__.py +11 -0
- waldiez/models/model/model.py +169 -0
- waldiez/models/model/model_data.py +86 -0
- waldiez/models/skill/__init__.py +9 -0
- waldiez/models/skill/skill.py +129 -0
- waldiez/models/skill/skill_data.py +37 -0
- waldiez/models/waldiez.py +301 -0
- waldiez/py.typed +0 -0
- waldiez/runner.py +304 -0
- waldiez/stream/__init__.py +7 -0
- waldiez/stream/consumer.py +139 -0
- waldiez/stream/provider.py +339 -0
- waldiez/stream/server.py +412 -0
- waldiez-0.1.0.dist-info/METADATA +181 -0
- waldiez-0.1.0.dist-info/RECORD +94 -0
- waldiez-0.1.0.dist-info/WHEEL +4 -0
- waldiez-0.1.0.dist-info/entry_points.txt +2 -0
- waldiez-0.1.0.dist-info/licenses/LICENSE +21 -0
waldiez/runner.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""Waldiez Flow runner.
|
|
2
|
+
|
|
3
|
+
Run a waldiez flow.
|
|
4
|
+
The flow is first converted to an autogen flow with agents, chats and skills.
|
|
5
|
+
We then chown to temporary directory, call the flow's `main()` and
|
|
6
|
+
return the results. Before running the flow, any additional environment
|
|
7
|
+
variables specified in the waldiez file are set.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import datetime
|
|
11
|
+
import importlib.util
|
|
12
|
+
import io
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess # nosemgrep # nosec
|
|
16
|
+
import sys
|
|
17
|
+
import tempfile
|
|
18
|
+
from contextlib import contextmanager
|
|
19
|
+
from contextvars import ContextVar
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from types import TracebackType
|
|
22
|
+
from typing import Callable, Dict, Iterator, List, Optional, Type, Union
|
|
23
|
+
|
|
24
|
+
from autogen import ChatResult # type: ignore
|
|
25
|
+
|
|
26
|
+
from .exporter import WaldiezExporter
|
|
27
|
+
from .io_stream import WaldiezIOStream
|
|
28
|
+
from .models.waldiez import Waldiez
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@contextmanager
|
|
32
|
+
def _chdir(to: Union[str, Path]) -> Iterator[None]:
|
|
33
|
+
"""Change the current working directory in a context.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
to : Union[str, Path]
|
|
38
|
+
The directory to change to.
|
|
39
|
+
|
|
40
|
+
Yields
|
|
41
|
+
------
|
|
42
|
+
Iterator[None]
|
|
43
|
+
The context manager.
|
|
44
|
+
"""
|
|
45
|
+
old_cwd = str(os.getcwd())
|
|
46
|
+
os.chdir(to)
|
|
47
|
+
try:
|
|
48
|
+
yield
|
|
49
|
+
finally:
|
|
50
|
+
os.chdir(old_cwd)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class WaldiezRunner:
|
|
54
|
+
"""Waldiez runner class."""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self, waldiez: Waldiez, file_path: Optional[Union[str, Path]] = None
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Initialize the Waldiez manager."""
|
|
60
|
+
self._waldiez = waldiez
|
|
61
|
+
self._running = False
|
|
62
|
+
self._file_path = file_path
|
|
63
|
+
self._stream: ContextVar[Optional[WaldiezIOStream]] = ContextVar(
|
|
64
|
+
"waldiez_stream", default=None
|
|
65
|
+
)
|
|
66
|
+
self._exporter = WaldiezExporter(waldiez)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def load(
|
|
70
|
+
cls,
|
|
71
|
+
waldiez_file: Union[str, Path],
|
|
72
|
+
name: Optional[str] = None,
|
|
73
|
+
description: Optional[str] = None,
|
|
74
|
+
tags: Optional[List[str]] = None,
|
|
75
|
+
requirements: Optional[List[str]] = None,
|
|
76
|
+
) -> "WaldiezRunner":
|
|
77
|
+
"""Create a WaldiezRunner instance from a file.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
waldiez_file : Union[str, Path]
|
|
82
|
+
The file path.
|
|
83
|
+
name : Optional[str], optional
|
|
84
|
+
The name of the Waldiez, by default None.
|
|
85
|
+
description : Optional[str], optional
|
|
86
|
+
The description of the Waldiez, by default None.
|
|
87
|
+
tags : Optional[List[str]], optional
|
|
88
|
+
The tags of the Waldiez, by default None.
|
|
89
|
+
requirements : Optional[List[str]], optional
|
|
90
|
+
The requirements of the Waldiez, by default None.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
WaldiezRunner
|
|
95
|
+
The Waldiez runner instance.
|
|
96
|
+
|
|
97
|
+
Raises
|
|
98
|
+
------
|
|
99
|
+
FileNotFoundError
|
|
100
|
+
If the file is not found.
|
|
101
|
+
RuntimeError
|
|
102
|
+
If the file is not a valid Waldiez file.
|
|
103
|
+
"""
|
|
104
|
+
waldiez = Waldiez.load(
|
|
105
|
+
waldiez_file,
|
|
106
|
+
name=name,
|
|
107
|
+
description=description,
|
|
108
|
+
tags=tags,
|
|
109
|
+
requirements=requirements,
|
|
110
|
+
)
|
|
111
|
+
return cls(waldiez, file_path=waldiez_file)
|
|
112
|
+
|
|
113
|
+
def __enter__(
|
|
114
|
+
self,
|
|
115
|
+
) -> "WaldiezRunner":
|
|
116
|
+
"""Enter the context manager."""
|
|
117
|
+
return self
|
|
118
|
+
|
|
119
|
+
def __exit__(
|
|
120
|
+
self,
|
|
121
|
+
exc_type: Type[BaseException],
|
|
122
|
+
exc_value: BaseException,
|
|
123
|
+
traceback: TracebackType,
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Exit the context manager."""
|
|
126
|
+
if self._running:
|
|
127
|
+
self._running = False
|
|
128
|
+
token = self._stream.get()
|
|
129
|
+
if token is not None:
|
|
130
|
+
self._stream.reset(token)
|
|
131
|
+
del token
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def waldiez(self) -> Waldiez:
|
|
135
|
+
"""Get the Waldiez instance."""
|
|
136
|
+
return self._waldiez
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def running(self) -> bool:
|
|
140
|
+
"""Get the running status."""
|
|
141
|
+
return self._running
|
|
142
|
+
|
|
143
|
+
def _get_print_function(self) -> Callable[..., None]:
|
|
144
|
+
"""Get the print function."""
|
|
145
|
+
token = self._stream.get()
|
|
146
|
+
if token is not None:
|
|
147
|
+
return token.print
|
|
148
|
+
return print
|
|
149
|
+
|
|
150
|
+
def _install_requirements(self) -> None:
|
|
151
|
+
"""Install the requirements for the flow."""
|
|
152
|
+
extra_requirements = set(
|
|
153
|
+
req for req in self.waldiez.requirements if req not in sys.modules
|
|
154
|
+
)
|
|
155
|
+
if extra_requirements:
|
|
156
|
+
print_function = self._get_print_function()
|
|
157
|
+
# pylint: disable=inconsistent-quotes
|
|
158
|
+
print_function(
|
|
159
|
+
f"Installing requirements: {', '.join(extra_requirements)}"
|
|
160
|
+
)
|
|
161
|
+
with subprocess.Popen(
|
|
162
|
+
[sys.executable, "-m", "pip", "install", *extra_requirements],
|
|
163
|
+
stdout=subprocess.PIPE,
|
|
164
|
+
stderr=subprocess.PIPE,
|
|
165
|
+
) as proc:
|
|
166
|
+
if proc.stdout:
|
|
167
|
+
for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
|
|
168
|
+
print_function(line.strip())
|
|
169
|
+
if proc.stderr:
|
|
170
|
+
for line in io.TextIOWrapper(proc.stderr, encoding="utf-8"):
|
|
171
|
+
print_function(line.strip())
|
|
172
|
+
print_function(
|
|
173
|
+
"Requirements installed.\n"
|
|
174
|
+
"NOTE: If new packages were added and you are using Jupyter, "
|
|
175
|
+
"you might need to restart the kernel."
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def _after_run(
|
|
179
|
+
self, temp_dir: Path, output_path: Optional[Union[str, Path]]
|
|
180
|
+
) -> None:
|
|
181
|
+
if output_path:
|
|
182
|
+
destination_dir = Path(output_path).parent
|
|
183
|
+
destination_dir = (
|
|
184
|
+
destination_dir
|
|
185
|
+
/ "waldiez_out"
|
|
186
|
+
/ datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
187
|
+
)
|
|
188
|
+
destination_dir.mkdir(parents=True, exist_ok=True)
|
|
189
|
+
# copy the contents of the temp dir to the destination dir
|
|
190
|
+
self._get_print_function()(
|
|
191
|
+
f"Copying the results to {destination_dir}"
|
|
192
|
+
)
|
|
193
|
+
for item in temp_dir.iterdir():
|
|
194
|
+
# skip cache files
|
|
195
|
+
if (
|
|
196
|
+
item.name.startswith("__pycache__")
|
|
197
|
+
or item.name.endswith(".pyc")
|
|
198
|
+
or item == ".cache"
|
|
199
|
+
):
|
|
200
|
+
continue
|
|
201
|
+
if item.is_file():
|
|
202
|
+
shutil.copy(item, destination_dir)
|
|
203
|
+
else:
|
|
204
|
+
shutil.copytree(item, destination_dir / item.name)
|
|
205
|
+
shutil.rmtree(temp_dir)
|
|
206
|
+
|
|
207
|
+
def _set_env_vars(self) -> Dict[str, str]:
|
|
208
|
+
"""Set environment variables and return the old ones (if any)."""
|
|
209
|
+
old_vars: Dict[str, str] = {}
|
|
210
|
+
for var_key, var_value in self.waldiez.get_flow_env_vars():
|
|
211
|
+
if var_key:
|
|
212
|
+
current = os.environ.get(var_key, "")
|
|
213
|
+
old_vars[var_key] = current
|
|
214
|
+
os.environ[var_key] = var_value
|
|
215
|
+
return old_vars
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def _reset_env_vars(old_vars: Dict[str, str]) -> None:
|
|
219
|
+
"""Reset the environment variables."""
|
|
220
|
+
for var_key, var_value in old_vars.items():
|
|
221
|
+
if not var_value:
|
|
222
|
+
os.environ.pop(var_key, "")
|
|
223
|
+
else:
|
|
224
|
+
os.environ[var_key] = var_value
|
|
225
|
+
|
|
226
|
+
def _do_run(
|
|
227
|
+
self, output_path: Optional[Union[str, Path]]
|
|
228
|
+
) -> Union[ChatResult, List[ChatResult]]:
|
|
229
|
+
"""Run the Waldiez workflow."""
|
|
230
|
+
results: Union[ChatResult, List[ChatResult]] = []
|
|
231
|
+
temp_dir = Path(tempfile.mkdtemp())
|
|
232
|
+
file_name = "flow.py" if not output_path else Path(output_path).name
|
|
233
|
+
if file_name.endswith((".json", ".waldiez")):
|
|
234
|
+
file_name = file_name.replace(".json", ".py").replace(
|
|
235
|
+
".waldiez", ".py"
|
|
236
|
+
)
|
|
237
|
+
if not file_name.endswith(".py"):
|
|
238
|
+
file_name += ".py"
|
|
239
|
+
module_name = file_name.replace(".py", "")
|
|
240
|
+
with _chdir(to=temp_dir):
|
|
241
|
+
self._exporter.export(Path(file_name))
|
|
242
|
+
spec = importlib.util.spec_from_file_location(
|
|
243
|
+
module_name, temp_dir / file_name
|
|
244
|
+
)
|
|
245
|
+
if not spec or not spec.loader:
|
|
246
|
+
raise ImportError("Could not import the flow")
|
|
247
|
+
sys.path.insert(0, str(temp_dir))
|
|
248
|
+
old_vars = self._set_env_vars()
|
|
249
|
+
module = importlib.util.module_from_spec(spec)
|
|
250
|
+
spec.loader.exec_module(module)
|
|
251
|
+
print_function = self._get_print_function()
|
|
252
|
+
print_function("Starting workflow...")
|
|
253
|
+
results = module.main()
|
|
254
|
+
sys.path.pop(0)
|
|
255
|
+
self._reset_env_vars(old_vars)
|
|
256
|
+
self._after_run(temp_dir, output_path)
|
|
257
|
+
return results
|
|
258
|
+
|
|
259
|
+
def _run(
|
|
260
|
+
self,
|
|
261
|
+
output_path: Optional[Union[str, Path]],
|
|
262
|
+
) -> Union[ChatResult, List[ChatResult]]:
|
|
263
|
+
self._install_requirements()
|
|
264
|
+
token = self._stream.get()
|
|
265
|
+
if token is not None:
|
|
266
|
+
with WaldiezIOStream.set_default(token):
|
|
267
|
+
return self._do_run(output_path)
|
|
268
|
+
return self._do_run(output_path)
|
|
269
|
+
|
|
270
|
+
def run(
|
|
271
|
+
self,
|
|
272
|
+
stream: Optional[WaldiezIOStream] = None,
|
|
273
|
+
output_path: Optional[Union[str, Path]] = None,
|
|
274
|
+
) -> Union[ChatResult, List[ChatResult]]:
|
|
275
|
+
"""Run the Waldiez workflow.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
stream : Optional[WaldiezIOStream], optional
|
|
280
|
+
The stream to use, by default None.
|
|
281
|
+
output_path : Optional[Union[str, Path]], optional
|
|
282
|
+
The output path, by default None.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
Union[ChatResult, List[ChatResult]]
|
|
287
|
+
The result of the chat(s).
|
|
288
|
+
|
|
289
|
+
Raises
|
|
290
|
+
------
|
|
291
|
+
RuntimeError
|
|
292
|
+
If the workflow is already running.
|
|
293
|
+
"""
|
|
294
|
+
if self._running is True:
|
|
295
|
+
raise RuntimeError("Workflow already running")
|
|
296
|
+
self._running = True
|
|
297
|
+
token = self._stream.set(stream)
|
|
298
|
+
file_path = output_path or self._file_path
|
|
299
|
+
try:
|
|
300
|
+
return self._run(file_path)
|
|
301
|
+
finally:
|
|
302
|
+
self._running = False
|
|
303
|
+
self._stream.reset(token)
|
|
304
|
+
del token
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""TCP socket input consumer.
|
|
2
|
+
|
|
3
|
+
It connects to a TCP server,
|
|
4
|
+
listens for `INPUT:` messages to get the user's input,
|
|
5
|
+
and sends `REQUEST:` messages to prompt the user,
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import socket
|
|
9
|
+
import time
|
|
10
|
+
from types import TracebackType
|
|
11
|
+
from typing import Optional, Type
|
|
12
|
+
|
|
13
|
+
END_OF_MESSAGE = b"\r\n"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TCPConsumer:
|
|
17
|
+
"""TCP socket input consumer."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self, host: str, port: int, timeout: Optional[float] = None
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Create a new input consumer.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
host : str
|
|
27
|
+
The host to connect to.
|
|
28
|
+
port : int
|
|
29
|
+
The port to connect to.
|
|
30
|
+
timeout : float, optional
|
|
31
|
+
The timeout for the consumer, by default None (no timeout).
|
|
32
|
+
"""
|
|
33
|
+
self.host = host
|
|
34
|
+
self.port = port
|
|
35
|
+
self.timeout = timeout
|
|
36
|
+
self._running = False
|
|
37
|
+
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
|
38
|
+
|
|
39
|
+
def __enter__(self) -> "TCPConsumer":
|
|
40
|
+
"""Enter the context."""
|
|
41
|
+
self.start()
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
def __exit__(
|
|
45
|
+
self,
|
|
46
|
+
exc_type: Optional[Type[BaseException]],
|
|
47
|
+
exc_value: Optional[BaseException],
|
|
48
|
+
traceback: Optional[TracebackType],
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Exit the context."""
|
|
51
|
+
self.stop()
|
|
52
|
+
|
|
53
|
+
def is_running(self) -> bool:
|
|
54
|
+
"""Check if the consumer is running.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
bool
|
|
59
|
+
True is the consumer is running, else False
|
|
60
|
+
"""
|
|
61
|
+
return self._running
|
|
62
|
+
|
|
63
|
+
def start(self) -> None:
|
|
64
|
+
"""Start the consumer."""
|
|
65
|
+
if self._running:
|
|
66
|
+
return
|
|
67
|
+
self._running = True
|
|
68
|
+
self.socket.connect((self.host, self.port))
|
|
69
|
+
self.socket.sendall("CONSUMER\r\n".encode("utf-8"))
|
|
70
|
+
|
|
71
|
+
def _get_response_no_timeout(self) -> Optional[str]:
|
|
72
|
+
"""Get the response."""
|
|
73
|
+
data = self.socket.recv(1024)
|
|
74
|
+
while not data.endswith(END_OF_MESSAGE):
|
|
75
|
+
data += self.socket.recv(1024)
|
|
76
|
+
if data.startswith(b"INPUT:"):
|
|
77
|
+
response = data[len(b"INPUT:") :]
|
|
78
|
+
if response.endswith(END_OF_MESSAGE):
|
|
79
|
+
response = response[: -len(END_OF_MESSAGE)]
|
|
80
|
+
return response.decode("utf-8")
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
def _get_response_with_timeout(self, timeout: float) -> Optional[str]:
|
|
84
|
+
"""Get the response using a timeout."""
|
|
85
|
+
start_time = time.monotonic()
|
|
86
|
+
data = b""
|
|
87
|
+
self.socket.settimeout(timeout)
|
|
88
|
+
while time.monotonic() - start_time < timeout:
|
|
89
|
+
try:
|
|
90
|
+
data += self.socket.recv(1024)
|
|
91
|
+
except TimeoutError:
|
|
92
|
+
return None
|
|
93
|
+
if data.endswith(END_OF_MESSAGE):
|
|
94
|
+
break
|
|
95
|
+
if not data:
|
|
96
|
+
return None
|
|
97
|
+
if data.startswith(b"INPUT:"):
|
|
98
|
+
response = data[len(b"INPUT:") :]
|
|
99
|
+
if response.endswith(END_OF_MESSAGE):
|
|
100
|
+
response = response[: -len(END_OF_MESSAGE)]
|
|
101
|
+
return response.decode("utf-8")
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
def get_response(self) -> Optional[str]:
|
|
105
|
+
"""Get the response.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
Optional[str]
|
|
110
|
+
The response if available, None otherwise.
|
|
111
|
+
"""
|
|
112
|
+
if not self._running:
|
|
113
|
+
self.start()
|
|
114
|
+
if self.timeout is None or self.timeout < 1:
|
|
115
|
+
return self._get_response_no_timeout()
|
|
116
|
+
return self._get_response_with_timeout(self.timeout)
|
|
117
|
+
|
|
118
|
+
def send_prompt(self, prompt: str) -> None:
|
|
119
|
+
"""Send a prompt.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
prompt : str
|
|
124
|
+
The prompt to send.
|
|
125
|
+
"""
|
|
126
|
+
if not self._running:
|
|
127
|
+
self.start()
|
|
128
|
+
message = f"REQUEST:{prompt}" + "\r\n"
|
|
129
|
+
self.socket.sendall(message.encode("utf-8"))
|
|
130
|
+
|
|
131
|
+
def stop(self) -> None:
|
|
132
|
+
"""Close the consumer."""
|
|
133
|
+
try:
|
|
134
|
+
self.socket.close()
|
|
135
|
+
except OSError:
|
|
136
|
+
pass
|
|
137
|
+
del self.socket
|
|
138
|
+
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
|
139
|
+
self._running = False
|