lionagi 0.0.112__py3-none-any.whl → 0.0.113__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. lionagi/__init__.py +3 -3
  2. lionagi/bridge/__init__.py +7 -0
  3. lionagi/bridge/langchain.py +131 -0
  4. lionagi/bridge/llama_index.py +157 -0
  5. lionagi/configs/__init__.py +7 -0
  6. lionagi/configs/oai_configs.py +49 -0
  7. lionagi/configs/openrouter_config.py +49 -0
  8. lionagi/core/__init__.py +8 -2
  9. lionagi/core/instruction_sets.py +1 -3
  10. lionagi/core/messages.py +2 -2
  11. lionagi/core/sessions.py +174 -27
  12. lionagi/datastore/__init__.py +1 -0
  13. lionagi/loader/__init__.py +9 -4
  14. lionagi/loader/chunker.py +157 -0
  15. lionagi/loader/reader.py +124 -0
  16. lionagi/objs/__init__.py +7 -0
  17. lionagi/objs/messenger.py +163 -0
  18. lionagi/objs/tool_registry.py +247 -0
  19. lionagi/schema/__init__.py +11 -0
  20. lionagi/schema/base_schema.py +239 -0
  21. lionagi/schema/base_tool.py +9 -0
  22. lionagi/schema/data_logger.py +94 -0
  23. lionagi/services/__init__.py +14 -0
  24. lionagi/{service_/oai.py → services/base_api_service.py} +49 -82
  25. lionagi/{endpoint/base_endpoint.py → services/chatcompletion.py} +19 -22
  26. lionagi/services/oai.py +34 -0
  27. lionagi/services/openrouter.py +32 -0
  28. lionagi/{service_/service_utils.py → services/service_objs.py} +0 -1
  29. lionagi/structure/__init__.py +7 -0
  30. lionagi/structure/relationship.py +128 -0
  31. lionagi/structure/structure.py +160 -0
  32. lionagi/tests/test_flatten_util.py +426 -0
  33. lionagi/tools/__init__.py +0 -5
  34. lionagi/tools/coder.py +1 -0
  35. lionagi/tools/scorer.py +1 -0
  36. lionagi/tools/validator.py +1 -0
  37. lionagi/utils/__init__.py +46 -20
  38. lionagi/utils/api_util.py +86 -0
  39. lionagi/utils/call_util.py +347 -0
  40. lionagi/utils/flat_util.py +540 -0
  41. lionagi/utils/io_util.py +102 -0
  42. lionagi/utils/load_utils.py +190 -0
  43. lionagi/utils/sys_util.py +191 -0
  44. lionagi/utils/tool_util.py +92 -0
  45. lionagi/utils/type_util.py +81 -0
  46. lionagi/version.py +1 -1
  47. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/METADATA +37 -13
  48. lionagi-0.0.113.dist-info/RECORD +84 -0
  49. lionagi/endpoint/chat_completion.py +0 -20
  50. lionagi/endpoint/endpoint_utils.py +0 -0
  51. lionagi/llm_configs.py +0 -21
  52. lionagi/loader/load_utils.py +0 -161
  53. lionagi/schema.py +0 -275
  54. lionagi/service_/__init__.py +0 -6
  55. lionagi/service_/base_service.py +0 -48
  56. lionagi/service_/openrouter.py +0 -1
  57. lionagi/services.py +0 -1
  58. lionagi/tools/tool_utils.py +0 -75
  59. lionagi/utils/sys_utils.py +0 -799
  60. lionagi-0.0.112.dist-info/RECORD +0 -67
  61. /lionagi/{core/responses.py → datastore/chroma.py} +0 -0
  62. /lionagi/{endpoint/assistants.py → datastore/deeplake.py} +0 -0
  63. /lionagi/{endpoint/audio.py → datastore/elasticsearch.py} +0 -0
  64. /lionagi/{endpoint/embeddings.py → datastore/lantern.py} +0 -0
  65. /lionagi/{endpoint/files.py → datastore/pinecone.py} +0 -0
  66. /lionagi/{endpoint/fine_tuning.py → datastore/postgres.py} +0 -0
  67. /lionagi/{endpoint/images.py → datastore/qdrant.py} +0 -0
  68. /lionagi/{endpoint/messages.py → schema/base_condition.py} +0 -0
  69. /lionagi/{service_ → services}/anthropic.py +0 -0
  70. /lionagi/{service_ → services}/anyscale.py +0 -0
  71. /lionagi/{service_ → services}/azure.py +0 -0
  72. /lionagi/{service_ → services}/bedrock.py +0 -0
  73. /lionagi/{service_ → services}/everlyai.py +0 -0
  74. /lionagi/{service_ → services}/gemini.py +0 -0
  75. /lionagi/{service_ → services}/gpt4all.py +0 -0
  76. /lionagi/{service_ → services}/huggingface.py +0 -0
  77. /lionagi/{service_ → services}/litellm.py +0 -0
  78. /lionagi/{service_ → services}/localai.py +0 -0
  79. /lionagi/{service_ → services}/mistralai.py +0 -0
  80. /lionagi/{service_ → services}/ollama.py +0 -0
  81. /lionagi/{service_ → services}/openllm.py +0 -0
  82. /lionagi/{service_ → services}/perplexity.py +0 -0
  83. /lionagi/{service_ → services}/predibase.py +0 -0
  84. /lionagi/{service_ → services}/rungpt.py +0 -0
  85. /lionagi/{service_ → services}/vllm.py +0 -0
  86. /lionagi/{service_ → services}/xinference.py +0 -0
  87. /lionagi/{endpoint → tests}/__init__.py +0 -0
  88. /lionagi/{endpoint/models.py → tools/planner.py} +0 -0
  89. /lionagi/{endpoint/moderations.py → tools/prompter.py} +0 -0
  90. /lionagi/{endpoint/runs.py → tools/sandbox.py} +0 -0
  91. /lionagi/{endpoint/threads.py → tools/summarizer.py} +0 -0
  92. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/LICENSE +0 -0
  93. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/WHEEL +0 -0
  94. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,160 @@
