lionagi 0.0.306__py3-none-any.whl → 0.0.307__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 -5
- lionagi/core/__init__.py +7 -5
- lionagi/core/agent/__init__.py +3 -0
- lionagi/core/agent/base_agent.py +10 -12
- lionagi/core/branch/__init__.py +4 -0
- lionagi/core/branch/base_branch.py +81 -81
- lionagi/core/branch/branch.py +16 -28
- lionagi/core/branch/branch_flow_mixin.py +3 -7
- lionagi/core/branch/executable_branch.py +86 -56
- lionagi/core/branch/util.py +77 -162
- lionagi/core/{flow/direct → direct}/__init__.py +1 -1
- lionagi/core/{flow/direct/predict.py → direct/parallel_predict.py} +39 -17
- lionagi/core/direct/parallel_react.py +0 -0
- lionagi/core/direct/parallel_score.py +0 -0
- lionagi/core/direct/parallel_select.py +0 -0
- lionagi/core/direct/parallel_sentiment.py +0 -0
- lionagi/core/direct/predict.py +174 -0
- lionagi/core/{flow/direct → direct}/react.py +2 -2
- lionagi/core/{flow/direct → direct}/score.py +28 -23
- lionagi/core/{flow/direct → direct}/select.py +48 -45
- lionagi/core/direct/utils.py +83 -0
- lionagi/core/flow/monoflow/ReAct.py +6 -5
- lionagi/core/flow/monoflow/__init__.py +9 -0
- lionagi/core/flow/monoflow/chat.py +10 -10
- lionagi/core/flow/monoflow/chat_mixin.py +11 -10
- lionagi/core/flow/monoflow/followup.py +6 -5
- lionagi/core/flow/polyflow/__init__.py +1 -0
- lionagi/core/flow/polyflow/chat.py +15 -3
- lionagi/core/mail/mail_manager.py +18 -19
- lionagi/core/mail/schema.py +5 -4
- lionagi/core/messages/schema.py +18 -20
- lionagi/core/prompt/__init__.py +0 -0
- lionagi/core/prompt/prompt_template.py +0 -0
- lionagi/core/schema/__init__.py +2 -2
- lionagi/core/schema/action_node.py +11 -3
- lionagi/core/schema/base_mixin.py +56 -59
- lionagi/core/schema/base_node.py +35 -38
- lionagi/core/schema/condition.py +24 -0
- lionagi/core/schema/data_logger.py +96 -99
- lionagi/core/schema/data_node.py +19 -19
- lionagi/core/schema/prompt_template.py +0 -0
- lionagi/core/schema/structure.py +171 -169
- lionagi/core/session/__init__.py +1 -3
- lionagi/core/session/session.py +196 -214
- lionagi/core/tool/tool_manager.py +95 -103
- lionagi/integrations/__init__.py +1 -3
- lionagi/integrations/bridge/langchain_/documents.py +17 -18
- lionagi/integrations/bridge/langchain_/langchain_bridge.py +14 -14
- lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +22 -22
- lionagi/integrations/bridge/llamaindex_/node_parser.py +12 -12
- lionagi/integrations/bridge/llamaindex_/reader.py +11 -11
- lionagi/integrations/bridge/llamaindex_/textnode.py +7 -7
- lionagi/integrations/config/openrouter_configs.py +0 -1
- lionagi/integrations/provider/oai.py +26 -26
- lionagi/integrations/provider/services.py +38 -38
- lionagi/libs/__init__.py +34 -1
- lionagi/libs/ln_api.py +211 -221
- lionagi/libs/ln_async.py +53 -60
- lionagi/libs/ln_convert.py +118 -120
- lionagi/libs/ln_dataframe.py +32 -33
- lionagi/libs/ln_func_call.py +334 -342
- lionagi/libs/ln_nested.py +99 -107
- lionagi/libs/ln_parse.py +161 -165
- lionagi/libs/sys_util.py +52 -52
- lionagi/tests/test_core/test_session.py +254 -266
- lionagi/tests/test_core/test_session_base_util.py +299 -300
- lionagi/tests/test_core/test_tool_manager.py +70 -74
- lionagi/tests/test_libs/test_nested.py +2 -7
- lionagi/tests/test_libs/test_parse.py +2 -2
- lionagi/version.py +1 -1
- {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/METADATA +4 -2
- lionagi-0.0.307.dist-info/RECORD +115 -0
- lionagi/core/flow/direct/utils.py +0 -43
- lionagi-0.0.306.dist-info/RECORD +0 -106
- /lionagi/core/{flow/direct → direct}/sentiment.py +0 -0
- {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/LICENSE +0 -0
- {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/WHEEL +0 -0
- {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/top_level.txt +0 -0
lionagi/core/schema/structure.py
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
|
1
|
+
import time
|
2
|
+
from typing import List, Any, Dict, Callable
|
2
3
|
from collections import deque
|
3
4
|
from pydantic import Field
|
4
5
|
|
5
|
-
from lionagi.libs
|
6
|
-
from lionagi.libs import ln_func_call as func_call
|
7
|
-
from lionagi.libs.ln_async import AsyncUtil
|
6
|
+
from lionagi.libs import SysUtil, func_call, AsyncUtil
|
8
7
|
|
9
|
-
from
|
8
|
+
from .base_node import BaseRelatableNode, BaseNode, Tool
|
10
9
|
from lionagi.core.mail.schema import BaseMail
|
11
10
|
|
11
|
+
from lionagi.core.schema.condition import Condition
|
12
12
|
|
13
13
|
from lionagi.core.schema.action_node import ActionNode, ActionSelection
|
14
14
|
from lionagi.core.schema.base_node import Tool
|
@@ -19,113 +19,44 @@ class Relationship(BaseRelatableNode):
|
|
19
19
|
Represents a relationship between two nodes in a graph.
|
20
20
|
|
21
21
|
Attributes:
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
source_node_id (str): The identifier of the source node.
|
23
|
+
target_node_id (str): The identifier of the target node.
|
24
|
+
condition (Dict[str, Any]): A dictionary representing conditions for the relationship.
|
25
25
|
|
26
26
|
Examples:
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
28
|
+
>>> relationship.add_condition({"key": "value"})
|
29
|
+
>>> condition_value = relationship.get_condition("key")
|
30
|
+
>>> relationship.remove_condition("key")
|
31
31
|
"""
|
32
32
|
|
33
33
|
source_node_id: str
|
34
34
|
target_node_id: str
|
35
35
|
bundle: bool = False
|
36
|
-
condition:
|
37
|
-
|
38
|
-
def add_condition(self, condition: Dict[str, Any]) -> None:
|
39
|
-
"""
|
40
|
-
Adds a condition to the relationship.
|
41
|
-
|
42
|
-
Args:
|
43
|
-
condition: The condition to be added.
|
44
|
-
|
45
|
-
Examples:
|
46
|
-
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
47
|
-
>>> relationship.add_condition({"key": "value"})
|
48
|
-
"""
|
49
|
-
self.condition.update(condition)
|
50
|
-
|
51
|
-
def remove_condition(self, condition_key: str) -> Any:
|
52
|
-
"""
|
53
|
-
Removes a condition from the relationship.
|
54
|
-
|
55
|
-
Args:
|
56
|
-
condition_key: The key of the condition to be removed.
|
57
|
-
|
58
|
-
Returns:
|
59
|
-
The value of the removed condition.
|
60
|
-
|
61
|
-
Raises:
|
62
|
-
KeyError: If the condition key is not found.
|
63
|
-
|
64
|
-
Examples:
|
65
|
-
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2", condition={"key": "value"})
|
66
|
-
>>> relationship.remove_condition("key")
|
67
|
-
'value'
|
68
|
-
"""
|
69
|
-
if condition_key not in self.condition.keys():
|
70
|
-
raise KeyError(f"condition {condition_key} is not found")
|
71
|
-
return self.condition.pop(condition_key)
|
72
|
-
|
73
|
-
def condition_exists(self, condition_key: str) -> bool:
|
74
|
-
"""
|
75
|
-
Checks if a condition exists in the relationship.
|
76
|
-
|
77
|
-
Args:
|
78
|
-
condition_key: The key of the condition to check.
|
79
|
-
|
80
|
-
Returns:
|
81
|
-
True if the condition exists, False otherwise.
|
82
|
-
|
83
|
-
Examples:
|
84
|
-
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2", condition={"key": "value"})
|
85
|
-
>>> relationship.condition_exists("key")
|
86
|
-
True
|
87
|
-
"""
|
88
|
-
if condition_key in self.condition.keys():
|
89
|
-
return True
|
90
|
-
else:
|
91
|
-
return False
|
92
|
-
|
93
|
-
def get_condition(self, condition_key: str | None = None) -> Any:
|
94
|
-
"""
|
95
|
-
Retrieves a specific condition or all conditions of the relationship.
|
36
|
+
condition: Callable = None
|
96
37
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
Raises:
|
104
|
-
ValueError: If the specified condition key does not exist.
|
38
|
+
def add_condition(self, condition: Condition):
|
39
|
+
if not isinstance(condition, Condition):
|
40
|
+
raise ValueError(
|
41
|
+
"Invalid condition type, please use Condition class to build a valid condition"
|
42
|
+
)
|
43
|
+
self.condition = condition
|
105
44
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
{'key': 'value'}
|
112
|
-
"""
|
113
|
-
if condition_key is None:
|
114
|
-
return self.condition
|
115
|
-
if self.condition_exists(condition_key=condition_key):
|
116
|
-
return self.condition[condition_key]
|
117
|
-
else:
|
118
|
-
raise ValueError(f"Condition {condition_key} does not exist")
|
45
|
+
def check_condition(self, source_obj):
|
46
|
+
try:
|
47
|
+
return bool(self.condition(source_obj))
|
48
|
+
except:
|
49
|
+
raise ValueError("Invalid relationship condition function")
|
119
50
|
|
120
51
|
def _source_existed(self, obj: Dict[str, Any]) -> bool:
|
121
52
|
"""
|
122
53
|
Checks if the source node exists in a given object.
|
123
54
|
|
124
55
|
Args:
|
125
|
-
|
56
|
+
obj (Dict[str, Any]): The object to check.
|
126
57
|
|
127
58
|
Returns:
|
128
|
-
|
59
|
+
bool: True if the source node exists, False otherwise.
|
129
60
|
"""
|
130
61
|
return self.source_node_id in obj.keys()
|
131
62
|
|
@@ -134,10 +65,10 @@ class Relationship(BaseRelatableNode):
|
|
134
65
|
Checks if the target node exists in a given object.
|
135
66
|
|
136
67
|
Args:
|
137
|
-
|
68
|
+
obj (Dict[str, Any]): The object to check.
|
138
69
|
|
139
70
|
Returns:
|
140
|
-
|
71
|
+
bool: True if the target node exists, False otherwise.
|
141
72
|
"""
|
142
73
|
return self.target_node_id in obj.keys()
|
143
74
|
|
@@ -146,13 +77,13 @@ class Relationship(BaseRelatableNode):
|
|
146
77
|
Validates the existence of both source and target nodes in a given object.
|
147
78
|
|
148
79
|
Args:
|
149
|
-
|
80
|
+
obj (Dict[str, Any]): The object to check.
|
150
81
|
|
151
82
|
Returns:
|
152
|
-
|
83
|
+
bool: True if both nodes exist.
|
153
84
|
|
154
85
|
Raises:
|
155
|
-
|
86
|
+
ValueError: If either the source or target node does not exist.
|
156
87
|
"""
|
157
88
|
if self._source_existed(obj) and self._target_existed(obj):
|
158
89
|
return True
|
@@ -167,9 +98,9 @@ class Relationship(BaseRelatableNode):
|
|
167
98
|
Returns a simple string representation of the Relationship.
|
168
99
|
|
169
100
|
Examples:
|
170
|
-
|
171
|
-
|
172
|
-
|
101
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
102
|
+
>>> str(relationship)
|
103
|
+
'Relationship (id_=None, from=node1, to=node2, label=None)'
|
173
104
|
"""
|
174
105
|
return (
|
175
106
|
f"Relationship (id_={self.id_}, from={self.source_node_id}, to={self.target_node_id}, "
|
@@ -181,9 +112,9 @@ class Relationship(BaseRelatableNode):
|
|
181
112
|
Returns a detailed string representation of the Relationship.
|
182
113
|
|
183
114
|
Examples:
|
184
|
-
|
185
|
-
|
186
|
-
|
115
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
116
|
+
>>> repr(relationship)
|
117
|
+
'Relationship(id_=None, from=node1, to=node2, content=None, metadata=None, label=None)'
|
187
118
|
"""
|
188
119
|
return (
|
189
120
|
f"Relationship(id_={self.id_}, from={self.source_node_id}, to={self.target_node_id}, "
|
@@ -196,20 +127,20 @@ class Graph(BaseRelatableNode):
|
|
196
127
|
Represents a graph structure, consisting of nodes and their relationship.
|
197
128
|
|
198
129
|
Attributes:
|
199
|
-
|
200
|
-
|
201
|
-
|
130
|
+
nodes (Dict[str, BaseNode]): A dictionary of nodes in the graph.
|
131
|
+
relationships (Dict[str, Relationship]): A dictionary of relationship between nodes in the graph.
|
132
|
+
node_relationships (Dict[str, Dict[str, Dict[str, str]]]): A dictionary tracking the relationship of each node.
|
202
133
|
|
203
134
|
Examples:
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
135
|
+
>>> graph = Graph()
|
136
|
+
>>> node = BaseNode(id_='node1')
|
137
|
+
>>> graph.add_node(node)
|
138
|
+
>>> graph.node_exists(node)
|
139
|
+
True
|
140
|
+
>>> relationship = Relationship(id_='rel1', source_node_id='node1', target_node_id='node2')
|
141
|
+
>>> graph.add_relationship(relationship)
|
142
|
+
>>> graph.relationship_exists(relationship)
|
143
|
+
True
|
213
144
|
"""
|
214
145
|
|
215
146
|
nodes: dict = Field(default={})
|
@@ -221,7 +152,7 @@ class Graph(BaseRelatableNode):
|
|
221
152
|
Adds a node to the graph.
|
222
153
|
|
223
154
|
Args:
|
224
|
-
|
155
|
+
node (BaseNode): The node to add to the graph.
|
225
156
|
"""
|
226
157
|
|
227
158
|
self.nodes[node.id_] = node
|
@@ -232,10 +163,10 @@ class Graph(BaseRelatableNode):
|
|
232
163
|
Adds a relationship between nodes in the graph.
|
233
164
|
|
234
165
|
Args:
|
235
|
-
|
166
|
+
relationship (Relationship): The relationship to add.
|
236
167
|
|
237
168
|
Raises:
|
238
|
-
|
169
|
+
KeyError: If either the source or target node of the relationship is not found in the graph.
|
239
170
|
"""
|
240
171
|
if relationship.source_node_id not in self.node_relationships.keys():
|
241
172
|
raise KeyError(f"node {relationship.source_node_id} is not found.")
|
@@ -257,14 +188,14 @@ class Graph(BaseRelatableNode):
|
|
257
188
|
Retrieves relationship of a specific node or all relationship in the graph.
|
258
189
|
|
259
190
|
Args:
|
260
|
-
|
261
|
-
|
191
|
+
node (Optional[BaseNode]): The node whose relationship to retrieve. If None, retrieves all relationship.
|
192
|
+
out_edge (bool): Whether to retrieve outgoing relationship. If False, retrieves incoming relationship.
|
262
193
|
|
263
194
|
Returns:
|
264
|
-
|
195
|
+
List[Relationship]: A list of relationship.
|
265
196
|
|
266
197
|
Raises:
|
267
|
-
|
198
|
+
KeyError: If the specified node is not found in the graph.
|
268
199
|
"""
|
269
200
|
if node is None:
|
270
201
|
return list(self.relationships.values())
|
@@ -285,18 +216,28 @@ class Graph(BaseRelatableNode):
|
|
285
216
|
)
|
286
217
|
return relationships
|
287
218
|
|
219
|
+
def get_predecessors(self, node: BaseNode):
|
220
|
+
node_ids = list(self.node_relationships[node.id_]["in"].values())
|
221
|
+
nodes = func_call.lcall(node_ids, lambda i: self.nodes[i])
|
222
|
+
return nodes
|
223
|
+
|
224
|
+
def get_successors(self, node: BaseNode):
|
225
|
+
node_ids = list(self.node_relationships[node.id_]["out"].values())
|
226
|
+
nodes = func_call.lcall(node_ids, lambda i: self.nodes[i])
|
227
|
+
return nodes
|
228
|
+
|
288
229
|
def remove_node(self, node: BaseNode) -> BaseNode:
|
289
230
|
"""
|
290
231
|
Removes a node and its associated relationship from the graph.
|
291
232
|
|
292
233
|
Args:
|
293
|
-
|
234
|
+
node (BaseNode): The node to remove.
|
294
235
|
|
295
236
|
Returns:
|
296
|
-
|
237
|
+
BaseNode: The removed node.
|
297
238
|
|
298
239
|
Raises:
|
299
|
-
|
240
|
+
KeyError: If the node is not found in the graph.
|
300
241
|
"""
|
301
242
|
if node.id_ not in self.nodes.keys():
|
302
243
|
raise KeyError(f"node {node.id_} is not found")
|
@@ -319,13 +260,13 @@ class Graph(BaseRelatableNode):
|
|
319
260
|
Removes a relationship from the graph.
|
320
261
|
|
321
262
|
Args:
|
322
|
-
|
263
|
+
relationship (Relationship): The relationship to remove.
|
323
264
|
|
324
265
|
Returns:
|
325
|
-
|
266
|
+
Relationship: The removed relationship.
|
326
267
|
|
327
268
|
Raises:
|
328
|
-
|
269
|
+
KeyError: If the relationship is not found in the graph.
|
329
270
|
"""
|
330
271
|
if relationship.id_ not in self.relationships.keys():
|
331
272
|
raise KeyError(f"relationship {relationship.id_} is not found")
|
@@ -342,10 +283,10 @@ class Graph(BaseRelatableNode):
|
|
342
283
|
Checks if a node exists in the graph.
|
343
284
|
|
344
285
|
Args:
|
345
|
-
|
286
|
+
node (BaseNode): The node to check.
|
346
287
|
|
347
288
|
Returns:
|
348
|
-
|
289
|
+
bool: True if the node exists, False otherwise.
|
349
290
|
"""
|
350
291
|
if node.id_ in self.nodes.keys():
|
351
292
|
return True
|
@@ -357,10 +298,10 @@ class Graph(BaseRelatableNode):
|
|
357
298
|
Checks if a relationship exists in the graph.
|
358
299
|
|
359
300
|
Args:
|
360
|
-
|
301
|
+
relationship (Relationship): The relationship to check.
|
361
302
|
|
362
303
|
Returns:
|
363
|
-
|
304
|
+
bool: True if the relationship exists, False otherwise.
|
364
305
|
"""
|
365
306
|
if relationship.id_ in self.relationships.keys():
|
366
307
|
return True
|
@@ -372,7 +313,7 @@ class Graph(BaseRelatableNode):
|
|
372
313
|
Determines if the graph is empty.
|
373
314
|
|
374
315
|
Returns:
|
375
|
-
|
316
|
+
bool: True if the graph has no nodes, False otherwise.
|
376
317
|
"""
|
377
318
|
if self.nodes:
|
378
319
|
return False
|
@@ -390,14 +331,14 @@ class Graph(BaseRelatableNode):
|
|
390
331
|
Converts the graph to a NetworkX graph object.
|
391
332
|
|
392
333
|
Args:
|
393
|
-
|
334
|
+
**kwargs: Additional keyword arguments to pass to the NetworkX DiGraph constructor.
|
394
335
|
|
395
336
|
Returns:
|
396
|
-
|
337
|
+
Any: A NetworkX directed graph representing the graph.
|
397
338
|
|
398
339
|
Examples:
|
399
|
-
|
400
|
-
|
340
|
+
>>> graph = Graph()
|
341
|
+
>>> nx_graph = graph.to_networkx()
|
401
342
|
"""
|
402
343
|
|
403
344
|
SysUtil.check_import("networkx")
|
@@ -408,11 +349,13 @@ class Graph(BaseRelatableNode):
|
|
408
349
|
for node_id, node in self.nodes.items():
|
409
350
|
node_info = node.to_dict()
|
410
351
|
node_info.pop("node_id")
|
352
|
+
node_info.update({"class_name": node.__class__.__name__})
|
411
353
|
g.add_node(node_id, **node_info)
|
412
354
|
|
413
355
|
for _, relationship in self.relationships.items():
|
414
356
|
relationship_info = relationship.to_dict()
|
415
357
|
relationship_info.pop("node_id")
|
358
|
+
relationship_info.update({"class_name": relationship.__class__.__name__})
|
416
359
|
source_node_id = relationship_info.pop("source_node_id")
|
417
360
|
target_node_id = relationship_info.pop("target_node_id")
|
418
361
|
g.add_edge(source_node_id, target_node_id, **relationship_info)
|
@@ -422,20 +365,37 @@ class Graph(BaseRelatableNode):
|
|
422
365
|
|
423
366
|
class Structure(BaseRelatableNode):
|
424
367
|
graph: Graph = Graph()
|
425
|
-
processing_mails: deque = deque()
|
426
368
|
pending_ins: dict = {}
|
427
369
|
pending_outs: deque = deque()
|
428
370
|
execute_stop: bool = False
|
371
|
+
condition_check_result: bool | None = None
|
429
372
|
|
430
373
|
def add_node(self, node: BaseNode):
|
431
374
|
self.graph.add_node(node)
|
432
375
|
|
433
|
-
|
434
|
-
|
435
|
-
|
376
|
+
def add_relationship(
|
377
|
+
self,
|
378
|
+
from_node: BaseNode,
|
379
|
+
to_node: BaseNode,
|
380
|
+
bundle=False,
|
381
|
+
condition=None,
|
382
|
+
**kwargs,
|
383
|
+
):
|
384
|
+
if isinstance(from_node, Tool) or isinstance(from_node, ActionSelection):
|
385
|
+
raise ValueError(
|
386
|
+
f"type {type(from_node)} should not be the head of the relationship, "
|
387
|
+
f"please switch position and attach it to the tail of the relationship"
|
388
|
+
)
|
389
|
+
if isinstance(to_node, Tool) or isinstance(to_node, ActionSelection):
|
390
|
+
bundle = True
|
436
391
|
relationship = Relationship(
|
437
|
-
source_node_id=from_node.id_,
|
392
|
+
source_node_id=from_node.id_,
|
393
|
+
target_node_id=to_node.id_,
|
394
|
+
bundle=bundle,
|
395
|
+
**kwargs,
|
438
396
|
)
|
397
|
+
if condition:
|
398
|
+
relationship.add_condition(condition)
|
439
399
|
self.graph.add_relationship(relationship)
|
440
400
|
|
441
401
|
def get_relationships(self) -> list[Relationship]:
|
@@ -453,6 +413,12 @@ class Structure(BaseRelatableNode):
|
|
453
413
|
relationships = result
|
454
414
|
return relationships
|
455
415
|
|
416
|
+
def get_predecessors(self, node: BaseNode):
|
417
|
+
return self.graph.get_predecessors(node)
|
418
|
+
|
419
|
+
def get_successors(self, node: BaseNode):
|
420
|
+
return self.graph.get_successors(node)
|
421
|
+
|
456
422
|
def node_exist(self, node: BaseNode) -> bool:
|
457
423
|
return self.graph.node_exist(node)
|
458
424
|
|
@@ -469,24 +435,12 @@ class Structure(BaseRelatableNode):
|
|
469
435
|
return self.graph.is_empty()
|
470
436
|
|
471
437
|
def get_heads(self):
|
472
|
-
heads =
|
438
|
+
heads = []
|
473
439
|
for key in self.graph.node_relationships:
|
474
440
|
if not self.graph.node_relationships[key]["in"]:
|
475
441
|
heads.append(self.graph.nodes[key])
|
476
442
|
return heads
|
477
443
|
|
478
|
-
# def get_next_step(self, current_node: BaseNode):
|
479
|
-
# next_nodes = deque()
|
480
|
-
# next_relationships = self.get_node_relationships(current_node)
|
481
|
-
# for relationship in next_relationships:
|
482
|
-
# node = self.graph.nodes[relationship.target_node_id]
|
483
|
-
# next_nodes.append(node)
|
484
|
-
# further_relationships = self.get_node_relationships(node)
|
485
|
-
# for f_relationship in further_relationships:
|
486
|
-
# if f_relationship.bundle:
|
487
|
-
# next_nodes.append(self.graph.nodes[f_relationship.target_node_id])
|
488
|
-
# return next_nodes
|
489
|
-
|
490
444
|
@staticmethod
|
491
445
|
def parse_to_action(instruction: BaseNode, bundled_nodes: deque):
|
492
446
|
action_node = ActionNode(instruction)
|
@@ -501,12 +455,36 @@ class Structure(BaseRelatableNode):
|
|
501
455
|
raise ValueError("Invalid bundles nodes")
|
502
456
|
return action_node
|
503
457
|
|
504
|
-
def
|
505
|
-
|
458
|
+
async def check_condition(self, relationship, executable_id):
|
459
|
+
if relationship.condition.source_type == "structure":
|
460
|
+
return self.check_condition_structure(relationship)
|
461
|
+
elif relationship.condition.source_type == "executable":
|
462
|
+
self.send(
|
463
|
+
recipient_id=executable_id, category="condition", package=relationship
|
464
|
+
)
|
465
|
+
while self.condition_check_result is None:
|
466
|
+
await AsyncUtil.sleep(0.1)
|
467
|
+
self.process_relationship_condition(relationship.id_)
|
468
|
+
continue
|
469
|
+
check_result = self.condition_check_result
|
470
|
+
self.condition_check_result = None
|
471
|
+
return check_result
|
472
|
+
else:
|
473
|
+
raise ValueError(f"Invalid source_type.")
|
474
|
+
|
475
|
+
def check_condition_structure(self, relationship):
|
476
|
+
return relationship.condition(self)
|
477
|
+
|
478
|
+
async def get_next_step(self, current_node: BaseNode, executable_id):
|
479
|
+
next_nodes = []
|
506
480
|
next_relationships = self.get_node_relationships(current_node)
|
507
481
|
for relationship in next_relationships:
|
508
482
|
if relationship.bundle:
|
509
483
|
continue
|
484
|
+
if relationship.condition:
|
485
|
+
check = await self.check_condition(relationship, executable_id)
|
486
|
+
if not check:
|
487
|
+
continue
|
510
488
|
node = self.graph.nodes[relationship.target_node_id]
|
511
489
|
further_relationships = self.get_node_relationships(node)
|
512
490
|
bundled_nodes = deque()
|
@@ -559,7 +537,21 @@ class Structure(BaseRelatableNode):
|
|
559
537
|
)
|
560
538
|
self.pending_outs.append(mail)
|
561
539
|
|
562
|
-
def
|
540
|
+
def process_relationship_condition(self, relationship_id):
|
541
|
+
for key in list(self.pending_ins.keys()):
|
542
|
+
skipped_requests = deque()
|
543
|
+
while self.pending_ins[key]:
|
544
|
+
mail = self.pending_ins[key].popleft()
|
545
|
+
if (
|
546
|
+
mail.category == "condition"
|
547
|
+
and mail.package["relationship_id"] == relationship_id
|
548
|
+
):
|
549
|
+
self.condition_check_result = mail.package["check_result"]
|
550
|
+
else:
|
551
|
+
skipped_requests.append(mail)
|
552
|
+
self.pending_ins[key] = skipped_requests
|
553
|
+
|
554
|
+
async def process(self) -> None:
|
563
555
|
for key in list(self.pending_ins.keys()):
|
564
556
|
while self.pending_ins[key]:
|
565
557
|
mail = self.pending_ins[key].popleft()
|
@@ -573,13 +565,15 @@ class Structure(BaseRelatableNode):
|
|
573
565
|
raise ValueError(
|
574
566
|
f"Node {mail.package} does not exist in the structure {self.id_}"
|
575
567
|
)
|
576
|
-
next_nodes = self.get_next_step(
|
568
|
+
next_nodes = await self.get_next_step(
|
569
|
+
self.graph.nodes[mail.package], mail.sender_id
|
570
|
+
)
|
577
571
|
elif mail.category == "node" and isinstance(mail.package, BaseNode):
|
578
572
|
if not self.node_exist(mail.package):
|
579
573
|
raise ValueError(
|
580
574
|
f"Node {mail.package} does not exist in the structure {self.id_}"
|
581
575
|
)
|
582
|
-
next_nodes = self.get_next_step(mail.package)
|
576
|
+
next_nodes = await self.get_next_step(mail.package, mail.sender_id)
|
583
577
|
else:
|
584
578
|
raise ValueError(f"Invalid mail type for structure")
|
585
579
|
|
@@ -587,16 +581,24 @@ class Structure(BaseRelatableNode):
|
|
587
581
|
self.send(
|
588
582
|
recipient_id=mail.sender_id, category="end", package="end"
|
589
583
|
)
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
584
|
+
else:
|
585
|
+
if len(next_nodes) == 1:
|
586
|
+
self.send(
|
587
|
+
recipient_id=mail.sender_id,
|
588
|
+
category="node",
|
589
|
+
package=next_nodes[0],
|
590
|
+
)
|
591
|
+
else:
|
592
|
+
self.send(
|
593
|
+
recipient_id=mail.sender_id,
|
594
|
+
category="node_list",
|
595
|
+
package=next_nodes,
|
596
|
+
)
|
595
597
|
|
596
598
|
async def execute(self, refresh_time=1):
|
597
599
|
if not self.acyclic():
|
598
600
|
raise ValueError("Structure is not acyclic")
|
599
601
|
|
600
602
|
while not self.execute_stop:
|
601
|
-
self.process()
|
603
|
+
await self.process()
|
602
604
|
await AsyncUtil.sleep(refresh_time)
|
lionagi/core/session/__init__.py
CHANGED