lionagi 0.0.306__py3-none-any.whl → 0.0.307__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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