1
+ from typing import TypeVar, Dict, Optional, Any, Type, Union, List
2
+ from pydantic import Field
3
+ from ..schema.base_schema import BaseNode
4
+ from .relationship import Relationship
5
+
6
+ T = TypeVar('T', bound='BaseNode')
7
+ R = TypeVar('R', bound='Relationship')
8
+
9
+
10
+ class Structure(BaseNode):
11
+ """
12
+ Represents the structure of a graph consisting of nodes and relationships.
13
+ """
14
+ nodes: Dict[str, T] = Field(default_factory=dict)
15
+ relationships: Dict[str, R] = Field(default_factory=dict)
16
+ node_relationships: Dict[str, Dict[str, Dict[str, str]]] = Field(default_factory=dict)
17
+
18
+ def add_node(self, node: T) -> None:
19
+ """
20
+ Adds a node to the structure.
21
+
22
+ Args:
23
+ node (T): The node instance to be added.
24
+ """
25
+ self.nodes[node.id_] = node
26
+ self.node_relationships[node.id_] = {'in': {}, 'out': {}}
27
+
28
+ def add_relationship(self, relationship: R) -> None:
29
+ """
30
+ Adds a relationship to the structure.
31
+
32
+ Args:
33
+ relationship (R): The relationship instance to be added.
34
+ """
35
+ id_, source_, target_ = (
36
+ relationship.id_, relationship.source_node_id, relationship.target_node_id
37
+ )
38
+
39
+ self.relationships.update({id_ : relationship})
40
+ self.node_relationships[source_]['out'].update({id_ : target_})
41
+ self.node_relationships[target_]['in'].update({id_ : source_})
42
+
43
+ # type can be dict or list
44
+ @staticmethod
45
+ def _typed_return(type: Type[Union[Dict, List]],
46
+ obj: Optional[Dict[str, Any]] = None
47
+ ) -> Union[Dict[str, Any], List[Any]]:
48
+ """
49
+ Returns the object in the specified type format.
50
+
51
+ Args:
52
+ type (Type[Union[Dict, List]]): The type to return the object as (dict or list).
53
+
54
+ obj (Optional[Dict[str, Any]]): The object to be converted.
55
+
56
+ Returns:
57
+ Union[Dict[str, Any], List[Any]]: The object in the specified type format.
58
+ """
59
+ if type is list:
60
+ return list(obj.values())
61
+ return obj
62
+
63
+ def get_relationships(self, type: Type = dict) -> Union[Dict[str, R], List[R]]:
64
+ """
65
+ Returns the relationships in the specified type format.
66
+
67
+ Args:
68
+ type (Type): The type to return the relationships as (dict or list).
69
+
70
+ Returns:
71
+ Union[Dict[str, R], List[R]]: The relationships in the specified type format.
72
+ """
73
+ return self._typed_return(self.relationships, type=type)
74
+
75
+ def get_node_relationships(self, id_: str, in_out: str, type: Type = dict
76
+ ) -> Union[Dict[str, str], List[str]]:
77
+ """
78
+ Returns the relationships of a node in the specified type format.
79
+
80
+ Args:
81
+ id_ (str): The ID of the node.
82
+
83
+ in_out (str): 'in' for incoming relationships, 'out' for outgoing relationships.
84
+
85
+ type (Type): The type to return the relationships as (dict or list).
86
+
87
+ Returns:
88
+ Union[Dict[str, str], List[str]]: The relationships of the node in the specified type format.
89
+ """
90
+ node_relationships = self.node_relationships[id_][in_out]
91
+ return self._typed_return(node_relationships, type=type)
92
+
93
+ def node_exist(self, node: Union[T, str]) -> bool:
94
+ """
95
+ Checks if a node exists in the structure.
96
+
97
+ Args:
98
+ node (Union[T, str]): The node instance or node ID to check for existence.
99
+
100
+ Returns:
101
+ bool: True if the node exists, False otherwise.
102
+ """
103
+
104
+ return node.id_ in self.nodes.keys()
105
+
106
+ def relationship_exist(self, relationship: R) -> bool:
107
+ """
108
+ Checks if a relationship exists in the structure.
109
+
110
+ Args:
111
+ relationship (R): The relationship instance to check for existence.
112
+
113
+ Returns:
114
+ bool: True if the relationship exists, False otherwise.
115
+ """
116
+ return relationship.id_ in self.relationships.keys()
117
+
118
+ def remove_node_relationships(self, relationship_dict: Dict[str, str], in_out: str) -> None:
119
+ """
120
+ Removes relationships of a node from the structure.
121
+
122
+ Args:
123
+ relationship_dict (Dict[str, str]): A dictionary of relationship IDs to node IDs.
124
+
125
+ in_out (str): 'in' to remove incoming relationships, 'out' to remove outgoing relationships.
126
+ """
127
+ for relationship_id, node_id in relationship_dict.items():
128
+ self.node_relationships[node_id][in_out].pop(relationship_id)
129
+ self.relationships.pop(relationship_id)
130
+
131
+ def remove_node(self, node: Union[T, str]) -> None:
132
+ """
133
+ Removes a node and its associated relationships from the structure.
134
+
135
+ Args:
136
+ node (Union[T, str]): The node instance or node ID to be removed.
137
+ """
138
+ node_id = node if isinstance(node, str) else node.id_
139
+ out_ = self.get_node_relationships(node_id, 'out')
140
+ in_ = self.get_node_relationships(node_id, 'in')
141
+
142
+ self.remove_node_relationships(out_, 'in')
143
+ self.remove_node_relationships(in_, 'out')
144
+ self.node_relationships.pop(node_id)
145
+
146
+ def remove_relationship(self, relationship: R) -> None:
147
+ """
148
+ Removes a relationship from the structure.
149
+
150
+ Args:
151
+ relationship (R): The relationship instance to be removed.
152
+ """
153
+ id_, source_, target_ = (relationship.id_,
154
+ relationship.source_node_id,
155
+ relationship.target_node_id)
156
+
157
+ self.node_relationships[source_]['out'].pop(id_)
158
+ self.node_relationships[target_]['in'].pop(id_)
159
+ self.relationships.pop(id_)
160
+
@@ -0,0 +1,426 @@
1
+ import unittest
2
+
3
+ from ..utils.flat_util import *
4
+
5
+ class TestFlattenDict(unittest.TestCase):
6
+
7
+ def test_flatten_empty_dict(self):
8
+ self.assertEqual(flatten_dict({}), {})
9
+
10
+ def test_flatten_flat_dict(self):
11
+ self.assertEqual(flatten_dict({'a': 1, 'b': 2, 'c': 3}), {'a': 1, 'b': 2, 'c': 3})
12
+
13
+ def test_flatten_nested_dict(self):
14
+ nested_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3, 'f': 4}}}
15
+ expected_flat_dict = {'a': 1, 'b_c': 2, 'b_d_e': 3, 'b_d_f': 4}
16
+ self.assertEqual(flatten_dict(nested_dict), expected_flat_dict)
17
+
18
+ def test_flatten_dict_with_custom_separator(self):
19
+ nested_dict = {'a': {'b': 1, 'c': {'d': 2}}}
20
+ expected_flat_dict = {'a.b': 1, 'a.c.d': 2}
21
+ self.assertEqual(flatten_dict(nested_dict, sep='.'), expected_flat_dict)
22
+
23
+ def test_flatten_dict_with_empty_subdict(self):
24
+ nested_dict = {'a': 1, 'b': {}}
25
+ expected_flat_dict = {'a': 1}
26
+ self.assertEqual(flatten_dict(nested_dict), expected_flat_dict)
27
+
28
+ def test_flatten_dict_with_non_dict_values(self):
29
+ nested_dict = {'a': [1, 2, 3], 'b': 'string', 'c': {'d': 4}}
30
+ expected_flat_dict = {'a': [1, 2, 3], 'b': 'string', 'c_d': 4}
31
+ self.assertEqual(flatten_dict(nested_dict), expected_flat_dict)
32
+
33
+ class TestFlattenList(unittest.TestCase):
34
+ def test_flatten_empty_list(self): self.assertEqual(flatten_list([]), [])
35
+
36
+ def test_flatten_flat_list(self):
37
+ self.assertEqual(flatten_list([1, 2, 3]), [1, 2, 3])
38
+
39
+ def test_flatten_nested_list(self):
40
+ self.assertEqual(flatten_list([1, [2, [3, 4]], 5]), [1, 2, 3, 4, 5])
41
+
42
+ def test_flatten_list_with_None(self):
43
+ self.assertEqual(flatten_list([1, None, [2, None], 3], dropna=True), [1, 2, 3])
44
+ self.assertEqual(flatten_list([1, None, [2, None], 3], dropna=False), [1, None, 2, None, 3])
45
+
46
+ def test_flatten_list_with_other_datatypes(self):
47
+ self.assertEqual(flatten_list([1, "string", [2, (3, 4)], 5]), [1, "string", 2, (3, 4), 5])
48
+
49
+ def test_flatten_list_with_multiple_nested_lists(self):
50
+ self.assertEqual(flatten_list([[1, [2, 3]], [4, [5, [6, 7]]]]), [1, 2, 3, 4, 5, 6, 7])
51
+
52
+ class TestChangeSeparator(unittest.TestCase):
53
+
54
+ def test_change_separator_basic(self):
55
+ input_dict = {'a_b': 1, 'a_b_c': 2, 'd': 3}
56
+ expected_dict = {'a-b': 1, 'a-b-c': 2, 'd': 3}
57
+ result_dict = change_separator(input_dict, '_', '-')
58
+ self.assertEqual(expected_dict, result_dict)
59
+
60
+ def test_change_separator_no_change(self):
61
+ input_dict = {'a-b': 1, 'b-c': 2}
62
+ expected_dict = {'a-b': 1, 'b-c': 2}
63
+ result_dict = change_separator(input_dict, '_', '-')
64
+ self.assertEqual(expected_dict, result_dict)
65
+
66
+ def test_change_separator_empty_dict(self):
67
+ self.assertEqual({}, change_separator({}, '_', '-'))
68
+
69
+ def test_change_separator_same_separators(self):
70
+ input_dict = {'a_b': 1, 'b_c': 2}
71
+ expected_dict = {'a_b': 1, 'b_c': 2}
72
+ result_dict = change_separator(input_dict, '_', '_')
73
+ self.assertEqual(expected_dict, result_dict)
74
+
75
+ def test_change_separator_multiple_occurrences(self):
76
+ input_dict = {'a_b_c_b': 1, 'd_e_f_e': 2}
77
+ expected_dict = {'a-b-c-b': 1, 'd-e-f-e': 2}
78
+ result_dict = change_separator(input_dict, '_', '-')
79
+ self.assertEqual(expected_dict, result_dict)
80
+
81
+ class TestUnflattenDict(unittest.TestCase):
82
+
83
+ def test_unflatten_simple(self):
84
+ flat_dict = {'a_b_c': 1, 'a_b_d': 2}
85
+ expected = {'a': {'b': {'c': 1, 'd': 2}}}
86
+ self.assertEqual(unflatten_dict(flat_dict), expected)
87
+
88
+ def test_unflatten_with_int_keys(self):
89
+ flat_dict = {'1_2_3': 'a', '1_2_4': 'b'}
90
+ expected = {1: {2: {3: 'a', 4: 'b'}}}
91
+ self.assertEqual(unflatten_dict(flat_dict), expected)
92
+
93
+ def test_unflatten_with_mixed_keys(self):
94
+ flat_dict = {'a_2_c': 1, 'a_2_d': 2, 'b_3': 3}
95
+ expected = {'a': {2: {'c': 1, 'd': 2}}, 'b': {3: 3}}
96
+ self.assertEqual(unflatten_dict(flat_dict), expected)
97
+
98
+ def test_unflatten_with_custom_separator(self):
99
+ flat_dict = {'a|b|c': 1, 'a|b|d': 2}
100
+ expected = {'a': {'b': {'c': 1, 'd': 2}}}
101
+ self.assertEqual(unflatten_dict(flat_dict, sep='|'), expected)
102
+
103
+ def test_unflatten_with_non_dict_value(self):
104
+ flat_dict = {'a_b': [1, 2, 3], 'a_c': (4, 5)}
105
+ expected = {'a': {'b': [1, 2, 3], 'c': (4, 5)}}
106
+ self.assertEqual(unflatten_dict(flat_dict), expected)
107
+
108
+ def test_unflatten_with_nested_list(self):
109
+ flat_dict = {'a_0': 1, 'a_1': 2, 'b_0': 3, 'b_1': 4}
110
+ expected = {'a': [1, 2], 'b': [3, 4]}
111
+ self.assertEqual(unflatten_dict(flat_dict), expected)
112
+
113
+ def test_unflatten_with_overlapping_keys(self):
114
+ flat_dict = {'a_b': 1, 'a': 2}
115
+ with self.assertRaises(ValueError):
116
+ unflatten_dict(flat_dict)
117
+
118
+ class TestIsFlattenable(unittest.TestCase):
119
+
120
+ def test_flattenable_dict(self):
121
+ self.assertTrue(is_flattenable({'a': {'b': 1}, 'c': [2, 3]}))
122
+
123
+ def test_flattenable_list(self):
124
+ self.assertTrue(is_flattenable([1, {'a': 2}, [3]]))
125
+
126
+ def test_non_flattenable_dict(self):
127
+ self.assertFalse(is_flattenable({'a': 1, 'b': 2}))
128
+
129
+ def test_non_flattenable_list(self):
130
+ self.assertFalse(is_flattenable([1, 2, 3]))
131
+
132
+ def test_empty_dict(self):
133
+ self.assertFalse(is_flattenable({}))
134
+
135
+ def test_empty_list(self):
136
+ self.assertFalse(is_flattenable([]))
137
+
138
+ def test_non_flattenable_types(self):
139
+ self.assertFalse(is_flattenable(1))
140
+ self.assertFalse(is_flattenable(1.0))
141
+ self.assertFalse(is_flattenable('string'))
142
+ self.assertFalse(is_flattenable(None))
143
+
144
+ def test_nested_mix_flattenable(self):
145
+ self.assertTrue(is_flattenable({'a': [{'b': 1}], 'c': (2, 3)}))
146
+
147
+ def test_deeply_nested_flattenable(self):
148
+ self.assertTrue(is_flattenable({'a': {'b': {'c': {'d': [1]}}}}))
149
+
150
+
151
+ def default_logic_func(parent_key, key, value, sep='_'):
152
+ new_key = f"{parent_key}{sep}{key}" if parent_key else key
153
+ return new_key, value
154
+
155
+
156
+ class TestFlattenWithCustomLogic(unittest.TestCase):
157
+ def test_flatten_dict(self):
158
+ input_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
159
+ expected_output = {'a': 1, 'b_c': 2, 'b_d_e': 3}
160
+ self.assertEqual(flatten_with_custom_logic(input_dict, default_logic_func), expected_output)
161
+
162
+ def test_flatten_list(self):
163
+ input_list = [1, 2, [3, 4]]
164
+ expected_output = {'0': 1, '1': 2, '2_0': 3, '2_1': 4}
165
+ self.assertEqual(flatten_with_custom_logic(input_list, default_logic_func), expected_output)
166
+
167
+
168
+ def test_empty_dict(self):
169
+ self.assertEqual(flatten_with_custom_logic({}, default_logic_func), {})
170
+
171
+ def test_empty_list(self):
172
+ self.assertEqual(flatten_with_custom_logic([], default_logic_func), {})
173
+
174
+ def test_nested_combination(self):
175
+ input_obj = {'a': [{'b': 1}, {'c': 2}], 'd': []}
176
+ expected_output = {'a_0_b': 1, 'a_1_c': 2, 'd': None}
177
+ self.assertEqual(flatten_with_custom_logic(input_obj, default_logic_func), expected_output)
178
+
179
+ def test_custom_separator(self):
180
+ input_obj = {'a': 1, 'b': {'c': 2}}
181
+ expected_output = {'a': 1, 'b#c': 2}
182
+ self.assertEqual(flatten_with_custom_logic(input_obj, default_logic_func, sep='#'), expected_output)
183
+
184
+ def test_logic_func_none(self):
185
+ input_obj = {'a': 1, 'b': {'c': 2}}
186
+ expected_output = {'a': 1, 'b_c': 2}
187
+ self.assertEqual(flatten_with_custom_logic(input_obj, None), expected_output)
188
+
189
+ def test_obj_with_none_values(self):
190
+ input_obj = {'a': None, 'b': {'c': None}}
191
+ expected_output = {'a': None, 'b_c': None}
192
+ self.assertEqual(flatten_with_custom_logic(input_obj, default_logic_func), expected_output)
193
+
194
+ def test_custom_logic_func(self):
195
+ def custom_logic(parent_key, key, value, sep='_'):
196
+ new_key = f"{parent_key}{sep}{key}".upper() if parent_key else key.upper()
197
+ return new_key, value*2 if isinstance(value, int) else value
198
+
199
+ input_obj = {'a': 1, 'b': {'c': 2}}
200
+ expected_output = {'A': 2, 'B_C': 4}
201
+ self.assertEqual(flatten_with_custom_logic(input_obj, custom_logic), expected_output)
202
+
203
+ class TestDynamicFlatten(unittest.TestCase):
204
+
205
+ def test_flatten_dict(self):
206
+ sample_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
207
+ flattened = dynamic_flatten(sample_dict)
208
+ expected = {'a': 1, 'b_c': 2, 'b_d_e': 3}
209
+ self.assertEqual(flattened, expected)
210
+
211
+ def test_flatten_list(self):
212
+ sample_list = [1, 2, [3, 4]]
213
+ flattened = dynamic_flatten(sample_list)
214
+ expected = {'0': 1, '1': 2, '2_0': 3, '2_1': 4}
215
+ self.assertEqual(flattened, expected)
216
+
217
+ def test_flatten_with_max_depth(self):
218
+ sample_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
219
+ flattened = dynamic_flatten(sample_dict, max_depth=1)
220
+ expected = {'a': 1, 'b_c': 2, 'b_d': {'e': 3}}
221
+ self.assertEqual(flattened, expected)
222
+
223
+ def test_flatten_with_different_separator(self):
224
+ sample_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
225
+ flattened = dynamic_flatten(sample_dict, sep='.')
226
+ expected = {'a': 1, 'b.c': 2, 'b.d.e': 3}
227
+ self.assertEqual(flattened, expected)
228
+
229
+ def test_flatten_empty_dictionary(self):
230
+ sample_dict = {}
231
+ flattened = dynamic_flatten(sample_dict)
232
+ expected = {}
233
+ self.assertEqual(flattened, expected)
234
+
235
+ def test_flatten_empty_list(self):
236
+ sample_list = []
237
+ flattened = dynamic_flatten(sample_list)
238
+ expected = {}
239
+ self.assertEqual(flattened, expected)
240
+
241
+ def test_flatten_non_dict_non_list(self):
242
+ with self.assertRaises(TypeError):
243
+ dynamic_flatten(42)
244
+
245
+ def test_flatten_nested_empty_dict(self):
246
+ sample_dict = {'a': {}, 'b': {'c': {}}}
247
+ flattened = dynamic_flatten(sample_dict)
248
+ expected = {'a': {}, 'b_c': {}}
249
+ self.assertEqual(flattened, expected)
250
+
251
+ def test_flatten_nested_empty_list(self):
252
+ sample_list = [1, [], [3, []]]
253
+ flattened = dynamic_flatten(sample_list)
254
+ expected = {'0': 1, '1': [], '2_0': 3, '2_1': []}
255
+ self.assertEqual(flattened, expected)
256
+
257
+ # class TestDynamicUnflatten(unittest.TestCase):
258
+
259
+ # def test_unflatten_dict(self):
260
+ # flat_dict = {'a': 1, 'b_c': 2, 'b_d_e': 3}
261
+ # expected = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
262
+ # self.assertEqual(dynamic_unflatten(flat_dict), expected)
263
+
264
+
265
+ # def test_unflatten_with_different_separator(self):
266
+ # flat_dict = {'a': 1, 'b.c': 2, 'b.d.e': 3}
267
+ # expected = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
268
+ # self.assertEqual(dynamic_unflatten(flat_dict, sep='.'), expected)
269
+
270
+ # def test_unflatten_empty_dict(self):
271
+ # flat_dict = {}
272
+ # expected = []
273
+ # self.assertEqual(dynamic_unflatten(flat_dict), expected)
274
+
275
+ # def test_unflatten_empty_string_keys(self):
276
+ # flat_dict = {'': 1}
277
+ # expected = {'': 1}
278
+ # self.assertEqual(dynamic_unflatten(flat_dict), expected)
279
+
280
+ # def test_unflatten_single_key(self):
281
+ # flat_dict = {'a': 1}
282
+ # expected = {'a': 1}
283
+ # self.assertEqual(dynamic_unflatten(flat_dict), expected)
284
+
285
+ # def test_unflatten_nested_empty_dict(self):
286
+ # flat_dict = {'a': {}, 'b_c': {}}
287
+ # expected = {'a': {}, 'b': {'c': {}}}
288
+ # self.assertEqual(dynamic_unflatten(flat_dict), expected)
289
+
290
+
291
+ # def test_unflatten_max_depth(self):
292
+ # flat_dict = {'a_b_c_d_e': 1}
293
+ # expected = {'a': {'b': {'c': {'d': {'e': 1}}}}}
294
+ # self.assertEqual(dynamic_unflatten(flat_dict, max_depth=3), expected)
295
+
296
+ class TestUnflattenToList(unittest.TestCase):
297
+
298
+ def test_unflatten_simple_list(self):
299
+ flat_dict = {'0': 'a', '1': 'b', '2': 'c'}
300
+ expected = ['a', 'b', 'c']
301
+ self.assertEqual(unflatten_to_list(flat_dict), expected)
302
+
303
+ def test_unflatten_nested_dicts_in_list(self):
304
+ flat_dict = {'0_a': 'value1', '1_b': 'value2', '2_c_d': 'value3'}
305
+ expected = [{'a': 'value1'}, {'b': 'value2'}, {'c': {'d': 'value3'}}]
306
+ self.assertEqual(unflatten_to_list(flat_dict), expected)
307
+
308
+ def test_unflatten_empty_dict(self):
309
+ flat_dict = {}
310
+ expected = []
311
+ self.assertEqual(unflatten_to_list(flat_dict), expected)
312
+
313
+ def test_unflatten_with_custom_separator(self):
314
+ flat_dict = {'0-a': 'value1', '1-b': 'value2', '2-c-d': 'value3'}
315
+ expected = [{'a': 'value1'}, {'b': 'value2'}, {'c': {'d': 'value3'}}]
316
+ self.assertEqual(unflatten_to_list(flat_dict, sep='-'), expected)
317
+
318
+ def test_unflatten_with_sparse_indices(self):
319
+ flat_dict = {'0': 'value1', '2': 'value2', '5': 'value3'}
320
+ expected = ['value1', None, 'value2', None, None, 'value3']
321
+ self.assertEqual(unflatten_to_list(flat_dict), expected)
322
+
323
+ def test_unflatten_with_negative_indices(self):
324
+ flat_dict = {'-1': 'value1', '1': 'value2'}
325
+ with self.assertRaises(ValueError): # Expect ValueError instead of IndexError
326
+ unflatten_to_list(flat_dict)
327
+
328
+ def test_unflatten_with_custom_separator(self):
329
+ flat_dict = {'0-a': 'value1', '1-b': 'value2', '2-c-d': 'value3'}
330
+ expected = [{'a': 'value1'}, {'b': 'value2'}, {'c': {'d': 'value3'}}]
331
+ self.assertEqual(unflatten_to_list(flat_dict, sep='-'), expected)
332
+
333
+
334
+ class TestFlattenIterable(unittest.TestCase):
335
+
336
+ def test_flatten_empty_list(self):
337
+ self.assertEqual(list(flatten_iterable([])), [])
338
+
339
+ def test_flatten_nested_lists(self):
340
+ nested_list = [1, [2, [3, 4], 5], 6]
341
+ expected_flat_list = [1, 2, 3, 4, 5, 6]
342
+ self.assertEqual(list(flatten_iterable(nested_list)), expected_flat_list)
343
+
344
+ def test_flatten_nested_tuples(self):
345
+ nested_tuple = (1, (2, (3, 4), 5), 6)
346
+ expected_flat_list = [1, 2, 3, 4, 5, 6]
347
+ self.assertEqual(list(flatten_iterable(nested_tuple)), expected_flat_list)
348
+
349
+ def test_flatten_with_max_depth(self):
350
+ nested_list = [1, [2, [3, 4], 5], 6]
351
+ expected_flat_list_depth_1 = [1, [2, [3, 4], 5], 6]
352
+ self.assertEqual(list(flatten_iterable(nested_list, max_depth=1)), expected_flat_list_depth_1)
353
+
354
+ expected_flat_list_depth_2 = [1, 2, [3, 4], 5, 6]
355
+ self.assertEqual(list(flatten_iterable(nested_list, max_depth=2)), expected_flat_list_depth_2)
356
+
357
+ # def test_flatten_strings(self):
358
+ # nested_list = ["hello", ["world", ["!"]]]
359
+ # expected_flat_list = ["hello", "world", ["!"]]
360
+ # self.assertEqual(list(flatten_iterable(nested_list)), expected_flat_list)
361
+
362
+ def test_flatten_with_non_iterable(self):
363
+ nested_list = [1, [2, 3], 4, "text", 5]
364
+ expected_flat_list = [1, 2, 3, 4, "text", 5]
365
+ self.assertEqual(list(flatten_iterable(nested_list)), expected_flat_list)
366
+
367
+ def test_flatten_with_none_max_depth(self):
368
+ nested_list = [1, [2, [3, [4, [5]]]]]
369
+ expected_flat_list = [1, 2, 3, 4, 5]
370
+ self.assertEqual(list(flatten_iterable(nested_list, max_depth=None)), expected_flat_list)
371
+
372
+ def test_flatten_iterable_to_list_function(self):
373
+ nested_list = [1, [2, [3, 4], 5], 6]
374
+ expected_flat_list = [1, 2, 3, 4, 5, 6]
375
+ self.assertEqual(flatten_iterable_to_list(nested_list), expected_flat_list)
376
+
377
+ nested_list_with_depth = [1, [2, [3, [4, [5]]]]]
378
+ expected_flat_list_with_depth = [1, 2, [3, [4, [5]]]]
379
+ self.assertEqual(flatten_iterable_to_list(nested_list_with_depth, max_depth=2), expected_flat_list_with_depth)
380
+
381
+
382
+ class TestUnflattenDictWithCustomLogic(unittest.TestCase):
383
+
384
+ @staticmethod
385
+ def logic_func_upper(key: str, value: Any) -> Tuple[str, Any]:
386
+ return key.upper(), value
387
+
388
+ @staticmethod
389
+ def logic_func_append_prefix(key: str, value: Any) -> Tuple[str, Any]:
390
+
391
+ return f"prefix_{key}", value
392
+
393
+ # def test_unflatten_dict_with_uppercase_logic(self):
394
+ # flat_dict = {'a_1': 10, 'b_2': 20, 'c_sub_1': 30}
395
+ # expected_dict = {'A': {'1': 10}, 'B': {'2': 20}, 'C_SUB': {'1': 30}}
396
+ # self.assertEqual(
397
+ # unflatten_dict_with_custom_logic(flat_dict, self.logic_func_upper),
398
+ # expected_dict
399
+ # )
400
+
401
+ # def test_unflatten_dict_with_prefix_logic(self):
402
+ # flat_dict = {'a_1': 10, 'b_2': 20, 'c_sub_1': 30}
403
+ # expected_dict = {'prefix_a': {'prefix_1': 10}, 'prefix_b': {'prefix_2': 20}, 'prefix_c_sub': {'prefix_1': 30}}
404
+ # self.assertEqual(
405
+ # unflatten_dict_with_custom_logic(flat_dict, self.logic_func_append_prefix),
406
+ # expected_dict
407
+ # )
408
+
409
+ def test_unflatten_dict_with_no_modification_logic(self):
410
+ flat_dict = {'a_1': 10, 'b_2': 20, 'c_sub_1': 30}
411
+ identity_logic_func = lambda key, value: (key, value)
412
+ expected_dict = {'a': {'1': 10}, 'b': {'2': 20}, 'c': {'sub': {'1': 30}}}
413
+ self.assertEqual(
414
+ unflatten_dict_with_custom_logic(flat_dict, identity_logic_func),
415
+ expected_dict
416
+ )
417
+
418
+ def test_unflatten_dict_empty(self):
419
+ flat_dict = {}
420
+ self.assertEqual(
421
+ unflatten_dict_with_custom_logic(flat_dict, self.logic_func_upper),
422
+ {}
423
+ )
424
+
425
+ if __name__ == "main":
426
+ unittest.main()
lionagi/tools/__init__.py CHANGED
@@ -1,5 +0,0 @@
1
- from .tool_utils import ToolManager
2
-
3
- __all__ = [
4
- "ToolManager"
5
- ]
lionagi/tools/coder.py CHANGED
@@ -0,0 +1 @@
1
+ # TODO
lionagi/tools/scorer.py CHANGED
@@ -0,0 +1 @@
1
+ # TODO
@@ -0,0 +1 @@
1
+ # TODO
lionagi/utils/__init__.py CHANGED
@@ -1,23 +1,49 @@
1
- from .sys_utils import to_flat_dict, to_list, str_to_num, create_copy, to_temp, to_csv, append_to_jsonl, hold_call, ahold_call, l_call, al_call, m_call, am_call, e_call, ae_call, get_timestamp, create_id, create_path, get_bins
1
+ from .flat_util import (
2
+ flatten_dict, flatten_list, change_separator,
3
+ unflatten_dict, is_flattenable, dynamic_flatten,
4
+ unflatten_to_list, flatten_iterable, flatten_iterable_to_list
5
+ )
6
+
7
+ from .sys_util import (
8
+ create_copy, get_timestamp, create_id, create_path,
9
+ split_path, get_bins, change_dict_key
10
+ )
11
+
12
+
13
+ from .api_util import (
14
+ api_method, api_endpoint_from_url, api_error,
15
+ api_rate_limit_error
16
+ )
17
+
18
+ from .call_util import (
19
+ hcall, ahcall, lcall, alcall,
20
+ mcall, amcall, ecall, aecall
21
+ )
22
+
23
+ from .io_util import to_temp, to_csv, append_to_jsonl
24
+ from .load_utils import dir_to_path, dir_to_nodes, chunk_text, read_text, file_to_chunks
25
+ from .type_util import str_to_num, to_list
26
+ from .tool_util import func_to_schema
27
+
28
+
29
+
30
+
2
31
 
