lionagi 0.2.1__py3-none-any.whl → 0.2.2__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.
- lionagi/__init__.py +2 -1
- lionagi/core/generic/graph.py +10 -3
- lionagi/core/generic/node.py +5 -1
- lionagi/core/report/base.py +1 -0
- lionagi/core/work/work_edge.py +96 -0
- lionagi/core/work/work_function.py +29 -6
- lionagi/core/work/work_function_node.py +44 -0
- lionagi/core/work/work_queue.py +20 -18
- lionagi/core/work/work_task.py +155 -0
- lionagi/core/work/worker.py +164 -39
- lionagi/core/work/worker_engine.py +179 -0
- lionagi/core/work/worklog.py +5 -3
- lionagi/tests/test_core/generic/test_structure.py +193 -0
- lionagi/tests/test_core/graph/__init__.py +0 -0
- lionagi/tests/test_core/graph/test_graph.py +70 -0
- lionagi/tests/test_core/graph/test_tree.py +75 -0
- lionagi/tests/test_core/mail/__init__.py +0 -0
- lionagi/tests/test_core/mail/test_mail.py +62 -0
- lionagi/tests/test_core/test_structure/__init__.py +0 -0
- lionagi/tests/test_core/test_structure/test_base_structure.py +196 -0
- lionagi/tests/test_core/test_structure/test_graph.py +54 -0
- lionagi/tests/test_core/test_structure/test_tree.py +48 -0
- lionagi/version.py +1 -1
- {lionagi-0.2.1.dist-info → lionagi-0.2.2.dist-info}/METADATA +5 -4
- {lionagi-0.2.1.dist-info → lionagi-0.2.2.dist-info}/RECORD +28 -14
- {lionagi-0.2.1.dist-info → lionagi-0.2.2.dist-info}/LICENSE +0 -0
- {lionagi-0.2.1.dist-info → lionagi-0.2.2.dist-info}/WHEEL +0 -0
- {lionagi-0.2.1.dist-info → lionagi-0.2.2.dist-info}/top_level.txt +0 -0
lionagi/core/work/worker.py
CHANGED
@@ -16,11 +16,8 @@ limitations under the License.
|
|
16
16
|
|
17
17
|
from abc import ABC
|
18
18
|
from functools import wraps
|
19
|
-
from typing import Callable
|
20
|
-
import asyncio
|
21
19
|
import inspect
|
22
20
|
from lionagi import logging as _logging
|
23
|
-
from lionagi.libs.ln_func_call import pcall
|
24
21
|
from lionagi.core.work.work_function import WorkFunction
|
25
22
|
from lionagi.core.work.work import Work
|
26
23
|
from lionagi.core.report.form import Form
|
@@ -39,12 +36,19 @@ class Worker(ABC):
|
|
39
36
|
"""
|
40
37
|
|
41
38
|
name: str = "Worker"
|
42
|
-
work_functions: dict[str, WorkFunction] = {}
|
43
39
|
|
44
40
|
def __init__(self, forms=None, default_form=None) -> None:
|
45
|
-
|
41
|
+
"""
|
42
|
+
Initializes a new instance of Worker.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
forms (dict[str, Form], optional): Dictionary mapping form identifier to Form objects.
|
46
|
+
default_form (str|None, optional): The default form to be used by the worker.
|
47
|
+
"""
|
48
|
+
self.work_functions: dict[str, WorkFunction] = {}
|
46
49
|
self.forms: dict[str, Form] = forms or {}
|
47
50
|
self.default_form = default_form
|
51
|
+
self._validate_worklink_functions()
|
48
52
|
|
49
53
|
async def stop(self):
|
50
54
|
"""
|
@@ -92,37 +96,58 @@ class Worker(ABC):
|
|
92
96
|
raise ValueError(f"Unable to change default form. Key {form_key} does not exist.")
|
93
97
|
self.default_form = self.forms[form_key]
|
94
98
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
99
|
+
def _get_decorated_functions(self, decorator_attr, name_only=True):
|
100
|
+
"""
|
101
|
+
Retrieves decorated functions based on the specified decorator attribute.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
decorator_attr (str): The attribute name of the decorator.
|
105
|
+
name_only (bool, optional): Whether to return only the function names. Defaults to True.
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
list: List of decorated function names or tuples containing function details.
|
109
|
+
"""
|
110
|
+
decorated_functions = []
|
111
|
+
for name, func in inspect.getmembers(self.__class__, predicate=inspect.isfunction):
|
112
|
+
if hasattr(func, decorator_attr):
|
113
|
+
if name_only:
|
114
|
+
decorated_functions.append(name)
|
115
|
+
else:
|
116
|
+
decorator_params = getattr(func, decorator_attr)
|
117
|
+
decorated_functions.append((name, func, decorator_params))
|
118
|
+
return decorated_functions
|
119
|
+
|
120
|
+
def _validate_worklink_functions(self):
|
121
|
+
"""
|
122
|
+
Validates worklink functions to ensure they have the required parameters.
|
123
|
+
"""
|
124
|
+
worklink_decorated_function = self._get_decorated_functions(decorator_attr="_worklink_decorator_params", name_only=False)
|
125
|
+
for func_name, func, _ in worklink_decorated_function:
|
126
|
+
func_signature = inspect.signature(func)
|
127
|
+
if "from_work" not in func_signature.parameters and "from_result" not in func_signature.parameters:
|
128
|
+
raise ValueError(f"Either \"from_work\" or \"from_result\" must be a parameter in function {func_name}")
|
129
|
+
|
130
|
+
def construct_all_work_functions(self):
|
131
|
+
"""
|
132
|
+
Constructs all work functions for the worker.
|
133
|
+
"""
|
134
|
+
if getattr(self, "work_functions", None) is None:
|
135
|
+
self.work_functions = {}
|
136
|
+
work_decorated_function = self._get_decorated_functions(decorator_attr="_work_decorator_params", name_only=False)
|
137
|
+
for func_name, func, dec_params in work_decorated_function:
|
138
|
+
if func_name not in self.work_functions:
|
139
|
+
self.work_functions[func_name] = WorkFunction(**dec_params)
|
140
|
+
|
141
|
+
async def _work_wrapper(
|
118
142
|
self,
|
119
143
|
*args,
|
120
|
-
|
144
|
+
function=None,
|
121
145
|
assignment=None,
|
122
146
|
form_param_key=None,
|
123
147
|
capacity=None,
|
124
148
|
retry_kwargs=None,
|
125
149
|
guidance=None,
|
150
|
+
refresh_time=1,
|
126
151
|
**kwargs,
|
127
152
|
):
|
128
153
|
"""
|
@@ -135,27 +160,28 @@ class Worker(ABC):
|
|
135
160
|
the function's signature. This parameter is used to locate and fill
|
136
161
|
the appropriate form according to the assignment. Raises an error
|
137
162
|
if the form parameter key is not found in the function's signature.
|
138
|
-
capacity (int): Capacity for the work
|
163
|
+
capacity (int): Capacity for the work queue batch processing.
|
139
164
|
retry_kwargs (dict): Retry arguments for the function.
|
140
165
|
guidance (str): Guidance or documentation for the function.
|
141
166
|
"""
|
142
167
|
if getattr(self, "work_functions", None) is None:
|
143
168
|
self.work_functions = {}
|
144
169
|
|
145
|
-
if
|
146
|
-
self.work_functions[
|
170
|
+
if function.__name__ not in self.work_functions:
|
171
|
+
self.work_functions[function.__name__] = WorkFunction(
|
147
172
|
assignment=assignment,
|
148
|
-
function=
|
173
|
+
function=function,
|
149
174
|
retry_kwargs=retry_kwargs or {},
|
150
|
-
guidance=guidance,
|
175
|
+
guidance=guidance or function.__doc__,
|
151
176
|
capacity=capacity,
|
177
|
+
refresh_time=refresh_time
|
152
178
|
)
|
153
179
|
|
154
|
-
work_func: WorkFunction = self.work_functions[
|
180
|
+
work_func: WorkFunction = self.work_functions[function.__name__]
|
155
181
|
|
156
182
|
# locate form that should be filled according to the assignment
|
157
183
|
if form_param_key:
|
158
|
-
func_signature = inspect.signature(
|
184
|
+
func_signature = inspect.signature(function)
|
159
185
|
if form_param_key not in func_signature.parameters:
|
160
186
|
raise KeyError(f"Failed to locate form. \"{form_param_key}\" is not defined in the function.")
|
161
187
|
if "self" in func_signature.parameters:
|
@@ -195,6 +221,7 @@ def work(
|
|
195
221
|
guidance=None,
|
196
222
|
retry_kwargs=None,
|
197
223
|
timeout=10,
|
224
|
+
refresh_time=1
|
198
225
|
):
|
199
226
|
"""
|
200
227
|
Decorator to mark a method as a work function.
|
@@ -205,10 +232,14 @@ def work(
|
|
205
232
|
the function's signature. This parameter is used to locate and fill
|
206
233
|
the appropriate form according to the assignment. Raises an error
|
207
234
|
if the form parameter key is not found in the function's signature.
|
208
|
-
capacity (int): Capacity for the work
|
235
|
+
capacity (int): Capacity for the work queue batch processing.
|
209
236
|
guidance (str): Guidance or documentation for the work function.
|
210
237
|
retry_kwargs (dict): Retry arguments for the work function.
|
211
238
|
timeout (int): Timeout for the work function.
|
239
|
+
refresh_time (int, optional): Refresh time for the work log queue.
|
240
|
+
|
241
|
+
Returns:
|
242
|
+
Callable: The decorated function.
|
212
243
|
"""
|
213
244
|
|
214
245
|
def decorator(func):
|
@@ -222,20 +253,114 @@ def work(
|
|
222
253
|
capacity=capacity,
|
223
254
|
retry_kwargs=retry_kwargs,
|
224
255
|
guidance=guidance,
|
256
|
+
refresh_time=refresh_time,
|
225
257
|
**kwargs,
|
226
258
|
):
|
259
|
+
if not inspect.iscoroutinefunction(func):
|
260
|
+
raise TypeError(f"{func.__name__} must be an asynchronous function")
|
227
261
|
retry_kwargs = retry_kwargs or {}
|
228
262
|
retry_kwargs["timeout"] = retry_kwargs.get("timeout", timeout)
|
229
|
-
return await self.
|
263
|
+
return await self._work_wrapper(
|
230
264
|
*args,
|
231
|
-
|
265
|
+
function=func,
|
232
266
|
assignment=assignment,
|
233
267
|
form_param_key=form_param_key,
|
234
268
|
capacity=capacity,
|
235
269
|
retry_kwargs=retry_kwargs,
|
236
270
|
guidance=guidance,
|
271
|
+
refresh_time=refresh_time,
|
237
272
|
**kwargs,
|
238
273
|
)
|
274
|
+
wrapper._work_decorator_params = {"assignment": assignment,
|
275
|
+
"function": func,
|
276
|
+
"retry_kwargs": retry_kwargs,
|
277
|
+
"guidance": guidance,
|
278
|
+
"capacity": capacity,
|
279
|
+
"refresh_time": refresh_time}
|
280
|
+
|
281
|
+
return wrapper
|
282
|
+
|
283
|
+
return decorator
|
284
|
+
|
285
|
+
|
286
|
+
def worklink(
|
287
|
+
from_: str,
|
288
|
+
to_: str,
|
289
|
+
auto_schedule: bool = True
|
290
|
+
):
|
291
|
+
"""
|
292
|
+
Decorator to create a link between two work functions.
|
293
|
+
|
294
|
+
Args:
|
295
|
+
from_ (str): The name of the source work function.
|
296
|
+
to_ (str): The name of the target work function.
|
297
|
+
auto_schedule (bool, optional): Whether to automatically schedule the next work function. Defaults to True.
|
298
|
+
|
299
|
+
Returns:
|
300
|
+
Callable: The decorated function.
|
301
|
+
"""
|
302
|
+
def decorator(func):
|
303
|
+
@wraps(func)
|
304
|
+
async def wrapper(
|
305
|
+
self: Worker,
|
306
|
+
*args,
|
307
|
+
func=func,
|
308
|
+
from_=from_,
|
309
|
+
to_=to_,
|
310
|
+
**kwargs
|
311
|
+
):
|
312
|
+
if not inspect.iscoroutinefunction(func):
|
313
|
+
raise TypeError(f"{func.__name__} must be an asynchronous function")
|
314
|
+
|
315
|
+
work_funcs = self._get_decorated_functions(decorator_attr="_work_decorator_params")
|
316
|
+
if from_ not in work_funcs or to_ not in work_funcs:
|
317
|
+
raise ValueError("Invalid link. 'from_' and 'to_' must be the name of work decorated functions.")
|
318
|
+
|
319
|
+
func_signature = inspect.signature(func)
|
320
|
+
if "from_work" not in func_signature.parameters and "from_result" not in func_signature.parameters:
|
321
|
+
raise ValueError(f"Either \"from_work\" or \"from_result\" must be a parameter in function {func.__name__}")
|
322
|
+
|
323
|
+
if "self" in func_signature.parameters:
|
324
|
+
bound_args = func_signature.bind(None, *args, **kwargs)
|
325
|
+
else:
|
326
|
+
bound_args = func_signature.bind(*args, **kwargs)
|
327
|
+
bound_args.apply_defaults()
|
328
|
+
arguments = bound_args.arguments
|
329
|
+
if "kwargs" in arguments:
|
330
|
+
arguments.update(arguments.pop("kwargs"))
|
331
|
+
|
332
|
+
if from_work := arguments.get("from_work"):
|
333
|
+
if not isinstance(from_work, Work):
|
334
|
+
raise ValueError("Invalid type for from_work. Only work objects are accepted.")
|
335
|
+
if from_work.async_task_name != from_:
|
336
|
+
raise ValueError(f"Invalid work object in from_work. "
|
337
|
+
f"async_task_name \"{from_work.async_task_name}\" does not match from_ \"{from_}\"")
|
338
|
+
|
339
|
+
next_params = await func(self, *args, **kwargs)
|
340
|
+
to_work_func = getattr(self, to_)
|
341
|
+
if next_params is None:
|
342
|
+
return
|
343
|
+
if isinstance(next_params, list):
|
344
|
+
if wrapper.auto_schedule:
|
345
|
+
return await to_work_func(*next_params)
|
346
|
+
elif isinstance(next_params, dict):
|
347
|
+
if wrapper.auto_schedule:
|
348
|
+
return await to_work_func(**next_params)
|
349
|
+
elif isinstance(next_params, tuple) and len(next_params) == 2:
|
350
|
+
if isinstance(next_params[0], list) and isinstance(next_params[1], dict):
|
351
|
+
if wrapper.auto_schedule:
|
352
|
+
return await to_work_func(*next_params[0], **next_params[1])
|
353
|
+
else:
|
354
|
+
raise TypeError(f"Invalid return type {func.__name__}")
|
355
|
+
else:
|
356
|
+
raise TypeError(f"Invalid return type {func.__name__}")
|
357
|
+
|
358
|
+
return next_params
|
359
|
+
|
360
|
+
wrapper.auto_schedule = auto_schedule
|
361
|
+
wrapper._worklink_decorator_params = {"func": func,
|
362
|
+
"from_": from_,
|
363
|
+
"to_": to_}
|
239
364
|
|
240
365
|
return wrapper
|
241
366
|
|
@@ -0,0 +1,179 @@
|
|
1
|
+
import asyncio
|
2
|
+
from lionagi.core.work.work import WorkStatus
|
3
|
+
from lionagi.core.work.worker import Worker
|
4
|
+
from lionagi.core.work.work_task import WorkTask
|
5
|
+
from lionagi.core.work.work_edge import WorkEdge
|
6
|
+
from lionagi.core.work.work_function_node import WorkFunctionNode
|
7
|
+
|
8
|
+
from lionagi.core.collections.pile import pile
|
9
|
+
from lionagi.core.generic.graph import Graph
|
10
|
+
|
11
|
+
|
12
|
+
class WorkerEngine:
|
13
|
+
"""
|
14
|
+
A class representing an engine that manages and executes work tasks for a worker.
|
15
|
+
|
16
|
+
Attributes:
|
17
|
+
worker (Worker): The worker instance that the engine manages.
|
18
|
+
tasks (Pile): A pile of tasks to be executed.
|
19
|
+
active_tasks (Pile): A pile of currently active tasks.
|
20
|
+
failed_tasks (Pile): A pile of tasks that have failed.
|
21
|
+
worker_graph (Graph): A graph representing the relationships between work functions.
|
22
|
+
refresh_time (int): The time interval for refreshing the work log queue.
|
23
|
+
_stop_event (asyncio.Event): An event to signal stopping the execution.
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__(self, worker: Worker, refresh_time=1):
|
27
|
+
"""
|
28
|
+
Initializes a new instance of WorkerEngine.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
worker (Worker): The worker instance to be managed.
|
32
|
+
refresh_time (int): The time interval for refreshing the work log queue.
|
33
|
+
"""
|
34
|
+
self.worker = worker
|
35
|
+
self.tasks = pile()
|
36
|
+
self.active_tasks = pile()
|
37
|
+
self.failed_tasks = pile()
|
38
|
+
self.worker_graph = Graph()
|
39
|
+
self._construct_work_functions()
|
40
|
+
self._construct_workedges()
|
41
|
+
self.refresh_time = refresh_time
|
42
|
+
self._stop_event = asyncio.Event()
|
43
|
+
|
44
|
+
async def add_task(self, *args, task_function: str, task_name=None, task_max_steps=10, task_post_processing=None, **kwargs):
|
45
|
+
"""
|
46
|
+
Adds a new task to the task queue.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
task_function (str): The name of the task function to execute.
|
50
|
+
task_name (str, optional): The name of the task.
|
51
|
+
task_max_steps (int, optional): The maximum number of steps for the task.
|
52
|
+
task_post_processing (Callable, optional): The post-processing function for the task.
|
53
|
+
*args: Positional arguments for the task function.
|
54
|
+
**kwargs: Keyword arguments for the task function.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
WorkTask: The newly created task.
|
58
|
+
"""
|
59
|
+
task = WorkTask(name=task_name, max_steps=task_max_steps, post_processing=task_post_processing)
|
60
|
+
self.tasks.append(task)
|
61
|
+
function = getattr(self.worker, task_function)
|
62
|
+
work = await function(*args, **kwargs)
|
63
|
+
task.current_work = work
|
64
|
+
task.work_history.append(work)
|
65
|
+
return task
|
66
|
+
|
67
|
+
async def activate_work_queues(self):
|
68
|
+
"""
|
69
|
+
Activates the work queues for all work functions.
|
70
|
+
"""
|
71
|
+
for work_function in self.worker.work_functions.values():
|
72
|
+
if not work_function.worklog.queue.execution_mode:
|
73
|
+
asyncio.create_task(work_function.worklog.queue.execute())
|
74
|
+
|
75
|
+
async def stop(self):
|
76
|
+
"""
|
77
|
+
Stops the execution of tasks.
|
78
|
+
"""
|
79
|
+
self._stop_event.set()
|
80
|
+
|
81
|
+
@property
|
82
|
+
def stopped(self) -> bool:
|
83
|
+
"""
|
84
|
+
Checks if the execution has been stopped.
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
bool: True if stopped, otherwise False.
|
88
|
+
"""
|
89
|
+
return self._stop_event.is_set()
|
90
|
+
|
91
|
+
async def process(self, task: WorkTask):
|
92
|
+
"""
|
93
|
+
Processes a single task.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
task (WorkTask): The task to be processed.
|
97
|
+
"""
|
98
|
+
current_work_func_name = task.current_work.async_task_name
|
99
|
+
current_work_func = self.worker.work_functions[current_work_func_name]
|
100
|
+
updated_task = await task.process(current_work_func)
|
101
|
+
if updated_task == "COMPLETED":
|
102
|
+
self.active_tasks.pop(task)
|
103
|
+
elif updated_task == "FAILED":
|
104
|
+
self.active_tasks.pop(task)
|
105
|
+
self.failed_tasks.append(task)
|
106
|
+
elif isinstance(updated_task, list):
|
107
|
+
self.tasks.include(updated_task)
|
108
|
+
self.active_tasks.include(updated_task)
|
109
|
+
else:
|
110
|
+
await asyncio.sleep(self.refresh_time)
|
111
|
+
|
112
|
+
async def execute(self, stop_queue=True):
|
113
|
+
"""
|
114
|
+
Executes all tasks in the task queue.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
stop_queue (bool, optional): Whether to stop the queue after execution. Defaults to True.
|
118
|
+
"""
|
119
|
+
for task in self.tasks:
|
120
|
+
if task.status == WorkStatus.PENDING:
|
121
|
+
task.status = WorkStatus.IN_PROGRESS
|
122
|
+
self.active_tasks.append(task)
|
123
|
+
|
124
|
+
await self.activate_work_queues()
|
125
|
+
self._stop_event.clear()
|
126
|
+
|
127
|
+
while len(self.active_tasks) > 0 and not self.stopped:
|
128
|
+
for work_function in self.worker.work_functions.values():
|
129
|
+
if len(work_function.worklog.pending) > 0:
|
130
|
+
await work_function.worklog.forward()
|
131
|
+
tasks = list(self.active_tasks)
|
132
|
+
await asyncio.gather(*[self.process(task) for task in tasks])
|
133
|
+
await asyncio.sleep(self.refresh_time)
|
134
|
+
|
135
|
+
if stop_queue:
|
136
|
+
await self.stop()
|
137
|
+
await self.worker.stop()
|
138
|
+
|
139
|
+
async def execute_lasting(self):
|
140
|
+
"""
|
141
|
+
Executes tasks continuously until stopped.
|
142
|
+
"""
|
143
|
+
self._stop_event.clear()
|
144
|
+
|
145
|
+
async def execute_lasting_inner():
|
146
|
+
while not self.stopped:
|
147
|
+
await self.execute(stop_queue=False)
|
148
|
+
await asyncio.sleep(self.refresh_time)
|
149
|
+
asyncio.create_task(execute_lasting_inner())
|
150
|
+
|
151
|
+
def _construct_work_functions(self):
|
152
|
+
"""
|
153
|
+
Constructs work functions for the worker.
|
154
|
+
"""
|
155
|
+
if getattr(self.worker, "work_functions", None) is None:
|
156
|
+
self.worker.work_functions = {}
|
157
|
+
work_decorated_function = self.worker._get_decorated_functions(decorator_attr="_work_decorator_params",
|
158
|
+
name_only=False)
|
159
|
+
for func_name, func, dec_params in work_decorated_function:
|
160
|
+
if func_name not in self.worker.work_functions:
|
161
|
+
self.worker.work_functions[func_name] = WorkFunctionNode(**dec_params)
|
162
|
+
self.worker_graph.add_node(self.worker.work_functions[func_name])
|
163
|
+
else:
|
164
|
+
if not isinstance(self.worker.work_functions[func_name], WorkFunctionNode):
|
165
|
+
raise TypeError(f"WorkFunction {func_name} already exists but is not a WorkFunctionNode. "
|
166
|
+
f"If you would like to use it in WorkerEngine, please convert it to a "
|
167
|
+
f"WorkFunctionNode, or initiate a new worker, or pop it from work_function dict")
|
168
|
+
|
169
|
+
def _construct_workedges(self):
|
170
|
+
"""
|
171
|
+
Constructs work edges for the worker graph.
|
172
|
+
"""
|
173
|
+
worklink_decorated_function = self.worker._get_decorated_functions(decorator_attr="_worklink_decorator_params",
|
174
|
+
name_only=False)
|
175
|
+
|
176
|
+
for func_name, func, dec_params in worklink_decorated_function:
|
177
|
+
head = self.worker.work_functions[dec_params["from_"]]
|
178
|
+
tail = self.worker.work_functions[dec_params["to_"]]
|
179
|
+
self.worker_graph.add_edge(head=head, tail=tail, convert_function=func, associated_worker=self.worker, edge_class=WorkEdge)
|
lionagi/core/work/worklog.py
CHANGED
@@ -30,19 +30,21 @@ class WorkLog(Progressable):
|
|
30
30
|
queue (WorkQueue): A queue to manage the execution of work items.
|
31
31
|
"""
|
32
32
|
|
33
|
-
def __init__(self, capacity=10, workpile=None):
|
33
|
+
def __init__(self, capacity=10, workpile=None, refresh_time=1):
|
34
34
|
"""
|
35
35
|
Initializes a new instance of WorkLog.
|
36
36
|
|
37
37
|
Args:
|
38
|
-
capacity (int): The capacity of the work queue.
|
38
|
+
capacity (int): The capacity of the work queue batch processing.
|
39
39
|
workpile (Pile, optional): An optional pile of initial work items.
|
40
|
+
refresh_time (int, optional): The time interval to refresh the work log queue.
|
41
|
+
Defaults to 1.
|
40
42
|
"""
|
41
43
|
self.pile = (
|
42
44
|
workpile if workpile and isinstance(workpile, Pile) else pile({}, Work)
|
43
45
|
)
|
44
46
|
self.pending = progression(workpile) if workpile else progression()
|
45
|
-
self.queue = WorkQueue(capacity=capacity)
|
47
|
+
self.queue = WorkQueue(capacity=capacity, refresh_time=refresh_time)
|
46
48
|
|
47
49
|
async def append(self, work: Work):
|
48
50
|
"""
|