lionagi 0.0.306__py3-none-any.whl → 0.0.307__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. lionagi/__init__.py +2 -5
  2. lionagi/core/__init__.py +7 -5
  3. lionagi/core/agent/__init__.py +3 -0
  4. lionagi/core/agent/base_agent.py +10 -12
  5. lionagi/core/branch/__init__.py +4 -0
  6. lionagi/core/branch/base_branch.py +81 -81
  7. lionagi/core/branch/branch.py +16 -28
  8. lionagi/core/branch/branch_flow_mixin.py +3 -7
  9. lionagi/core/branch/executable_branch.py +86 -56
  10. lionagi/core/branch/util.py +77 -162
  11. lionagi/core/{flow/direct → direct}/__init__.py +1 -1
  12. lionagi/core/{flow/direct/predict.py → direct/parallel_predict.py} +39 -17
  13. lionagi/core/direct/parallel_react.py +0 -0
  14. lionagi/core/direct/parallel_score.py +0 -0
  15. lionagi/core/direct/parallel_select.py +0 -0
  16. lionagi/core/direct/parallel_sentiment.py +0 -0
  17. lionagi/core/direct/predict.py +174 -0
  18. lionagi/core/{flow/direct → direct}/react.py +2 -2
  19. lionagi/core/{flow/direct → direct}/score.py +28 -23
  20. lionagi/core/{flow/direct → direct}/select.py +48 -45
  21. lionagi/core/direct/utils.py +83 -0
  22. lionagi/core/flow/monoflow/ReAct.py +6 -5
  23. lionagi/core/flow/monoflow/__init__.py +9 -0
  24. lionagi/core/flow/monoflow/chat.py +10 -10
  25. lionagi/core/flow/monoflow/chat_mixin.py +11 -10
  26. lionagi/core/flow/monoflow/followup.py +6 -5
  27. lionagi/core/flow/polyflow/__init__.py +1 -0
  28. lionagi/core/flow/polyflow/chat.py +15 -3
  29. lionagi/core/mail/mail_manager.py +18 -19
  30. lionagi/core/mail/schema.py +5 -4
  31. lionagi/core/messages/schema.py +18 -20
  32. lionagi/core/prompt/__init__.py +0 -0
  33. lionagi/core/prompt/prompt_template.py +0 -0
  34. lionagi/core/schema/__init__.py +2 -2
  35. lionagi/core/schema/action_node.py +11 -3
  36. lionagi/core/schema/base_mixin.py +56 -59
  37. lionagi/core/schema/base_node.py +35 -38
  38. lionagi/core/schema/condition.py +24 -0
  39. lionagi/core/schema/data_logger.py +96 -99
  40. lionagi/core/schema/data_node.py +19 -19
  41. lionagi/core/schema/prompt_template.py +0 -0
  42. lionagi/core/schema/structure.py +171 -169
  43. lionagi/core/session/__init__.py +1 -3
  44. lionagi/core/session/session.py +196 -214
  45. lionagi/core/tool/tool_manager.py +95 -103
  46. lionagi/integrations/__init__.py +1 -3
  47. lionagi/integrations/bridge/langchain_/documents.py +17 -18
  48. lionagi/integrations/bridge/langchain_/langchain_bridge.py +14 -14
  49. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +22 -22
  50. lionagi/integrations/bridge/llamaindex_/node_parser.py +12 -12
  51. lionagi/integrations/bridge/llamaindex_/reader.py +11 -11
  52. lionagi/integrations/bridge/llamaindex_/textnode.py +7 -7
  53. lionagi/integrations/config/openrouter_configs.py +0 -1
  54. lionagi/integrations/provider/oai.py +26 -26
  55. lionagi/integrations/provider/services.py +38 -38
  56. lionagi/libs/__init__.py +34 -1
  57. lionagi/libs/ln_api.py +211 -221
  58. lionagi/libs/ln_async.py +53 -60
  59. lionagi/libs/ln_convert.py +118 -120
  60. lionagi/libs/ln_dataframe.py +32 -33
  61. lionagi/libs/ln_func_call.py +334 -342
  62. lionagi/libs/ln_nested.py +99 -107
  63. lionagi/libs/ln_parse.py +161 -165
  64. lionagi/libs/sys_util.py +52 -52
  65. lionagi/tests/test_core/test_session.py +254 -266
  66. lionagi/tests/test_core/test_session_base_util.py +299 -300
  67. lionagi/tests/test_core/test_tool_manager.py +70 -74
  68. lionagi/tests/test_libs/test_nested.py +2 -7
  69. lionagi/tests/test_libs/test_parse.py +2 -2
  70. lionagi/version.py +1 -1
  71. {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/METADATA +4 -2
  72. lionagi-0.0.307.dist-info/RECORD +115 -0
  73. lionagi/core/flow/direct/utils.py +0 -43
  74. lionagi-0.0.306.dist-info/RECORD +0 -106
  75. /lionagi/core/{flow/direct → direct}/sentiment.py +0 -0
  76. {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/LICENSE +0 -0
  77. {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/WHEEL +0 -0
  78. {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/top_level.txt +0 -0
lionagi/libs/ln_async.py CHANGED
@@ -11,6 +11,7 @@ import aiohttp
11
11
 
12
12
  class AsyncUtil:
13
13
 
14
+ @staticmethod
14
15
  async def _call_handler(
15
16
  func: Callable, *args, error_map: dict[type, Callable] = None, **kwargs
16
17
  ) -> Any:
@@ -19,43 +20,42 @@ class AsyncUtil:
19
20
  functions.
20
21
 
21
22
  Args:
22
- func (Callable):
23
- The function to call.
24
- *args:
25
- Positional arguments to pass to the function.
26
- error_map (Dict[type, Callable], optional):
27
- A dictionary mapping error types to handler functions.
28
- **kwargs:
29
- Keyword arguments to pass to the function.
23
+ func (Callable):
24
+ The function to call.
25
+ *args:
26
+ Positional arguments to pass to the function.
27
+ error_map (Dict[type, Callable], optional):
28
+ A dictionary mapping error types to handler functions.
29
+ **kwargs:
30
+ Keyword arguments to pass to the function.
30
31
 
31
32
  Returns:
32
- Any: The result of the function call.
33
+ Any: The result of the function call.
33
34
 
34
35
  Raises:
35
- Exception: Propagates any exceptions not handled by the error_map.
36
+ Exception: Propagates any exceptions not handled by the error_map.
36
37
 
37
38
  examples:
38
- >>> async def async_add(x, y): return x + y
39
- >>> asyncio.run(_call_handler(async_add, 1, 2))
40
- 3
39
+ >>> async def async_add(x, y): return x + y
40
+ >>> asyncio.run(_call_handler(async_add, 1, 2))
41
+ 3
41
42
  """
42
43
  try:
43
- if AsyncUtil.is_coroutine_func(func):
44
- # Checking for a running event loop
45
- try:
46
- loop = asyncio.get_running_loop()
47
- except RuntimeError: # No running event loop
48
- loop = asyncio.new_event_loop()
49
- result = loop.run_until_complete(func(*args, **kwargs))
44
+ if not AsyncUtil.is_coroutine_func(func):
45
+ return func(*args, **kwargs)
50
46
 
51
- loop.close()
52
- return result
47
+ # Checking for a running event loop
48
+ try:
49
+ loop = asyncio.get_running_loop()
50
+ except RuntimeError: # No running event loop
51
+ loop = asyncio.new_event_loop()
52
+ result = loop.run_until_complete(func(*args, **kwargs))
53
53
 
54
- if loop.is_running():
55
- return await func(*args, **kwargs)
54
+ loop.close()
55
+ return result
56
56
 
57
- else:
58
- return func(*args, **kwargs)
57
+ if loop.is_running():
58
+ return await func(*args, **kwargs)
59
59
 
60
60
  except Exception as e:
61
61
  if error_map:
@@ -71,10 +71,10 @@ class AsyncUtil:
71
71
  Checks whether a function is an asyncio coroutine function.
72
72
 
73
73
  Args:
74
- func: The function to check.
74
+ func: The function to check.
75
75
 
76
76
  Returns:
77
- True if the function is a coroutine function, False otherwise.
77
+ True if the function is a coroutine function, False otherwise.
78
78
  """
79
79
  return asyncio.iscoroutinefunction(func)
80
80
 
@@ -87,18 +87,17 @@ class AsyncUtil:
87
87
  handle errors based on a given error mapping.
88
88
 
89
89
  Args:
90
- error (Exception):
91
- The error to handle.
92
- error_map (Mapping[type, Callable]):
93
- A dictionary mapping error types to handler functions.
90
+ error (Exception):
91
+ The error to handle.
92
+ error_map (Mapping[type, Callable]):
93
+ A dictionary mapping error types to handler functions.
94
94
 
95
95
  examples:
96
- >>> def handle_value_error(e): print("ValueError occurred")
97
- >>> custom_error_handler(ValueError(), {ValueError: handle_value_error})
98
- ValueError occurred
96
+ >>> def handle_value_error(e): print("ValueError occurred")
97
+ >>> custom_error_handler(ValueError(), {ValueError: handle_value_error})
98
+ ValueError occurred
99
99
  """
100
- handler = error_map.get(type(error))
101
- if handler:
100
+ if handler := error_map.get(type(error)):
102
101
  handler(error)
103
102
  else:
104
103
  logging.error(f"Unhandled error: {error}")
@@ -111,30 +110,28 @@ class AsyncUtil:
111
110
  Executes a function, automatically handling synchronous and asynchronous functions.
112
111
 
113
112
  Args:
114
- func: The function to execute.
115
- *args: Positional arguments for the function.
116
- **kwargs: Keyword arguments for the function.
113
+ func: The function to execute.
114
+ *args: Positional arguments for the function.
115
+ **kwargs: Keyword arguments for the function.
117
116
 
118
117
  Returns:
119
- The result of the function execution.
118
+ The result of the function execution.
120
119
  """
121
120
 
122
121
  try:
123
- if AsyncUtil.is_coroutine_func(func):
124
-
125
- try:
126
- loop = asyncio.get_event_loop()
127
-
128
- if loop.is_running():
129
- return await func(*args, **kwargs)
130
- else:
131
- return await asyncio.run(func(*args, **kwargs))
122
+ if not AsyncUtil.is_coroutine_func(func):
123
+ return func(*args, **kwargs)
132
124
 
133
- except RuntimeError:
134
- return asyncio.run(func(*args, **kwargs))
125
+ try:
126
+ loop = asyncio.get_event_loop()
135
127
 
136
- else:
137
- return func(*args, **kwargs)
128
+ return (
129
+ await func(*args, **kwargs)
130
+ if loop.is_running()
131
+ else await asyncio.run(func(*args, **kwargs))
132
+ )
133
+ except RuntimeError:
134
+ return asyncio.run(func(*args, **kwargs))
138
135
 
139
136
  except Exception as e:
140
137
  if error_map:
@@ -205,10 +202,6 @@ class AsyncUtil:
205
202
  def create_lock(*args, **kwargs):
206
203
  return asyncio.Lock(*args, **kwargs)
207
204
 
208
- # @classmethod
209
- # def HttpClientSession(cls):
210
- # return aiohttp.ClientSession
205
+ # @classmethod # def HttpClientSession(cls): # return aiohttp.ClientSession
211
206
 
212
- # @classmethod
213
- # def HttpClientError(cls):
214
- # return aiohttp.ClientError
207
+ # @classmethod # def HttpClientError(cls): # return aiohttp.ClientError
@@ -23,29 +23,28 @@ def to_list(input_, /, *, flatten: bool = True, dropna: bool = True) -> list[Any
23
23
  Specialized implementations may use additional keyword arguments specific to their conversion logic.
24
24
 
25
25
  Args:
26
- input_ (Any): The input object to convert to a list.
27
- flatten (bool): If True, and the input is a nested list, the function will attempt to flatten it.
28
- dropna (bool): If True, None values will be removed from the resulting list.
26
+ input_ (Any): The input object to convert to a list.
27
+ flatten (bool): If True, and the input is a nested list, the function will attempt to flatten it.
28
+ dropna (bool): If True, None values will be removed from the resulting list.
29
29
 
30
30
  Returns:
31
- list[Any]: A list representation of the input, with modifications based on `flatten` and `dropna`.
31
+ list[Any]: A list representation of the input, with modifications based on `flatten` and `dropna`.
32
32
 
33
33
  Raises:
34
- ValueError: If the input type is unsupported or cannot be converted to a list.
34
+ ValueError: If the input type is unsupported or cannot be converted to a list.
35
35
 
36
36
  Note:
37
- - This function uses `@singledispatch` to handle different input types via overloading.
38
- - The default behavior for dictionaries is to wrap them in a list without flattening.
39
- - For specific behaviors with lists, tuples, sets, and other types, see the registered implementations.
37
+ - This function uses `@singledispatch` to handle different input types via overloading.
38
+ - The default behavior for dictionaries is to wrap them in a list without flattening.
39
+ - For specific behaviors with lists, tuples, sets, and other types, see the registered implementations.
40
40
  """
41
41
  try:
42
- if isinstance(input_, Iterable) and not isinstance(
42
+ if not isinstance(input_, Iterable) or isinstance(
43
43
  input_, (str, bytes, bytearray, dict)
44
44
  ):
45
- iterable_list = list(input_)
46
- return _flatten_list(iterable_list, dropna) if flatten else iterable_list
47
- else:
48
45
  return [input_]
46
+ iterable_list = list(input_)
47
+ return _flatten_list(iterable_list, dropna) if flatten else iterable_list
49
48
  except Exception as e:
50
49
  raise ValueError(f"Could not convert {type(input_)} object to list: {e}") from e
51
50
 
@@ -76,19 +75,19 @@ def to_dict(input_, /, *args, **kwargs) -> dict[Any, Any]:
76
75
  and Pydantic's BaseModel, utilizing the single dispatch mechanism for type-specific conversions.
77
76
 
78
77
  Args:
79
- input_ (Any): The input object to convert to a dictionary.
80
- *args: Variable length argument list for additional options in type-specific handlers.
81
- **kwargs: Arbitrary keyword arguments for additional options in type-specific handlers.
78
+ input_ (Any): The input object to convert to a dictionary.
79
+ *args: Variable length argument list for additional options in type-specific handlers.
80
+ **kwargs: Arbitrary keyword arguments for additional options in type-specific handlers.
82
81
 
83
82
  Returns:
84
- dict[Any, Any]: A dictionary representation of the input object.
83
+ dict[Any, Any]: A dictionary representation of the input object.
85
84
 
86
85
  Raises:
87
- ValueError: If the input type is not supported or cannot be converted to a dictionary.
86
+ ValueError: If the input type is not supported or cannot be converted to a dictionary.
88
87
 
89
88
  Note:
90
- - For specific behaviors with dict, str, pandas.Series, pandas.DataFrame, and BaseModel,
91
- see the registered implementations.
89
+ - For specific behaviors with dict, str, pandas.Series, pandas.DataFrame, and BaseModel,
90
+ see the registered implementations.
92
91
  """
93
92
  try:
94
93
  return dict(input_, *args, **kwargs)
@@ -104,10 +103,10 @@ def _(input_) -> dict[Any, Any]:
104
103
  Handles dictionary inputs directly, returning the input without modification.
105
104
 
106
105
  Args:
107
- input_ (dict[Any, Any]): The dictionary to be returned.
106
+ input_ (dict[Any, Any]): The dictionary to be returned.
108
107
 
109
108
  Returns:
110
- dict[Any, Any]: The input dictionary, unchanged.
109
+ dict[Any, Any]: The input dictionary, unchanged.
111
110
  """
112
111
  return input_
113
112
 
@@ -118,15 +117,15 @@ def _(input_, /, *args, **kwargs) -> dict[Any, Any]:
118
117
  Converts a JSON-formatted string to a dictionary.
119
118
 
120
119
  Args:
121
- input_ (str): The JSON string to convert.
122
- *args: Variable length argument list for json.loads().
123
- **kwargs: Arbitrary keyword arguments for json.loads().
120
+ input_ (str): The JSON string to convert.
121
+ *args: Variable length argument list for json.loads().
122
+ **kwargs: Arbitrary keyword arguments for json.loads().
124
123
 
125
124
  Returns:
126
- dict[Any, Any]: The dictionary representation of the JSON string.
125
+ dict[Any, Any]: The dictionary representation of the JSON string.
127
126
 
128
127
  Raises:
129
- ValueError: If the string cannot be decoded into a dictionary.
128
+ ValueError: If the string cannot be decoded into a dictionary.
130
129
  """
131
130
  try:
132
131
  return json.loads(input_, *args, **kwargs)
@@ -140,12 +139,12 @@ def _(input_, /, *args, **kwargs) -> dict[Any, Any]:
140
139
  Converts a pandas Series to a dictionary.
141
140
 
142
141
  Args:
143
- input_ (pd.Series): The pandas Series to convert.
144
- *args: Variable length argument list for Series.to_dict().
145
- **kwargs: Arbitrary keyword arguments for Series.to_dict().
142
+ input_ (pd.Series): The pandas Series to convert.
143
+ *args: Variable length argument list for Series.to_dict().
144
+ **kwargs: Arbitrary keyword arguments for Series.to_dict().
146
145
 
147
146
  Returns:
148
- dict[Any, Any]: The dictionary representation of the pandas Series.
147
+ dict[Any, Any]: The dictionary representation of the pandas Series.
149
148
  """
150
149
  return input_.to_dict(*args, **kwargs)
151
150
 
@@ -158,15 +157,15 @@ def _(
158
157
  Converts a pandas DataFrame to a dictionary or a list of dictionaries, based on the `orient` and `as_list` parameters.
159
158
 
160
159
  Args:
161
- input_ (pd.DataFrame): The pandas DataFrame to convert.
162
- *args: Variable length argument list for DataFrame.to_dict() or DataFrame.iterrows().
163
- orient (str): The orientation of the data. Default is 'list'.
164
- as_list (bool): If True, returns a list of dictionaries, one for each row. Default is False.
165
- **kwargs: Arbitrary keyword arguments for DataFrame.to_dict().
160
+ input_ (pd.DataFrame): The pandas DataFrame to convert.
161
+ *args: Variable length argument list for DataFrame.to_dict() or DataFrame.iterrows().
162
+ orient (str): The orientation of the data. Default is 'list'.
163
+ as_list (bool): If True, returns a list of dictionaries, one for each row. Default is False.
164
+ **kwargs: Arbitrary keyword arguments for DataFrame.to_dict().
166
165
 
167
166
  Returns:
168
- dict[Any, Any] | list[dict[Any, Any]]: Depending on `as_list`, either a dictionary representation
169
- of the DataFrame or a list of dictionaries, one for each row.
167
+ dict[Any, Any] | list[dict[Any, Any]]: Depending on `as_list`, either a dictionary representation
168
+ of the DataFrame or a list of dictionaries, one for each row.
170
169
  """
171
170
  if as_list:
172
171
  return [row.to_dict(*args, **kwargs) for _, row in input_.iterrows()]
@@ -179,12 +178,12 @@ def _(input_, /, *args, **kwargs) -> dict[Any, Any]:
179
178
  Converts a Pydantic BaseModel instance to a dictionary.
180
179
 
181
180
  Args:
182
- input_ (BaseModel): The Pydantic BaseModel instance to convert.
183
- *args: Variable length argument list for the model's dict() method.
184
- **kwargs: Arbitrary keyword arguments for the model's dict() method.
181
+ input_ (BaseModel): The Pydantic BaseModel instance to convert.
182
+ *args: Variable length argument list for the model's dict() method.
183
+ **kwargs: Arbitrary keyword arguments for the model's dict() method.
185
184
 
186
185
  Returns:
187
- dict[Any, Any]: The dictionary representation of the BaseModel instance.
186
+ dict[Any, Any]: The dictionary representation of the BaseModel instance.
188
187
  """
189
188
  return input_.model_dump(*args, **kwargs)
190
189
 
@@ -198,17 +197,17 @@ def to_str(input_) -> str:
198
197
  providing type-specific conversions to string format.
199
198
 
200
199
  Args:
201
- input_ (Any): The input object to convert to a string.
202
- *args: Variable length argument list for additional options in type-specific handlers.
203
- **kwargs: Arbitrary keyword arguments for additional options in type-specific handlers.
200
+ input_ (Any): The input object to convert to a string.
201
+ *args: Variable length argument list for additional options in type-specific handlers.
202
+ **kwargs: Arbitrary keyword arguments for additional options in type-specific handlers.
204
203
 
205
204
  Returns:
206
- str: A string representation of the input object.
205
+ str: A string representation of the input object.
207
206
 
208
207
  Note:
209
- - The base implementation simply uses the str() function for conversion.
210
- - For detailed behaviors with dict, str, list, pandas.Series, and pandas.DataFrame,
211
- refer to the registered implementations.
208
+ - The base implementation simply uses the str() function for conversion.
209
+ - For detailed behaviors with dict, str, list, pandas.Series, and pandas.DataFrame,
210
+ refer to the registered implementations.
212
211
  """
213
212
  return str(input_)
214
213
 
@@ -219,12 +218,12 @@ def _(input_, /, *args, **kwargs) -> str:
219
218
  Converts a dictionary to a JSON-formatted string.
220
219
 
221
220
  Args:
222
- input_ (dict): The dictionary to convert.
223
- *args: Variable length argument list for json.dumps().
224
- **kwargs: Arbitrary keyword arguments for json.dumps().
221
+ input_ (dict): The dictionary to convert.
222
+ *args: Variable length argument list for json.dumps().
223
+ **kwargs: Arbitrary keyword arguments for json.dumps().
225
224
 
226
225
  Returns:
227
- str: The JSON string representation of the dictionary.
226
+ str: The JSON string representation of the dictionary.
228
227
  """
229
228
  return json.dumps(input_, *args, **kwargs)
230
229
 
@@ -235,12 +234,12 @@ def _(input_) -> str:
235
234
  Returns the input string unchanged.
236
235
 
237
236
  Args:
238
- input_ (str): The input string.
239
- *args: Ignored.
240
- **kwargs: Ignored.
237
+ input_ (str): The input string.
238
+ *args: Ignored.
239
+ **kwargs: Ignored.
241
240
 
242
241
  Returns:
243
- str: The input string, unchanged.
242
+ str: The input string, unchanged.
244
243
  """
245
244
  return input_
246
245
 
@@ -252,15 +251,15 @@ def _(input_, /, *args, as_list: bool = False, **kwargs) -> str | list[str]:
252
251
  of the list itself or join the string representations of its elements.
253
252
 
254
253
  Args:
255
- input_ (list): The list to convert.
256
- *args: Variable length argument list for additional options in element conversion.
257
- as_list (bool): If True, returns the string representation of the list. If False,
258
- returns the elements joined by a comma. Default is False.
259
- **kwargs: Arbitrary keyword arguments for additional options in element conversion.
254
+ input_ (list): The list to convert.
255
+ *args: Variable length argument list for additional options in element conversion.
256
+ as_list (bool): If True, returns the string representation of the list. If False,
257
+ returns the elements joined by a comma. Default is False.
258
+ **kwargs: Arbitrary keyword arguments for additional options in element conversion.
260
259
 
261
260
  Returns:
262
- str: Depending on `as_list`, either the string representation of the list or a string
263
- of the elements joined by a comma.
261
+ str: Depending on `as_list`, either the string representation of the list or a string
262
+ of the elements joined by a comma.
264
263
  """
265
264
  lst_ = [to_str(item, *args, **kwargs) for item in input_]
266
265
  return lst_ if as_list else ", ".join(lst_)
@@ -272,12 +271,12 @@ def _(input_, /, *args, **kwargs) -> str:
272
271
  Converts a pandas Series to a JSON-formatted string.
273
272
 
274
273
  Args:
275
- input_ (pd.Series): The pandas Series to convert.
276
- *args: Variable length argument list for Series.to_json().
277
- **kwargs: Arbitrary keyword arguments for Series.to_json().
274
+ input_ (pd.Series): The pandas Series to convert.
275
+ *args: Variable length argument list for Series.to_json().
276
+ **kwargs: Arbitrary keyword arguments for Series.to_json().
278
277
 
279
278
  Returns:
280
- str: The JSON string representation of the pandas Series.
279
+ str: The JSON string representation of the pandas Series.
281
280
  """
282
281
  return input_.to_json(*args, **kwargs)
283
282
 
@@ -289,15 +288,15 @@ def _(input_, /, *args, as_list: bool = False, **kwargs) -> str | list[str]:
289
288
  first if `as_list` is True, then to a string representation of that list.
290
289
 
291
290
  Args:
292
- input_ (pd.DataFrame): The pandas DataFrame to convert.
293
- *args: Variable length argument list for additional options in conversion.
294
- as_list (bool): If True, converts the DataFrame to a list of dictionaries before converting
295
- to a string. Default is False.
296
- **kwargs: Arbitrary keyword arguments for DataFrame.to_json() or to_dict().
291
+ input_ (pd.DataFrame): The pandas DataFrame to convert.
292
+ *args: Variable length argument list for additional options in conversion.
293
+ as_list (bool): If True, converts the DataFrame to a list of dictionaries before converting
294
+ to a string. Default is False.
295
+ **kwargs: Arbitrary keyword arguments for DataFrame.to_json() or to_dict().
297
296
 
298
297
  Returns:
299
- str: Depending on `as_list`, either a JSON string representation of the DataFrame or a string
300
- representation of a list of dictionaries derived from the DataFrame.
298
+ str: Depending on `as_list`, either a JSON string representation of the DataFrame or a string
299
+ representation of a list of dictionaries derived from the DataFrame.
301
300
  """
302
301
  if as_list:
303
302
  return to_dict(input_, as_list=True, *args, **kwargs)
@@ -324,20 +323,20 @@ def to_df(
324
323
  The base implementation attempts to directly convert the input to a DataFrame, applying dropna and reset_index as specified.
325
324
 
326
325
  Args:
327
- input_ (Any): The input data to convert into a DataFrame. Accepts a wide range of types thanks to overloads.
328
- how (str): Specifies how missing values are dropped. Passed directly to DataFrame.dropna().
329
- drop_kwargs (dict[str, Any] | None): Additional keyword arguments for DataFrame.dropna().
330
- reset_index (bool): If True, the DataFrame index will be reset, removing the index labels.
331
- **kwargs: Additional keyword arguments passed to the pandas DataFrame constructor.
326
+ input_ (Any): The input data to convert into a DataFrame. Accepts a wide range of types thanks to overloads.
327
+ how (str): Specifies how missing values are dropped. Passed directly to DataFrame.dropna().
328
+ drop_kwargs (dict[str, Any] | None): Additional keyword arguments for DataFrame.dropna().
329
+ reset_index (bool): If True, the DataFrame index will be reset, removing the index labels.
330
+ **kwargs: Additional keyword arguments passed to the pandas DataFrame constructor.
332
331
 
333
332
  Returns:
334
- pd.DataFrame: A pandas DataFrame constructed from the input data.
333
+ pd.DataFrame: A pandas DataFrame constructed from the input data.
335
334
 
336
335
  Raises:
337
- ValueError: If there is an error during the conversion process.
336
+ ValueError: If there is an error during the conversion process.
338
337
 
339
338
  Note:
340
- - This function is overloaded to provide specialized behavior for different input types, enhancing its flexibility.
339
+ - This function is overloaded to provide specialized behavior for different input types, enhancing its flexibility.
341
340
  """
342
341
 
343
342
  if drop_kwargs is None:
@@ -345,7 +344,7 @@ def to_df(
345
344
 
346
345
  try:
347
346
  dfs = pd.DataFrame(input_, **kwargs)
348
- dfs.dropna(**(drop_kwargs | {"how": how}), inplace=True)
347
+ dfs = dfs.dropna(**(drop_kwargs | {"how": how}))
349
348
  return dfs.reset_index(drop=True) if reset_index else dfs
350
349
 
351
350
  except Exception as e:
@@ -367,7 +366,7 @@ def _(
367
366
  drop_kwargs = {}
368
367
  try:
369
368
  dfs = pd.DataFrame(input_, **kwargs)
370
- dfs.dropna(**(drop_kwargs | {"how": how}), inplace=True)
369
+ dfs = dfs.dropna(**(drop_kwargs | {"how": how}))
371
370
  return dfs.reset_index(drop=True) if reset_index else dfs
372
371
  except Exception as e:
373
372
  raise ValueError(f"Error converting input_ to DataFrame: {e}") from e
@@ -387,7 +386,9 @@ def _(
387
386
  dfs = pd.concat([dfs, i], **kwargs)
388
387
 
389
388
  except Exception as e2:
390
- raise ValueError(f"Error converting input_ to DataFrame: {e1}, {e2}")
389
+ raise ValueError(
390
+ f"Error converting input_ to DataFrame: {e1}, {e2}"
391
+ ) from e2
391
392
 
392
393
  dfs.dropna(**(drop_kwargs | {"how": how}), inplace=True)
393
394
  return dfs.reset_index(drop=True) if reset_index else dfs
@@ -406,17 +407,17 @@ def to_num(
406
407
  Converts the input to a numeric value of specified type, with optional bounds and precision.
407
408
 
408
409
  Args:
409
- input_ (Any): The input value to convert. Can be of any type that `to_str` can handle.
410
- upper_bound (float | None): The upper bound for the numeric value. If specified, values above this bound will raise an error.
411
- lower_bound (float | None): The lower bound for the numeric value. If specified, values below this bound will raise an error.
412
- num_type (Type[int | float]): The numeric type to convert to. Can be `int` or `float`.
413
- precision (int | None): The number of decimal places for the result. Applies only to `float` type.
410
+ input_ (Any): The input value to convert. Can be of any type that `to_str` can handle.
411
+ upper_bound (float | None): The upper bound for the numeric value. If specified, values above this bound will raise an error.
412
+ lower_bound (float | None): The lower bound for the numeric value. If specified, values below this bound will raise an error.
413
+ num_type (Type[int | float]): The numeric type to convert to. Can be `int` or `float`.
414
+ precision (int | None): The number of decimal places for the result. Applies only to `float` type.
414
415
 
415
416
  Returns:
416
- int | float: The converted numeric value, adhering to specified type and precision.
417
+ int | float: The converted numeric value, adhering to specified type and precision.
417
418
 
418
419
  Raises:
419
- ValueError: If the input cannot be converted to a number, or if it violates the specified bounds.
420
+ ValueError: If the input cannot be converted to a number, or if it violates the specified bounds.
420
421
  """
421
422
  str_ = to_str(input_)
422
423
  return _str_to_num(str_, upper_bound, lower_bound, num_type, precision)
@@ -427,10 +428,10 @@ def to_readable_dict(input_: Any | list[Any]) -> str | list[Any]:
427
428
  Converts a given input to a readable dictionary format, either as a string or a list of dictionaries.
428
429
 
429
430
  Args:
430
- input_ (Any | list[Any]): The input to convert to a readable dictionary format.
431
+ input_ (Any | list[Any]): The input to convert to a readable dictionary format.
431
432
 
432
433
  Returns:
433
- str | list[str]: The readable dictionary format of the input.
434
+ str | list[str]: The readable dictionary format of the input.
434
435
  """
435
436
 
436
437
  try:
@@ -445,11 +446,11 @@ def is_same_dtype(input_: list | dict, dtype: Type | None = None) -> bool:
445
446
  Checks if all elements in a list or dictionary values are of the same data type.
446
447
 
447
448
  Args:
448
- input_ (list | dict): The input list or dictionary to check.
449
- dtype (Type | None): The data type to check against. If None, uses the type of the first element.
449
+ input_ (list | dict): The input list or dictionary to check.
450
+ dtype (Type | None): The data type to check against. If None, uses the type of the first element.
450
451
 
451
452
  Returns:
452
- bool: True if all elements are of the same type (or if the input is empty), False otherwise.
453
+ bool: True if all elements are of the same type (or if the input is empty), False otherwise.
453
454
  """
454
455
  if not input_:
455
456
  return True
@@ -486,13 +487,13 @@ def strip_lower(input_: Any) -> str:
486
487
  Converts the input to a lowercase string with leading and trailing whitespace removed.
487
488
 
488
489
  Args:
489
- input_ (Any): The input value to convert and process.
490
+ input_ (Any): The input value to convert and process.
490
491
 
491
492
  Returns:
492
- str: The processed string.
493
+ str: The processed string.
493
494
 
494
495
  Raises:
495
- ValueError: If the input cannot be converted to a string.
496
+ ValueError: If the input cannot be converted to a string.
496
497
  """
497
498
  try:
498
499
  return str(input_).strip().lower()
@@ -517,11 +518,11 @@ def is_structure_homogeneous(
517
518
  either list | dict, or None).
518
519
 
519
520
  examples:
520
- >>> _is_structure_homogeneous({'a': {'b': 1}, 'c': {'d': 2}})
521
- True
521
+ >>> _is_structure_homogeneous({'a': {'b': 1}, 'c': {'d': 2}})
522
+ True
522
523
 
523
- >>> _is_structure_homogeneous({'a': {'b': 1}, 'c': [1, 2]})
524
- False
524
+ >>> _is_structure_homogeneous({'a': {'b': 1}, 'c': [1, 2]})
525
+ False
525
526
  """
526
527
 
527
528
  # noinspection PyShadowingNames
@@ -550,10 +551,7 @@ def is_structure_homogeneous(
550
551
  return True, structure_type
551
552
 
552
553
  is_, structure_type = _check_structure(structure)
553
- if return_structure_type:
554
- return is_, structure_type
555
- else:
556
- return is_
554
+ return (is_, structure_type) if return_structure_type else is_
557
555
 
558
556
 
559
557
  def is_homogeneous(iterables: list[Any] | dict[Any, Any], type_check: type) -> bool:
@@ -614,17 +612,17 @@ def _flatten_list(lst_: list[Any], dropna: bool = True) -> list[Any]:
614
612
  flatten a nested list, optionally removing None values.
615
613
 
616
614
  Args:
617
- lst_ (list[Any]): A nested list to flatten.
618
- dropna (bool): If True, None values are removed. default is True.
615
+ lst_ (list[Any]): A nested list to flatten.
616
+ dropna (bool): If True, None values are removed. default is True.
619
617
 
620
618
  Returns:
621
- list[Any]: A flattened list.
619
+ list[Any]: A flattened list.
622
620
 
623
621
  examples:
624
- >>> flatten_list([[1, 2], [3, None]], dropna=True)
625
- [1, 2, 3]
626
- >>> flatten_list([[1, [2, None]], 3], dropna=False)
627
- [1, 2, None, 3]
622
+ >>> flatten_list([[1, 2], [3, None]], dropna=True)
623
+ [1, 2, 3]
624
+ >>> flatten_list([[1, [2, None]], 3], dropna=False)
625
+ [1, 2, None, 3]
628
626
  """
629
627
  flattened_list = list(_flatten_list_generator(lst_, dropna))
630
628
  return list(_dropna_iterator(flattened_list)) if dropna else flattened_list
@@ -637,15 +635,15 @@ def _flatten_list_generator(
637
635
  Generator for flattening a nested list.
638
636
 
639
637
  Args:
640
- lst_ (list[Any]): A nested list to flatten.
641
- dropna (bool): If True, None values are omitted. Default is True.
638
+ lst_ (list[Any]): A nested list to flatten.
639
+ dropna (bool): If True, None values are omitted. Default is True.
642
640
 
643
641
  Yields:
644
- Generator[Any, None, None]: A generator yielding flattened elements.
642
+ Generator[Any, None, None]: A generator yielding flattened elements.
645
643
 
646
644
  Examples:
647
- >>> list(_flatten_list_generator([[1, [2, None]], 3], dropna=False))
648
- [1, 2, None, 3]
645
+ >>> list(_flatten_list_generator([[1, [2, None]], 3], dropna=False))
646
+ [1, 2, None, 3]
649
647
  """
650
648
  for i in lst_:
651
649
  if isinstance(i, list):