3
32
  __all__ = [
4
- "to_flat_dict",
5
- "to_list",
6
- "str_to_num",
7
- "create_copy",
8
- "to_temp",
9
- "to_csv",
10
- "append_to_jsonl",
11
- "hold_call",
12
- "ahold_call",
13
- "l_call",
14
- "al_call",
15
- "m_call",
16
- "am_call",
17
- "e_call",
18
- "ae_call",
19
- "get_timestamp",
20
- "create_id",
21
- "create_path",
22
- "get_bins"
33
+ "api_method", "api_endpoint_from_url", "api_error",
34
+ "api_rate_limit_error",
35
+
36
+ "flatten_dict", "flatten_list", "change_separator",
37
+ "unflatten_dict", "is_flattenable", "dynamic_flatten",
38
+ "unflatten_to_list", "flatten_iterable", "flatten_iterable_to_list",
39
+
40
+ "create_copy", "get_timestamp", "create_id", "create_path",
41
+ "split_path", "get_bins", "change_dict_key",
42
+
43
+ "hcall", "ahcall", "lcall", "alcall",
44
+ "mcall", "amcall", "ecall", "aecall",
45
+
46
+ "to_temp", "to_csv", "append_to_jsonl",
47
+ "dir_to_path", "chunk_text", "file_to_chunks",
48
+ "str_to_num", "to_list", "func_to_schema"
23
49
  ]