lionagi 0.0.115__py3-none-any.whl → 0.0.204__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- lionagi/__init__.py +1 -2
- lionagi/_services/__init__.py +5 -0
- lionagi/_services/anthropic.py +79 -0
- lionagi/_services/base_service.py +414 -0
- lionagi/_services/oai.py +98 -0
- lionagi/_services/openrouter.py +44 -0
- lionagi/_services/services.py +91 -0
- lionagi/_services/transformers.py +46 -0
- lionagi/bridge/langchain.py +26 -16
- lionagi/bridge/llama_index.py +50 -20
- lionagi/configs/oai_configs.py +2 -14
- lionagi/configs/openrouter_configs.py +2 -2
- lionagi/core/__init__.py +7 -8
- lionagi/core/branch/branch.py +589 -0
- lionagi/core/branch/branch_manager.py +139 -0
- lionagi/core/branch/conversation.py +484 -0
- lionagi/core/core_util.py +59 -0
- lionagi/core/flow/flow.py +19 -0
- lionagi/core/flow/flow_util.py +62 -0
- lionagi/core/instruction_set/__init__.py +0 -5
- lionagi/core/instruction_set/instruction_set.py +343 -0
- lionagi/core/messages/messages.py +176 -0
- lionagi/core/sessions/__init__.py +0 -5
- lionagi/core/sessions/session.py +428 -0
- lionagi/loaders/chunker.py +51 -47
- lionagi/loaders/load_util.py +2 -2
- lionagi/loaders/reader.py +45 -39
- lionagi/models/imodel.py +53 -0
- lionagi/schema/async_queue.py +158 -0
- lionagi/schema/base_node.py +318 -147
- lionagi/schema/base_tool.py +31 -1
- lionagi/schema/data_logger.py +74 -38
- lionagi/schema/data_node.py +57 -6
- lionagi/structures/graph.py +132 -10
- lionagi/structures/relationship.py +58 -20
- lionagi/structures/structure.py +36 -25
- lionagi/tests/test_utils/test_api_util.py +219 -0
- lionagi/tests/test_utils/test_call_util.py +785 -0
- lionagi/tests/test_utils/test_encrypt_util.py +323 -0
- lionagi/tests/test_utils/test_io_util.py +238 -0
- lionagi/tests/test_utils/test_nested_util.py +338 -0
- lionagi/tests/test_utils/test_sys_util.py +358 -0
- lionagi/tools/tool_manager.py +186 -0
- lionagi/tools/tool_util.py +266 -3
- lionagi/utils/__init__.py +21 -61
- lionagi/utils/api_util.py +359 -71
- lionagi/utils/call_util.py +839 -264
- lionagi/utils/encrypt_util.py +283 -16
- lionagi/utils/io_util.py +178 -93
- lionagi/utils/nested_util.py +672 -0
- lionagi/utils/pd_util.py +57 -0
- lionagi/utils/sys_util.py +284 -156
- lionagi/utils/url_util.py +55 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/METADATA +21 -17
- lionagi-0.0.204.dist-info/RECORD +106 -0
- lionagi/core/conversations/__init__.py +0 -5
- lionagi/core/conversations/conversation.py +0 -107
- lionagi/core/flows/__init__.py +0 -8
- lionagi/core/flows/flow.py +0 -8
- lionagi/core/flows/flow_util.py +0 -62
- lionagi/core/instruction_set/instruction_sets.py +0 -7
- lionagi/core/sessions/sessions.py +0 -185
- lionagi/endpoints/__init__.py +0 -5
- lionagi/endpoints/audio.py +0 -17
- lionagi/endpoints/chatcompletion.py +0 -54
- lionagi/messages/__init__.py +0 -11
- lionagi/messages/instruction.py +0 -15
- lionagi/messages/message.py +0 -110
- lionagi/messages/response.py +0 -33
- lionagi/messages/system.py +0 -12
- lionagi/objs/__init__.py +0 -11
- lionagi/objs/abc_objs.py +0 -39
- lionagi/objs/async_queue.py +0 -135
- lionagi/objs/messenger.py +0 -85
- lionagi/objs/tool_manager.py +0 -253
- lionagi/services/__init__.py +0 -11
- lionagi/services/base_api_service.py +0 -230
- lionagi/services/oai.py +0 -34
- lionagi/services/openrouter.py +0 -31
- lionagi/tests/test_api_util.py +0 -46
- lionagi/tests/test_call_util.py +0 -115
- lionagi/tests/test_convert_util.py +0 -202
- lionagi/tests/test_encrypt_util.py +0 -33
- lionagi/tests/test_flat_util.py +0 -426
- lionagi/tests/test_sys_util.py +0 -0
- lionagi/utils/convert_util.py +0 -229
- lionagi/utils/flat_util.py +0 -599
- lionagi-0.0.115.dist-info/RECORD +0 -110
- /lionagi/{services → _services}/anyscale.py +0 -0
- /lionagi/{services → _services}/azure.py +0 -0
- /lionagi/{services → _services}/bedrock.py +0 -0
- /lionagi/{services → _services}/everlyai.py +0 -0
- /lionagi/{services → _services}/gemini.py +0 -0
- /lionagi/{services → _services}/gpt4all.py +0 -0
- /lionagi/{services → _services}/huggingface.py +0 -0
- /lionagi/{services → _services}/litellm.py +0 -0
- /lionagi/{services → _services}/localai.py +0 -0
- /lionagi/{services → _services}/mistralai.py +0 -0
- /lionagi/{services → _services}/ollama.py +0 -0
- /lionagi/{services → _services}/openllm.py +0 -0
- /lionagi/{services → _services}/perplexity.py +0 -0
- /lionagi/{services → _services}/predibase.py +0 -0
- /lionagi/{services → _services}/rungpt.py +0 -0
- /lionagi/{services → _services}/vllm.py +0 -0
- /lionagi/{services → _services}/xinference.py +0 -0
- /lionagi/{endpoints/assistants.py → agents/__init__.py} +0 -0
- /lionagi/{tools → agents}/planner.py +0 -0
- /lionagi/{tools → agents}/prompter.py +0 -0
- /lionagi/{tools → agents}/scorer.py +0 -0
- /lionagi/{tools → agents}/summarizer.py +0 -0
- /lionagi/{tools → agents}/validator.py +0 -0
- /lionagi/{endpoints/embeddings.py → core/branch/__init__.py} +0 -0
- /lionagi/{services/anthropic.py → core/branch/cluster.py} +0 -0
- /lionagi/{endpoints/finetune.py → core/flow/__init__.py} +0 -0
- /lionagi/{endpoints/image.py → core/messages/__init__.py} +0 -0
- /lionagi/{endpoints/moderation.py → models/__init__.py} +0 -0
- /lionagi/{endpoints/vision.py → models/base_model.py} +0 -0
- /lionagi/{objs → schema}/status_tracker.py +0 -0
- /lionagi/tests/{test_io_util.py → test_utils/__init__.py} +0 -0
- {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/LICENSE +0 -0
- {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/WHEEL +0 -0
- {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/top_level.txt +0 -0
lionagi/schema/data_logger.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from collections import deque
|
2
|
-
from typing import
|
3
|
-
from
|
2
|
+
from typing import Dict, Any
|
3
|
+
from ..utils.sys_util import get_timestamp, create_path, as_dict
|
4
|
+
from ..utils.io_util import IOUtil
|
4
5
|
|
5
6
|
|
6
7
|
class DataLogger:
|
@@ -38,53 +39,88 @@ class DataLogger:
|
|
38
39
|
self.dir = dir
|
39
40
|
self.log = deque(log) if log else deque()
|
40
41
|
|
41
|
-
def
|
42
|
+
def add_entry(self, entry: Dict[str, Any], level: str = "INFO") -> None:
|
42
43
|
"""
|
43
|
-
Adds a new entry to the log.
|
44
|
+
Adds a new entry to the log with a timestamp and a log level.
|
44
45
|
|
45
|
-
|
46
|
-
entry: The data entry to be added to the log.
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
Args:
|
47
|
+
entry (Dict[str, Any]): The data entry to be added to the log.
|
48
|
+
level (str): The log level for the entry (e.g., "INFO", "ERROR"). Defaults to "INFO".
|
49
|
+
"""
|
50
|
+
self.log.append({
|
51
|
+
"timestamp": get_timestamp(), "level": level, **as_dict(entry)
|
52
|
+
})
|
53
|
+
|
54
|
+
def set_dir(self, dir: str) -> None:
|
52
55
|
"""
|
53
|
-
|
56
|
+
Sets the default directory for saving CSV files.
|
54
57
|
|
55
58
|
Parameters:
|
56
|
-
|
59
|
+
dir (str): The directory to be set as the default for saving files.
|
60
|
+
"""
|
61
|
+
self.dir = dir
|
57
62
|
|
58
|
-
|
63
|
+
def to_csv(
|
64
|
+
self, filename: str,
|
65
|
+
file_exist_ok: bool = False,
|
66
|
+
timestamp = True,
|
67
|
+
time_prefix: bool = False,
|
68
|
+
verbose: bool = True,
|
69
|
+
clear = True
|
70
|
+
) -> None:
|
71
|
+
"""
|
72
|
+
Exports the logged data to a CSV file, using the provided utilities for path creation and timestamping.
|
59
73
|
|
74
|
+
Args:
|
75
|
+
filename (str): The name of the CSV file.
|
76
|
+
file_exist_ok (bool): If True, creates the directory for the file if it does not exist. Defaults to False.
|
60
77
|
verbose (bool): If True, prints a message upon completion. Defaults to True.
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
file_exist_ok (bool): If True, overwrites the file if it exists. Defaults to False.
|
67
|
-
|
68
|
-
Side Effects:
|
69
|
-
Clears the log after saving the CSV file.
|
70
|
-
|
71
|
-
Prints a message indicating the save location and number of logs saved if verbose is True.
|
72
|
-
"""
|
73
|
-
dir = dir or self.dir
|
78
|
+
time_prefix (bool): If True, adds the timestamp as a prefix to the filename. Defaults to False.
|
79
|
+
"""
|
80
|
+
if not filename.endswith('.csv'):
|
81
|
+
filename += '.csv'
|
82
|
+
|
74
83
|
filepath = create_path(
|
75
|
-
dir
|
76
|
-
|
77
|
-
|
78
|
-
self.log
|
84
|
+
self.dir, filename, timestamp=timestamp,
|
85
|
+
dir_exist_ok=file_exist_ok, time_prefix=time_prefix
|
86
|
+
)
|
87
|
+
IOUtil.to_csv(list(self.log), filepath)
|
88
|
+
|
79
89
|
if verbose:
|
80
|
-
print(f"{
|
81
|
-
|
82
|
-
|
90
|
+
print(f"{len(self.log)} logs saved to {filepath}")
|
91
|
+
|
92
|
+
if clear:
|
93
|
+
self.log.clear()
|
94
|
+
|
95
|
+
def to_jsonl(
|
96
|
+
self, filename: str,
|
97
|
+
timestamp = False,
|
98
|
+
time_prefix=False,
|
99
|
+
file_exist_ok: bool = False,
|
100
|
+
verbose: bool = True,
|
101
|
+
clear = True
|
102
|
+
) -> None:
|
83
103
|
"""
|
84
|
-
|
104
|
+
Exports the logged data to a JSONL file and optionally clears the log.
|
85
105
|
|
86
106
|
Parameters:
|
87
|
-
|
107
|
+
filename (str): The name of the JSONL file.
|
108
|
+
file_exist_ok (bool): If True, creates the directory for the file if it does not exist. Defaults to False.
|
109
|
+
verbose (bool): If True, prints a message upon completion. Defaults to True.
|
88
110
|
"""
|
89
|
-
|
90
|
-
|
111
|
+
if not filename.endswith('.jsonl'):
|
112
|
+
filename += '.jsonl'
|
113
|
+
|
114
|
+
filepath = create_path(
|
115
|
+
self.dir, filename, timestamp=timestamp,
|
116
|
+
dir_exist_ok=file_exist_ok, time_prefix=time_prefix
|
117
|
+
)
|
118
|
+
|
119
|
+
for entry in self.log:
|
120
|
+
IOUtil.append_to_jsonl(entry, filepath)
|
121
|
+
|
122
|
+
if verbose:
|
123
|
+
print(f"{len(self.log)} logs saved to {filepath}")
|
124
|
+
|
125
|
+
if clear:
|
126
|
+
self.log.clear()
|
lionagi/schema/data_node.py
CHANGED
@@ -4,23 +4,74 @@ from typing import Any
|
|
4
4
|
|
5
5
|
class DataNode(BaseNode):
|
6
6
|
|
7
|
-
def to_llama_index(self, **kwargs):
|
8
|
-
|
7
|
+
def to_llama_index(self, **kwargs) -> Any:
|
8
|
+
"""
|
9
|
+
Converts node to llama index format.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
**kwargs: Variable length argument list.
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
The llama index representation of the node.
|
16
|
+
|
17
|
+
Examples:
|
18
|
+
node = DataNode()
|
19
|
+
llama_index = node.to_llama_index()
|
20
|
+
"""
|
9
21
|
from lionagi.bridge.llama_index import to_llama_index_textnode
|
10
22
|
return to_llama_index_textnode(self, **kwargs)
|
11
23
|
|
12
|
-
def to_langchain(self, **kwargs):
|
13
|
-
|
24
|
+
def to_langchain(self, **kwargs) -> Any:
|
25
|
+
"""
|
26
|
+
Converts node to langchain document format.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
**kwargs: Variable length argument list.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
The langchain document representation of the node.
|
33
|
+
|
34
|
+
Examples:
|
35
|
+
node = DataNode()
|
36
|
+
langchain_doc = node.to_langchain()
|
37
|
+
"""
|
14
38
|
from lionagi.bridge.langchain import to_langchain_document
|
15
39
|
return to_langchain_document(self, **kwargs)
|
16
40
|
|
17
41
|
@classmethod
|
18
|
-
def from_llama_index(cls, llama_node: Any, **kwargs):
|
42
|
+
def from_llama_index(cls, llama_node: Any, **kwargs) -> "DataNode":
|
43
|
+
"""
|
44
|
+
Creates a DataNode instance from a llama index node.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
llama_node: The llama index node object.
|
48
|
+
**kwargs: Variable length argument list.
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
An instance of DataNode.
|
52
|
+
|
53
|
+
Examples:
|
54
|
+
llama_node = SomeLlamaIndexNode()
|
55
|
+
data_node = DataNode.from_llama_index(llama_node)
|
56
|
+
"""
|
19
57
|
llama_dict = llama_node.to_dict(**kwargs)
|
20
58
|
return cls.from_dict(llama_dict)
|
21
59
|
|
22
60
|
@classmethod
|
23
|
-
def from_langchain(cls, lc_doc: Any):
|
61
|
+
def from_langchain(cls, lc_doc: Any) -> "DataNode":
|
62
|
+
"""
|
63
|
+
Creates a DataNode instance from a langchain document.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
lc_doc: The langchain document object.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
An instance of DataNode.
|
70
|
+
|
71
|
+
Examples:
|
72
|
+
lc_doc = SomeLangChainDocument()
|
73
|
+
data_node = DataNode.from_langchain(lc_doc)
|
74
|
+
"""
|
24
75
|
info_json = lc_doc.to_json()
|
25
76
|
info_node = {'lc_id': info_json['id']}
|
26
77
|
info_node = {**info_node, **info_json['kwargs']}
|
lionagi/structures/graph.py
CHANGED
@@ -1,20 +1,56 @@
|
|
1
|
+
from typing import List, Any
|
1
2
|
from pydantic import Field
|
2
3
|
|
3
|
-
from
|
4
|
+
from ..utils.call_util import lcall
|
5
|
+
from ..schema.base_node import BaseNode
|
4
6
|
from .relationship import Relationship
|
5
|
-
from lionagi.utils.call_util import lcall
|
6
7
|
|
7
8
|
|
8
9
|
class Graph(BaseNode):
|
10
|
+
"""
|
11
|
+
Represents a graph structure, consisting of nodes and their relationships.
|
12
|
+
|
13
|
+
Attributes:
|
14
|
+
nodes (Dict[str, BaseNode]): A dictionary of nodes in the graph.
|
15
|
+
relationships (Dict[str, Relationship]): A dictionary of relationships between nodes in the graph.
|
16
|
+
node_relationships (Dict[str, Dict[str, Dict[str, str]]]): A dictionary tracking the relationships of each node.
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
>>> graph = Graph()
|
20
|
+
>>> node = BaseNode(id_='node1')
|
21
|
+
>>> graph.add_node(node)
|
22
|
+
>>> graph.node_exists(node)
|
23
|
+
True
|
24
|
+
>>> relationship = Relationship(id_='rel1', source_node_id='node1', target_node_id='node2')
|
25
|
+
>>> graph.add_relationship(relationship)
|
26
|
+
>>> graph.relationship_exists(relationship)
|
27
|
+
True
|
28
|
+
"""
|
9
29
|
nodes: dict = Field(default={})
|
10
30
|
relationships: dict = Field(default={})
|
11
31
|
node_relationships: dict = Field(default={})
|
12
32
|
|
13
|
-
def add_node(self, node: BaseNode):
|
33
|
+
def add_node(self, node: BaseNode) -> None:
|
34
|
+
"""
|
35
|
+
Adds a node to the graph.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
node (BaseNode): The node to add to the graph.
|
39
|
+
"""
|
40
|
+
|
14
41
|
self.nodes[node.id_] = node
|
15
42
|
self.node_relationships[node.id_] = {'in': {}, 'out': {}}
|
16
43
|
|
17
|
-
def add_relationship(self, relationships: Relationship):
|
44
|
+
def add_relationship(self, relationships: Relationship) -> None:
|
45
|
+
"""
|
46
|
+
Adds a relationship between nodes in the graph.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
relationship (Relationship): The relationship to add.
|
50
|
+
|
51
|
+
Raises:
|
52
|
+
KeyError: If either the source or target node of the relationship is not found in the graph.
|
53
|
+
"""
|
18
54
|
if relationships.source_node_id not in self.node_relationships.keys():
|
19
55
|
raise KeyError(f'node {relationships.source_node_id} is not found.')
|
20
56
|
if relationships.target_node_id not in self.node_relationships.keys():
|
@@ -24,7 +60,20 @@ class Graph(BaseNode):
|
|
24
60
|
self.node_relationships[relationships.source_node_id]['out'][relationships.id_] = relationships.target_node_id
|
25
61
|
self.node_relationships[relationships.target_node_id]['in'][relationships.id_] = relationships.source_node_id
|
26
62
|
|
27
|
-
def get_node_relationships(self, node: BaseNode = None, out_edge=True):
|
63
|
+
def get_node_relationships(self, node: BaseNode = None, out_edge=True) -> List[Relationship]:
|
64
|
+
"""
|
65
|
+
Retrieves relationships of a specific node or all relationships in the graph.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
node (Optional[BaseNode]): The node whose relationships to retrieve. If None, retrieves all relationships.
|
69
|
+
out_edge (bool): Whether to retrieve outgoing relationships. If False, retrieves incoming relationships.
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
List[Relationship]: A list of relationships.
|
73
|
+
|
74
|
+
Raises:
|
75
|
+
KeyError: If the specified node is not found in the graph.
|
76
|
+
"""
|
28
77
|
if node is None:
|
29
78
|
return list(self.relationships.values())
|
30
79
|
|
@@ -40,7 +89,19 @@ class Graph(BaseNode):
|
|
40
89
|
relationships = lcall(relationship_ids, lambda i: self.relationships[i])
|
41
90
|
return relationships
|
42
91
|
|
43
|
-
def remove_node(self, node: BaseNode):
|
92
|
+
def remove_node(self, node: BaseNode)-> BaseNode:
|
93
|
+
"""
|
94
|
+
Removes a node and its associated relationships from the graph.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
node (BaseNode): The node to remove.
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
BaseNode: The removed node.
|
101
|
+
|
102
|
+
Raises:
|
103
|
+
KeyError: If the node is not found in the graph.
|
104
|
+
"""
|
44
105
|
if node.id_ not in self.nodes.keys():
|
45
106
|
raise KeyError(f'node {node.id_} is not found')
|
46
107
|
|
@@ -57,7 +118,19 @@ class Graph(BaseNode):
|
|
57
118
|
self.node_relationships.pop(node.id_)
|
58
119
|
return self.nodes.pop(node.id_)
|
59
120
|
|
60
|
-
def remove_relationship(self, relationship: Relationship):
|
121
|
+
def remove_relationship(self, relationship: Relationship) -> Relationship:
|
122
|
+
"""
|
123
|
+
Removes a relationship from the graph.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
relationship (Relationship): The relationship to remove.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
Relationship: The removed relationship.
|
130
|
+
|
131
|
+
Raises:
|
132
|
+
KeyError: If the relationship is not found in the graph.
|
133
|
+
"""
|
61
134
|
if relationship.id_ not in self.relationships.keys():
|
62
135
|
raise KeyError(f'relationship {relationship.id_} is not found')
|
63
136
|
|
@@ -66,19 +139,68 @@ class Graph(BaseNode):
|
|
66
139
|
|
67
140
|
return self.relationships.pop(relationship.id_)
|
68
141
|
|
69
|
-
def node_exists(self, node: BaseNode):
|
142
|
+
def node_exists(self, node: BaseNode) -> bool:
|
143
|
+
"""
|
144
|
+
Checks if a node exists in the graph.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
node (BaseNode): The node to check.
|
148
|
+
|
149
|
+
Returns:
|
150
|
+
bool: True if the node exists, False otherwise.
|
151
|
+
"""
|
70
152
|
if node.id_ in self.nodes.keys():
|
71
153
|
return True
|
72
154
|
else:
|
73
155
|
return False
|
74
156
|
|
75
|
-
def relationship_exists(self, relationship: Relationship):
|
157
|
+
def relationship_exists(self, relationship: Relationship) -> bool:
|
158
|
+
"""
|
159
|
+
Checks if a relationship exists in the graph.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
relationship (Relationship): The relationship to check.
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
bool: True if the relationship exists, False otherwise.
|
166
|
+
"""
|
76
167
|
if relationship.id_ in self.relationships.keys():
|
77
168
|
return True
|
78
169
|
else:
|
79
170
|
return False
|
80
171
|
|
81
|
-
def
|
172
|
+
def is_empty(self) -> bool:
|
173
|
+
"""
|
174
|
+
Determines if the graph is empty.
|
175
|
+
|
176
|
+
Returns:
|
177
|
+
bool: True if the graph has no nodes, False otherwise.
|
178
|
+
"""
|
179
|
+
if self.nodes:
|
180
|
+
return False
|
181
|
+
else:
|
182
|
+
return True
|
183
|
+
|
184
|
+
def clear(self)-> None:
|
185
|
+
"""Clears the graph of all nodes and relationships."""
|
186
|
+
self.nodes.clear()
|
187
|
+
self.relationships.clear()
|
188
|
+
self.node_relationships.clear()
|
189
|
+
|
190
|
+
def to_networkx(self, **kwargs) -> Any:
|
191
|
+
"""
|
192
|
+
Converts the graph to a NetworkX graph object.
|
193
|
+
|
194
|
+
Args:
|
195
|
+
**kwargs: Additional keyword arguments to pass to the NetworkX DiGraph constructor.
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
Any: A NetworkX directed graph representing the graph.
|
199
|
+
|
200
|
+
Examples:
|
201
|
+
>>> graph = Graph()
|
202
|
+
>>> nx_graph = graph.to_networkx()
|
203
|
+
"""
|
82
204
|
import networkx as nx
|
83
205
|
g = nx.DiGraph(**kwargs)
|
84
206
|
for node_id, node in self.nodes.items():
|
@@ -5,15 +5,18 @@ from ..schema.base_node import BaseNode
|
|
5
5
|
|
6
6
|
class Relationship(BaseNode):
|
7
7
|
"""
|
8
|
-
|
9
|
-
|
10
|
-
Inherits from BaseNode and adds functionality to manage conditions and relationships
|
11
|
-
between source and target nodes.
|
8
|
+
Represents a relationship between two nodes in a graph.
|
12
9
|
|
13
10
|
Attributes:
|
14
11
|
source_node_id (str): The identifier of the source node.
|
15
12
|
target_node_id (str): The identifier of the target node.
|
16
13
|
condition (Dict[str, Any]): A dictionary representing conditions for the relationship.
|
14
|
+
|
15
|
+
Examples:
|
16
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
17
|
+
>>> relationship.add_condition({"key": "value"})
|
18
|
+
>>> condition_value = relationship.get_condition("key")
|
19
|
+
>>> relationship.remove_condition("key")
|
17
20
|
"""
|
18
21
|
|
19
22
|
source_node_id: str
|
@@ -24,8 +27,12 @@ class Relationship(BaseNode):
|
|
24
27
|
"""
|
25
28
|
Adds a condition to the relationship.
|
26
29
|
|
27
|
-
|
28
|
-
condition
|
30
|
+
Args:
|
31
|
+
condition: The condition to be added.
|
32
|
+
|
33
|
+
Examples:
|
34
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
35
|
+
>>> relationship.add_condition({"key": "value"})
|
29
36
|
"""
|
30
37
|
self.condition.update(condition)
|
31
38
|
|
@@ -33,14 +40,19 @@ class Relationship(BaseNode):
|
|
33
40
|
"""
|
34
41
|
Removes a condition from the relationship.
|
35
42
|
|
36
|
-
|
37
|
-
condition_key
|
43
|
+
Args:
|
44
|
+
condition_key: The key of the condition to be removed.
|
38
45
|
|
39
46
|
Returns:
|
40
|
-
|
47
|
+
The value of the removed condition.
|
41
48
|
|
42
49
|
Raises:
|
43
50
|
KeyError: If the condition key is not found.
|
51
|
+
|
52
|
+
Examples:
|
53
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2", condition={"key": "value"})
|
54
|
+
>>> relationship.remove_condition("key")
|
55
|
+
'value'
|
44
56
|
"""
|
45
57
|
if condition_key not in self.condition.keys():
|
46
58
|
raise KeyError(f'condition {condition_key} is not found')
|
@@ -50,11 +62,16 @@ class Relationship(BaseNode):
|
|
50
62
|
"""
|
51
63
|
Checks if a condition exists in the relationship.
|
52
64
|
|
53
|
-
|
54
|
-
condition_key
|
65
|
+
Args:
|
66
|
+
condition_key: The key of the condition to check.
|
55
67
|
|
56
68
|
Returns:
|
57
|
-
|
69
|
+
True if the condition exists, False otherwise.
|
70
|
+
|
71
|
+
Examples:
|
72
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2", condition={"key": "value"})
|
73
|
+
>>> relationship.condition_exists("key")
|
74
|
+
True
|
58
75
|
"""
|
59
76
|
if condition_key in self.condition.keys():
|
60
77
|
return True
|
@@ -65,14 +82,21 @@ class Relationship(BaseNode):
|
|
65
82
|
"""
|
66
83
|
Retrieves a specific condition or all conditions of the relationship.
|
67
84
|
|
68
|
-
|
69
|
-
condition_key
|
85
|
+
Args:
|
86
|
+
condition_key: The key of the specific condition. If None, all conditions are returned.
|
70
87
|
|
71
88
|
Returns:
|
72
|
-
|
89
|
+
The requested condition or all conditions if no key is provided.
|
73
90
|
|
74
91
|
Raises:
|
75
92
|
ValueError: If the specified condition key does not exist.
|
93
|
+
|
94
|
+
Examples:
|
95
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2", condition={"key": "value"})
|
96
|
+
>>> relationship.get_condition("key")
|
97
|
+
'value'
|
98
|
+
>>> relationship.get_condition()
|
99
|
+
{'key': 'value'}
|
76
100
|
"""
|
77
101
|
if condition_key is None:
|
78
102
|
return self.condition
|
@@ -85,7 +109,7 @@ class Relationship(BaseNode):
|
|
85
109
|
"""
|
86
110
|
Checks if the source node exists in a given object.
|
87
111
|
|
88
|
-
|
112
|
+
Args:
|
89
113
|
obj (Dict[str, Any]): The object to check.
|
90
114
|
|
91
115
|
Returns:
|
@@ -97,7 +121,7 @@ class Relationship(BaseNode):
|
|
97
121
|
"""
|
98
122
|
Checks if the target node exists in a given object.
|
99
123
|
|
100
|
-
|
124
|
+
Args:
|
101
125
|
obj (Dict[str, Any]): The object to check.
|
102
126
|
|
103
127
|
Returns:
|
@@ -109,7 +133,7 @@ class Relationship(BaseNode):
|
|
109
133
|
"""
|
110
134
|
Validates the existence of both source and target nodes in a given object.
|
111
135
|
|
112
|
-
|
136
|
+
Args:
|
113
137
|
obj (Dict[str, Any]): The object to check.
|
114
138
|
|
115
139
|
Returns:
|
@@ -127,10 +151,24 @@ class Relationship(BaseNode):
|
|
127
151
|
raise ValueError(f"Source node {self.target_node_id} does not exist")
|
128
152
|
|
129
153
|
def __str__(self) -> str:
|
130
|
-
"""
|
154
|
+
"""
|
155
|
+
Returns a simple string representation of the Relationship.
|
156
|
+
|
157
|
+
Examples:
|
158
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
159
|
+
>>> str(relationship)
|
160
|
+
'Relationship (id_=None, from=node1, to=node2, label=None)'
|
161
|
+
"""
|
131
162
|
return f"Relationship (id_={self.id_}, from={self.source_node_id}, to={self.target_node_id}, label={self.label})"
|
132
163
|
|
133
164
|
def __repr__(self) -> str:
|
134
|
-
"""
|
165
|
+
"""
|
166
|
+
Returns a detailed string representation of the Relationship.
|
167
|
+
|
168
|
+
Examples:
|
169
|
+
>>> relationship = Relationship(source_node_id="node1", target_node_id="node2")
|
170
|
+
>>> repr(relationship)
|
171
|
+
'Relationship(id_=None, from=node1, to=node2, content=None, metadata=None, label=None)'
|
172
|
+
"""
|
135
173
|
return f"Relationship(id_={self.id_}, from={self.source_node_id}, to={self.target_node_id}, content={self.content}, " \
|
136
174
|
f"metadata={self.metadata}, label={self.label})"
|
lionagi/structures/structure.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TypeVar
|
2
|
-
from .graph import Graph
|
3
2
|
from ..schema import BaseNode
|
3
|
+
from .graph import Graph
|
4
4
|
from .relationship import Relationship
|
5
5
|
|
6
6
|
T = TypeVar('T', bound='BaseNode')
|
@@ -11,7 +11,7 @@ class Structure(BaseNode):
|
|
11
11
|
"""
|
12
12
|
Represents the structure of a graph consisting of nodes and relationships.
|
13
13
|
"""
|
14
|
-
graph: Graph
|
14
|
+
graph: Graph = Graph()
|
15
15
|
|
16
16
|
def add_node(self, node: T) -> None:
|
17
17
|
"""
|
@@ -31,31 +31,20 @@ class Structure(BaseNode):
|
|
31
31
|
"""
|
32
32
|
self.graph.add_relationship(relationship)
|
33
33
|
|
34
|
-
# type can be dict or list
|
35
|
-
# @staticmethod
|
36
|
-
# def _typed_return(type: Type[Union[Dict, List]],
|
37
|
-
# obj: Optional[Dict[str, Any]] = None
|
38
|
-
# ) -> Union[Dict[str, Any], List[Any]]:
|
39
|
-
# """
|
40
|
-
# Returns the object in the specified type format.
|
41
|
-
#
|
42
|
-
# Args:
|
43
|
-
# type (Type[Union[Dict, List]]): The type to return the object as (dict or list).
|
44
|
-
#
|
45
|
-
# obj (Optional[Dict[str, Any]]): The object to be converted.
|
46
|
-
#
|
47
|
-
# Returns:
|
48
|
-
# Union[Dict[str, Any], List[Any]]: The object in the specified type format.
|
49
|
-
# """
|
50
|
-
# if type is list:
|
51
|
-
# return list(obj.values())
|
52
|
-
# return obj
|
53
|
-
|
54
34
|
def get_relationships(self) -> list[R]:
|
55
35
|
return self.graph.get_node_relationships()
|
56
36
|
|
57
|
-
def get_node_relationships(self, node: T, out_edge=True) -> R:
|
58
|
-
|
37
|
+
def get_node_relationships(self, node: T, out_edge=True, labels=None) -> R:
|
38
|
+
relationships = self.graph.get_node_relationships(node, out_edge)
|
39
|
+
if labels:
|
40
|
+
if not isinstance(labels, list):
|
41
|
+
labels = [labels]
|
42
|
+
result = []
|
43
|
+
for r in relationships:
|
44
|
+
if r.label in labels:
|
45
|
+
result.append(r)
|
46
|
+
relationships = result
|
47
|
+
return relationships
|
59
48
|
|
60
49
|
def node_exist(self, node: T) -> bool:
|
61
50
|
"""
|
@@ -99,4 +88,26 @@ class Structure(BaseNode):
|
|
99
88
|
relationship (R): The relationship instance to be removed.
|
100
89
|
"""
|
101
90
|
return self.graph.remove_relationship(relationship)
|
102
|
-
|
91
|
+
|
92
|
+
def is_empty(self) -> bool:
|
93
|
+
return self.graph.is_empty()
|
94
|
+
|
95
|
+
# type can be dict or list
|
96
|
+
# @staticmethod
|
97
|
+
# def _typed_return(type: Type[Union[Dict, List]],
|
98
|
+
# obj: Optional[Dict[str, Any]] = None
|
99
|
+
# ) -> Union[Dict[str, Any], List[Any]]:
|
100
|
+
# """
|
101
|
+
# Returns the object in the specified type format.
|
102
|
+
#
|
103
|
+
# Args:
|
104
|
+
# type (Type[Union[Dict, List]]): The type to return the object as (dict or list).
|
105
|
+
#
|
106
|
+
# obj (Optional[Dict[str, Any]]): The object to be converted.
|
107
|
+
#
|
108
|
+
# Returns:
|
109
|
+
# Union[Dict[str, Any], List[Any]]: The object in the specified type format.
|
110
|
+
# """
|
111
|
+
# if type is list:
|
112
|
+
# return list(obj.values())
|
113
|
+
# return obj
|