lionagi 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. lionagi/core/agent/base_agent.py +2 -3
  2. lionagi/core/branch/base.py +1 -1
  3. lionagi/core/branch/branch.py +2 -1
  4. lionagi/core/branch/flow_mixin.py +1 -1
  5. lionagi/core/branch/util.py +1 -1
  6. lionagi/core/execute/base_executor.py +1 -4
  7. lionagi/core/execute/branch_executor.py +66 -3
  8. lionagi/core/execute/instruction_map_executor.py +48 -0
  9. lionagi/core/execute/neo4j_executor.py +381 -0
  10. lionagi/core/execute/structure_executor.py +120 -4
  11. lionagi/core/flow/monoflow/ReAct.py +21 -19
  12. lionagi/core/flow/monoflow/chat_mixin.py +1 -1
  13. lionagi/core/flow/monoflow/followup.py +14 -13
  14. lionagi/core/flow/polyflow/__init__.py +1 -1
  15. lionagi/core/generic/component.py +197 -122
  16. lionagi/core/generic/condition.py +3 -1
  17. lionagi/core/generic/edge.py +77 -25
  18. lionagi/core/graph/graph.py +1 -1
  19. lionagi/core/mail/mail_manager.py +3 -2
  20. lionagi/core/session/session.py +1 -1
  21. lionagi/core/tool/tool_manager.py +10 -9
  22. lionagi/experimental/__init__.py +0 -0
  23. lionagi/experimental/directive/__init__.py +0 -0
  24. lionagi/experimental/directive/evaluator/__init__.py +0 -0
  25. lionagi/experimental/directive/evaluator/ast_evaluator.py +115 -0
  26. lionagi/experimental/directive/evaluator/base_evaluator.py +202 -0
  27. lionagi/experimental/directive/evaluator/sandbox_.py +14 -0
  28. lionagi/experimental/directive/evaluator/script_engine.py +83 -0
  29. lionagi/experimental/directive/parser/__init__.py +0 -0
  30. lionagi/experimental/directive/parser/base_parser.py +215 -0
  31. lionagi/experimental/directive/schema.py +36 -0
  32. lionagi/experimental/directive/template_/__init__.py +0 -0
  33. lionagi/experimental/directive/template_/base_template.py +63 -0
  34. lionagi/experimental/report/__init__.py +0 -0
  35. lionagi/experimental/report/form.py +64 -0
  36. lionagi/experimental/report/report.py +138 -0
  37. lionagi/experimental/report/util.py +47 -0
  38. lionagi/experimental/tool/__init__.py +0 -0
  39. lionagi/experimental/tool/function_calling.py +43 -0
  40. lionagi/experimental/tool/manual.py +66 -0
  41. lionagi/experimental/tool/schema.py +59 -0
  42. lionagi/experimental/tool/tool_manager.py +138 -0
  43. lionagi/experimental/tool/util.py +16 -0
  44. lionagi/experimental/validator/__init__.py +0 -0
  45. lionagi/experimental/validator/rule.py +139 -0
  46. lionagi/experimental/validator/validator.py +56 -0
  47. lionagi/experimental/work/__init__.py +10 -0
  48. lionagi/experimental/work/async_queue.py +54 -0
  49. lionagi/experimental/work/schema.py +73 -0
  50. lionagi/experimental/work/work_function.py +67 -0
  51. lionagi/experimental/work/worker.py +56 -0
  52. lionagi/experimental/work2/__init__.py +0 -0
  53. lionagi/experimental/work2/form.py +371 -0
  54. lionagi/experimental/work2/report.py +289 -0
  55. lionagi/experimental/work2/schema.py +30 -0
  56. lionagi/experimental/work2/tests.py +72 -0
  57. lionagi/experimental/work2/util.py +0 -0
  58. lionagi/experimental/work2/work.py +0 -0
  59. lionagi/experimental/work2/work_function.py +89 -0
  60. lionagi/experimental/work2/worker.py +12 -0
  61. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  62. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  63. lionagi/integrations/bridge/llamaindex_/get_index.py +294 -0
  64. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  65. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  66. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  67. lionagi/integrations/config/oai_configs.py +1 -1
  68. lionagi/integrations/config/ollama_configs.py +1 -1
  69. lionagi/integrations/config/openrouter_configs.py +1 -1
  70. lionagi/integrations/storage/__init__.py +3 -0
  71. lionagi/integrations/storage/neo4j.py +673 -0
  72. lionagi/integrations/storage/storage_util.py +289 -0
  73. lionagi/integrations/storage/structure_excel.py +268 -0
  74. lionagi/integrations/storage/to_csv.py +63 -0
  75. lionagi/integrations/storage/to_excel.py +76 -0
  76. lionagi/libs/__init__.py +4 -0
  77. lionagi/libs/ln_knowledge_graph.py +405 -0
  78. lionagi/libs/ln_queue.py +101 -0
  79. lionagi/libs/ln_tokenizer.py +57 -0
  80. lionagi/libs/sys_util.py +1 -1
  81. lionagi/lions/__init__.py +0 -0
  82. lionagi/lions/coder/__init__.py +0 -0
  83. lionagi/lions/coder/add_feature.py +20 -0
  84. lionagi/lions/coder/base_prompts.py +22 -0
  85. lionagi/lions/coder/coder.py +121 -0
  86. lionagi/lions/coder/util.py +91 -0
  87. lionagi/lions/researcher/__init__.py +0 -0
  88. lionagi/lions/researcher/data_source/__init__.py +0 -0
  89. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  90. lionagi/lions/researcher/data_source/google_.py +199 -0
  91. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  92. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  93. lionagi/tests/libs/test_queue.py +67 -0
  94. lionagi/tests/test_core/generic/__init__.py +0 -0
  95. lionagi/tests/test_core/generic/test_component.py +89 -0
  96. lionagi/tests/test_core/test_branch.py +0 -1
  97. lionagi/version.py +1 -1
  98. {lionagi-0.1.0.dist-info → lionagi-0.1.2.dist-info}/METADATA +1 -1
  99. lionagi-0.1.2.dist-info/RECORD +206 -0
  100. lionagi-0.1.0.dist-info/RECORD +0 -136
  101. {lionagi-0.1.0.dist-info → lionagi-0.1.2.dist-info}/LICENSE +0 -0
  102. {lionagi-0.1.0.dist-info → lionagi-0.1.2.dist-info}/WHEEL +0 -0
  103. {lionagi-0.1.0.dist-info → lionagi-0.1.2.dist-info}/top_level.txt +0 -0
