lionagi 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (257) hide show
  1. lionagi/__init__.py +60 -5
  2. lionagi/core/__init__.py +0 -25
  3. lionagi/core/_setting/_setting.py +59 -0
  4. lionagi/core/action/__init__.py +14 -0
  5. lionagi/core/action/function_calling.py +136 -0
  6. lionagi/core/action/manual.py +1 -0
  7. lionagi/core/action/node.py +109 -0
  8. lionagi/core/action/tool.py +114 -0
  9. lionagi/core/action/tool_manager.py +356 -0
  10. lionagi/core/agent/base_agent.py +27 -13
  11. lionagi/core/agent/eval/evaluator.py +1 -0
  12. lionagi/core/agent/eval/vote.py +40 -0
  13. lionagi/core/agent/learn/learner.py +59 -0
  14. lionagi/core/agent/plan/unit_template.py +1 -0
  15. lionagi/core/collections/__init__.py +17 -0
  16. lionagi/core/{generic/data_logger.py → collections/_logger.py} +69 -55
  17. lionagi/core/collections/abc/__init__.py +53 -0
  18. lionagi/core/collections/abc/component.py +615 -0
  19. lionagi/core/collections/abc/concepts.py +297 -0
  20. lionagi/core/collections/abc/exceptions.py +150 -0
  21. lionagi/core/collections/abc/util.py +45 -0
  22. lionagi/core/collections/exchange.py +161 -0
  23. lionagi/core/collections/flow.py +426 -0
  24. lionagi/core/collections/model.py +419 -0
  25. lionagi/core/collections/pile.py +913 -0
  26. lionagi/core/collections/progression.py +236 -0
  27. lionagi/core/collections/util.py +64 -0
  28. lionagi/core/director/direct.py +314 -0
  29. lionagi/core/director/director.py +2 -0
  30. lionagi/core/{execute/branch_executor.py → engine/branch_engine.py} +134 -97
  31. lionagi/core/{execute/instruction_map_executor.py → engine/instruction_map_engine.py} +80 -55
  32. lionagi/{experimental/directive/evaluator → core/engine}/script_engine.py +17 -1
  33. lionagi/core/executor/base_executor.py +90 -0
  34. lionagi/core/{execute/structure_executor.py → executor/graph_executor.py} +83 -67
  35. lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
  36. lionagi/core/generic/__init__.py +3 -33
  37. lionagi/core/generic/edge.py +42 -92
  38. lionagi/core/generic/edge_condition.py +16 -0
  39. lionagi/core/generic/graph.py +236 -0
  40. lionagi/core/generic/hyperedge.py +1 -0
  41. lionagi/core/generic/node.py +156 -221
  42. lionagi/core/generic/tree.py +48 -0
  43. lionagi/core/generic/tree_node.py +79 -0
  44. lionagi/core/mail/__init__.py +12 -0
  45. lionagi/core/mail/mail.py +25 -0
  46. lionagi/core/mail/mail_manager.py +139 -58
  47. lionagi/core/mail/package.py +45 -0
  48. lionagi/core/mail/start_mail.py +36 -0
  49. lionagi/core/message/__init__.py +19 -0
  50. lionagi/core/message/action_request.py +133 -0
  51. lionagi/core/message/action_response.py +135 -0
  52. lionagi/core/message/assistant_response.py +95 -0
  53. lionagi/core/message/instruction.py +234 -0
  54. lionagi/core/message/message.py +101 -0
  55. lionagi/core/message/system.py +86 -0
  56. lionagi/core/message/util.py +283 -0
  57. lionagi/core/report/__init__.py +4 -0
  58. lionagi/core/report/base.py +217 -0
  59. lionagi/core/report/form.py +231 -0
  60. lionagi/core/report/report.py +166 -0
  61. lionagi/core/report/util.py +28 -0
  62. lionagi/core/rule/_default.py +16 -0
  63. lionagi/core/rule/action.py +99 -0
  64. lionagi/core/rule/base.py +238 -0
  65. lionagi/core/rule/boolean.py +56 -0
  66. lionagi/core/rule/choice.py +47 -0
  67. lionagi/core/rule/mapping.py +96 -0
  68. lionagi/core/rule/number.py +71 -0
  69. lionagi/core/rule/rulebook.py +109 -0
  70. lionagi/core/rule/string.py +52 -0
  71. lionagi/core/rule/util.py +35 -0
  72. lionagi/core/session/branch.py +431 -0
  73. lionagi/core/session/directive_mixin.py +287 -0
  74. lionagi/core/session/session.py +229 -903
  75. lionagi/core/structure/__init__.py +1 -0
  76. lionagi/core/structure/chain.py +1 -0
  77. lionagi/core/structure/forest.py +1 -0
  78. lionagi/core/structure/graph.py +1 -0
  79. lionagi/core/structure/tree.py +1 -0
  80. lionagi/core/unit/__init__.py +5 -0
  81. lionagi/core/unit/parallel_unit.py +245 -0
  82. lionagi/core/unit/template/action.py +81 -0
  83. lionagi/core/unit/template/base.py +51 -0
  84. lionagi/core/unit/template/plan.py +84 -0
  85. lionagi/core/unit/template/predict.py +109 -0
  86. lionagi/core/unit/template/score.py +124 -0
  87. lionagi/core/unit/template/select.py +104 -0
  88. lionagi/core/unit/unit.py +362 -0
  89. lionagi/core/unit/unit_form.py +305 -0
  90. lionagi/core/unit/unit_mixin.py +1168 -0
  91. lionagi/core/unit/util.py +71 -0
  92. lionagi/core/validator/validator.py +364 -0
  93. lionagi/core/work/work.py +74 -0
  94. lionagi/core/work/work_function.py +92 -0
  95. lionagi/core/work/work_queue.py +81 -0
  96. lionagi/core/work/worker.py +195 -0
  97. lionagi/core/work/worklog.py +124 -0
  98. lionagi/experimental/compressor/base.py +46 -0
  99. lionagi/experimental/compressor/llm_compressor.py +247 -0
  100. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  101. lionagi/experimental/compressor/util.py +70 -0
  102. lionagi/experimental/directive/__init__.py +19 -0
  103. lionagi/experimental/directive/parser/base_parser.py +69 -2
  104. lionagi/experimental/directive/{template_ → template}/base_template.py +17 -1
  105. lionagi/{libs/ln_tokenizer.py → experimental/directive/tokenizer.py} +16 -0
  106. lionagi/experimental/{directive/evaluator → evaluator}/ast_evaluator.py +16 -0
  107. lionagi/experimental/{directive/evaluator → evaluator}/base_evaluator.py +16 -0
  108. lionagi/experimental/knowledge/__init__.py +0 -0
  109. lionagi/experimental/knowledge/base.py +10 -0
  110. lionagi/experimental/knowledge/graph.py +0 -0
  111. lionagi/experimental/memory/__init__.py +0 -0
  112. lionagi/experimental/strategies/__init__.py +0 -0
  113. lionagi/experimental/strategies/base.py +1 -0
  114. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  115. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  116. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  117. lionagi/integrations/chunker/chunk.py +161 -24
  118. lionagi/integrations/config/oai_configs.py +34 -3
  119. lionagi/integrations/config/openrouter_configs.py +14 -2
  120. lionagi/integrations/loader/load.py +122 -21
  121. lionagi/integrations/loader/load_util.py +6 -77
  122. lionagi/integrations/provider/_mapping.py +46 -0
  123. lionagi/integrations/provider/litellm.py +2 -1
  124. lionagi/integrations/provider/mlx_service.py +16 -9
  125. lionagi/integrations/provider/oai.py +91 -4
  126. lionagi/integrations/provider/ollama.py +6 -5
  127. lionagi/integrations/provider/openrouter.py +115 -8
  128. lionagi/integrations/provider/services.py +2 -2
  129. lionagi/integrations/provider/transformers.py +18 -22
  130. lionagi/integrations/storage/__init__.py +3 -3
  131. lionagi/integrations/storage/neo4j.py +52 -60
  132. lionagi/integrations/storage/storage_util.py +45 -47
  133. lionagi/integrations/storage/structure_excel.py +285 -0
  134. lionagi/integrations/storage/to_excel.py +23 -7
  135. lionagi/libs/__init__.py +26 -1
  136. lionagi/libs/ln_api.py +75 -20
  137. lionagi/libs/ln_context.py +37 -0
  138. lionagi/libs/ln_convert.py +21 -9
  139. lionagi/libs/ln_func_call.py +69 -28
  140. lionagi/libs/ln_image.py +107 -0
  141. lionagi/libs/ln_nested.py +26 -11
  142. lionagi/libs/ln_parse.py +82 -23
  143. lionagi/libs/ln_queue.py +16 -0
  144. lionagi/libs/ln_tokenize.py +164 -0
  145. lionagi/libs/ln_validate.py +16 -0
  146. lionagi/libs/special_tokens.py +172 -0
  147. lionagi/libs/sys_util.py +95 -24
  148. lionagi/lions/coder/code_form.py +13 -0
  149. lionagi/lions/coder/coder.py +50 -3
  150. lionagi/lions/coder/util.py +30 -25
  151. lionagi/tests/libs/test_func_call.py +23 -21
  152. lionagi/tests/libs/test_nested.py +36 -21
  153. lionagi/tests/libs/test_parse.py +1 -1
  154. lionagi/tests/test_core/collections/__init__.py +0 -0
  155. lionagi/tests/test_core/collections/test_component.py +206 -0
  156. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  157. lionagi/tests/test_core/collections/test_flow.py +145 -0
  158. lionagi/tests/test_core/collections/test_pile.py +171 -0
  159. lionagi/tests/test_core/collections/test_progression.py +129 -0
  160. lionagi/tests/test_core/generic/__init__.py +0 -0
  161. lionagi/tests/test_core/generic/test_edge.py +67 -0
  162. lionagi/tests/test_core/generic/test_graph.py +96 -0
  163. lionagi/tests/test_core/generic/test_node.py +106 -0
  164. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  165. lionagi/tests/test_core/test_branch.py +115 -294
  166. lionagi/tests/test_core/test_form.py +46 -0
  167. lionagi/tests/test_core/test_report.py +105 -0
  168. lionagi/tests/test_core/test_validator.py +111 -0
  169. lionagi/version.py +1 -1
  170. lionagi-0.2.0.dist-info/LICENSE +202 -0
  171. lionagi-0.2.0.dist-info/METADATA +272 -0
  172. lionagi-0.2.0.dist-info/RECORD +240 -0
  173. lionagi/core/branch/base.py +0 -653
  174. lionagi/core/branch/branch.py +0 -474
  175. lionagi/core/branch/flow_mixin.py +0 -96
  176. lionagi/core/branch/util.py +0 -323
  177. lionagi/core/direct/__init__.py +0 -19
  178. lionagi/core/direct/cot.py +0 -123
  179. lionagi/core/direct/plan.py +0 -164
  180. lionagi/core/direct/predict.py +0 -166
  181. lionagi/core/direct/react.py +0 -171
  182. lionagi/core/direct/score.py +0 -279
  183. lionagi/core/direct/select.py +0 -170
  184. lionagi/core/direct/sentiment.py +0 -1
  185. lionagi/core/direct/utils.py +0 -110
  186. lionagi/core/direct/vote.py +0 -64
  187. lionagi/core/execute/base_executor.py +0 -47
  188. lionagi/core/flow/baseflow.py +0 -23
  189. lionagi/core/flow/monoflow/ReAct.py +0 -238
  190. lionagi/core/flow/monoflow/__init__.py +0 -9
  191. lionagi/core/flow/monoflow/chat.py +0 -95
  192. lionagi/core/flow/monoflow/chat_mixin.py +0 -253
  193. lionagi/core/flow/monoflow/followup.py +0 -213
  194. lionagi/core/flow/polyflow/__init__.py +0 -1
  195. lionagi/core/flow/polyflow/chat.py +0 -251
  196. lionagi/core/form/action_form.py +0 -26
  197. lionagi/core/form/field_validator.py +0 -287
  198. lionagi/core/form/form.py +0 -302
  199. lionagi/core/form/mixin.py +0 -214
  200. lionagi/core/form/scored_form.py +0 -13
  201. lionagi/core/generic/action.py +0 -26
  202. lionagi/core/generic/component.py +0 -455
  203. lionagi/core/generic/condition.py +0 -44
  204. lionagi/core/generic/mail.py +0 -90
  205. lionagi/core/generic/mailbox.py +0 -36
  206. lionagi/core/generic/relation.py +0 -70
  207. lionagi/core/generic/signal.py +0 -22
  208. lionagi/core/generic/structure.py +0 -362
  209. lionagi/core/generic/transfer.py +0 -20
  210. lionagi/core/generic/work.py +0 -40
  211. lionagi/core/graph/graph.py +0 -126
  212. lionagi/core/graph/tree.py +0 -190
  213. lionagi/core/mail/schema.py +0 -63
  214. lionagi/core/messages/schema.py +0 -325
  215. lionagi/core/tool/__init__.py +0 -5
  216. lionagi/core/tool/tool.py +0 -28
  217. lionagi/core/tool/tool_manager.py +0 -282
  218. lionagi/experimental/tool/function_calling.py +0 -43
  219. lionagi/experimental/tool/manual.py +0 -66
  220. lionagi/experimental/tool/schema.py +0 -59
  221. lionagi/experimental/tool/tool_manager.py +0 -138
  222. lionagi/experimental/tool/util.py +0 -16
  223. lionagi/experimental/work/_logger.py +0 -25
  224. lionagi/experimental/work/schema.py +0 -30
  225. lionagi/experimental/work/tests.py +0 -72
  226. lionagi/experimental/work/work_function.py +0 -89
  227. lionagi/experimental/work/worker.py +0 -12
  228. lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
  229. lionagi/tests/test_core/test_base_branch.py +0 -426
  230. lionagi/tests/test_core/test_chat_flow.py +0 -63
  231. lionagi/tests/test_core/test_mail_manager.py +0 -75
  232. lionagi/tests/test_core/test_prompts.py +0 -51
  233. lionagi/tests/test_core/test_session.py +0 -254
  234. lionagi/tests/test_core/test_session_base_util.py +0 -313
  235. lionagi/tests/test_core/test_tool_manager.py +0 -95
  236. lionagi-0.1.1.dist-info/LICENSE +0 -9
  237. lionagi-0.1.1.dist-info/METADATA +0 -174
  238. lionagi-0.1.1.dist-info/RECORD +0 -190
  239. /lionagi/core/{branch → _setting}/__init__.py +0 -0
  240. /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
  241. /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
  242. /lionagi/core/{form → agent/plan}/__init__.py +0 -0
  243. /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
  244. /lionagi/core/{graph → director}/__init__.py +0 -0
  245. /lionagi/core/{messages → engine}/__init__.py +0 -0
  246. /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
  247. /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
  248. /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
  249. /lionagi/{experimental/tool → core/unit/template}/__init__.py +0 -0
  250. /lionagi/{experimental/work → core/validator}/__init__.py +0 -0
  251. /lionagi/core/{flow/mono_chat_mixin.py → work/__init__.py} +0 -0
  252. /lionagi/experimental/{work/exchange.py → compressor/__init__.py} +0 -0
  253. /lionagi/experimental/{work/util.py → directive/template/__init__.py} +0 -0
  254. /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
  255. /lionagi/{tests/libs/test_async.py → experimental/evaluator/__init__.py} +0 -0
  256. {lionagi-0.1.1.dist-info → lionagi-0.2.0.dist-info}/WHEEL +0 -0
  257. {lionagi-0.1.1.dist-info → lionagi-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,90 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+ from pydantic import Field
5
+
6
+ from lionagi.core.collections.abc import Element, Progressable, Executable
7
+ from lionagi.core.collections import Exchange
8
+ from lionagi.core.mail.mail import Mail, Package
9
+
10
+
11
+ class BaseExecutor(Element, Progressable, Executable, ABC):
12
+ """
13
+ BaseExecutor is an abstract base class that defines the structure for executors
14
+ handling mails, execution control, and context management within the LionAGI system.
15
+ """
16
+
17
+ mailbox: Exchange = Field(
18
+ default_factory=Exchange[Mail], description="The pending mails."
19
+ )
20
+
21
+ execute_stop: bool = Field(
22
+ False, description="A flag indicating whether to stop execution."
23
+ )
24
+
25
+ context: dict | str | list | None = Field(
26
+ None, description="The context buffer for the next instruction."
27
+ )
28
+
29
+ execution_responses: list = Field(
30
+ default_factory=list, description="The list of responses."
31
+ )
32
+
33
+ context_log: list = Field(default_factory=list, description="The context log.")
34
+
35
+ verbose: bool = Field(
36
+ True, description="A flag indicating whether to provide verbose output."
37
+ )
38
+
39
+ def send(
40
+ self, recipient: str, category: str, package: Any, request_source: str = None
41
+ ) -> None:
42
+ """
43
+ Sends a mail to a recipient.
44
+
45
+ Args:
46
+ recipient (str): The ID of the recipient.
47
+ category (str): The category of the mail.
48
+ package (Any): The package to send in the mail.
49
+ request_source (str): The source of the request.
50
+ """
51
+ pack = Package(
52
+ category=category, package=package, request_source=request_source
53
+ )
54
+ mail = Mail(
55
+ sender=self.ln_id,
56
+ recipient=recipient,
57
+ package=pack,
58
+ )
59
+ self.mailbox.include(mail, "out")
60
+
61
+ @abstractmethod
62
+ async def execute(self, *args: Any, **kwargs: Any) -> Any:
63
+ """
64
+ Execute the executor's main function.
65
+
66
+ Args:
67
+ *args (Any): Positional arguments.
68
+ **kwargs (Any): Keyword arguments.
69
+
70
+ Returns:
71
+ Any: The result of the execution.
72
+
73
+ Raises:
74
+ NotImplementedError: If the method is not implemented.
75
+ """
76
+ raise NotImplementedError("The execute method must be implemented.")
77
+
78
+ @abstractmethod
79
+ async def forward(self, *args: Any, **kwargs: Any) -> None:
80
+ """
81
+ Forward the execution flow.
82
+
83
+ Args:
84
+ *args (Any): Positional arguments.
85
+ **kwargs (Any): Keyword arguments.
86
+
87
+ Raises:
88
+ NotImplementedError: If the method is not implemented.
89
+ """
90
+ raise NotImplementedError("The forward method must be implemented.")
@@ -1,24 +1,19 @@
1
- from typing import overload
2
-
3
- from abc import ABC
4
1
  from collections import deque
5
2
 
6
3
  from lionagi.libs import AsyncUtil, convert
7
4
 
8
- from lionagi.core.generic import BaseNode, ActionNode, ActionSelection, Edge
9
- from lionagi.core.tool import Tool
10
- from lionagi.core.mail.schema import BaseMail
11
- from lionagi.core.execute.base_executor import BaseExecutor
5
+ from lionagi.core.generic.node import Node
6
+ from lionagi.core.generic.edge import Edge
7
+ from lionagi.core.executor.base_executor import BaseExecutor
12
8
 
13
- from lionagi.libs import AsyncUtil
14
- from lionagi.core.generic import Node, ActionSelection, Edge
15
- from lionagi.core.tool import Tool
9
+ from lionagi.core.action import Tool, DirectiveSelection, ActionNode
16
10
 
17
- from lionagi.core.mail.schema import BaseMail
18
- from lionagi.core.graph.graph import Graph
11
+ from lionagi.core.mail import Mail
12
+ from lionagi.core.generic.graph import Graph
13
+ from lionagi.core.collections.progression import progression
19
14
 
20
15
 
21
- class StructureExecutor(BaseExecutor, Graph):
16
+ class GraphExecutor(BaseExecutor, Graph):
22
17
  """
23
18
  Executes tasks within a graph structure, handling dynamic node flows and conditional edge logic.
24
19
 
@@ -45,10 +40,12 @@ class StructureExecutor(BaseExecutor, Graph):
45
40
  Raises:
46
41
  ValueError: If the source_type of the condition is invalid.
47
42
  """
48
- if edge.condition.source_type == "structure":
49
- return edge.condition(self)
43
+ if edge.condition.source == "structure" or isinstance(
44
+ edge.condition.source, Node
45
+ ):
46
+ return await edge.check_condition(self)
50
47
 
51
- elif edge.condition.source_type == "executable":
48
+ elif edge.condition.source == "executable":
52
49
  return await self._check_executable_condition(
53
50
  edge, executable_id, request_source
54
51
  )
@@ -63,20 +60,20 @@ class StructureExecutor(BaseExecutor, Graph):
63
60
  Args:
64
61
  edge_id (str): The ID of the edge.
65
62
  """
66
- for key in list(self.pending_ins.keys()):
67
- skipped_requests = deque()
68
- while self.pending_ins[key]:
69
- mail: BaseMail = self.pending_ins[key].popleft()
63
+ for key in list(self.mailbox.pending_ins.keys()):
64
+ skipped_requests = progression()
65
+ while self.mailbox.pending_ins[key].size() > 0:
66
+ mail_id = self.mailbox.pending_ins[key].popleft()
67
+ mail = self.mailbox.pile[mail_id]
70
68
  if (
71
69
  mail.category == "condition"
72
- and mail.package["package"]["edge_id"] == edge_id
70
+ and mail.package.package["edge_id"] == edge_id
73
71
  ):
74
- self.condition_check_result = mail.package["package"][
75
- "check_result"
76
- ]
72
+ self.mailbox.pile.pop(mail_id)
73
+ self.condition_check_result = mail.package.package["check_result"]
77
74
  else:
78
75
  skipped_requests.append(mail)
79
- self.pending_ins[key] = skipped_requests
76
+ self.mailbox.pending_ins[key] = skipped_requests
80
77
 
81
78
  async def _check_executable_condition(
82
79
  self, edge: Edge, executable_id, request_source
@@ -93,19 +90,20 @@ class StructureExecutor(BaseExecutor, Graph):
93
90
  bool: The result of the condition check.
94
91
  """
95
92
  self.send(
96
- recipient_id=executable_id,
93
+ recipient=executable_id,
97
94
  category="condition",
98
- package={"request_source": request_source, "package": edge},
95
+ package=edge,
96
+ request_source=request_source,
99
97
  )
100
98
  while self.condition_check_result is None:
101
99
  await AsyncUtil.sleep(0.1)
102
- self._process_edge_condition(edge.id_)
100
+ self._process_edge_condition(edge.ln_id)
103
101
  continue
104
102
  check_result = self.condition_check_result
105
103
  self.condition_check_result = None
106
104
  return check_result
107
105
 
108
- async def _handle_node_id(self, mail: BaseMail):
106
+ async def _handle_node_id(self, mail: Mail):
109
107
  """
110
108
  Processes the node identified by its ID in the mail's package, ensuring it exists and retrieving the next set of
111
109
  nodes based on the current node.
@@ -116,17 +114,17 @@ class StructureExecutor(BaseExecutor, Graph):
116
114
  Raises:
117
115
  ValueError: If the node does not exist within the structure.
118
116
  """
119
- if mail.package["package"] not in self.internal_nodes:
117
+ if mail.package.package not in self.internal_nodes:
120
118
  raise ValueError(
121
- f"Node {mail.package} does not exist in the structure {self.id_}"
119
+ f"Node {mail.package.package}: Node does not exist in the structure {self.ln_id}"
122
120
  )
123
121
  return await self._next_node(
124
- self.internal_nodes[mail.package["package"]],
125
- mail.sender_id,
126
- mail.package["request_source"],
122
+ self.internal_nodes[mail.package.package],
123
+ mail.sender,
124
+ mail.package.request_source,
127
125
  )
128
126
 
129
- async def _handle_node(self, mail: BaseMail):
127
+ async def _handle_node(self, mail: Mail):
130
128
  """
131
129
  Processes the node specified in the mail's package, ensuring it exists within the structure.
132
130
 
@@ -136,15 +134,15 @@ class StructureExecutor(BaseExecutor, Graph):
136
134
  Raises:
137
135
  ValueError: If the node does not exist within the structure.
138
136
  """
139
- if not self.node_exist(mail.package["package"]):
137
+ if not self.node_exist(mail.package.package):
140
138
  raise ValueError(
141
- f"Node {mail.package} does not exist in the structure {self.id_}"
139
+ f"Node {mail.package.package.ln_id}: does not exist in the structure {self.ln_id}"
142
140
  )
143
141
  return await self._next_node(
144
- mail.package["package"], mail.sender_id, mail.package["request_source"]
142
+ mail.package.package, mail.sender, mail.package.request_source
145
143
  )
146
144
 
147
- async def _handle_mail(self, mail: BaseMail):
145
+ async def _handle_mail(self, mail: Mail):
148
146
  """
149
147
  Processes incoming mail based on its category, initiating node execution or structure operations accordingly.
150
148
 
@@ -165,9 +163,9 @@ class StructureExecutor(BaseExecutor, Graph):
165
163
  try:
166
164
  return await self._handle_node_id(mail)
167
165
  except Exception as e:
168
- raise ValueError(f"Error handling node id: {e}") from e
166
+ raise ValueError(f"Error handling node_id: {e}") from e
169
167
 
170
- elif mail.category == "node" and isinstance(mail.package["package"], BaseNode):
168
+ elif mail.category == "node" and isinstance(mail.package.package, Node):
171
169
  try:
172
170
  return await self._handle_node(mail)
173
171
  except Exception as e:
@@ -188,7 +186,7 @@ class StructureExecutor(BaseExecutor, Graph):
188
186
  list[Node]: The next step nodes.
189
187
  """
190
188
  next_nodes = []
191
- next_edges = self.get_node_edges(current_node, node_as="out")
189
+ next_edges = self.get_node_edges(current_node, direction="out")
192
190
  for edge in convert.to_list(list(next_edges.values())):
193
191
  if edge.bundle:
194
192
  continue
@@ -199,7 +197,7 @@ class StructureExecutor(BaseExecutor, Graph):
199
197
  if not check:
200
198
  continue
201
199
  node = self.internal_nodes[edge.tail]
202
- further_edges = self.get_node_edges(node, node_as="out")
200
+ further_edges = self.get_node_edges(node, direction="out")
203
201
  bundled_nodes = deque()
204
202
  for f_edge in convert.to_list(list(further_edges.values())):
205
203
  if f_edge.bundle:
@@ -209,7 +207,7 @@ class StructureExecutor(BaseExecutor, Graph):
209
207
  next_nodes.append(node)
210
208
  return next_nodes
211
209
 
212
- def _send_mail(self, next_nodes: list | None, mail: BaseMail):
210
+ def _send_mail(self, next_nodes: list | None, mail: Mail):
213
211
  """
214
212
  Sends mails to the next nodes or signals the end of execution if no next nodes exist.
215
213
 
@@ -219,31 +217,25 @@ class StructureExecutor(BaseExecutor, Graph):
219
217
  """
220
218
  if not next_nodes: # tail
221
219
  self.send(
222
- recipient_id=mail.sender_id,
220
+ recipient=mail.sender,
223
221
  category="end",
224
- package={
225
- "request_source": mail.package["request_source"],
226
- "package": "end",
227
- },
222
+ package="end",
223
+ request_source=mail.package.request_source,
228
224
  )
229
225
  else:
230
226
  if len(next_nodes) == 1:
231
227
  self.send(
232
- recipient_id=mail.sender_id,
228
+ recipient=mail.sender,
233
229
  category="node",
234
- package={
235
- "request_source": mail.package["request_source"],
236
- "package": next_nodes[0],
237
- },
230
+ package=next_nodes[0],
231
+ request_source=mail.package.request_source,
238
232
  )
239
233
  else:
240
234
  self.send(
241
- recipient_id=mail.sender_id,
235
+ recipient=mail.sender,
242
236
  category="node_list",
243
- package={
244
- "request_source": mail.package["request_source"],
245
- "package": next_nodes,
246
- },
237
+ package=next_nodes,
238
+ request_source=mail.package.request_source,
247
239
  )
248
240
 
249
241
  @staticmethod
@@ -271,9 +263,9 @@ class StructureExecutor(BaseExecutor, Graph):
271
263
  action_node = ActionNode(instruction=instruction)
272
264
  while bundled_nodes:
273
265
  node = bundled_nodes.popleft()
274
- if isinstance(node, ActionSelection):
275
- action_node.action = node.action
276
- action_node.action_kwargs = node.action_kwargs
266
+ if isinstance(node, DirectiveSelection):
267
+ action_node.directive = node.directive
268
+ action_node.directive_kwargs = node.directive_kwargs
277
269
  elif isinstance(node, Tool):
278
270
  action_node.tools.append(node)
279
271
  else:
@@ -284,17 +276,20 @@ class StructureExecutor(BaseExecutor, Graph):
284
276
  """
285
277
  Process the pending incoming mails and perform the corresponding actions.
286
278
  """
287
- for key in list(self.pending_ins.keys()):
288
- while self.pending_ins[key]:
289
- mail: BaseMail = self.pending_ins[key].popleft()
279
+ for key in list(self.mailbox.pending_ins.keys()):
280
+ while self.mailbox.pending_ins[key].size() > 0:
281
+ mail_id = self.mailbox.pending_ins[key].popleft()
282
+ mail = self.mailbox.pile.pop(mail_id)
290
283
  try:
291
- if mail == "end":
284
+ if mail.category == "end":
292
285
  self.execute_stop = True
293
286
  return
294
287
  next_nodes = await self._handle_mail(mail)
295
288
  self._send_mail(next_nodes, mail)
296
289
  except Exception as e:
297
290
  raise ValueError(f"Error handling mail: {e}") from e
291
+ if self.mailbox.pending_ins[key].size() == 0:
292
+ self.mailbox.pending_ins.pop(key)
298
293
 
299
294
  async def execute(self, refresh_time=1):
300
295
  """
@@ -306,9 +301,30 @@ class StructureExecutor(BaseExecutor, Graph):
306
301
  Raises:
307
302
  ValueError: If the graph structure is found to be cyclic, which is unsupported.
308
303
  """
309
- if not self.acyclic:
304
+ if not self.is_acyclic():
310
305
  raise ValueError("Structure is not acyclic")
311
306
 
312
307
  while not self.execute_stop:
313
308
  await self.forward()
314
309
  await AsyncUtil.sleep(refresh_time)
310
+
311
+ def to_excel(self, structure_name, dir="structure_storage"):
312
+ """
313
+ Exports the current structure to an Excel file using a specified structure name and directory.
314
+
315
+ This method utilizes the `to_excel` function from the `lionagi.integrations.storage.to_excel` module,
316
+ saving the current structure instance into an Excel file format. The Excel file will contain details
317
+ about nodes, edges, and other relevant data as separate sheets within the file.
318
+
319
+ Args:
320
+ structure_name (str): The name to assign to the structure within the Excel file. This name is
321
+ used as part of the file naming convention.
322
+ dir (str, optional): The directory where the Excel file will be saved. Defaults to "structure_storage".
323
+
324
+ Raises:
325
+ Exception: Propagates any exceptions raised by the `to_excel` function, which might occur during
326
+ the file writing process or data formatting.
327
+ """
328
+ from lionagi.integrations.storage.to_excel import to_excel
329
+
330
+ to_excel(self, structure_name, dir)