lionagi 0.1.2__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 (268) 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} +62 -66
  35. lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
  36. lionagi/core/generic/__init__.py +3 -33
  37. lionagi/core/generic/edge.py +29 -79
  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/base.py +10 -0
  109. lionagi/experimental/memory/__init__.py +0 -0
  110. lionagi/experimental/strategies/__init__.py +0 -0
  111. lionagi/experimental/strategies/base.py +1 -0
  112. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  113. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  114. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  115. lionagi/integrations/chunker/chunk.py +161 -24
  116. lionagi/integrations/config/oai_configs.py +34 -3
  117. lionagi/integrations/config/openrouter_configs.py +14 -2
  118. lionagi/integrations/loader/load.py +122 -21
  119. lionagi/integrations/loader/load_util.py +6 -77
  120. lionagi/integrations/provider/_mapping.py +46 -0
  121. lionagi/integrations/provider/litellm.py +2 -1
  122. lionagi/integrations/provider/mlx_service.py +16 -9
  123. lionagi/integrations/provider/oai.py +91 -4
  124. lionagi/integrations/provider/ollama.py +6 -5
  125. lionagi/integrations/provider/openrouter.py +115 -8
  126. lionagi/integrations/provider/services.py +2 -2
  127. lionagi/integrations/provider/transformers.py +18 -22
  128. lionagi/integrations/storage/__init__.py +3 -3
  129. lionagi/integrations/storage/neo4j.py +52 -60
  130. lionagi/integrations/storage/storage_util.py +44 -46
  131. lionagi/integrations/storage/structure_excel.py +43 -26
  132. lionagi/integrations/storage/to_excel.py +11 -4
  133. lionagi/libs/__init__.py +22 -1
  134. lionagi/libs/ln_api.py +75 -20
  135. lionagi/libs/ln_context.py +37 -0
  136. lionagi/libs/ln_convert.py +21 -9
  137. lionagi/libs/ln_func_call.py +69 -28
  138. lionagi/libs/ln_image.py +107 -0
  139. lionagi/libs/ln_nested.py +26 -11
  140. lionagi/libs/ln_parse.py +82 -23
  141. lionagi/libs/ln_queue.py +16 -0
  142. lionagi/libs/ln_tokenize.py +164 -0
  143. lionagi/libs/ln_validate.py +16 -0
  144. lionagi/libs/special_tokens.py +172 -0
  145. lionagi/libs/sys_util.py +95 -24
  146. lionagi/lions/coder/code_form.py +13 -0
  147. lionagi/lions/coder/coder.py +50 -3
  148. lionagi/lions/coder/util.py +30 -25
  149. lionagi/tests/libs/test_func_call.py +23 -21
  150. lionagi/tests/libs/test_nested.py +36 -21
  151. lionagi/tests/libs/test_parse.py +1 -1
  152. lionagi/tests/test_core/collections/__init__.py +0 -0
  153. lionagi/tests/test_core/collections/test_component.py +206 -0
  154. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  155. lionagi/tests/test_core/collections/test_flow.py +145 -0
  156. lionagi/tests/test_core/collections/test_pile.py +171 -0
  157. lionagi/tests/test_core/collections/test_progression.py +129 -0
  158. lionagi/tests/test_core/generic/test_edge.py +67 -0
  159. lionagi/tests/test_core/generic/test_graph.py +96 -0
  160. lionagi/tests/test_core/generic/test_node.py +106 -0
  161. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  162. lionagi/tests/test_core/test_branch.py +115 -294
  163. lionagi/tests/test_core/test_form.py +46 -0
  164. lionagi/tests/test_core/test_report.py +105 -0
  165. lionagi/tests/test_core/test_validator.py +111 -0
  166. lionagi/version.py +1 -1
  167. lionagi-0.2.0.dist-info/LICENSE +202 -0
  168. lionagi-0.2.0.dist-info/METADATA +272 -0
  169. lionagi-0.2.0.dist-info/RECORD +240 -0
  170. lionagi/core/branch/base.py +0 -653
  171. lionagi/core/branch/branch.py +0 -474
  172. lionagi/core/branch/flow_mixin.py +0 -96
  173. lionagi/core/branch/util.py +0 -323
  174. lionagi/core/direct/__init__.py +0 -19
  175. lionagi/core/direct/cot.py +0 -123
  176. lionagi/core/direct/plan.py +0 -164
  177. lionagi/core/direct/predict.py +0 -166
  178. lionagi/core/direct/react.py +0 -171
  179. lionagi/core/direct/score.py +0 -279
  180. lionagi/core/direct/select.py +0 -170
  181. lionagi/core/direct/sentiment.py +0 -1
  182. lionagi/core/direct/utils.py +0 -110
  183. lionagi/core/direct/vote.py +0 -64
  184. lionagi/core/execute/base_executor.py +0 -47
  185. lionagi/core/flow/baseflow.py +0 -23
  186. lionagi/core/flow/monoflow/ReAct.py +0 -240
  187. lionagi/core/flow/monoflow/__init__.py +0 -9
  188. lionagi/core/flow/monoflow/chat.py +0 -95
  189. lionagi/core/flow/monoflow/chat_mixin.py +0 -253
  190. lionagi/core/flow/monoflow/followup.py +0 -215
  191. lionagi/core/flow/polyflow/__init__.py +0 -1
  192. lionagi/core/flow/polyflow/chat.py +0 -251
  193. lionagi/core/form/action_form.py +0 -26
  194. lionagi/core/form/field_validator.py +0 -287
  195. lionagi/core/form/form.py +0 -302
  196. lionagi/core/form/mixin.py +0 -214
  197. lionagi/core/form/scored_form.py +0 -13
  198. lionagi/core/generic/action.py +0 -26
  199. lionagi/core/generic/component.py +0 -532
  200. lionagi/core/generic/condition.py +0 -46
  201. lionagi/core/generic/mail.py +0 -90
  202. lionagi/core/generic/mailbox.py +0 -36
  203. lionagi/core/generic/relation.py +0 -70
  204. lionagi/core/generic/signal.py +0 -22
  205. lionagi/core/generic/structure.py +0 -362
  206. lionagi/core/generic/transfer.py +0 -20
  207. lionagi/core/generic/work.py +0 -40
  208. lionagi/core/graph/graph.py +0 -126
  209. lionagi/core/graph/tree.py +0 -190
  210. lionagi/core/mail/schema.py +0 -63
  211. lionagi/core/messages/schema.py +0 -325
  212. lionagi/core/tool/__init__.py +0 -5
  213. lionagi/core/tool/tool.py +0 -28
  214. lionagi/core/tool/tool_manager.py +0 -283
  215. lionagi/experimental/report/form.py +0 -64
  216. lionagi/experimental/report/report.py +0 -138
  217. lionagi/experimental/report/util.py +0 -47
  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/validator/rule.py +0 -139
  224. lionagi/experimental/validator/validator.py +0 -56
  225. lionagi/experimental/work/__init__.py +0 -10
  226. lionagi/experimental/work/async_queue.py +0 -54
  227. lionagi/experimental/work/schema.py +0 -73
  228. lionagi/experimental/work/work_function.py +0 -67
  229. lionagi/experimental/work/worker.py +0 -56
  230. lionagi/experimental/work2/form.py +0 -371
  231. lionagi/experimental/work2/report.py +0 -289
  232. lionagi/experimental/work2/schema.py +0 -30
  233. lionagi/experimental/work2/tests.py +0 -72
  234. lionagi/experimental/work2/work_function.py +0 -89
  235. lionagi/experimental/work2/worker.py +0 -12
  236. lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
  237. lionagi/tests/test_core/generic/test_component.py +0 -89
  238. lionagi/tests/test_core/test_base_branch.py +0 -426
  239. lionagi/tests/test_core/test_chat_flow.py +0 -63
  240. lionagi/tests/test_core/test_mail_manager.py +0 -75
  241. lionagi/tests/test_core/test_prompts.py +0 -51
  242. lionagi/tests/test_core/test_session.py +0 -254
  243. lionagi/tests/test_core/test_session_base_util.py +0 -313
  244. lionagi/tests/test_core/test_tool_manager.py +0 -95
  245. lionagi-0.1.2.dist-info/LICENSE +0 -9
  246. lionagi-0.1.2.dist-info/METADATA +0 -174
  247. lionagi-0.1.2.dist-info/RECORD +0 -206
  248. /lionagi/core/{branch → _setting}/__init__.py +0 -0
  249. /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
  250. /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
  251. /lionagi/core/{form → agent/plan}/__init__.py +0 -0
  252. /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
  253. /lionagi/core/{graph → director}/__init__.py +0 -0
  254. /lionagi/core/{messages → engine}/__init__.py +0 -0
  255. /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
  256. /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
  257. /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
  258. /lionagi/{experimental/report → core/unit/template}/__init__.py +0 -0
  259. /lionagi/{experimental/tool → core/validator}/__init__.py +0 -0
  260. /lionagi/{experimental/validator → core/work}/__init__.py +0 -0
  261. /lionagi/experimental/{work2 → compressor}/__init__.py +0 -0
  262. /lionagi/{core/flow/mono_chat_mixin.py → experimental/directive/template/__init__.py} +0 -0
  263. /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
  264. /lionagi/experimental/{work2/util.py → evaluator/__init__.py} +0 -0
  265. /lionagi/experimental/{work2/work.py → knowledge/__init__.py} +0 -0
  266. /lionagi/{tests/libs/test_async.py → experimental/knowledge/graph.py} +0 -0
  267. {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.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,9 +276,10 @@ 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
284
  if mail.category == "end":
292
285
  self.execute_stop = True
@@ -295,6 +288,8 @@ class StructureExecutor(BaseExecutor, Graph):
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,7 +301,7 @@ 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:
@@ -331,4 +326,5 @@ class StructureExecutor(BaseExecutor, Graph):
331
326
  the file writing process or data formatting.
332
327
  """
333
328
  from lionagi.integrations.storage.to_excel import to_excel
329
+
334
330
  to_excel(self, structure_name, dir)
@@ -2,16 +2,16 @@ from collections import deque
2
2
  import json
3
3
  from typing import Callable
4
4
 
5
- from lionagi.core.execute.base_executor import BaseExecutor
5
+ from lionagi.core.executor.base_executor import BaseExecutor
6
6
  from lionagi.integrations.storage.neo4j import Neo4j
7
7
  from lionagi.integrations.storage.storage_util import ParseNode
8
- from lionagi.core.generic import ActionNode
9
8
  from lionagi.core.agent.base_agent import BaseAgent
10
- from lionagi.core.execute.instruction_map_executor import InstructionMapExecutor
9
+ from lionagi.core.engine.instruction_map_engine import InstructionMapEngine
11
10
 
12
- from lionagi.core.mail.schema import BaseMail
13
- from lionagi.core.tool import Tool
14
- from lionagi.core.generic import ActionSelection, Edge
11
+ from lionagi.core.mail import Mail
12
+ from lionagi.core.action import Tool, DirectiveSelection, ActionNode
13
+ from lionagi.core.generic.edge import Edge
14
+ from lionagi.core.collections.progression import progression
15
15
 
16
16
  from lionagi.libs import AsyncUtil
17
17
 
@@ -33,9 +33,12 @@ class Neo4jExecutor(BaseExecutor):
33
33
  structure_id: str = None
34
34
  structure_name: str = None
35
35
  middle_agents: list | None = None
36
- default_agent_executable: BaseExecutor = InstructionMapExecutor()
36
+ default_agent_executable: BaseExecutor = InstructionMapEngine()
37
37
  condition_check_result: bool | None = None
38
38
 
39
+ class Config:
40
+ arbitrary_types_allowed = True
41
+
39
42
  async def check_edge_condition(
40
43
  self, condition, executable_id, request_source, head, tail
41
44
  ):
@@ -52,9 +55,9 @@ class Neo4jExecutor(BaseExecutor):
52
55
  Returns:
53
56
  bool: Result of the condition check.
54
57
  """
55
- if condition.source_type == "structure":
56
- return condition(self)
57
- elif condition.source_type == "executable":
58
+ if condition.source == "structure":
59
+ return condition.applies(self)
60
+ elif condition.source == "executable":
58
61
  return await self._check_executable_condition(
59
62
  condition, executable_id, head, tail, request_source
60
63
  )
@@ -66,20 +69,20 @@ class Neo4jExecutor(BaseExecutor):
66
69
  Args:
67
70
  edge_id (str): The ID of the edge.
68
71
  """
69
- for key in list(self.pending_ins.keys()):
70
- skipped_requests = deque()
71
- while self.pending_ins[key]:
72
- mail: BaseMail = self.pending_ins[key].popleft()
72
+ for key in list(self.mailbox.pending_ins.keys()):
73
+ skipped_requests = progression()
74
+ while self.mailbox.pending_ins[key].size() > 0:
75
+ mail_id = self.mailbox.pending_ins[key].popleft()
76
+ mail = self.mailbox.pile[mail_id]
73
77
  if (
74
78
  mail.category == "condition"
75
- and mail.package["package"]["edge_id"] == edge_id
79
+ and mail.package.package["edge_id"] == edge_id
76
80
  ):
77
- self.condition_check_result = mail.package["package"][
78
- "check_result"
79
- ]
81
+ self.mailbox.pile.pop(mail_id)
82
+ self.condition_check_result = mail.package.package["check_result"]
80
83
  else:
81
84
  skipped_requests.append(mail)
82
- self.pending_ins[key] = skipped_requests
85
+ self.mailbox.pending_ins[key] = skipped_requests
83
86
 
84
87
  async def _check_executable_condition(
85
88
  self, condition, executable_id, head, tail, request_source
@@ -99,13 +102,14 @@ class Neo4jExecutor(BaseExecutor):
99
102
  """
100
103
  edge = Edge(head=head, tail=tail, condition=condition)
101
104
  self.send(
102
- recipient_id=executable_id,
105
+ recipient=executable_id,
103
106
  category="condition",
104
- package={"request_source": request_source, "package": edge},
107
+ package=edge,
108
+ request_source=request_source,
105
109
  )
106
110
  while self.condition_check_result is None:
107
111
  await AsyncUtil.sleep(0.1)
108
- self._process_edge_condition(edge.id_)
112
+ self._process_edge_condition(edge.ln_id)
109
113
  continue
110
114
  check_result = self.condition_check_result
111
115
  self.condition_check_result = None
@@ -126,27 +130,27 @@ class Neo4jExecutor(BaseExecutor):
126
130
  bundled_nodes = deque()
127
131
  for node_labels, node_properties in bundle_list:
128
132
  try:
129
- if "ActionSelection" in node_labels:
130
- node = ParseNode.parse_actionSelection(node_properties)
133
+ if "DirectiveSelection" in node_labels:
134
+ node = ParseNode.parse_directiveSelection(node_properties)
131
135
  bundled_nodes.append(node)
132
136
  elif "Tool" in node_labels:
133
137
  node = ParseNode.parse_tool(node_properties)
134
138
  bundled_nodes.append(node)
135
139
  else:
136
140
  raise ValueError(
137
- f"Invalid bundle node {node_properties.id}. Valid nodes are ActionSelection or Tool"
141
+ f"Invalid bundle node {node_properties.ln_id}. Valid nodes are ActionSelection or Tool"
138
142
  )
139
143
  except Exception as e:
140
144
  raise ValueError(
141
- f"Failed to parse ActionSelection or Tool node {node_properties.id}. Error: {e}"
145
+ f"Failed to parse ActionSelection or Tool node {node_properties.ln_id}. Error: {e}"
142
146
  )
143
147
 
144
148
  action_node = ActionNode(instruction=instruction)
145
149
  while bundled_nodes:
146
150
  node = bundled_nodes.popleft()
147
- if isinstance(node, ActionSelection):
148
- action_node.action = node.action
149
- action_node.action_kwargs = node.action_kwargs
151
+ if isinstance(node, DirectiveSelection):
152
+ action_node.directive = node.directive
153
+ action_node.directive_kwargs = node.directive_kwargs
150
154
  elif isinstance(node, Tool):
151
155
  action_node.tools.append(node)
152
156
  return action_node
@@ -161,7 +165,11 @@ class Neo4jExecutor(BaseExecutor):
161
165
  Returns:
162
166
  BaseAgent: An agent executor configured with the given properties.
163
167
  """
164
- output_parser = ParseNode.convert_to_def(node_properties["outputParser"])
168
+ output_parser = (
169
+ ParseNode.convert_to_def(node_properties["outputParser"])
170
+ if "outputParser" in node_properties
171
+ else None
172
+ )
165
173
 
166
174
  structure = Neo4jExecutor(
167
175
  driver=self.driver, structure_id=node_properties["structureId"]
@@ -170,9 +178,9 @@ class Neo4jExecutor(BaseExecutor):
170
178
  structure=structure,
171
179
  executable=self.default_agent_executable,
172
180
  output_parser=output_parser,
181
+ ln_id=node_properties["ln_id"],
182
+ timestamp=node_properties["timestamp"],
173
183
  )
174
- agent.id_ = node_properties["id"]
175
- agent.timestamp = node_properties["timestamp"]
176
184
  return agent
177
185
 
178
186
  async def _next_node(
@@ -202,7 +210,7 @@ class Neo4jExecutor(BaseExecutor):
202
210
  condition_obj = ParseNode.parse_condition(condition, condition_cls)
203
211
 
204
212
  head = node_id
205
- tail = node_properties["id"]
213
+ tail = node_properties["ln_id"]
206
214
  check = await self.check_edge_condition(
207
215
  condition_obj, executable_id, request_source, head, tail
208
216
  )
@@ -210,7 +218,7 @@ class Neo4jExecutor(BaseExecutor):
210
218
  continue
211
219
  except Exception as e:
212
220
  raise ValueError(
213
- f"Failed to use condition {edge_properties['condition']} from {node_id} to {node_properties['id']}, Error: {e}"
221
+ f"Failed to use condition {edge_properties['condition']} from {node_id} to {node_properties['ln_id']}, Error: {e}"
214
222
  )
215
223
 
216
224
  try:
@@ -223,14 +231,14 @@ class Neo4jExecutor(BaseExecutor):
223
231
 
224
232
  else:
225
233
  raise ValueError(
226
- f"Invalid start node {node_properties.id}. Valid nodes are System or Instruction"
234
+ f"Invalid start node {node_properties.ln_id}. Valid nodes are System or Instruction"
227
235
  )
228
236
  except Exception as e:
229
237
  raise ValueError(
230
- f"Failed to parse System or Instruction node {node_properties.id}. Error: {e}"
238
+ f"Failed to parse System or Instruction node {node_properties.ln_id}. Error: {e}"
231
239
  )
232
240
 
233
- bundle_list = await self.driver.get_bundle(node.id_)
241
+ bundle_list = await self.driver.get_bundle(node.ln_id)
234
242
 
235
243
  if bundle_list and "System" in node_labels:
236
244
  raise ValueError("System node does not support bundle edge")
@@ -247,10 +255,10 @@ class Neo4jExecutor(BaseExecutor):
247
255
  ValueError: If there is an issue with finding or starting the structure.
248
256
  """
249
257
  try:
250
- id, head_list = await self.driver.get_heads(
258
+ id_, head_list = await self.driver.get_heads(
251
259
  self.structure_name, self.structure_id
252
260
  )
253
- self.structure_id = id
261
+ self.structure_id = id_
254
262
  return await self._next_node(head_list)
255
263
  except Exception as e:
256
264
  raise ValueError(f"Error in searching for structure in Neo4j. Error: {e}")
@@ -273,12 +281,12 @@ class Neo4jExecutor(BaseExecutor):
273
281
  node_list = await self.driver.get_forwards(node_id)
274
282
  return await self._next_node(node_list, node_id, executable_id, request_source)
275
283
 
276
- async def _handle_mail(self, mail: BaseMail):
284
+ async def _handle_mail(self, mail: Mail):
277
285
  """
278
286
  Processes incoming mail, determining the next action based on the mail's category and content.
279
287
 
280
288
  Args:
281
- mail (BaseMail): The incoming mail to be processed.
289
+ mail (Mail): The incoming mail to be processed.
282
290
 
283
291
  Raises:
284
292
  ValueError: If there is an error processing the mail.
@@ -295,9 +303,9 @@ class Neo4jExecutor(BaseExecutor):
295
303
 
296
304
  elif mail.category == "node_id":
297
305
  try:
298
- node_id = mail.package["package"]
299
- executable_id = mail.sender_id
300
- request_source = mail.package["request_source"]
306
+ node_id = mail.package.package
307
+ executable_id = mail.sender
308
+ request_source = mail.package.request_source
301
309
  return await self._handle_node_id(
302
310
  node_id, executable_id, request_source
303
311
  )
@@ -305,9 +313,9 @@ class Neo4jExecutor(BaseExecutor):
305
313
  raise ValueError(f"Error in handling node_id: {e}")
306
314
  elif mail.category == "node":
307
315
  try:
308
- node_id = mail.package["package"].id_
309
- executable_id = mail.sender_id
310
- request_source = mail.package["request_source"]
316
+ node_id = mail.package.package.ln_id
317
+ executable_id = mail.sender
318
+ request_source = mail.package.request_source
311
319
  return await self._handle_node_id(
312
320
  node_id, executable_id, request_source
313
321
  )
@@ -316,50 +324,45 @@ class Neo4jExecutor(BaseExecutor):
316
324
  else:
317
325
  raise ValueError(f"Invalid mail type for structure")
318
326
 
319
- def _send_mail(self, next_nodes: list | None, mail: BaseMail):
327
+ def _send_mail(self, next_nodes: list | None, mail: Mail):
320
328
  """
321
329
  Sends out mail to the next nodes or marks the execution as ended if there are no next nodes.
322
330
 
323
331
  Args:
324
332
  next_nodes (list | None): List of next nodes to which mail should be sent.
325
- mail (BaseMail): The current mail being processed.
333
+ mail (Mail): The current mail being processed.
326
334
  """
327
335
  if not next_nodes: # tail
328
336
  self.send(
329
- recipient_id=mail.sender_id,
337
+ recipient=mail.sender,
330
338
  category="end",
331
- package={
332
- "request_source": mail.package["request_source"],
333
- "package": "end",
334
- },
339
+ package="end",
340
+ request_source=mail.package.request_source,
335
341
  )
336
342
  else:
337
343
  if len(next_nodes) == 1:
338
344
  self.send(
339
- recipient_id=mail.sender_id,
345
+ recipient=mail.sender,
340
346
  category="node",
341
- package={
342
- "request_source": mail.package["request_source"],
343
- "package": next_nodes[0],
344
- },
347
+ package=next_nodes[0],
348
+ request_source=mail.package.request_source,
345
349
  )
346
350
  else:
347
351
  self.send(
348
- recipient_id=mail.sender_id,
352
+ recipient=mail.sender,
349
353
  category="node_list",
350
- package={
351
- "request_source": mail.package["request_source"],
352
- "package": next_nodes,
353
- },
354
+ package=next_nodes,
355
+ request_source=mail.package.request_source,
354
356
  )
355
357
 
356
358
  async def forward(self) -> None:
357
359
  """
358
360
  Forwards execution by processing all pending mails and advancing to next nodes or actions.
359
361
  """
360
- for key in list(self.pending_ins.keys()):
361
- while self.pending_ins[key]:
362
- mail: BaseMail = self.pending_ins[key].popleft()
362
+ for key in list(self.mailbox.pending_ins.keys()):
363
+ while self.mailbox.pending_ins[key].size() > 0:
364
+ mail_id = self.mailbox.pending_ins[key].popleft()
365
+ mail = self.mailbox.pile.pop(mail_id)
363
366
  try:
364
367
  if mail == "end":
365
368
  self.execute_stop = True