@@ -19,10 +19,32 @@ from lionagi.core.graph.graph import Graph
19
19
 
20
20
 
21
21
  class StructureExecutor(BaseExecutor, Graph):
22
+ """
23
+ Executes tasks within a graph structure, handling dynamic node flows and conditional edge logic.
24
+
25
+ Attributes:
26
+ condition_check_result (bool | None): Result of the last condition check performed during execution,
27
+ used to control flow based on dynamic conditions.
28
+ """
22
29
 
23
30
  condition_check_result: bool | None = None
24
31
 
25
32
  async def check_edge_condition(self, edge: Edge, executable_id, request_source):
33
+ """
34
+ Evaluates the condition associated with an edge, determining if execution should proceed along that edge based
35
+ on the condition's source type.
36
+
37
+ Args:
38
+ edge (Edge): The edge whose condition needs to be checked.
39
+ executable_id (str): ID of the executor handling this edge's condition.
40
+ request_source (str): Origin of the request prompting this condition check.
41
+
42
+ Returns:
43
+ bool: Result of the condition evaluation.
44
+
45
+ Raises:
46
+ ValueError: If the source_type of the condition is invalid.
47
+ """
26
48
  if edge.condition.source_type == "structure":
27
49
  return edge.condition(self)
28
50
 
@@ -59,6 +81,17 @@ class StructureExecutor(BaseExecutor, Graph):
59
81
  async def _check_executable_condition(
60
82
  self, edge: Edge, executable_id, request_source
61
83
  ):
