lionagi 0.0.312__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,665 @@
1
+ from neo4j import AsyncGraphDatabase
2
+
3
+ from lionagi.integrations.storage.storage_util import output_node_list, output_edge_list
4
+
5
+
6
+ class Neo4j:
7
+ """
8
+ Manages interactions with a Neo4j graph database, facilitating the creation, retrieval, and management
9
+ of graph nodes and relationships asynchronously.
10
+
11
+ Provides methods to add various types of nodes and relationships, query the graph based on specific criteria,
12
+ and enforce database constraints to ensure data integrity.
13
+
14
+ Attributes:
15
+ database (str): The name of the database to connect to.
16
+ driver (neo4j.AsyncGraphDatabase.driver): The Neo4j driver for asynchronous database operations.
17
+ """
18
+
19
+ def __init__(self, uri, user, password, database):
20
+ """
21
+ Initializes the Neo4j database connection using provided credentials and database information.
22
+
23
+ Args:
24
+ uri (str): The URI for the Neo4j database.
25
+ user (str): The username for database authentication.
26
+ password (str): The password for database authentication.
27
+ database (str): The name of the database to use.
28
+ """
29
+ self.database = database
30
+ self.driver = AsyncGraphDatabase.driver(uri, auth=(user, password))
31
+
32
+ # ---------------------to_neo4j---------------------------------
33
+ @staticmethod
34
+ async def add_structure_node(tx, node, name):
35
+ """
36
+ Asynchronously adds a structure node to the graph.
37
+
38
+ Args:
39
+ tx: The Neo4j transaction.
40
+ node (dict): The properties of the node to be added, including 'ln_id' and 'timestamp'.
41
+ name (str): The name of the structure, which is set on the node.
42
+ """
43
+ query = """
44
+ MERGE (n:Structure:LionNode {ln_id:$ln_id})
45
+ SET n.timestamp = $timestamp
46
+ SET n.name = $name
47
+ """
48
+ await tx.run(query, ln_id=node["ln_id"], timestamp=node["timestamp"], name=name)
49
+ # heads=node['head_nodes'],
50
+ # nodes=node['nodes'],
51
+ # edges=node['edges'])
52
+
53
+ @staticmethod
54
+ async def add_system_node(tx, node):
55
+ """
56
+ Asynchronously adds a system node to the graph.
57
+
58
+ Args:
59
+ tx: The Neo4j transaction.
60
+ node (dict): The properties of the system node including 'ln_id', 'timestamp', 'content', 'sender', and 'recipient'.
61
+ """
62
+ query = """
63
+ MERGE (n:System:LionNode {ln_id: $ln_id})
64
+ SET n.timestamp = $timestamp
65
+ SET n.content = $content
66
+ """
67
+ await tx.run(
68
+ query,
69
+ ln_id=node["ln_id"],
70
+ timestamp=node["timestamp"],
71
+ content=node["content"],
72
+ )
73
+
74
+ @staticmethod
75
+ async def add_instruction_node(tx, node):
76
+ """
77
+ Asynchronously adds an instruction node to the graph.
78
+
79
+ Args:
80
+ tx: The Neo4j transaction.
81
+ node (dict): The properties of the instruction node including 'ln_id', 'timestamp', 'content', 'sender', and 'recipient'.
82
+ """
83
+ query = """
84
+ MERGE (n:Instruction:LionNode {ln_id: $ln_id})
85
+ SET n.timestamp = $timestamp
86
+ SET n.content = $content
87
+ """
88
+ await tx.run(
89
+ query,
90
+ ln_id=node["ln_id"],
91
+ timestamp=node["timestamp"],
92
+ content=node["content"],
93
+ )
94
+
95
+ # TODO: tool.manual
96
+ @staticmethod
97
+ async def add_tool_node(tx, node):
98
+ """
99
+ Asynchronously adds a tool node to the graph.
100
+
101
+ Args:
102
+ tx: The Neo4j transaction.
103
+ node (dict): The properties of the tool node including 'ln_id', 'timestamp', 'function', and 'parser'.
104
+ """
105
+ query = """
106
+ MERGE (n:Tool:LionNode {ln_id: $ln_id})
107
+ SET n.timestamp = $timestamp
108
+ SET n.function = $function
109
+ SET n.parser = $parser
110
+ """
111
+ await tx.run(
112
+ query,
113
+ ln_id=node["ln_id"],
114
+ timestamp=node["timestamp"],
115
+ function=node["function"],
116
+ parser=node["parser"],
117
+ )
118
+
119
+ @staticmethod
120
+ async def add_directiveSelection_node(tx, node):
121
+ """
122
+ Asynchronously adds an directive selection node to the graph.
123
+
124
+ Args:
125
+ tx: The Neo4j transaction.
126
+ node (dict): The properties of the directive selection node including 'ln_id', 'directive', and 'directiveKwargs'.
127
+ """
128
+ query = """
129
+ MERGE (n:DirectiveSelection:LionNode {ln_id: $ln_id})
130
+ SET n.directive = $directive
131
+ SET n.directiveKwargs = $directiveKwargs
132
+ """
133
+ await tx.run(
134
+ query,
135
+ ln_id=node["ln_id"],
136
+ directive=node["directive"],
137
+ directiveKwargs=node["directive_kwargs"],
138
+ )
139
+
140
+ @staticmethod
141
+ async def add_baseAgent_node(tx, node):
142
+ """
143
+ Asynchronously adds an agent node to the graph.
144
+
145
+ Args:
146
+ tx: The Neo4j transaction.
147
+ node (dict): The properties of the agent node including 'ln_id', 'timestamp', 'structureId', and 'outputParser'.
148
+ """
149
+ query = """
150
+ MERGE (n:Agent:LionNode {ln_id:$ln_id})
151
+ SET n.timestamp = $timestamp
152
+ SET n.structureId = $structureId
153
+ SET n.outputParser = $outputParser
154
+ """
155
+ await tx.run(
156
+ query,
157
+ ln_id=node["ln_id"],
158
+ timestamp=node["timestamp"],
159
+ structureId=node["structure_id"],
160
+ outputParser=node["output_parser"],
161
+ )
162
+
163
+ @staticmethod
164
+ async def add_forward_edge(tx, edge):
165
+ """
166
+ Asynchronously adds a forward relationship between two nodes in the graph.
167
+
168
+ Args:
169
+ tx: The Neo4j transaction.
170
+ edge (dict): The properties of the edge including 'ln_id', 'timestamp', 'head', 'tail', 'label', and 'condition'.
171
+ """
172
+ query = """
173
+ MATCH (m:LionNode) WHERE m.ln_id = $head
174
+ MATCH (n:LionNode) WHERE n.ln_id = $tail
175
+ MERGE (m)-[r:FORWARD]->(n)
176
+ SET r.ln_id = $ln_id
177
+ SET r.timestamp = $timestamp
178
+ SET r.label = $label
179
+ SET r.condition = $condition
180
+ """
181
+ await tx.run(
182
+ query,
183
+ ln_id=edge["ln_id"],
184
+ timestamp=edge["timestamp"],
185
+ head=edge["head"],
186
+ tail=edge["tail"],
187
+ label=edge["label"],
188
+ condition=edge["condition"],
189
+ )
190
+
191
+ @staticmethod
192
+ async def add_bundle_edge(tx, edge):
193
+ """
194
+ Asynchronously adds a bundle relationship between two nodes in the graph.
195
+
196
+ Args:
197
+ tx: The Neo4j transaction.
198
+ edge (dict): The properties of the edge including 'ln_id', 'timestamp', 'head', 'tail', 'label', and 'condition'.
199
+ """
200
+ query = """
201
+ MATCH (m:LionNode) WHERE m.ln_id = $head
202
+ MATCH (n:LionNode) WHERE n.ln_id = $tail
203
+ MERGE (m)-[r:BUNDLE]->(n)
204
+ SET r.ln_id = $ln_id
205
+ SET r.timestamp = $timestamp
206
+ SET r.label = $label
207
+ SET r.condition = $condition
208
+ """
209
+ await tx.run(
210
+ query,
211
+ ln_id=edge["ln_id"],
212
+ timestamp=edge["timestamp"],
213
+ head=edge["head"],
214
+ tail=edge["tail"],
215
+ label=edge["label"],
216
+ condition=edge["condition"],
217
+ )
218
+
219
+ @staticmethod
220
+ async def add_head_edge(tx, structure):
221
+ """
222
+ Asynchronously adds head relationships from a structure node to its head nodes.
223
+
224
+ Args:
225
+ tx: The Neo4j transaction.
226
+ structure: The structure node from which head relationships are established.
227
+ """
228
+ for head in structure.get_heads():
229
+ head_id = head.ln_id
230
+ query = """
231
+ MATCH (m:Structure) WHERE m.ln_id = $structureId
232
+ MATCH (n:LionNode) WHERE n.ln_id = $headId
233
+ MERGE (m)-[:HEAD]->(n)
234
+ """
235
+ await tx.run(query, structureId=structure.ln_id, headId=head_id)
236
+
237
+ @staticmethod
238
+ async def add_single_condition_cls(tx, condCls):
239
+ """
240
+ Asynchronously adds a condition class node to the graph.
241
+
242
+ Args:
243
+ tx: The Neo4j transaction.
244
+ condCls (dict): The properties of the condition class node including 'className' and 'code'.
245
+ """
246
+ query = """
247
+ MERGE (n:EdgeCondition:LionNode {className: $className})
248
+ SET n.code = $code
249
+ """
250
+ await tx.run(query, className=condCls["class_name"], code=condCls["class"])
251
+
252
+ async def add_node(self, tx, node_dict, structure_name):
253
+ """
254
+ Asynchronously adds various types of nodes to the Neo4j graph based on the provided dictionary of node lists.
255
+
256
+ This method iterates through a dictionary where each key is a node type and each value is a list of nodes.
257
+ It adds each node to the graph according to its type.
258
+
259
+ Args:
260
+ tx: The Neo4j transaction.
261
+ node_dict (dict): A dictionary where keys are node type strings and values are lists of node properties dictionaries.
262
+ structure_name (str): The name of the structure to which these nodes belong, used specifically for 'StructureExecutor' nodes.
263
+
264
+ Raises:
265
+ ValueError: If an unsupported node type is detected in the dictionary.
266
+ """
267
+ for node in node_dict:
268
+ node_list = node_dict[node]
269
+ if node == "GraphExecutor":
270
+ [
271
+ await self.add_structure_node(tx, i, structure_name)
272
+ for i in node_list
273
+ ]
274
+ elif node == "System":
275
+ [await self.add_system_node(tx, i) for i in node_list]
276
+ elif node == "Instruction":
277
+ [await self.add_instruction_node(tx, i) for i in node_list]
278
+ elif node == "Tool":
279
+ [await self.add_tool_node(tx, i) for i in node_list]
280
+ elif node == "DirectiveSelection":
281
+ [await self.add_directiveSelection_node(tx, i) for i in node_list]
282
+ elif node == "BaseAgent":
283
+ [await self.add_baseAgent_node(tx, i) for i in node_list]
284
+ else:
285
+ raise ValueError("Not supported node type detected")
286
+
287
+ async def add_edge(self, tx, edge_list):
288
+ """
289
+ Asynchronously adds edges to the Neo4j graph based on a list of edge properties.
290
+
291
+ This method processes a list of edges, each represented by a dictionary of properties, and creates either
292
+ 'BUNDLE' or 'FORWARD' relationships in the graph based on the 'bundle' property of each edge.
293
+
294
+ Args:
295
+ tx: The Neo4j transaction.
296
+ edge_list (list[dict]): A list of dictionaries where each dictionary contains properties of an edge including
297
+ its type, identifiers of the head and tail nodes, and additional attributes like label and condition.
298
+
299
+ Raises:
300
+ ValueError: If an edge dictionary is missing required properties or contains invalid values.
301
+ """
302
+ for edge in edge_list:
303
+ if edge["bundle"] == "True":
304
+ await self.add_bundle_edge(tx, edge)
305
+ else:
306
+ await self.add_forward_edge(tx, edge)
307
+
308
+ async def add_condition_cls(self, tx, edge_cls_list):
309
+ """
310
+ Asynchronously adds condition class nodes to the Neo4j graph.
311
+
312
+ This method iterates over a list of condition class definitions and adds each as a node in the graph.
313
+ Each condition class is typically used to define logic or conditions within the graph structure.
314
+
315
+ Args:
316
+ tx: The Neo4j transaction.
317
+ edge_cls_list (list[dict]): A list of dictionaries where each dictionary represents the properties of a
318
+ condition class, including the class name and its corresponding code.
319
+
320
+ Raises:
321
+ ValueError: If any condition class dictionary is missing required properties or the properties do not adhere
322
+ to expected formats.
323
+ """
324
+ for cls in edge_cls_list:
325
+ await self.add_single_condition_cls(tx, cls)
326
+
327
+ @staticmethod
328
+ async def check_id_constraint(tx):
329
+ """
330
+ Asynchronously applies a unique constraint on the 'ln_id' attribute for all nodes of type 'LionNode' in the graph.
331
+
332
+ This constraint ensures that each node in the graph has a unique identifier.
333
+
334
+ Args:
335
+ tx: The Neo4j transaction.
336
+ """
337
+ query = """
338
+ CREATE CONSTRAINT node_id IF NOT EXISTS
339
+ FOR (n:LionNode) REQUIRE (n.ln_id) IS UNIQUE
340
+ """
341
+ await tx.run(query)
342
+
343
+ @staticmethod
344
+ async def check_structure_name_constraint(tx):
345
+ """
346
+ Asynchronously applies a unique constraint on the 'name' attribute for all nodes of type 'Structure' in the graph.
347
+
348
+ This constraint ensures that each structure in the graph can be uniquely identified by its name.
349
+
350
+ Args:
351
+ tx: The Neo4j transaction.
352
+ """
353
+ query = """
354
+ CREATE CONSTRAINT structure_name IF NOT EXISTS
355
+ FOR (n:Structure) REQUIRE (n.name) IS UNIQUE
356
+ """
357
+ await tx.run(query)
358
+
359
+ async def store(self, structure, structure_name):
360
+ """
361
+ Asynchronously stores a structure and its components in the Neo4j graph.
362
+
363
+ This method orchestrates the storage of nodes, edges, and other related elements that make up a structure,
364
+ ensuring all elements are added transactionally.
365
+
366
+ Args:
367
+ structure: The structure object containing the nodes and edges to be stored.
368
+ structure_name (str): The name of the structure, used to uniquely identify it in the graph.
369
+
370
+ Raises:
371
+ ValueError: If the transaction fails due to an exception, indicating an issue with the data or constraints.
372
+ """
373
+ node_list, node_dict = output_node_list(structure)
374
+ edge_list, edge_cls_list = output_edge_list(structure)
375
+
376
+ async with self.driver as driver:
377
+ async with driver.session(database=self.database) as session:
378
+ # constraint
379
+ tx = await session.begin_transaction()
380
+ try:
381
+ await self.check_id_constraint(tx)
382
+ await self.check_structure_name_constraint(tx)
383
+ await tx.commit()
384
+ except Exception as e:
385
+ raise ValueError(f"transaction rolled back due to exception: {e}")
386
+ finally:
387
+ await tx.close()
388
+
389
+ # query
390
+ tx = await session.begin_transaction()
391
+ try:
392
+ await self.add_node(tx, node_dict, structure_name)
393
+ await self.add_edge(tx, edge_list)
394
+ await self.add_condition_cls(tx, edge_cls_list)
395
+ await self.add_head_edge(tx, structure)
396
+ await tx.commit()
397
+ except Exception as e:
398
+ raise ValueError(f"transaction rolled back due to exception: {e}")
399
+ finally:
400
+ await tx.close()
401
+
402
+ # ---------------------frpm_neo4j---------------------------------
403
+ @staticmethod
404
+ async def match_node(tx, node_id):
405
+ """
406
+ Asynchronously retrieves a node from the graph based on its identifier.
407
+
408
+ Args:
409
+ tx: The Neo4j transaction.
410
+ node_id (str): The unique identifier of the node to retrieve.
411
+
412
+ Returns:
413
+ A dictionary containing the properties of the node if found, otherwise None.
414
+ """
415
+ query = """
416
+ MATCH (n:LionNode) WHERE n.ln_id = $ln_id
417
+ RETURN labels(n), n{.*}
418
+ """
419
+ result = await tx.run(query, ln_id=node_id)
420
+ result = [record.values() async for record in result]
421
+ if result:
422
+ return result[0]
423
+ else:
424
+ return None
425
+
426
+ @staticmethod
427
+ async def match_structure_id(tx, name):
428
+ """
429
+ Asynchronously retrieves the identifier of a structure based on its name.
430
+
431
+ Args:
432
+ tx: The Neo4j transaction.
433
+ name (str): The name of the structure to retrieve the identifier for.
434
+
435
+ Returns:
436
+ A list containing the identifier(s) of the matching structure(s).
437
+ """
438
+ query = """
439
+ MATCH (n:Structure) WHERE n.name = $name
440
+ RETURN n.ln_id
441
+ """
442
+ result = await tx.run(query, name=name)
443
+ result = [record.values() async for record in result]
444
+ result = sum(result, [])
445
+ return result
446
+
447
+ @staticmethod
448
+ async def head(tx, node_id):
449
+ """
450
+ Asynchronously retrieves the head nodes associated with a structure node in the graph.
451
+
452
+ Args:
453
+ tx: The Neo4j transaction.
454
+ node_id (str): The identifier of the structure node whose head nodes are to be retrieved.
455
+
456
+ Returns:
457
+ A list of dictionaries representing the properties and labels of each head node connected to the structure.
458
+ """
459
+ query = """
460
+ MATCH (n:Structure)-[r:HEAD]->(m) WHERE n.ln_id = $nodeId
461
+ RETURN r{.*}, labels(m), m{.*}
462
+ """
463
+ result = await tx.run(query, nodeId=node_id)
464
+ result = [record.values() async for record in result]
465
+ return result
466
+
467
+ @staticmethod
468
+ async def forward(tx, node_id):
469
+ """
470
+ Asynchronously retrieves all forward relationships and their target nodes for a given node.
471
+
472
+ Args:
473
+ tx: The Neo4j transaction.
474
+ node_id (str): The identifier of the node from which to retrieve forward relationships.
475
+
476
+ Returns:
477
+ A list of dictionaries representing the properties and labels of each node connected by a forward relationship.
478
+ """
479
+ query = """
480
+ MATCH (n:LionNode)-[r:FORWARD]->(m) WHERE n.ln_id = $nodeId
481
+ RETURN r{.*}, labels(m), m{.*}
482
+ """
483
+ result = await tx.run(query, nodeId=node_id)
484
+ result = [record.values() async for record in result]
485
+ return result
486
+
487
+ @staticmethod
488
+ async def bundle(tx, node_id):
489
+ """
490
+ Asynchronously retrieves all bundle relationships and their target nodes for a given node.
491
+
492
+ Args:
493
+ tx: The Neo4j transaction.
494
+ node_id (str): The identifier of the node from which to retrieve bundle relationships.
495
+
496
+ Returns:
497
+ A list of dictionaries representing the properties and labels of each node connected by a bundle relationship.
498
+ """
499
+ query = """
500
+ MATCH (n:LionNode)-[r:BUNDLE]->(m) WHERE n.ln_id = $nodeId
501
+ RETURN labels(m), m{.*}
502
+ """
503
+ result = await tx.run(query, nodeId=node_id)
504
+ result = [record.values() async for record in result]
505
+ return result
506
+
507
+ @staticmethod
508
+ async def match_condition_class(tx, name):
509
+ """
510
+ Asynchronously retrieves the code for a condition class based on its class name.
511
+
512
+ Args:
513
+ tx: The Neo4j transaction.
514
+ name (str): The class name of the condition to retrieve the code for.
515
+
516
+ Returns:
517
+ The code of the condition class if found, otherwise None.
518
+ """
519
+ query = """
520
+ MATCH (n:EdgeCondition) WHERE n.className = $name
521
+ RETURN n.code
522
+ """
523
+ result = await tx.run(query, name=name)
524
+ result = [record.values() async for record in result]
525
+ if result:
526
+ return result[0][0]
527
+ else:
528
+ return None
529
+
530
+ async def locate_structure(
531
+ self, tx, structure_name: str = None, structure_id: str = None
532
+ ):
533
+ """
534
+ Asynchronously locates a structure by its name or ID in the Neo4j graph.
535
+
536
+ This method is designed to find a structure either by its name or by a specific identifier,
537
+ returning the identifier if found.
538
+
539
+ Args:
540
+ tx: The Neo4j transaction.
541
+ structure_name (str, optional): The name of the structure to locate.
542
+ structure_id (str, optional): The unique identifier of the structure to locate.
543
+
544
+ Returns:
545
+ str: The identifier of the located structure.
546
+
547
+ Raises:
548
+ ValueError: If neither structure name nor ID is provided, or if the provided name or ID does not correspond
549
+ to any existing structure.
550
+ """
551
+ if not structure_name and not structure_id:
552
+ raise ValueError("Please provide the structure name or ln_id")
553
+ if structure_name:
554
+ id = await self.match_structure_id(tx, structure_name)
555
+ if not id:
556
+ raise ValueError(f"Structure: {structure_name} is not found")
557
+ elif structure_id is not None and structure_id not in id:
558
+ raise ValueError(
559
+ f"{structure_name} and id {structure_id} does not match"
560
+ )
561
+ return id[0]
562
+ else:
563
+ result = await self.match_node(tx, structure_id)
564
+ if result:
565
+ return structure_id
566
+ else:
567
+ raise ValueError(f"Structure id {structure_id} is invalid")
568
+
569
+ async def get_heads(self, structure_name: str = None, structure_id: str = None):
570
+ """
571
+ Asynchronously retrieves the head nodes associated with a given structure in the graph.
572
+
573
+ Args:
574
+ structure_name (str, optional): The name of the structure whose head nodes are to be retrieved.
575
+ structure_id (str, optional): The identifier of the structure whose head nodes are to be retrieved.
576
+
577
+ Returns:
578
+ tuple: A tuple containing the structure identifier and a list of dictionaries, each representing a head node
579
+ connected to the structure.
580
+
581
+ Raises:
582
+ ValueError: If both structure name and ID are not provided, or if the specified structure cannot be found.
583
+ """
584
+ async with self.driver as driver:
585
+ async with driver.session(database=self.database) as session:
586
+ id = await session.execute_read(
587
+ self.locate_structure, structure_name, structure_id
588
+ )
589
+ result = await session.execute_read(self.head, id)
590
+ return id, result
591
+
592
+ async def get_bundle(self, node_id):
593
+ """
594
+ Asynchronously retrieves all nodes connected by a bundle relationship to a given node in the graph.
595
+
596
+ Args:
597
+ node_id (str): The identifier of the node from which bundle relationships are to be retrieved.
598
+
599
+ Returns:
600
+ list: A list of dictionaries representing each node connected by a bundle relationship from the specified node.
601
+ """
602
+ async with self.driver as driver:
603
+ async with driver.session(database=self.database) as session:
604
+ result = await session.execute_read(self.bundle, node_id)
605
+ return result
606
+
607
+ async def get_forwards(self, node_id):
608
+ """
609
+ Asynchronously retrieves all nodes connected by forward relationships to a given node in the graph.
610
+
611
+ Args:
612
+ node_id (str): The identifier of the node from which forward relationships are to be retrieved.
613
+
614
+ Returns:
615
+ list: A list of dictionaries representing each node connected by a forward relationship from the specified node.
616
+ """
617
+ async with self.driver as driver:
618
+ async with driver.session(database=self.database) as session:
619
+ result = await session.execute_read(self.forward, node_id)
620
+ return result
621
+
622
+ async def get_condition_cls_code(self, class_name):
623
+ """
624
+ Asynchronously retrieves the code associated with a specified condition class from the Neo4j graph.
625
+
626
+ This method queries the graph to find the code that defines the behavior or logic of a condition class by its name.
627
+
628
+ Args:
629
+ class_name (str): The name of the condition class whose code is to be retrieved.
630
+
631
+ Returns:
632
+ str: The code of the condition class if found, or None if the class does not exist in the graph.
633
+
634
+ Raises:
635
+ ValueError: If the class_name is not provided or if the query fails due to incorrect syntax or database issues.
636
+ """
637
+ async with self.driver as driver:
638
+ async with driver.session(database=self.database) as session:
639
+ result = await session.execute_read(
640
+ self.match_condition_class, class_name
641
+ )
642
+ return result
643
+
644
+ async def node_exist(self, node_id):
645
+ """
646
+ Asynchronously checks if a node with the specified identifier exists in the Neo4j graph.
647
+
648
+ This method is useful for validation checks before attempting operations that assume the existence of a node.
649
+
650
+ Args:
651
+ node_id (str): The unique identifier of the node to check for existence.
652
+
653
+ Returns:
654
+ bool: True if the node exists in the graph, False otherwise.
655
+
656
+ Raises:
657
+ ValueError: If the node_id is not provided or if the query fails due to incorrect syntax or database issues.
658
+ """
659
+ async with self.driver as driver:
660
+ async with driver.session(database=self.database) as session:
661
+ result = await session.execute_read(self.match_node, node_id)
662
+ if result:
663
+ return True
664
+ else:
665
+ return False