lionagi 0.0.312__py3-none-any.whl → 0.2.1__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 +61 -3
  2. lionagi/core/__init__.py +0 -14
  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/__init__.py +0 -3
  11. lionagi/core/agent/base_agent.py +45 -36
  12. lionagi/core/agent/eval/evaluator.py +1 -0
  13. lionagi/core/agent/eval/vote.py +40 -0
  14. lionagi/core/agent/learn/learner.py +59 -0
  15. lionagi/core/agent/plan/unit_template.py +1 -0
  16. lionagi/core/collections/__init__.py +17 -0
  17. lionagi/core/collections/_logger.py +319 -0
  18. lionagi/core/collections/abc/__init__.py +53 -0
  19. lionagi/core/collections/abc/component.py +615 -0
  20. lionagi/core/collections/abc/concepts.py +297 -0
  21. lionagi/core/collections/abc/exceptions.py +150 -0
  22. lionagi/core/collections/abc/util.py +45 -0
  23. lionagi/core/collections/exchange.py +161 -0
  24. lionagi/core/collections/flow.py +426 -0
  25. lionagi/core/collections/model.py +419 -0
  26. lionagi/core/collections/pile.py +913 -0
  27. lionagi/core/collections/progression.py +236 -0
  28. lionagi/core/collections/util.py +64 -0
  29. lionagi/core/director/direct.py +314 -0
  30. lionagi/core/director/director.py +2 -0
  31. lionagi/core/engine/branch_engine.py +333 -0
  32. lionagi/core/engine/instruction_map_engine.py +204 -0
  33. lionagi/core/engine/sandbox_.py +14 -0
  34. lionagi/core/engine/script_engine.py +99 -0
  35. lionagi/core/executor/base_executor.py +90 -0
  36. lionagi/core/executor/graph_executor.py +330 -0
  37. lionagi/core/executor/neo4j_executor.py +384 -0
  38. lionagi/core/generic/__init__.py +7 -0
  39. lionagi/core/generic/edge.py +112 -0
  40. lionagi/core/generic/edge_condition.py +16 -0
  41. lionagi/core/generic/graph.py +236 -0
  42. lionagi/core/generic/hyperedge.py +1 -0
  43. lionagi/core/generic/node.py +220 -0
  44. lionagi/core/generic/tree.py +48 -0
  45. lionagi/core/generic/tree_node.py +79 -0
  46. lionagi/core/mail/__init__.py +7 -3
  47. lionagi/core/mail/mail.py +25 -0
  48. lionagi/core/mail/mail_manager.py +142 -58
  49. lionagi/core/mail/package.py +45 -0
  50. lionagi/core/mail/start_mail.py +36 -0
  51. lionagi/core/message/__init__.py +19 -0
  52. lionagi/core/message/action_request.py +133 -0
  53. lionagi/core/message/action_response.py +135 -0
  54. lionagi/core/message/assistant_response.py +95 -0
  55. lionagi/core/message/instruction.py +234 -0
  56. lionagi/core/message/message.py +101 -0
  57. lionagi/core/message/system.py +86 -0
  58. lionagi/core/message/util.py +283 -0
  59. lionagi/core/report/__init__.py +4 -0
  60. lionagi/core/report/base.py +217 -0
  61. lionagi/core/report/form.py +231 -0
  62. lionagi/core/report/report.py +166 -0
  63. lionagi/core/report/util.py +28 -0
  64. lionagi/core/rule/__init__.py +0 -0
  65. lionagi/core/rule/_default.py +16 -0
  66. lionagi/core/rule/action.py +99 -0
  67. lionagi/core/rule/base.py +238 -0
  68. lionagi/core/rule/boolean.py +56 -0
  69. lionagi/core/rule/choice.py +47 -0
  70. lionagi/core/rule/mapping.py +96 -0
  71. lionagi/core/rule/number.py +71 -0
  72. lionagi/core/rule/rulebook.py +109 -0
  73. lionagi/core/rule/string.py +52 -0
  74. lionagi/core/rule/util.py +35 -0
  75. lionagi/core/session/__init__.py +0 -3
  76. lionagi/core/session/branch.py +431 -0
  77. lionagi/core/session/directive_mixin.py +287 -0
  78. lionagi/core/session/session.py +230 -902
  79. lionagi/core/structure/__init__.py +1 -0
  80. lionagi/core/structure/chain.py +1 -0
  81. lionagi/core/structure/forest.py +1 -0
  82. lionagi/core/structure/graph.py +1 -0
  83. lionagi/core/structure/tree.py +1 -0
  84. lionagi/core/unit/__init__.py +5 -0
  85. lionagi/core/unit/parallel_unit.py +245 -0
  86. lionagi/core/unit/template/__init__.py +0 -0
  87. lionagi/core/unit/template/action.py +81 -0
  88. lionagi/core/unit/template/base.py +51 -0
  89. lionagi/core/unit/template/plan.py +84 -0
  90. lionagi/core/unit/template/predict.py +109 -0
  91. lionagi/core/unit/template/score.py +124 -0
  92. lionagi/core/unit/template/select.py +104 -0
  93. lionagi/core/unit/unit.py +362 -0
  94. lionagi/core/unit/unit_form.py +305 -0
  95. lionagi/core/unit/unit_mixin.py +1168 -0
  96. lionagi/core/unit/util.py +71 -0
  97. lionagi/core/validator/__init__.py +0 -0
  98. lionagi/core/validator/validator.py +364 -0
  99. lionagi/core/work/__init__.py +0 -0
  100. lionagi/core/work/work.py +76 -0
  101. lionagi/core/work/work_function.py +101 -0
  102. lionagi/core/work/work_queue.py +103 -0
  103. lionagi/core/work/worker.py +258 -0
  104. lionagi/core/work/worklog.py +120 -0
  105. lionagi/experimental/__init__.py +0 -0
  106. lionagi/experimental/compressor/__init__.py +0 -0
  107. lionagi/experimental/compressor/base.py +46 -0
  108. lionagi/experimental/compressor/llm_compressor.py +247 -0
  109. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  110. lionagi/experimental/compressor/util.py +70 -0
  111. lionagi/experimental/directive/__init__.py +19 -0
  112. lionagi/experimental/directive/parser/__init__.py +0 -0
  113. lionagi/experimental/directive/parser/base_parser.py +282 -0
  114. lionagi/experimental/directive/template/__init__.py +0 -0
  115. lionagi/experimental/directive/template/base_template.py +79 -0
  116. lionagi/experimental/directive/template/schema.py +36 -0
  117. lionagi/experimental/directive/tokenizer.py +73 -0
  118. lionagi/experimental/evaluator/__init__.py +0 -0
  119. lionagi/experimental/evaluator/ast_evaluator.py +131 -0
  120. lionagi/experimental/evaluator/base_evaluator.py +218 -0
  121. lionagi/experimental/knowledge/__init__.py +0 -0
  122. lionagi/experimental/knowledge/base.py +10 -0
  123. lionagi/experimental/knowledge/graph.py +0 -0
  124. lionagi/experimental/memory/__init__.py +0 -0
  125. lionagi/experimental/strategies/__init__.py +0 -0
  126. lionagi/experimental/strategies/base.py +1 -0
  127. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  128. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  129. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  130. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  131. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  132. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  133. lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
  134. lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
  135. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  136. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  137. lionagi/integrations/chunker/__init__.py +0 -0
  138. lionagi/integrations/chunker/chunk.py +312 -0
  139. lionagi/integrations/config/oai_configs.py +38 -7
  140. lionagi/integrations/config/ollama_configs.py +1 -1
  141. lionagi/integrations/config/openrouter_configs.py +14 -2
  142. lionagi/integrations/loader/__init__.py +0 -0
  143. lionagi/integrations/loader/load.py +253 -0
  144. lionagi/integrations/loader/load_util.py +195 -0
  145. lionagi/integrations/provider/_mapping.py +46 -0
  146. lionagi/integrations/provider/litellm.py +2 -1
  147. lionagi/integrations/provider/mlx_service.py +16 -9
  148. lionagi/integrations/provider/oai.py +91 -4
  149. lionagi/integrations/provider/ollama.py +7 -6
  150. lionagi/integrations/provider/openrouter.py +115 -8
  151. lionagi/integrations/provider/services.py +2 -2
  152. lionagi/integrations/provider/transformers.py +18 -22
  153. lionagi/integrations/storage/__init__.py +3 -0
  154. lionagi/integrations/storage/neo4j.py +665 -0
  155. lionagi/integrations/storage/storage_util.py +287 -0
  156. lionagi/integrations/storage/structure_excel.py +285 -0
  157. lionagi/integrations/storage/to_csv.py +63 -0
  158. lionagi/integrations/storage/to_excel.py +83 -0
  159. lionagi/libs/__init__.py +26 -1
  160. lionagi/libs/ln_api.py +78 -23
  161. lionagi/libs/ln_context.py +37 -0
  162. lionagi/libs/ln_convert.py +21 -9
  163. lionagi/libs/ln_func_call.py +69 -28
  164. lionagi/libs/ln_image.py +107 -0
  165. lionagi/libs/ln_knowledge_graph.py +405 -0
  166. lionagi/libs/ln_nested.py +26 -11
  167. lionagi/libs/ln_parse.py +110 -14
  168. lionagi/libs/ln_queue.py +117 -0
  169. lionagi/libs/ln_tokenize.py +164 -0
  170. lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
  171. lionagi/libs/special_tokens.py +172 -0
  172. lionagi/libs/sys_util.py +107 -2
  173. lionagi/lions/__init__.py +0 -0
  174. lionagi/lions/coder/__init__.py +0 -0
  175. lionagi/lions/coder/add_feature.py +20 -0
  176. lionagi/lions/coder/base_prompts.py +22 -0
  177. lionagi/lions/coder/code_form.py +13 -0
  178. lionagi/lions/coder/coder.py +168 -0
  179. lionagi/lions/coder/util.py +96 -0
  180. lionagi/lions/researcher/__init__.py +0 -0
  181. lionagi/lions/researcher/data_source/__init__.py +0 -0
  182. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  183. lionagi/lions/researcher/data_source/google_.py +199 -0
  184. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  185. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  186. lionagi/tests/integrations/__init__.py +0 -0
  187. lionagi/tests/libs/__init__.py +0 -0
  188. lionagi/tests/libs/test_field_validators.py +353 -0
  189. lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
  190. lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
  191. lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
  192. lionagi/tests/libs/test_queue.py +67 -0
  193. lionagi/tests/test_core/collections/__init__.py +0 -0
  194. lionagi/tests/test_core/collections/test_component.py +206 -0
  195. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  196. lionagi/tests/test_core/collections/test_flow.py +145 -0
  197. lionagi/tests/test_core/collections/test_pile.py +171 -0
  198. lionagi/tests/test_core/collections/test_progression.py +129 -0
  199. lionagi/tests/test_core/generic/__init__.py +0 -0
  200. lionagi/tests/test_core/generic/test_edge.py +67 -0
  201. lionagi/tests/test_core/generic/test_graph.py +96 -0
  202. lionagi/tests/test_core/generic/test_node.py +106 -0
  203. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  204. lionagi/tests/test_core/test_branch.py +115 -292
  205. lionagi/tests/test_core/test_form.py +46 -0
  206. lionagi/tests/test_core/test_report.py +105 -0
  207. lionagi/tests/test_core/test_validator.py +111 -0
  208. lionagi/version.py +1 -1
  209. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
  210. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
  211. lionagi-0.2.1.dist-info/RECORD +240 -0
  212. lionagi/core/branch/__init__.py +0 -4
  213. lionagi/core/branch/base_branch.py +0 -654
  214. lionagi/core/branch/branch.py +0 -471
  215. lionagi/core/branch/branch_flow_mixin.py +0 -96
  216. lionagi/core/branch/executable_branch.py +0 -347
  217. lionagi/core/branch/util.py +0 -323
  218. lionagi/core/direct/__init__.py +0 -6
  219. lionagi/core/direct/predict.py +0 -161
  220. lionagi/core/direct/score.py +0 -278
  221. lionagi/core/direct/select.py +0 -169
  222. lionagi/core/direct/utils.py +0 -87
  223. lionagi/core/direct/vote.py +0 -64
  224. lionagi/core/flow/base/baseflow.py +0 -23
  225. lionagi/core/flow/monoflow/ReAct.py +0 -238
  226. lionagi/core/flow/monoflow/__init__.py +0 -9
  227. lionagi/core/flow/monoflow/chat.py +0 -95
  228. lionagi/core/flow/monoflow/chat_mixin.py +0 -263
  229. lionagi/core/flow/monoflow/followup.py +0 -214
  230. lionagi/core/flow/polyflow/__init__.py +0 -1
  231. lionagi/core/flow/polyflow/chat.py +0 -248
  232. lionagi/core/mail/schema.py +0 -56
  233. lionagi/core/messages/__init__.py +0 -3
  234. lionagi/core/messages/schema.py +0 -533
  235. lionagi/core/prompt/prompt_template.py +0 -316
  236. lionagi/core/schema/__init__.py +0 -22
  237. lionagi/core/schema/action_node.py +0 -29
  238. lionagi/core/schema/base_mixin.py +0 -296
  239. lionagi/core/schema/base_node.py +0 -199
  240. lionagi/core/schema/condition.py +0 -24
  241. lionagi/core/schema/data_logger.py +0 -354
  242. lionagi/core/schema/data_node.py +0 -93
  243. lionagi/core/schema/prompt_template.py +0 -67
  244. lionagi/core/schema/structure.py +0 -910
  245. lionagi/core/tool/__init__.py +0 -3
  246. lionagi/core/tool/tool_manager.py +0 -280
  247. lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
  248. lionagi/tests/test_core/test_base_branch.py +0 -427
  249. lionagi/tests/test_core/test_chat_flow.py +0 -63
  250. lionagi/tests/test_core/test_mail_manager.py +0 -75
  251. lionagi/tests/test_core/test_prompts.py +0 -51
  252. lionagi/tests/test_core/test_session.py +0 -254
  253. lionagi/tests/test_core/test_session_base_util.py +0 -312
  254. lionagi/tests/test_core/test_tool_manager.py +0 -95
  255. lionagi-0.0.312.dist-info/RECORD +0 -111
  256. /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
  257. /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
  258. /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
  259. /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
  260. /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
  261. /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
  262. /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
  263. /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
  264. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  265. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  266. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  267. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,287 @@
