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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. lionagi/core/__init__.py +19 -8
  2. lionagi/core/agent/__init__.py +0 -3
  3. lionagi/core/agent/base_agent.py +25 -30
  4. lionagi/core/branch/__init__.py +0 -4
  5. lionagi/core/branch/{base_branch.py → base.py} +12 -13
  6. lionagi/core/branch/branch.py +22 -19
  7. lionagi/core/branch/executable_branch.py +0 -347
  8. lionagi/core/branch/{branch_flow_mixin.py → flow_mixin.py} +5 -5
  9. lionagi/core/direct/__init__.py +10 -1
  10. lionagi/core/direct/cot.py +61 -26
  11. lionagi/core/direct/plan.py +10 -8
  12. lionagi/core/direct/predict.py +5 -5
  13. lionagi/core/direct/react.py +8 -8
  14. lionagi/core/direct/score.py +4 -4
  15. lionagi/core/direct/select.py +4 -4
  16. lionagi/core/direct/utils.py +7 -4
  17. lionagi/core/direct/vote.py +2 -2
  18. lionagi/core/execute/base_executor.py +47 -0
  19. lionagi/core/execute/branch_executor.py +296 -0
  20. lionagi/core/execute/instruction_map_executor.py +179 -0
  21. lionagi/core/execute/neo4j_executor.py +381 -0
  22. lionagi/core/execute/structure_executor.py +314 -0
  23. lionagi/core/flow/monoflow/ReAct.py +20 -20
  24. lionagi/core/flow/monoflow/chat.py +6 -6
  25. lionagi/core/flow/monoflow/chat_mixin.py +23 -33
  26. lionagi/core/flow/monoflow/followup.py +14 -15
  27. lionagi/core/flow/polyflow/chat.py +15 -12
  28. lionagi/core/{prompt/action_template.py → form/action_form.py} +2 -2
  29. lionagi/core/{prompt → form}/field_validator.py +40 -31
  30. lionagi/core/form/form.py +302 -0
  31. lionagi/core/form/mixin.py +214 -0
  32. lionagi/core/{prompt/scored_template.py → form/scored_form.py} +2 -2
  33. lionagi/core/generic/__init__.py +37 -0
  34. lionagi/core/generic/action.py +26 -0
  35. lionagi/core/generic/component.py +455 -0
  36. lionagi/core/generic/condition.py +44 -0
  37. lionagi/core/generic/data_logger.py +305 -0
  38. lionagi/core/generic/edge.py +162 -0
  39. lionagi/core/generic/mail.py +90 -0
  40. lionagi/core/generic/mailbox.py +36 -0
  41. lionagi/core/generic/node.py +285 -0
  42. lionagi/core/generic/relation.py +70 -0
  43. lionagi/core/generic/signal.py +22 -0
  44. lionagi/core/generic/structure.py +362 -0
  45. lionagi/core/generic/transfer.py +20 -0
  46. lionagi/core/generic/work.py +40 -0
  47. lionagi/core/graph/graph.py +126 -0
  48. lionagi/core/graph/tree.py +190 -0
  49. lionagi/core/mail/__init__.py +0 -8
  50. lionagi/core/mail/mail_manager.py +15 -12
  51. lionagi/core/mail/schema.py +9 -2
  52. lionagi/core/messages/__init__.py +0 -3
  53. lionagi/core/messages/schema.py +17 -225
  54. lionagi/core/session/__init__.py +0 -3
  55. lionagi/core/session/session.py +24 -22
  56. lionagi/core/tool/__init__.py +3 -1
  57. lionagi/core/tool/tool.py +28 -0
  58. lionagi/core/tool/tool_manager.py +75 -75
  59. lionagi/experimental/directive/evaluator/__init__.py +0 -0
  60. lionagi/experimental/directive/evaluator/ast_evaluator.py +115 -0
  61. lionagi/experimental/directive/evaluator/base_evaluator.py +202 -0
  62. lionagi/experimental/directive/evaluator/sandbox_.py +14 -0
  63. lionagi/experimental/directive/evaluator/script_engine.py +83 -0
  64. lionagi/experimental/directive/parser/__init__.py +0 -0
  65. lionagi/experimental/directive/parser/base_parser.py +215 -0
  66. lionagi/experimental/directive/schema.py +36 -0
  67. lionagi/experimental/directive/template_/__init__.py +0 -0
  68. lionagi/experimental/directive/template_/base_template.py +63 -0
  69. lionagi/experimental/tool/__init__.py +0 -0
  70. lionagi/experimental/tool/function_calling.py +43 -0
  71. lionagi/experimental/tool/manual.py +66 -0
  72. lionagi/experimental/tool/schema.py +59 -0
  73. lionagi/experimental/tool/tool_manager.py +138 -0
  74. lionagi/experimental/tool/util.py +16 -0
  75. lionagi/experimental/work/__init__.py +0 -0
  76. lionagi/experimental/work/_logger.py +25 -0
  77. lionagi/experimental/work/exchange.py +0 -0
  78. lionagi/experimental/work/schema.py +30 -0
  79. lionagi/experimental/work/tests.py +72 -0
  80. lionagi/experimental/work/util.py +0 -0
  81. lionagi/experimental/work/work_function.py +89 -0
  82. lionagi/experimental/work/worker.py +12 -0
  83. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  84. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  85. lionagi/integrations/bridge/llamaindex_/get_index.py +294 -0
  86. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  87. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  88. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  89. lionagi/integrations/chunker/chunk.py +7 -7
  90. lionagi/integrations/config/oai_configs.py +5 -5
  91. lionagi/integrations/config/ollama_configs.py +1 -1
  92. lionagi/integrations/config/openrouter_configs.py +1 -1
  93. lionagi/integrations/loader/load.py +6 -6
  94. lionagi/integrations/loader/load_util.py +8 -8
  95. lionagi/integrations/storage/__init__.py +3 -0
  96. lionagi/integrations/storage/neo4j.py +673 -0
  97. lionagi/integrations/storage/storage_util.py +289 -0
  98. lionagi/integrations/storage/to_csv.py +63 -0
  99. lionagi/integrations/storage/to_excel.py +67 -0
  100. lionagi/libs/ln_api.py +3 -3
  101. lionagi/libs/ln_knowledge_graph.py +405 -0
  102. lionagi/libs/ln_parse.py +43 -6
  103. lionagi/libs/ln_queue.py +101 -0
  104. lionagi/libs/ln_tokenizer.py +57 -0
  105. lionagi/libs/ln_validate.py +288 -0
  106. lionagi/libs/sys_util.py +29 -7
  107. lionagi/lions/__init__.py +0 -0
  108. lionagi/lions/coder/__init__.py +0 -0
  109. lionagi/lions/coder/add_feature.py +20 -0
  110. lionagi/lions/coder/base_prompts.py +22 -0
  111. lionagi/lions/coder/coder.py +121 -0
  112. lionagi/lions/coder/util.py +91 -0
  113. lionagi/lions/researcher/__init__.py +0 -0
  114. lionagi/lions/researcher/data_source/__init__.py +0 -0
  115. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  116. lionagi/lions/researcher/data_source/google_.py +199 -0
  117. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  118. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  119. lionagi/tests/integrations/__init__.py +0 -0
  120. lionagi/tests/libs/__init__.py +0 -0
  121. lionagi/tests/libs/test_async.py +0 -0
  122. lionagi/tests/libs/test_field_validators.py +353 -0
  123. lionagi/tests/libs/test_queue.py +67 -0
  124. lionagi/tests/test_core/test_base_branch.py +0 -1
  125. lionagi/tests/test_core/test_branch.py +2 -0
  126. lionagi/tests/test_core/test_session_base_util.py +1 -0
  127. lionagi/version.py +1 -1
  128. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/METADATA +1 -1
  129. lionagi-0.1.1.dist-info/RECORD +190 -0
  130. lionagi/core/prompt/prompt_template.py +0 -312
  131. lionagi/core/schema/__init__.py +0 -22
  132. lionagi/core/schema/action_node.py +0 -29
  133. lionagi/core/schema/base_mixin.py +0 -296
  134. lionagi/core/schema/base_node.py +0 -199
  135. lionagi/core/schema/condition.py +0 -24
  136. lionagi/core/schema/data_logger.py +0 -354
  137. lionagi/core/schema/data_node.py +0 -93
  138. lionagi/core/schema/prompt_template.py +0 -67
  139. lionagi/core/schema/structure.py +0 -912
  140. lionagi/core/tool/manual.py +0 -1
  141. lionagi-0.0.316.dist-info/RECORD +0 -121
  142. /lionagi/core/{branch/base → execute}/__init__.py +0 -0
  143. /lionagi/core/flow/{base/baseflow.py → baseflow.py} +0 -0
  144. /lionagi/core/flow/{base/__init__.py → mono_chat_mixin.py} +0 -0
  145. /lionagi/core/{prompt → form}/__init__.py +0 -0
  146. /lionagi/{tests/test_integrations → core/graph}/__init__.py +0 -0
  147. /lionagi/{tests/test_libs → experimental}/__init__.py +0 -0
  148. /lionagi/{tests/test_libs/test_async.py → experimental/directive/__init__.py} +0 -0
  149. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  150. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  151. /lionagi/tests/{test_libs → libs}/test_func_call.py +0 -0
  152. /lionagi/tests/{test_libs → libs}/test_nested.py +0 -0
  153. /lionagi/tests/{test_libs → libs}/test_parse.py +0 -0
  154. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  155. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/LICENSE +0 -0
  156. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/WHEEL +0 -0
  157. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,285 @@
