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.
Files changed (123) hide show
  1. lionagi/__init__.py +1 -2
  2. lionagi/_services/__init__.py +5 -0
  3. lionagi/_services/anthropic.py +79 -0
  4. lionagi/_services/base_service.py +414 -0
  5. lionagi/_services/oai.py +98 -0
  6. lionagi/_services/openrouter.py +44 -0
  7. lionagi/_services/services.py +91 -0
  8. lionagi/_services/transformers.py +46 -0
  9. lionagi/bridge/langchain.py +26 -16
  10. lionagi/bridge/llama_index.py +50 -20
  11. lionagi/configs/oai_configs.py +2 -14
  12. lionagi/configs/openrouter_configs.py +2 -2
  13. lionagi/core/__init__.py +7 -8
  14. lionagi/core/branch/branch.py +589 -0
  15. lionagi/core/branch/branch_manager.py +139 -0
  16. lionagi/core/branch/conversation.py +484 -0
  17. lionagi/core/core_util.py +59 -0
  18. lionagi/core/flow/flow.py +19 -0
  19. lionagi/core/flow/flow_util.py +62 -0
  20. lionagi/core/instruction_set/__init__.py +0 -5
  21. lionagi/core/instruction_set/instruction_set.py +343 -0
  22. lionagi/core/messages/messages.py +176 -0
  23. lionagi/core/sessions/__init__.py +0 -5
  24. lionagi/core/sessions/session.py +428 -0
  25. lionagi/loaders/chunker.py +51 -47
  26. lionagi/loaders/load_util.py +2 -2
  27. lionagi/loaders/reader.py +45 -39
  28. lionagi/models/imodel.py +53 -0
  29. lionagi/schema/async_queue.py +158 -0
  30. lionagi/schema/base_node.py +318 -147
  31. lionagi/schema/base_tool.py +31 -1
  32. lionagi/schema/data_logger.py +74 -38
  33. lionagi/schema/data_node.py +57 -6
  34. lionagi/structures/graph.py +132 -10
  35. lionagi/structures/relationship.py +58 -20
  36. lionagi/structures/structure.py +36 -25
  37. lionagi/tests/test_utils/test_api_util.py +219 -0
  38. lionagi/tests/test_utils/test_call_util.py +785 -0
  39. lionagi/tests/test_utils/test_encrypt_util.py +323 -0
  40. lionagi/tests/test_utils/test_io_util.py +238 -0
  41. lionagi/tests/test_utils/test_nested_util.py +338 -0
  42. lionagi/tests/test_utils/test_sys_util.py +358 -0
  43. lionagi/tools/tool_manager.py +186 -0
  44. lionagi/tools/tool_util.py +266 -3
  45. lionagi/utils/__init__.py +21 -61
  46. lionagi/utils/api_util.py +359 -71
  47. lionagi/utils/call_util.py +839 -264
  48. lionagi/utils/encrypt_util.py +283 -16
  49. lionagi/utils/io_util.py +178 -93
  50. lionagi/utils/nested_util.py +672 -0
  51. lionagi/utils/pd_util.py +57 -0
  52. lionagi/utils/sys_util.py +284 -156
  53. lionagi/utils/url_util.py +55 -0
  54. lionagi/version.py +1 -1
  55. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/METADATA +21 -17
  56. lionagi-0.0.204.dist-info/RECORD +106 -0
  57. lionagi/core/conversations/__init__.py +0 -5
  58. lionagi/core/conversations/conversation.py +0 -107
  59. lionagi/core/flows/__init__.py +0 -8
  60. lionagi/core/flows/flow.py +0 -8
  61. lionagi/core/flows/flow_util.py +0 -62
  62. lionagi/core/instruction_set/instruction_sets.py +0 -7
  63. lionagi/core/sessions/sessions.py +0 -185
  64. lionagi/endpoints/__init__.py +0 -5
  65. lionagi/endpoints/audio.py +0 -17
  66. lionagi/endpoints/chatcompletion.py +0 -54
  67. lionagi/messages/__init__.py +0 -11
  68. lionagi/messages/instruction.py +0 -15
  69. lionagi/messages/message.py +0 -110
  70. lionagi/messages/response.py +0 -33
  71. lionagi/messages/system.py +0 -12
  72. lionagi/objs/__init__.py +0 -11
  73. lionagi/objs/abc_objs.py +0 -39
  74. lionagi/objs/async_queue.py +0 -135
  75. lionagi/objs/messenger.py +0 -85
  76. lionagi/objs/tool_manager.py +0 -253
  77. lionagi/services/__init__.py +0 -11
  78. lionagi/services/base_api_service.py +0 -230
  79. lionagi/services/oai.py +0 -34
  80. lionagi/services/openrouter.py +0 -31
  81. lionagi/tests/test_api_util.py +0 -46
  82. lionagi/tests/test_call_util.py +0 -115
  83. lionagi/tests/test_convert_util.py +0 -202
  84. lionagi/tests/test_encrypt_util.py +0 -33
  85. lionagi/tests/test_flat_util.py +0 -426
  86. lionagi/tests/test_sys_util.py +0 -0
  87. lionagi/utils/convert_util.py +0 -229
  88. lionagi/utils/flat_util.py +0 -599
  89. lionagi-0.0.115.dist-info/RECORD +0 -110
  90. /lionagi/{services → _services}/anyscale.py +0 -0
  91. /lionagi/{services → _services}/azure.py +0 -0
  92. /lionagi/{services → _services}/bedrock.py +0 -0
  93. /lionagi/{services → _services}/everlyai.py +0 -0
  94. /lionagi/{services → _services}/gemini.py +0 -0
  95. /lionagi/{services → _services}/gpt4all.py +0 -0
  96. /lionagi/{services → _services}/huggingface.py +0 -0
  97. /lionagi/{services → _services}/litellm.py +0 -0
  98. /lionagi/{services → _services}/localai.py +0 -0
  99. /lionagi/{services → _services}/mistralai.py +0 -0
  100. /lionagi/{services → _services}/ollama.py +0 -0
  101. /lionagi/{services → _services}/openllm.py +0 -0
  102. /lionagi/{services → _services}/perplexity.py +0 -0
  103. /lionagi/{services → _services}/predibase.py +0 -0
  104. /lionagi/{services → _services}/rungpt.py +0 -0
  105. /lionagi/{services → _services}/vllm.py +0 -0
  106. /lionagi/{services → _services}/xinference.py +0 -0
  107. /lionagi/{endpoints/assistants.py → agents/__init__.py} +0 -0
  108. /lionagi/{tools → agents}/planner.py +0 -0
  109. /lionagi/{tools → agents}/prompter.py +0 -0
  110. /lionagi/{tools → agents}/scorer.py +0 -0
  111. /lionagi/{tools → agents}/summarizer.py +0 -0
  112. /lionagi/{tools → agents}/validator.py +0 -0
  113. /lionagi/{endpoints/embeddings.py → core/branch/__init__.py} +0 -0
  114. /lionagi/{services/anthropic.py → core/branch/cluster.py} +0 -0
  115. /lionagi/{endpoints/finetune.py → core/flow/__init__.py} +0 -0
  116. /lionagi/{endpoints/image.py → core/messages/__init__.py} +0 -0
  117. /lionagi/{endpoints/moderation.py → models/__init__.py} +0 -0
  118. /lionagi/{endpoints/vision.py → models/base_model.py} +0 -0
  119. /lionagi/{objs → schema}/status_tracker.py +0 -0
  120. /lionagi/tests/{test_io_util.py → test_utils/__init__.py} +0 -0
  121. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/LICENSE +0 -0
  122. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/WHEEL +0 -0
  123. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  from collections import deque