1
+ import json
2
+ import inspect
3
+ import re
4
+
5
+ from lionagi.core.message import System, Instruction, RoledMessage
6
+ from lionagi.core.action import Tool, DirectiveSelection, func_to_tool
7
+ from lionagi.core.action import DirectiveSelection
8
+ from lionagi.core.agent.base_agent import BaseAgent
9
+ from lionagi.core.generic.edge_condition import EdgeCondition
10
+
11
+
12
+ def output_node_list(structure):
13
+ """
14
+ Processes a structure object to extract and format all associated nodes into a summary list and detailed output dictionary.
15
+
16
+ This function traverses a structure, extracting key properties of nodes and organizing them by type into a dictionary for easy access and manipulation.
17
+
18
+ Args:
19
+ structure: The structure object containing nodes and potentially other nested structures.
20
+
21
+ Returns:
22
+ tuple: A tuple containing a summary list of all nodes and a dictionary categorizing nodes by type.
23
+ """
24
+ summary_list = []
25
+ output = {}
26
+
27
+ structure_output = {
28
+ "ln_id": structure.ln_id,
29
+ "timestamp": structure.timestamp,
30
+ "type": structure.class_name,
31
+ }
32
+ summary_list.append(structure_output.copy())
33
+ structure_output["head_nodes"] = json.dumps(
34
+ [i.ln_id for i in structure.get_heads()]
35
+ )
36
+ # structure_output['nodes'] = json.dumps([i for i in structure.internal_nodes.keys()])
37
+ # structure_output['edges'] = json.dumps([i for i in structure.internal_edges.keys()])
38
+ output[structure_output["type"]] = [structure_output]
39
+ for node in structure.internal_nodes.values():
40
+ node_output = {
41
+ "ln_id": node.ln_id,
42
+ "timestamp": node.timestamp,
43
+ "type": node.class_name,
44
+ }
45
+ summary_list.append(node_output.copy())
46
+ if isinstance(node, System) or isinstance(node, Instruction):
47
+ node_output["content"] = json.dumps(node.content)
48
+ # node_output["sender"] = node.sender
49
+ # node_output["recipient"] = node.recipient
50
+ elif isinstance(node, Tool):
51
+ node_output["function"] = inspect.getsource(node.function)
52
+ # node_output['manual'] = node.manual
53
+ node_output["parser"] = (
54
+ inspect.getsource(node.parser) if node.parser else None
55
+ )
56
+ elif isinstance(node, DirectiveSelection):
57
+ node_output["directive"] = node.directive
58
+ node_output["directive_kwargs"] = json.dumps(node.directive_kwargs)
59
+ elif isinstance(node, BaseAgent):
60
+ node_output["structure_id"] = node.structure.ln_id
61
+ node_output["output_parser"] = (
62
+ inspect.getsource(node.output_parser) if node.output_parser else None
63
+ )
64
+ else:
65
+ raise ValueError("Not supported node type detected")
66
+ if node_output["type"] not in output:
67
+ output[node_output["type"]] = []
68
+ output[node_output["type"]].append(node_output)
69
+
70
+ return summary_list, output
71
+
72
+
73
+ def output_edge_list(structure):
74
+ """
75
+ Extracts and formats all edges from a given structure into a list and maps any associated condition classes.
76
+
77
+ This function collects edge data from a structure, including identifiers, timestamps, labels, and conditions. It also compiles any unique condition classes associated with these edges.
78
+
79
+ Args:
80
+ structure: The structure object containing edges.
81
+
82
+ Returns:
83
+ tuple: A tuple containing a list of all edges with their details and a list of unique condition classes.
84
+ """
85
+ edge_list = []
86
+ edge_cls_dict = {}
87
+ for edge in structure.internal_edges.values():
88
+ edge_output = {
89
+ "ln_id": edge.ln_id,
90
+ "timestamp": edge.timestamp,
91
+ "head": edge.head,
92
+ "tail": edge.tail,
93
+ "label": edge.label,
94
+ "bundle": str(edge.bundle),
95
+ }
96
+ if edge.condition:
97
+ cls_name = edge.condition.__class__.__qualname__
98
+ cond = json.dumps({"class": cls_name, "args": edge.condition.model_dump()})
99
+ cls = edge.string_condition()
100
+ if cls is not None and cls_name not in edge_cls_dict:
101
+ edge_cls_dict.update({cls_name: cls})
102
+ else:
103
+ cond = None
104
+ edge_output["condition"] = cond
105
+ edge_list.append(edge_output)
106
+
107
+ edge_cls_list = []
108
+ for key, value in edge_cls_dict.items():
109
+ edge_cls_list.append({"class_name": key, "class": value})
110
+
111
+ return edge_list, edge_cls_list
112
+
113
+
114
+ class ParseNode:
115
+ """
116
+ Provides static methods for converting code strings to functional definitions, classes, and for parsing various types of structured nodes based on dictionary definitions.
117
+
118
+ This utility class facilitates the dynamic execution of code and the instantiation of objects from serialized data.
119
+ """
120
+
121
+ @staticmethod
122
+ def convert_to_def(function_code):
123
+ """
124
+ Converts a string containing a function definition into a callable function object.
125
+
126
+ Args:
127
+ function_code (str): The string code of the function to convert.
128
+
129
+ Returns:
130
+ function: The converted function as a callable.
131
+
132
+ Raises:
133
+ ValueError: If the function code is invalid or the function name cannot be detected.
134
+ """
135
+ import re
136
+
137
+ match = re.search(r"def (\w+)\(", function_code)
138
+ if match:
139
+ class_name = match.group(1)
140
+ try:
141
+ exec(function_code, globals())
142
+ func = globals()[class_name]
143
+ return func
144
+ except Exception as e:
145
+ raise ValueError(f"Failed to convert str to def. Error: {e}")
146
+ else:
147
+ raise ValueError("Failed to detect function name")
148
+
149
+ @staticmethod
150
+ def convert_to_class(cls_code):
151
+ """
152
+ Converts a string containing a class definition into a class object.
153
+
154
+ Args:
155
+ cls_code (str): The string code of the class to convert.
156
+
157
+ Returns:
158
+ class: The converted class.
159
+
160
+ Raises:
161
+ ValueError: If the class code is invalid or the class name cannot be detected.
162
+ """
163
+ import re
164
+
165
+ match = re.search(r"class (\w+)\s*(?:\(([^)]+)\))?:", cls_code)
166
+ if match:
167
+ class_name = match.group(1)
168
+ try:
169
+ exec(cls_code, globals())
170
+ cls = globals()[class_name]
171
+ return cls
172
+ except Exception as e:
173
+ raise ValueError(f"Failed to convert str to class. Error: {e}")
174
+ else:
175
+ raise ValueError("Failed to detect class name")
176
+
177
+ @staticmethod
178
+ def parse_system(info_dict):
179
+ """
180
+ Parses dictionary information into a System node object.
181
+
182
+ Args:
183
+ info_dict (dict): A dictionary containing properties of a system node.
184
+
185
+ Returns:
186
+ System: An instantiated System node filled with properties from info_dict.
187
+ """
188
+ info_dict["system"] = json.loads(info_dict.pop("content"))["system_info"]
189
+ node = System.from_obj(info_dict)
190
+ return node
191
+
192
+ @staticmethod
193
+ def parse_instruction(info_dict):
194
+ """
195
+ Parses dictionary information into an Instruction node object.
196
+
197
+ Args:
198
+ info_dict (dict): A dictionary containing properties of an instruction node.
199
+
200
+ Returns:
201
+ Instruction: An instantiated Instruction node filled with properties from info_dict.
202
+ """
203
+ info_dict["instruction"] = json.loads(info_dict.pop("content"))["instruction"]
204
+ node = Instruction.from_obj(info_dict)
205
+ return node
206
+
207
+ @staticmethod
208
+ def parse_directiveSelection(info_dict):
209
+ """
210
+ Parses dictionary information into an DirectiveSelection node object.
211
+
212
+ Args:
213
+ info_dict (dict): A dictionary containing properties of an action selection node.
214
+
215
+ Returns:
216
+ ActionSelection: An instantiated ActionSelection node filled with properties from info_dict.
217
+ """
218
+ node = DirectiveSelection(ln_id=info_dict["ln_id"])
219
+ node.directive = info_dict["directive"]
220
+ if "directive_kwargs" in info_dict:
221
+ if info_dict["directive_kwargs"]:
222
+ node.directive_kwargs = json.loads(info_dict["directive_kwargs"])
223
+ elif "directiveKwargs" in info_dict:
224
+ if info_dict["directiveKwargs"]:
225
+ node.directive_kwargs = json.loads(info_dict["directiveKwargs"])
226
+ return node
227
+
228
+ @staticmethod
229
+ def parse_tool(info_dict):
230
+ """
231
+ Parses dictionary information into a Tool node object, converting associated function code into a callable.
232
+
233
+ Args:
234
+ info_dict (dict): A dictionary containing properties and function code for a tool node.
235
+
236
+ Returns:
237
+ Tool: An instantiated Tool node with the function converted from code.
238
+
239
+ Raises:
240
+ ValueError: If unsafe code is detected in the function definition.
241
+ """
242
+ func_code = info_dict["function"]
243
+ if "import os" in func_code or "__" in func_code or "import sys" in func_code:
244
+ raise ValueError(
245
+ "Unsafe code detected in Tool function. Please double check or implement explicitly"
246
+ )
247
+
248
+ func = ParseNode.convert_to_def(func_code)
249
+ tool = func_to_tool(
250
+ func, ln_id=info_dict["ln_id"], timestamp=info_dict["timestamp"]
251
+ )
252
+ if func.__doc__:
253
+ if re.search(r":param \w+:", func.__doc__):
254
+ tool = func_to_tool(
255
+ func,
256
+ docstring_style="reST",
257
+ ln_id=info_dict["ln_id"],
258
+ timestamp=info_dict["timestamp"],
259
+ )
260
+
261
+ tool = tool[0]
262
+ return tool
263
+
264
+ @staticmethod
265
+ def parse_condition(condition, cls_code):
266
+ """
267
+ Parses a condition dictionary and corresponding class code into a class instance representing the condition.
268
+
269
+ Args:
270
+ condition (dict): A dictionary containing the serialized form of the condition's arguments.
271
+ cls_code (str): The class code to instantiate the condition class.
272
+
273
+ Returns:
274
+ An instance of the condition class filled with properties from the condition dictionary.
275
+
276
+ Raises:
277
+ ValueError: If the condition or class code is invalid.
278
+ """
279
+ args = condition["args"]
280
+ cls = ParseNode.convert_to_class(cls_code)
281
+
282
+ init_params = {}
283
+ for key in inspect.signature(cls.__init__).parameters.keys():
284
+ if key == "self":
285
+ continue
286
+ init_params[key] = args[key]
287
+ return cls(**init_params)
@@ -0,0 +1,285 @@
1
+ import pandas as pd
2
+ import json
3
+ from pathlib import Path
4
+
5
+ from lionagi.integrations.storage.storage_util import ParseNode
6
+ from lionagi.core.executor.graph_executor import GraphExecutor
7
+ from lionagi.core.agent.base_agent import BaseAgent
8
+ from lionagi.core.executor.base_executor import BaseExecutor
9
+ from lionagi.core.engine.instruction_map_engine import InstructionMapEngine
10
+
11
+
12
+ def excel_reload(structure_name=None, structure_id=None, dir="structure_storage"):
13
+ """
14
+ Loads a structure from an Excel file into a StructureExecutor instance.
15
+
16
+ This function uses the StructureExcel class to handle the reloading process. It identifies the
17
+ Excel file based on the provided structure name or ID and reloads the structure from it.
18
+
19
+ Args:
20
+ structure_name (str, optional): The name of the structure to reload.
21
+ structure_id (str, optional): The unique identifier of the structure to reload.
22
+ dir (str): The directory path where the Excel files are stored.
23
+
24
+ Returns:
25
+ StructureExecutor: An instance of StructureExecutor containing the reloaded structure.
26
+
27
+ Raises:
28
+ ValueError: If neither structure_name nor structure_id is provided, or if multiple or no files
29
+ are found matching the criteria.
30
+ """
31
+ excel_structure = StructureExcel(structure_name, structure_id, dir)
32
+ excel_structure.reload()
33
+ return excel_structure.structure
34
+
35
+
36
+ class StructureExcel:
37
+ """
38
+ Manages the reloading of structures from Excel files.
39
+
40
+ This class handles the identification and parsing of structure data from an Excel workbook. It supports
41
+ loading from specifically named Excel files based on the structure name or ID.
42
+
43
+ Attributes:
44
+ structure (StructureExecutor): The loaded structure, ready for execution.
45
+ default_agent_executable (BaseExecutor): The default executor for agents within the structure.
46
+
47
+ Methods:
48
+ get_heads(): Retrieves the head nodes of the loaded structure.
49
+ parse_node(info_dict): Parses a node from the structure based on the provided dictionary.
50
+ get_next(node_id): Gets the next nodes connected by outgoing edges from a given node.
51
+ relate(parent_node, node): Relates two nodes within the structure based on the edge definitions.
52
+ parse(node_list, parent_node=None): Recursively parses nodes and their relationships from the structure.
53
+ reload(): Reloads the structure from the Excel file based on the initially provided parameters.
54
+ """
55
+
56
+ structure: GraphExecutor = GraphExecutor()
57
+ default_agent_executable: BaseExecutor = InstructionMapEngine()
58
+
59
+ def __init__(
60
+ self, structure_name=None, structure_id=None, file_path="structure_storage"
61
+ ):
62
+ """
63
+ Initializes the StructureExcel class with specified parameters.
64
+
65
+ This method sets up the paths and reads the Excel file, preparing the internal dataframes used for
66
+ structure parsing.
67
+
68
+ Args:
69
+ structure_name (str, optional): The name of the structure to reload.
70
+ structure_id (str, optional): The unique identifier of the structure to reload.
71
+ file_path (str): The base path where the Excel files are stored.
72
+
73
+ Raises:
74
+ ValueError: If both structure_name and structure_id are provided but do not correspond to a valid file,
75
+ or if multiple or no files are found when one of the identifiers is provided.
76
+ """
77
+ self.file_path = file_path
78
+ if not structure_name and not structure_id:
79
+ raise ValueError("Please provide the structure name or id")
80
+ if structure_name and structure_id:
81
+ self.filename = f"{file_path}/{structure_name}_{structure_id}.xlsx"
82
+ self.file = pd.read_excel(self.filename, sheet_name=None)
83
+ elif structure_name and not structure_id:
84
+ dir_path = Path(file_path)
85
+ files = list(dir_path.glob(f"{structure_name}*.xlsx"))
86
+ filename = []
87
+ for file in files:
88
+ try:
89
+ name = file.name
90
+ name = name.rsplit("_", 1)[0]
91
+ if name == structure_name:
92
+ filename.append(file.name)
93
+ except:
94
+ continue
95
+ if len(filename) > 1:
96
+ raise ValueError(
97
+ f"Multiple files starting with the same structure name {structure_name} has found, please specify the structure id"
98
+ )
99
+ self.filename = f"{file_path}/{filename[0]}"
100
+ self.file = pd.read_excel(self.filename, sheet_name=None)
101
+ elif structure_id and not structure_name:
102
+ dir_path = Path(file_path)
103
+ files = list(dir_path.glob(f"*{structure_id}.xlsx"))
104
+ filename = [file.name for file in files]
105
+ if len(filename) > 1:
106
+ raise ValueError(
107
+ f"Multiple files with the same structure id {structure_id} has found, please double check the stored structure"
108
+ )
109
+ self.filename = f"{file_path}/{filename[0]}"
110
+ self.file = pd.read_excel(self.filename, sheet_name=None)
111
+ self.nodes = self.file["Nodes"]
112
+ self.edges = self.file["Edges"]
113
+
114
+ def get_heads(self):
115
+ """
116
+ Retrieves the list of head node identifiers from the loaded structure data.
117
+
118
+ This method parses the 'StructureExecutor' sheet in the loaded Excel file to extract the list of head nodes.
119
+
120
+ Returns:
121
+ list: A list of identifiers for the head nodes in the structure.
122
+ """
123
+ structure_df = self.file["GraphExecutor"]
124
+ head_list = json.loads(structure_df["head_nodes"].iloc[0])
125
+ return head_list
126
+
127
+ def _reload_info_dict(self, node_id):
128
+ """
129
+ Retrieves detailed information about a specific node from the Excel file based on its identifier.
130
+
131
+ This method looks up a node's information within the loaded Excel sheets and returns a dictionary
132
+ containing all the relevant details.
133
+
134
+ Args:
135
+ node_id (str): The identifier of the node to look up.
136
+
137
+ Returns:
138
+ dict: A dictionary containing the properties and values for the specified node.
139
+ """
140
+ node_type = self.nodes[self.nodes["ln_id"] == node_id]["type"].iloc[0]
141
+ node_file = self.file[node_type]
142
+ row = node_file[node_file["ln_id"] == node_id].iloc[0]
143
+ info_dict = row.to_dict()
144
+ return info_dict
145
+
146
+ def parse_agent(self, info_dict):
147
+ """
148
+ Parses an agent node from the structure using the agent's specific details provided in a dictionary.
149
+
150
+ This method creates an agent instance based on the information from the dictionary, which includes
151
+ dynamically loading the output parser code.
152
+
153
+ Args:
154
+ info_dict (dict): A dictionary containing details about the agent node, including its class, structure ID,
155
+ and output parser code.
156
+
157
+ Returns:
158
+ BaseAgent: An initialized agent object.
159
+ """
160
+ output_parser = ParseNode.convert_to_def(info_dict["output_parser"])
161
+
162
+ structure_excel = StructureExcel(
163
+ structure_id=info_dict["structure_id"], file_path=self.file_path
164
+ )
165
+ structure_excel.reload()
166
+ structure = structure_excel.structure
167
+ agent = BaseAgent(
168
+ structure=structure,
169
+ executable=self.default_agent_executable,
170
+ output_parser=output_parser,
171
+ ln_id=info_dict["ln_id"],
172
+ timestamp=info_dict["timestamp"],
173
+ )
174
+ return agent
175
+
176
+ def parse_node(self, info_dict):
177
+ """
178
+ Parses a node from its dictionary representation into a specific node type like System, Instruction, etc.
179
+
180
+ This method determines the type of node from the info dictionary and uses the appropriate parsing method
181
+ to create an instance of that node type.
182
+
183
+ Args:
184
+ info_dict (dict): A dictionary containing node data, including the node type and associated properties.
185
+
186
+ Returns:
187
+ Node: An instance of the node corresponding to the type specified in the info_dict.
188
+ """
189
+ if info_dict["type"] == "System":
190
+ return ParseNode.parse_system(info_dict)
191
+ elif info_dict["type"] == "Instruction":
192
+ return ParseNode.parse_instruction(info_dict)
193
+ elif info_dict["type"] == "Tool":
194
+ return ParseNode.parse_tool(info_dict)
195
+ elif info_dict["type"] == "DirectiveSelection":
196
+ return ParseNode.parse_directiveSelection(info_dict)
197
+ elif info_dict["type"] == "BaseAgent":
198
+ return self.parse_agent(info_dict)
199
+
200
+ def get_next(self, node_id):
201
+ """
202
+ Retrieves the list of identifiers for nodes that are directly connected via outgoing edges from the specified node.
203
+
204
+ This method searches the 'Edges' DataFrame for all entries where the specified node is a head and returns
205
+ a list of the tail node identifiers.
206
+
207
+ Args:
208
+ node_id (str): The identifier of the node whose successors are to be found.
209
+
210
+ Returns:
211
+ list[str]: A list of identifiers for the successor nodes.
212
+ """
213
+ return self.edges[self.edges["head"] == node_id]["tail"].to_list()
214
+
215
+ def relate(self, parent_node, node):
216
+ """
217
+ Establishes a relationship between two nodes in the structure based on the Excel data for edges.
218
+
219
+ This method looks up the edge details connecting the two nodes and applies any conditions associated
220
+ with the edge to the structure being rebuilt.
221
+
222
+ Args:
223
+ parent_node (Node): The parent node in the relationship.
224
+ node (Node): The child node in the relationship.
225
+
226
+ Raises:
227
+ ValueError: If there are issues with the edge data such as multiple undefined edges.
228
+ """
229
+ if not parent_node:
230
+ return
231
+ row = self.edges[
232
+ (self.edges["head"] == parent_node.ln_id)
233
+ & (self.edges["tail"] == node.ln_id)
234
+ ]
235
+ if len(row) > 1:
236
+ raise ValueError(
237
+ f"currently does not support handle multiple edges between two nodes, Error node: from {parent_node.ln_id} to {node.ln_id}"
238
+ )
239
+ if row["condition"].isna().any():
240
+ self.structure.add_edge(parent_node, node)
241
+ else:
242
+ cond = json.loads(row["condition"].iloc[0])
243
+ cond_cls = cond["class"]
244
+ cond_row = self.file["EdgesCondClass"][
245
+ self.file["EdgesCondClass"]["class_name"] == cond_cls
246
+ ]
247
+ cond_code = cond_row["class"].iloc[0]
248
+ condition = ParseNode.parse_condition(cond, cond_code)
249
+ self.structure.add_edge(parent_node, node, condition=condition)
250
+
251
+ def parse(self, node_list, parent_node=None):
252
+ """
253
+ Recursively parses a list of nodes and establishes their interconnections based on the Excel data.
254
+
255
+ This method processes each node ID in the list, parsing individual nodes and relating them according
256
+ to their connections defined in the Excel file.
257
+
258
+ Args:
259
+ node_list (list[str]): A list of node identifiers to be parsed.
260
+ parent_node (Node, optional): The parent node to which the nodes in the list are connected.
261
+
262
+ Raises:
263
+ ValueError: If an error occurs during parsing or relating nodes.
264
+ """
265
+ for node_id in node_list:
266
+ info_dict = self._reload_info_dict(node_id)
267
+ node = self.parse_node(info_dict)
268
+
269
+ if node.ln_id not in self.structure.internal_nodes:
270
+ self.structure.add_node(node)
271
+ self.relate(parent_node, node)
272
+
273
+ next_node_list = self.get_next(node_id)
274
+ self.parse(next_node_list, node)
275
+
276
+ def reload(self):
277
+ """
278
+ Reloads the entire structure from the Excel file.
279
+
280
+ This method initializes a new StructureExecutor and uses the Excel data to rebuild the entire structure,
281
+ starting from the head nodes and recursively parsing and connecting all nodes defined within.
282
+ """
283
+ self.structure = GraphExecutor()
284
+ heads = self.get_heads()
285
+ self.parse(heads)
@@ -0,0 +1,63 @@
1
+ import zipfile
2
+ import pandas as pd
3
+ from pathlib import Path
4
+
5
+ from lionagi.integrations.storage.storage_util import output_node_list, output_edge_list
6
+
7
+
8
+ def _output_csv(
9
+ node_list, node_dict, edge_list, edge_cls_list, zipname="structure_storage"
10
+ ):
11
+ """
12
+ Writes provided node and edge data into multiple CSV files and compresses them into a ZIP archive.
13
+
14
+ This helper function takes lists and dictionaries of nodes and edges, converts them to pandas DataFrames,
15
+ and then writes each DataFrame to a CSV file stored inside a ZIP archive. This includes a separate CSV
16
+ for each type of node and edge, as well as edge conditions if they exist.
17
+
18
+ Args:
19
+ node_list (list): A list of dictionaries where each dictionary contains attributes of a single node.
20
+ node_dict (dict): A dictionary of lists where each key represents a node type and the value is a list of
21
+ node attributes for nodes of that type.
22
+ edge_list (list): A list of dictionaries where each dictionary contains attributes of a single edge.
23
+ edge_cls_list (list): A list of dictionaries where each dictionary contains attributes of edge conditions.
24
+ zipname (str): The base name for the output ZIP file that will store the CSV files.
25
+
26
+ Returns:
27
+ None: This function does not return a value but outputs a ZIP file containing the CSVs.
28
+ """
29
+ tables = {"Nodes": pd.DataFrame(node_list), "Edges": pd.DataFrame(edge_list)}
30
+ if edge_cls_list:
31
+ tables["EdgesCondClass"] = pd.DataFrame(edge_cls_list)
32
+ for i in node_dict:
33
+ tables[i] = pd.DataFrame(node_dict[i])
34
+
35
+ zipname = zipname + ".zip"
36
+
37
+ with zipfile.ZipFile(zipname, "w") as zf:
38
+ for i in tables:
39
+ filename = i + ".csv"
40
+ with zf.open(filename, "w") as file:
41
+ tables[i].to_csv(file, index=False)
42
+
43
+
44
+ def to_csv(structure, filename="structure_storage"):
45
+ """
46
+ Converts a structure into a series of CSV files and stores them in a compressed ZIP archive.
47
+
48
+ This function processes a given structure to extract detailed node and edge information,
49
+ including conditions if applicable. These details are then saved into separate CSV files
50
+ for nodes, edges, and any edge conditions, which are subsequently bundled into a ZIP file.
51
+
52
+ Args:
53
+ structure: An object representing the structure to be serialized. This structure should
54
+ have methods to return lists of nodes and edges.
55
+ filename (str): The base name of the output ZIP file that will store the CSV files.
56
+
57
+ Returns:
58
+ None: This function does not return a value but outputs a ZIP file containing CSVs.
59
+ """
60
+ node_list, node_dict = output_node_list(structure)
61
+ edge_list, edge_cls_list = output_edge_list(structure)
62
+
63
+ _output_csv(node_list, node_dict, edge_list, edge_cls_list, filename)