1
+ from typing import Any, Type
2
+ from pydantic import Field
3
+ from lionagi.integrations.bridge import LlamaIndexBridge, LangchainBridge
4
+
5
+ from lionagi.core.generic.component import BaseNode
6
+ from lionagi.core.generic.condition import Condition
7
+ from lionagi.core.generic.edge import Edge
8
+ from lionagi.core.generic.relation import Relations
9
+ from lionagi.core.generic.mailbox import MailBox
10
+
11
+
12
+ class Node(BaseNode):
13
+ """
14
+ Represents a node with relations to other nodes.
15
+
16
+ Attributes:
17
+ relations (Relations): The relations of the node, managed through a
18
+ `Relations` instance.
19
+
20
+ Properties:
21
+ related_nodes: A set of IDs representing nodes related to this node.
22
+ edges: A dictionary of all edges connected to this node.
23
+ node_relations: A dictionary categorizing preceding and succeeding
24
+ relations to this node.
25
+ precedessors: A list of node IDs that precede this node.
26
+ successors: A list of node IDs that succeed this node.
27
+
28
+ Methods:
29
+ relate(node, self_as, condition, **kwargs): Relates this node to
30
+ another node with an edge.
31
+ unrelate(node, edge): Removes one or all relations between this node
32
+ and another.
33
+ to_llama_index(node_type, **kwargs): Serializes this node for
34
+ LlamaIndex.
35
+ to_langchain(**kwargs): Serializes this node for Langchain.
36
+ from_llama_index(llama_node, **kwargs): Deserializes a node from
37
+ LlamaIndex data.
38
+ from_langchain(lc_doc): Deserializes a node from Langchain data.
39
+ __str__(): String representation of the node.
40
+
41
+ Raises:
42
+ ValueError: When invalid parameters are provided to methods.
43
+ """
44
+
45
+ relations: Relations = Field(
46
+ default_factory=Relations,
47
+ description="The relations of the node.",
48
+ alias="node_relations",
49
+ )
50
+
51
+ mailbox: MailBox = Field(
52
+ default_factory=MailBox,
53
+ description="The mailbox for incoming and outgoing mails.",
54
+ )
55
+
56
+ @property
57
+ def related_nodes(self) -> list[str]:
58
+ """Returns a set of node IDs related to this node, excluding itself."""
59
+ nodes = set(self.relations.all_nodes)
60
+ nodes.discard(self.id_)
61
+ return list(nodes)
62
+
63
+ @property
64
+ def edges(self) -> dict[str, Edge]:
65
+ """Returns a dictionary of all edges connected to this node."""
66
+ return self.relations.all_edges
67
+
68
+ @property
69
+ def node_relations(self) -> dict:
70
+ """Categorizes preceding and succeeding relations to this node."""
71
+
72
+ points_to_nodes = {}
73
+ for edge in self.relations.points_to.values():
74
+ for i in self.related_nodes:
75
+ if edge.tail == i:
76
+ if i in points_to_nodes:
77
+ points_to_nodes[i].append(edge)
78
+ else:
79
+ points_to_nodes[i] = [edge]
80
+
81
+ pointed_by_nodes = {}
82
+ for edge in self.relations.pointed_by.values():
83
+ for i in self.related_nodes:
84
+ if edge.head == i:
85
+ if i in pointed_by_nodes:
86
+ pointed_by_nodes[i].append(edge)
87
+ else:
88
+ pointed_by_nodes[i] = [edge]
89
+
90
+ return {"points_to": points_to_nodes, "pointed_by": pointed_by_nodes}
91
+
92
+ @property
93
+ def precedessors(self) -> list[str]:
94
+ """return a list of nodes id that precede this node"""
95
+ return [k for k, v in self.node_relations["pointed_by"].items() if len(v) > 0]
96
+
97
+ @property
98
+ def successors(self) -> list[str]:
99
+ """return a list of nodes id that succeed this node"""
100
+ return [k for k, v in self.node_relations["points_to"].items() if len(v) > 0]
101
+
102
+ def relate(
103
+ self,
104
+ node: "Node",
105
+ node_as: str = "head",
106
+ condition: Condition | None = None,
107
+ label: str | None = None,
108
+ bundle=False,
109
+ ) -> None:
110
+ """Relates this node to another node with an edge.
111
+
112
+ Args:
113
+ node (Node): The node to relate to.
114
+ self_as (str): Specifies whether this node is the 'head' or 'tail'
115
+ of the relation. Defaults to "head".
116
+ condition (Condition | None): The condition associated with the
117
+ edge, if any. Defaults to None.
118
+ **kwargs: Additional keyword arguments for edge creation.
119
+
120
+ Raises:
121
+ ValueError: If `self_as` is not 'head' or 'tail'.
122
+ """
123
+ if node_as == "head":
124
+ edge = Edge(
125
+ head=self, tail=node, condition=condition, bundle=bundle, label=label
126
+ )
127
+ self.relations.points_to[edge.id_] = edge
128
+ node.relations.pointed_by[edge.id_] = edge
129
+
130
+ elif node_as == "tail":
131
+ edge = Edge(
132
+ head=node, tail=self, condition=condition, label=label, bundle=bundle
133
+ )
134
+ self.relations.pointed_by[edge.id_] = edge
135
+ node.relations.points_to[edge.id_] = edge
136
+
137
+ else:
138
+ raise ValueError(
139
+ f"Invalid value for self_as: {node_as}, must be 'head' or 'tail'"
140
+ )
141
+
142
+ def remove_edge(self, node: "Node", edge: Edge | str) -> bool:
143
+ if node.id_ not in self.related_nodes:
144
+ raise ValueError(f"Node {self.id_} is not related to node {node.id_}.")
145
+
146
+ edge_id = edge.id_ if isinstance(edge, Edge) else edge
147
+
148
+ if (
149
+ edge_id not in self.relations.all_edges
150
+ or edge_id not in node.relations.all_edges
151
+ ):
152
+ raise ValueError(
153
+ f"Edge {edge_id} does not exist between nodes {self.id_} and "
154
+ f"{node.id_}."
155
+ )
156
+
157
+ all_dicts = [
158
+ self.relations.points_to,
159
+ self.relations.pointed_by,
160
+ node.relations.points_to,
161
+ node.relations.pointed_by,
162
+ ]
163
+ try:
164
+ for _dict in all_dicts:
165
+ edge_id = edge.id_ if isinstance(edge, Edge) else edge
166
+ _dict.pop(edge_id, None)
167
+ return True
168
+
169
+ except Exception as e:
170
+ raise ValueError(
171
+ f"Failed to remove edge between nodes {self.id_} and " f"{node.id_}."
172
+ ) from e
173
+
174
+ def unrelate(self, node: "Node", edge: Edge | str = "all") -> bool:
175
+ """
176
+ Removes one or all relations between this node and another.
177
+
178
+ Args:
179
+ node (Node): The node to unrelate from.
180
+ edge (Edge | str): Specific edge or 'all' to remove all relations.
181
+ Defaults to "all".
182
+
183
+ Returns:
184
+ bool: True if the operation is successful, False otherwise.
185
+
186
+ Raises:
187
+ ValueError: If the node is not related or the edge does not exist.
188
+ """
189
+ if edge == "all":
190
+ edge = self.node_relations["points_to"].get(
191
+ node.id_, []
192
+ ) + self.node_relations["pointed_by"].get(node.id_, [])
193
+ else:
194
+ edge = [edge.id_] if isinstance(edge, Edge) else [edge]
195
+
196
+ if len(edge) == 0:
197
+ raise ValueError(f"Node {self.id_} is not related to node {node.id_}.")
198
+
199
+ try:
200
+ for edge_id in edge:
201
+ self.remove_edge(node, edge_id)
202
+ return True
203
+ except Exception as e:
204
+ raise ValueError(
205
+ f"Failed to remove edge between nodes {self.id_} and " f"{node.id_}."
206
+ ) from e
207
+
208
+ def to_llama_index(self, node_type: Type | str | Any = None, **kwargs) -> Any:
209
+ """
210
+ Serializes this node for LlamaIndex.
211
+
212
+ Args:
213
+ node_type (Type | str | Any): The type of node in LlamaIndex.
214
+ Defaults to None.
215
+ **kwargs: Additional keyword arguments for serialization.
216
+
217
+ Returns:
218
+ Any: The serialized node for LlamaIndex.
219
+ """
220
+ return LlamaIndexBridge.to_llama_index_node(self, node_type=node_type, **kwargs)
221
+
222
+ def to_langchain(self, **kwargs) -> Any:
223
+ """
224
+ Serializes this node for Langchain.
225
+
226
+ Args:
227
+ **kwargs: Additional keyword arguments for serialization.
228
+
229
+ Returns:
230
+ Any: The serialized node for Langchain.
231
+ """
232
+ return LangchainBridge.to_langchain_document(self, **kwargs)
233
+
234
+ @classmethod
235
+ def from_llama_index(cls, llama_node: Any, **kwargs) -> "Node":
236
+ """
237
+ Deserializes a node from LlamaIndex data.
238
+
239
+ Args:
240
+ llama_node (Any): The LlamaIndex node data.
241
+ **kwargs: Additional keyword arguments for deserialization.
242
+
243
+ Returns:
244
+ Node: The deserialized node.
245
+ """
246
+ llama_dict = llama_node.to_dict(**kwargs)
247
+ return cls.from_obj(llama_dict)
248
+
249
+ @classmethod
250
+ def from_langchain(cls, lc_doc: Any) -> "Node":
251
+ """Deserializes a node from Langchain data.
252
+
253
+ Args:
254
+ lc_doc (Any): The Langchain document data.
255
+
256
+ Returns:
257
+ Node: The deserialized node.
258
+ """
259
+ langchain_json = lc_doc.to_json()
260
+ langchain_dict = {"lc_id": langchain_json["id"], **langchain_json["kwargs"]}
261
+ return cls.from_obj(langchain_dict)
262
+
263
+ def __str__(self) -> str:
264
+ """
265
+ Provides a string representation of the node.
266
+
267
+ Returns:
268
+ str: The string representation of the node.
269
+ """
270
+ timestamp = f" ({self.timestamp})" if self.timestamp else ""
271
+ if self.content:
272
+ content_preview = (
273
+ f"{self.content[:50]}..." if len(self.content) > 50 else self.content
274
+ )
275
+ else:
276
+ content_preview = ""
277
+ meta_preview = (
278
+ f"{str(self.metadata)[:50]}..."
279
+ if len(str(self.metadata)) > 50
280
+ else str(self.metadata)
281
+ )
282
+ return (
283
+ f"{self.class_name()}({self.id_}, {content_preview}, {meta_preview},"
284
+ f"{timestamp})"
285
+ )
@@ -0,0 +1,70 @@
1
+ """
2
+ A module for representing relationships between nodes in a graph structure,
3
+ encapsulating incoming and outgoing edges.
4
+ """
5
+
6
+ from pydantic import Field
7
+ from pydantic.dataclasses import dataclass
8
+ from lionagi.libs import convert
9
+ from lionagi.core.generic.edge import Edge
10
+
11
+
12
+ @dataclass
13
+ class Relations:
14
+ """
15
+ Represents the relationships of a node via its incoming and outgoing edges.
16
+
17
+ This class stores edges in two dictionaries: `preceding` for outgoing edges
18
+ and `succeeding` for incoming edges. It provides properties to access all
19
+ edges together and to get a unique set of all connected node IDs.
20
+
21
+ Attributes:
22
+ preceding (dict[str, Edge]): A dictionary of outgoing edges from the
23
+ node, with the edge ID as the key and the `Edge` object as the
24
+ value. Represents edges leading from this node to other nodes.
25
+ succeeding (dict[str, Edge]): A dictionary of incoming edges to the
26
+ node, with the edge ID as the key and the `Edge` object as the
27
+ value. Represents edges from other nodes leading to this node.
28
+ """
29
+
30
+ points_to: dict[str, Edge] = Field(
31
+ title="Outgoing edges",
32
+ default_factory=dict,
33
+ description="The Outgoing edges of the node, reads self precedes other, \
34
+ {edge_id: Edge}",
35
+ )
36
+
37
+ pointed_by: dict[str, Edge] = Field(
38
+ title="Incoming edges",
39
+ default_factory=dict,
40
+ description="The Incoming edges of the node, reads self succeeds other, \
41
+ {edge_id: Edge}",
42
+ )
43
+
44
+ @property
45
+ def all_edges(self) -> dict[str, Edge]:
46
+ """
47
+ Combines and returns all incoming and outgoing edges of the node.
48
+
49
+ Returns:
50
+ dict[str, Edge]: A dictionary of all edges connected to the node,
51
+ including both preceding (outgoing) and succeeding (incoming)
52
+ edges, indexed by edge IDs.
53
+ """
54
+ return {**self.points_to, **self.pointed_by}
55
+
56
+ @property
57
+ def all_nodes(self) -> set[str]:
58
+ """
59
+ Extracts and returns a unique set of all node IDs connected to this
60
+ node through its edges.
61
+
62
+ It processes both heads and tails of each edge in `all_edges`, flattens
63
+ the list to a one-dimensional list, and then converts it to a set to
64
+ ensure uniqueness.
65
+
66
+ Returns:
67
+ set[str]: A set of unique node IDs connected to this node, derived
68
+ from both incoming and outgoing edges.
69
+ """
70
+ return set(convert.to_list([[i.head, i.tail] for i in self.all_edges.values()]))
@@ -0,0 +1,22 @@
1
+ from abc import ABC
2
+ from collections import deque
3
+ from lionagi.core.generic import BaseNode
4
+ from lionagi.core.generic.mail import Mail
5
+ from typing import Any
6
+
7
+
8
+ class Signal(BaseNode, ABC): ...
9
+
10
+
11
+ class Start(Signal):
12
+ pending_outs: deque = deque()
13
+
14
+ def trigger(self, context: Any, structure_id: str, executable_id: str):
15
+ start_mail_content = {"context": context, "structure_id": structure_id}
16
+ start_mail = Mail(
17
+ sender=self.id_,
18
+ recipient=executable_id,
19
+ category="start",
20
+ package=start_mail_content,
21
+ )
22
+ self.pending_outs.append(start_mail)