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,426 +0,0 @@
1
- import unittest
2
-
3
- from lionagi.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()
File without changes
@@ -1,229 +0,0 @@
1
- # this module has no internal dependency
2
- import inspect
3
- import re
4
- import xml.etree.ElementTree as ET
5
- from typing import Optional, Union, Any, Type, Dict
6
-
7
-
8
- def str_to_num(input_: str,
9
- upper_bound: Optional[Union[int, float]] = None,
10
- lower_bound: Optional[Union[int, float]] = None,
11
- num_type: Type[Union[int, float]] = int,
12
- precision: Optional[int] = None) -> Union[int, float]:
13
- """
14
- Converts the first number in the input string to the specified numeric type.
15
-
16
- Parameters:
17
- input_ (str): The input string to extract the number from.
18
-
19
- upper_bound (Optional[Union[int, float]]): The upper bound for the number. Defaults to None.
20
-
21
- lower_bound (Optional[Union[int, float]]): The lower bound for the number. Defaults to None.
22
-
23
- num_type (Type[Union[int, float]]): The type of the number to return (int or float). Defaults to int.
24
-
25
- precision (Optional[int]): The precision for the floating-point number. Defaults to None.
26
-
27
- Returns:
28
- Union[int, float]: The converted number.
29
-
30
- Raises:
31
- ValueError: If no numeric values are found in the string or if there are conversion errors.
32
- """
33
- numbers = re.findall(r'-?\d+\.?\d*', input_)
34
- if not numbers:
35
- raise ValueError(f"No numeric values found in the string: {input_}")
36
-
37
- try:
38
- numbers = numbers[0]
39
- if num_type is int:
40
- numbers = int(float(numbers))
41
- elif num_type is float:
42
- numbers = round(float(numbers), precision) if precision is not None else float(numbers)
43
- else:
44
- raise ValueError(f"Invalid number type: {num_type}")
45
- if upper_bound is not None and numbers > upper_bound:
46
- raise ValueError(f"Number {numbers} is greater than the upper bound of {upper_bound}.")
47
- if lower_bound is not None and numbers < lower_bound:
48
- raise ValueError(f"Number {numbers} is less than the lower bound of {lower_bound}.")
49
- return numbers
50
-
51
- except ValueError as e:
52
- raise ValueError(f"Error converting string to number: {e}")
53
-
54
- def dict_to_xml(data: Dict[str, Any], root_tag: str = 'node') -> str:
55
- """
56
- Helper method to convert a dictionary to an XML string.
57
-
58
- Parameters:
59
- data (Dict[str, Any]): The dictionary to convert to XML.
60
- root_tag (str): The root tag name for the XML.
61
-
62
- Returns:
63
- str: An XML string representation of the dictionary.
64
- """
65
- root = ET.Element(root_tag)
66
- _build_xml(root, data)
67
- return ET.tostring(root, encoding='unicode')
68
-
69
- def _build_xml(element: ET.Element, data: Any):
70
- """Recursively builds XML elements from data."""
71
- if isinstance(data, dict):
72
- for key, value in data.items():
73
- sub_element = ET.SubElement(element, key)
74
- _build_xml(sub_element, value)
75
- elif isinstance(data, list):
76
- for item in data:
77
- item_element = ET.SubElement(element, 'item')
78
- _build_xml(item_element, item)
79
- else:
80
- element.text = str(data)
81
-
82
- def xml_to_dict(element: ET.Element) -> Dict[str, Any]:
83
- """
84
- Helper method to convert an XML element back into a dictionary.
85
- """
86
- dict_data = {}
87
- for child in element:
88
- if child.getchildren():
89
- dict_data[child.tag] = xml_to_dict(child)
90
- else:
91
- dict_data[child.tag] = child.text
92
- return dict_data
93
-
94
- def extract_docstring_details_google(func):
95
- docstring = inspect.getdoc(func)
96
- if not docstring:
97
- return "No description available.", {}
98
- lines = docstring.split('\n')
99
- func_description = lines[0].strip()
100
-
101
- param_start_pos = 0
102
- lines_len = len(lines)
103
-
104
- params_description = {}
105
- for i in range(1, lines_len):
106
- if lines[i].startswith('Args') or lines[i].startswith('Arguments') or lines[i].startswith('Parameters'):
107
- param_start_pos = i + 1
108
- break
109
-
110
- current_param = None
111
- for i in range(param_start_pos, lines_len):
112
- if lines[i] == '':
113
- continue
114
- elif lines[i].startswith(' '):
115
- param_desc = lines[i].split(':', 1)
116
- if len(param_desc) == 1:
117
- params_description[current_param] += ' ' + param_desc[0].strip()
118
- continue
119
- param, desc = param_desc
120
- param = param.split('(')[0].strip()
121
- params_description[param] = desc.strip()
122
- current_param = param
123
- else:
124
- break
125
- return func_description, params_description
126
-
127
- def extract_docstring_details_rest(func):
128
- docstring = inspect.getdoc(func)
129
- if not docstring:
130
- return "No description available.", {}
131
- lines = docstring.split('\n')
132
- func_description = lines[0].strip()
133
-
134
- params_description = {}
135
- current_param = None
136
- for line in lines[1:]:
137
- line = line.strip()
138
- if line.startswith(':param'):
139
- param_desc = line.split(':', 2)
140
- _, param, desc = param_desc
141
- param = param.split()[-1].strip()
142
- params_description[param] = desc.strip()
143
- current_param = param
144
- elif line.startswith(' '):
145
- params_description[current_param] += ' ' + line
146
-
147
- return func_description, params_description
148
-
149
- def extract_docstring_details(func, style='google'):
150
- if style == 'google':
151
- func_description, params_description = extract_docstring_details_google(func)
152
- elif style == 'reST':
153
- func_description, params_description = extract_docstring_details_rest(func)
154
- else:
155
- raise ValueError(f'{style} is not supported. Please choose either "google" or "reST".')
156
- return func_description, params_description
157
-
158
- def python_to_json_type(py_type):
159
- """
160
- Converts a Python type to its JSON type equivalent.
161
-
162
- Parameters:
163
- py_type (str): The name of the Python type.
164
-
165
- Returns:
166
- str: The corresponding JSON type.
167
- """
168
- type_mapping = {
169
- 'str': 'string',
170
- 'int': 'number',
171
- 'float': 'number',
172
- 'list': 'array',
173
- 'tuple': 'array',
174
- 'bool': 'boolean',
175
- 'dict': 'object'
176
- }
177
- return type_mapping.get(py_type, 'object')
178
-
179
- def func_to_schema(func, style='google'):
180
- """
181
- Generates a schema description for a given function, using typing hints and docstrings.
182
- The schema includes the function's name, description, and parameters.
183
-
184
- Parameters:
185
- func (function): The function to generate a schema for.
186
-
187
- style (str): The docstring format.
188
-
189
- Returns:
190
- dict: A schema describing the function.
191
- """
192
- # Extracting function name and docstring details
193
- func_name = func.__name__
194
- func_description, params_description = extract_docstring_details(func, style)
195
-
196
- # Extracting parameters with typing hints
197
- sig = inspect.signature(func)
198
- parameters = {
199
- "type": "object",
200
- "properties": {},
201
- "required": [],
202
- }
203
-
204
- for name, param in sig.parameters.items():
205
- # Default type to string and update if type hint is available
206
- param_type = "string"
207
- if param.annotation is not inspect.Parameter.empty:
208
- param_type = python_to_json_type(param.annotation.__name__)
209
-
210
- # Extract parameter description from docstring, if available
211
- param_description = params_description.get(name, "No description available.")
212
-
213
- # Assuming all parameters are required for simplicity
214
- parameters["required"].append(name)
215
- parameters["properties"][name] = {
216
- "type": param_type,
217
- "description": param_description,
218
- }
219
-
220
- # Constructing the schema
221
- schema = {
222
- "type": "function",
223
- "function": {
224
- "name": func_name,
225
- "description": func_description,
226
- "parameters": parameters,
227
- }
228
- }
229
- return schema