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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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):