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.
- lionagi/__init__.py +3 -3
- lionagi/bridge/__init__.py +7 -0
- lionagi/bridge/langchain.py +131 -0
- lionagi/bridge/llama_index.py +157 -0
- lionagi/configs/__init__.py +7 -0
- lionagi/configs/oai_configs.py +49 -0
- lionagi/configs/openrouter_config.py +49 -0
- lionagi/core/__init__.py +8 -2
- lionagi/core/instruction_sets.py +1 -3
- lionagi/core/messages.py +2 -2
- lionagi/core/sessions.py +174 -27
- lionagi/datastore/__init__.py +1 -0
- lionagi/loader/__init__.py +9 -4
- lionagi/loader/chunker.py +157 -0
- lionagi/loader/reader.py +124 -0
- lionagi/objs/__init__.py +7 -0
- lionagi/objs/messenger.py +163 -0
- lionagi/objs/tool_registry.py +247 -0
- lionagi/schema/__init__.py +11 -0
- lionagi/schema/base_schema.py +239 -0
- lionagi/schema/base_tool.py +9 -0
- lionagi/schema/data_logger.py +94 -0
- lionagi/services/__init__.py +14 -0
- lionagi/{service_/oai.py → services/base_api_service.py} +49 -82
- lionagi/{endpoint/base_endpoint.py → services/chatcompletion.py} +19 -22
- lionagi/services/oai.py +34 -0
- lionagi/services/openrouter.py +32 -0
- lionagi/{service_/service_utils.py → services/service_objs.py} +0 -1
- lionagi/structure/__init__.py +7 -0
- lionagi/structure/relationship.py +128 -0
- lionagi/structure/structure.py +160 -0
- lionagi/tests/test_flatten_util.py +426 -0
- lionagi/tools/__init__.py +0 -5
- lionagi/tools/coder.py +1 -0
- lionagi/tools/scorer.py +1 -0
- lionagi/tools/validator.py +1 -0
- lionagi/utils/__init__.py +46 -20
- lionagi/utils/api_util.py +86 -0
- lionagi/utils/call_util.py +347 -0
- lionagi/utils/flat_util.py +540 -0
- lionagi/utils/io_util.py +102 -0
- lionagi/utils/load_utils.py +190 -0
- lionagi/utils/sys_util.py +191 -0
- lionagi/utils/tool_util.py +92 -0
- lionagi/utils/type_util.py +81 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/METADATA +37 -13
- lionagi-0.0.113.dist-info/RECORD +84 -0
- lionagi/endpoint/chat_completion.py +0 -20
- lionagi/endpoint/endpoint_utils.py +0 -0
- lionagi/llm_configs.py +0 -21
- lionagi/loader/load_utils.py +0 -161
- lionagi/schema.py +0 -275
- lionagi/service_/__init__.py +0 -6
- lionagi/service_/base_service.py +0 -48
- lionagi/service_/openrouter.py +0 -1
- lionagi/services.py +0 -1
- lionagi/tools/tool_utils.py +0 -75
- lionagi/utils/sys_utils.py +0 -799
- lionagi-0.0.112.dist-info/RECORD +0 -67
- /lionagi/{core/responses.py → datastore/chroma.py} +0 -0
- /lionagi/{endpoint/assistants.py → datastore/deeplake.py} +0 -0
- /lionagi/{endpoint/audio.py → datastore/elasticsearch.py} +0 -0
- /lionagi/{endpoint/embeddings.py → datastore/lantern.py} +0 -0
- /lionagi/{endpoint/files.py → datastore/pinecone.py} +0 -0
- /lionagi/{endpoint/fine_tuning.py → datastore/postgres.py} +0 -0
- /lionagi/{endpoint/images.py → datastore/qdrant.py} +0 -0
- /lionagi/{endpoint/messages.py → schema/base_condition.py} +0 -0
- /lionagi/{service_ → services}/anthropic.py +0 -0
- /lionagi/{service_ → services}/anyscale.py +0 -0
- /lionagi/{service_ → services}/azure.py +0 -0
- /lionagi/{service_ → services}/bedrock.py +0 -0
- /lionagi/{service_ → services}/everlyai.py +0 -0
- /lionagi/{service_ → services}/gemini.py +0 -0
- /lionagi/{service_ → services}/gpt4all.py +0 -0
- /lionagi/{service_ → services}/huggingface.py +0 -0
- /lionagi/{service_ → services}/litellm.py +0 -0
- /lionagi/{service_ → services}/localai.py +0 -0
- /lionagi/{service_ → services}/mistralai.py +0 -0
- /lionagi/{service_ → services}/ollama.py +0 -0
- /lionagi/{service_ → services}/openllm.py +0 -0
- /lionagi/{service_ → services}/perplexity.py +0 -0
- /lionagi/{service_ → services}/predibase.py +0 -0
- /lionagi/{service_ → services}/rungpt.py +0 -0
- /lionagi/{service_ → services}/vllm.py +0 -0
- /lionagi/{service_ → services}/xinference.py +0 -0
- /lionagi/{endpoint → tests}/__init__.py +0 -0
- /lionagi/{endpoint/models.py → tools/planner.py} +0 -0
- /lionagi/{endpoint/moderations.py → tools/prompter.py} +0 -0
- /lionagi/{endpoint/runs.py → tools/sandbox.py} +0 -0
- /lionagi/{endpoint/threads.py → tools/summarizer.py} +0 -0
- {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/LICENSE +0 -0
- {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/WHEEL +0 -0
- {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
lionagi/tools/coder.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
lionagi/tools/scorer.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
lionagi/tools/validator.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
lionagi/utils/__init__.py
CHANGED
@@ -1,23 +1,49 @@
|
|
1
|
-
from .
|
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
|
-
"
|
5
|
-
"
|
6
|
-
|
7
|
-
"
|
8
|
-
"
|
9
|
-
"
|
10
|
-
|
11
|
-
"
|
12
|
-
"
|
13
|
-
|
14
|
-
"
|
15
|
-
"
|
16
|
-
|
17
|
-
"
|
18
|
-
"
|
19
|
-
"
|
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
|
]
|