84
+ """
85
+ Sends the edge's condition to an external executable for evaluation and waits for the result.
86
+
87
+ Args:
88
+ edge (Edge): The edge containing the condition to be checked.
89
+ executable_id (str): ID of the executable that will evaluate the condition.
90
+ request_source (str): Source of the request for condition evaluation.
91
+
92
+ Returns:
93
+ bool: The result of the condition check.
94
+ """
62
95
  self.send(
63
96
  recipient_id=executable_id,
64
97
  category="condition",
@@ -73,6 +106,16 @@ class StructureExecutor(BaseExecutor, Graph):
73
106
  return check_result
74
107
 
75
108
  async def _handle_node_id(self, mail: BaseMail):
109
+ """
110
+ Processes the node identified by its ID in the mail's package, ensuring it exists and retrieving the next set of
111
+ nodes based on the current node.
112
+
113
+ Args:
114
+ mail (BaseMail): The mail containing the node ID and related execution details.
115
+
116
+ Raises:
117
+ ValueError: If the node does not exist within the structure.
118
+ """
76
119
  if mail.package["package"] not in self.internal_nodes:
77
120
  raise ValueError(
78
121
  f"Node {mail.package} does not exist in the structure {self.id_}"
@@ -84,6 +127,15 @@ class StructureExecutor(BaseExecutor, Graph):
84
127
  )
85
128
 
86
129
  async def _handle_node(self, mail: BaseMail):
130
+ """
131
+ Processes the node specified in the mail's package, ensuring it exists within the structure.
132
+
133
+ Args:
134
+ mail (BaseMail): The mail containing the node details to be processed.
135
+
136
+ Raises:
137
+ ValueError: If the node does not exist within the structure.
138
+ """
87
139
  if not self.node_exist(mail.package["package"]):
88
140
  raise ValueError(
89
141
  f"Node {mail.package} does not exist in the structure {self.id_}"
@@ -93,7 +145,15 @@ class StructureExecutor(BaseExecutor, Graph):
93
145
  )
94
146
 
95
147
  async def _handle_mail(self, mail: BaseMail):
148
+ """
149
+ Processes incoming mail based on its category, initiating node execution or structure operations accordingly.
150
+
151
+ Args:
152
+ mail (BaseMail): The mail to be processed, containing category and package information.
96
153
 
154
+ Raises:
155
+ ValueError: If the mail type is invalid for the current structure or an error occurs in handling the node ID.
156
+ """
97
157
  if mail.category == "start":
98
158
  return self.get_heads()
99
159
 
@@ -116,7 +176,7 @@ class StructureExecutor(BaseExecutor, Graph):
116
176
  else:
117
177
  raise ValueError(f"Invalid mail type for structure")
118
178
 
119
- async def _next_node(self, current_node: BaseNode, executable_id, request_source):
179
+ async def _next_node(self, current_node: Node, executable_id, request_source):
120
180
  """
121
181
  Get the next step nodes based on the current node.
122
182
 
@@ -128,7 +188,7 @@ class StructureExecutor(BaseExecutor, Graph):
128
188
  list[Node]: The next step nodes.
129
189
  """
130
190
  next_nodes = []
131
- next_edges: dict[Edge] = self.get_node_edges(current_node, node_as="out")
191
+ next_edges = self.get_node_edges(current_node, node_as="out")
132
192
  for edge in convert.to_list(list(next_edges.values())):
133
193
  if edge.bundle:
134
194
  continue
@@ -139,7 +199,7 @@ class StructureExecutor(BaseExecutor, Graph):
139
199
  if not check:
140
200
  continue
141
201
  node = self.internal_nodes[edge.tail]
142
- further_edges: dict[Edge] = self.get_node_edges(node, node_as="out")
202
+ further_edges = self.get_node_edges(node, node_as="out")
143
203
  bundled_nodes = deque()
144
204
  for f_edge in convert.to_list(list(further_edges.values())):
145
205
  if f_edge.bundle:
@@ -150,6 +210,13 @@ class StructureExecutor(BaseExecutor, Graph):
150
210
  return next_nodes
151
211
 
152
212
  def _send_mail(self, next_nodes: list | None, mail: BaseMail):
213
+ """
214
+ Sends mails to the next nodes or signals the end of execution if no next nodes exist.
215
+
216
+ Args:
217
+ next_nodes (list | None): List of next nodes to process or None if no further nodes are available.
218
+ mail (BaseMail): The base mail used for sending follow-up actions.
219
+ """
153
220
  if not next_nodes: # tail
154
221
  self.send(
155
222
  recipient_id=mail.sender_id,
@@ -181,6 +248,26 @@ class StructureExecutor(BaseExecutor, Graph):
181
248
 
182
249
  @staticmethod
183
250
  def parse_bundled_to_action(instruction: Node, bundled_nodes: deque):
251
+ """
252
+ Constructs an action node from a bundle of nodes, combining various types of nodes like ActionSelection or Tool
253
+ into a single actionable unit.
254
+
255
+ This method takes a bundle of nodes and systematically integrates their functionalities into a single `ActionNode`.
256
+ This is crucial in scenarios where multiple actions or tools need to be executed sequentially or in a coordinated
257
+ manner as part of a larger instruction flow.
258
+
259
+ Args:
260
+ instruction (Node): The initial instruction node leading to this action.
261
+ bundled_nodes (deque): A deque containing nodes to be bundled into the action. These nodes typically represent
262
+ either actions to be taken or tools to be utilized.
263
+
264
+ Returns:
265
+ ActionNode: An `ActionNode` that encapsulates the combined functionality of the bundled nodes, ready for execution.
266
+
267
+ Raises:
268
+ ValueError: If an unrecognized node type is encountered within the bundled nodes. Only `ActionSelection` and
269
+ `Tool` nodes are valid for bundling into an `ActionNode`.
270
+ """
184
271
  action_node = ActionNode(instruction=instruction)
185
272
  while bundled_nodes:
186
273
  node = bundled_nodes.popleft()
@@ -201,7 +288,7 @@ class StructureExecutor(BaseExecutor, Graph):
201
288
  while self.pending_ins[key]:
202
289
  mail: BaseMail = self.pending_ins[key].popleft()
203
290
  try:
204
- if mail == "end":
291
+ if mail.category == "end":
205
292
  self.execute_stop = True
206
293
  return
207
294
  next_nodes = await self._handle_mail(mail)
@@ -210,9 +297,38 @@ class StructureExecutor(BaseExecutor, Graph):
210
297
  raise ValueError(f"Error handling mail: {e}") from e
211
298
 
212
299
  async def execute(self, refresh_time=1):
300
+ """
301
+ Executes the forward processing loop, checking conditions and processing nodes at defined intervals.
302
+
303
+ Args:
304
+ refresh_time (int): The delay between execution cycles, allowing for asynchronous operations to complete.
305
+
306
+ Raises:
307
+ ValueError: If the graph structure is found to be cyclic, which is unsupported.
308
+ """
213
309
  if not self.acyclic:
214
310
  raise ValueError("Structure is not acyclic")
215
311
 
216
312
  while not self.execute_stop:
217
313
  await self.forward()
218
314
  await AsyncUtil.sleep(refresh_time)
315
+
316
+ def to_excel(self, structure_name, dir="structure_storage"):
317
+ """
318
+ Exports the current structure to an Excel file using a specified structure name and directory.
319
+
320
+ This method utilizes the `to_excel` function from the `lionagi.integrations.storage.to_excel` module,
321
+ saving the current structure instance into an Excel file format. The Excel file will contain details
322
+ about nodes, edges, and other relevant data as separate sheets within the file.
323
+
324
+ Args:
325
+ structure_name (str): The name to assign to the structure within the Excel file. This name is
326
+ used as part of the file naming convention.
327
+ dir (str, optional): The directory where the Excel file will be saved. Defaults to "structure_storage".
328
+
329
+ Raises:
330
+ Exception: Propagates any exceptions raised by the `to_excel` function, which might occur during
331
+ the file writing process or data formatting.
332
+ """
333
+ from lionagi.integrations.storage.to_excel import to_excel
334
+ to_excel(self, structure_name, dir)
@@ -46,8 +46,7 @@ class MonoReAct(MonoChat):
46
46
  """
47
47
 
48
48
  ACTION_PROMPT = """
49
- You have {num_steps} steps left in the current task. If further actions are needed, invoke tool usage.
50
- If you are done, present the final result to the user without further tool usage.
49
+ You have {num_steps} steps left in the current task. invoke tool usage.
51
50
  """
52
51
 
53
52
  OUTPUT_PROMPT = "Notice: Present the final output to the user. Original user instruction: {instruction}"
@@ -127,15 +126,15 @@ class MonoReAct(MonoChat):
127
126
 
128
127
  def _create_followup_config(self, tools, tool_choice="auto", **kwargs):
129
128
  """
130
- Creates the configuration for the followup steps based on the provided tools and parameters.
129
+ Creates the configuration for the followup chat based on the provided tools and parameters.
131
130
 
132
131
  Args:
133
- tools (Optional[Any]): Specifies tools to be invoked during the followup steps.
132
+ tools (Optional[Any]): Specifies tools to be invoked during the followup chat.
134
133
  tool_choice (str): The choice of tools to use (default: "auto").
135
- **kwargs: Additional keyword arguments for the followup configuration.
134
+ **kwargs: Additional keyword arguments for the followup chat configuration.
136
135
 
137
136
  Returns:
138
- dict: The configuration for the followup steps.
137
+ dict: The configuration for the followup chat.
139
138
 
140
139
  Raises:
141
140
  ValueError: If no tools are found and registered.
@@ -146,7 +145,9 @@ class MonoReAct(MonoChat):
146
145
  if not self.branch.tool_manager.has_tools:
147
146
  raise ValueError("No tools found. You need to register tools.")
148
147
 
149
- config = self.branch.tool_manager.parse_tool(tools=True, **kwargs)
148
+ if tools is None:
149
+ tools = True
150
+ config = self.branch.tool_manager.parse_tool(tools=tools, **kwargs)
150
151
  config["tool_parsed"] = True
151
152
  config["tool_choice"] = tool_choice
152
153
  return config
@@ -159,7 +160,7 @@ class MonoReAct(MonoChat):
159
160
  system=None,
160
161
  tools=None,
161
162
  num_rounds: int = 1,
162
- auto=False,
163
+ auto=True,
163
164
  reason_prompt=None,
164
165
  action_prompt=None,
165
166
  output_prompt=None,
@@ -188,6 +189,7 @@ class MonoReAct(MonoChat):
188
189
  The result of the reasoning and action steps.
189
190
  """
190
191
  config = self._create_followup_config(tools, **kwargs)
192
+ kwargs.pop("tools", None)
191
193
 
192
194
  i = 0
193
195
  _out = ""
@@ -220,19 +222,19 @@ class MonoReAct(MonoChat):
220
222
 
221
223
  _out = await self.chat(_prompt, sender=sender, **config)
222
224
 
223
- if auto and not self.branch._is_invoked():
225
+ if not self.branch._is_invoked():
224
226
  return _out if out else None
225
227
 
226
228
  i += 1
227
229
 
228
- if auto:
229
- if not self.branch._is_invoked():
230
- return _out if out else None
231
230
 
232
- _prompt = self._get_prompt(
233
- prompt=output_prompt,
234
- default=self.OUTPUT_PROMPT,
235
- instruction=instruction,
236
- )
237
- _out = await self.chat(_prompt, sender=sender, **kwargs)
238
- return _out if out else None
231
+ if not self.branch._is_invoked():
232
+ return _out if out else None
233
+
234
+ _prompt = self._get_prompt(
235
+ prompt=output_prompt,
236
+ default=self.OUTPUT_PROMPT,
237
+ instruction=instruction,
238
+ )
239
+ _out = await self.chat(_prompt, sender=sender, **kwargs)
240
+ return _out if out else None
@@ -250,4 +250,4 @@ class MonoChatMixin(MonoChatConfigMixin, MonoChatInvokeMixin, ABC):
250
250
  Mixin class that combines MonoChatConfigMixin and MonoChatInvokeMixin.
251
251
  """
252
252
 
253
- pass
253
+ pass
@@ -137,7 +137,9 @@ class MonoFollowup(MonoChat):
137
137
  if not self.branch.tool_manager.has_tools:
138
138
  raise ValueError("No tools found. You need to register tools.")
139
139
 
140
- config = self.branch.tool_manager.parse_tool(tools=True, **kwargs)
140
+ if tools is None:
141
+ tools = True
142
+ config = self.branch.tool_manager.parse_tool(tools=tools, **kwargs)
141
143
  config["tool_parsed"] = True
142
144
  config["tool_choice"] = tool_choice
143
145
  return config
@@ -150,7 +152,7 @@ class MonoFollowup(MonoChat):
150
152
  system=None,
151
153
  tools=None,
152
154
  max_followup: int = 1,
153
- auto=False,
155
+ auto=True,
154
156
  followup_prompt=None,
155
157
  output_prompt=None,
156
158
  out=True,
@@ -196,19 +198,18 @@ class MonoFollowup(MonoChat):
196
198
  else:
197
199
  _out = await self.chat(_prompt, sender=sender, **config)
198
200
 
199
- if auto and not self.branch._is_invoked():
201
+ if not self.branch._is_invoked():
200
202
  return _out if out else None
201
203
 
202
204
  i += 1
203
205
 
204
- if auto:
205
- if not self.branch._is_invoked():
206
- return _out if out else None
206
+ if not self.branch._is_invoked():
207
+ return _out if out else None
207
208
 
208
- _prompt = self._get_prompt(
209
- prompt=output_prompt,
210
- default=self.OUTPUT_PROMPT,
211
- instruction=instruction,
212
- )
213
- _out = await self.chat(_prompt, sender=sender, **kwargs)
214
- return _out if out else None
209
+ _prompt = self._get_prompt(
210
+ prompt=output_prompt,
211
+ default=self.OUTPUT_PROMPT,
212
+ instruction=instruction,
213
+ )
214
+ _out = await self.chat(_prompt, sender=sender, **kwargs)
215
+ return _out if out else None
@@ -1 +1 @@
1
- from .chat import PolyChat
1
+ from .chat import PolyChat