2
- from typing import Optional
3
- from lionagi.utils import create_path, to_csv
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 __call__(self, entry):
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
- Parameters:
46
- entry: The data entry to be added to the log.
47
- """
48
- self.log.append(entry)
49
-
50
- def to_csv(self, filename: str, dir: Optional[str] = None, verbose: bool = True,
51
- timestamp: bool = True, dir_exist_ok: bool = True, file_exist_ok: bool = False) -> None:
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
- Exports the logged data to a CSV file and optionally clears the log.
56
+ Sets the default directory for saving CSV files.
54
57
 
55
58
  Parameters:
56
- filename (str): The name of the CSV file.
59
+ dir (str): The directory to be set as the default for saving files.
60
+ """
61
+ self.dir = dir
57
62
 
58
- dir (Optional[str]): The directory to save the file. Defaults to the instance's dir attribute.
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
- timestamp (bool): If True, appends a timestamp to the filename. Defaults to True.
63
-
64
- dir_exist_ok (bool): If True, will not raise an error if the directory already exists. Defaults to True.
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=dir, filename=filename, timestamp=timestamp, dir_exist_ok=dir_exist_ok)
76
- to_csv(list(self.log), filepath, file_exist_ok=file_exist_ok)
77
- n_logs = len(list(self.log))
78
- self.log = deque()
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"{n_logs} logs saved to {filepath}")
81
-
82
- def set_dir(self, dir: str) -> None:
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
- Sets the default directory for saving CSV files.
104
+ Exports the logged data to a JSONL file and optionally clears the log.
85
105
 
86
106
  Parameters:
87
- dir (str): The directory to be set as the default for saving files.
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
- self.dir = dir
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()
@@ -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
- # to llama_index textnode
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
- # to langchain document
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']}
@@ -1,20 +1,56 @@
1
+ from typing import List, Any
1
2
  from pydantic import Field
2
3
 
3
- from lionagi.schema.base_node import BaseNode
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 to_networkx(self, **kwargs):
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
- Relationship class represents a relationship between two nodes in a graph.
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
- Parameters:
28
- condition (Dict[str, Any]): The condition to be added.
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
- Parameters:
37
- condition_key (str): The key of the condition to be removed.
43
+ Args:
44
+ condition_key: The key of the condition to be removed.
38
45
 
39
46
  Returns:
40
- Any: The value of the removed condition.
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
- Parameters:
54
- condition_key (str): The key of the condition to check.
65
+ Args:
66
+ condition_key: The key of the condition to check.
55
67
 
56
68
  Returns:
57
- bool: True if the condition exists, False otherwise.
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
- Parameters:
69
- condition_key (Optional[str]): The key of the specific condition. Defaults to None.
85
+ Args:
86
+ condition_key: The key of the specific condition. If None, all conditions are returned.
70
87
 
71
88
  Returns:
72
- Any: The requested condition or all conditions if no key is provided.
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
- Parameters:
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
- Parameters:
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
- Parameters:
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
- """Returns a simple string representation of the Relationship."""
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
- """Returns a detailed string representation of the Relationship."""
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})"
@@ -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
- return self.graph.get_node_relationships(node, out_edge)
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