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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. lionagi/__init__.py +3 -3
  2. lionagi/bridge/__init__.py +7 -0
  3. lionagi/bridge/langchain.py +131 -0
  4. lionagi/bridge/llama_index.py +157 -0
  5. lionagi/configs/__init__.py +7 -0
  6. lionagi/configs/oai_configs.py +49 -0
  7. lionagi/configs/openrouter_config.py +49 -0
  8. lionagi/core/__init__.py +8 -2
  9. lionagi/core/instruction_sets.py +1 -3
  10. lionagi/core/messages.py +2 -2
  11. lionagi/core/sessions.py +174 -27
  12. lionagi/datastore/__init__.py +1 -0
  13. lionagi/loader/__init__.py +9 -4
  14. lionagi/loader/chunker.py +157 -0
  15. lionagi/loader/reader.py +124 -0
  16. lionagi/objs/__init__.py +7 -0
  17. lionagi/objs/messenger.py +163 -0
  18. lionagi/objs/tool_registry.py +247 -0
  19. lionagi/schema/__init__.py +11 -0
  20. lionagi/schema/base_schema.py +239 -0
  21. lionagi/schema/base_tool.py +9 -0
  22. lionagi/schema/data_logger.py +94 -0
  23. lionagi/services/__init__.py +14 -0
  24. lionagi/{service_/oai.py → services/base_api_service.py} +49 -82
  25. lionagi/{endpoint/base_endpoint.py → services/chatcompletion.py} +19 -22
  26. lionagi/services/oai.py +34 -0
  27. lionagi/services/openrouter.py +32 -0
  28. lionagi/{service_/service_utils.py → services/service_objs.py} +0 -1
  29. lionagi/structure/__init__.py +7 -0
  30. lionagi/structure/relationship.py +128 -0
  31. lionagi/structure/structure.py +160 -0
  32. lionagi/tests/test_flatten_util.py +426 -0
  33. lionagi/tools/__init__.py +0 -5
  34. lionagi/tools/coder.py +1 -0
  35. lionagi/tools/scorer.py +1 -0
  36. lionagi/tools/validator.py +1 -0
  37. lionagi/utils/__init__.py +46 -20
  38. lionagi/utils/api_util.py +86 -0
  39. lionagi/utils/call_util.py +347 -0
  40. lionagi/utils/flat_util.py +540 -0
  41. lionagi/utils/io_util.py +102 -0
  42. lionagi/utils/load_utils.py +190 -0
  43. lionagi/utils/sys_util.py +191 -0
  44. lionagi/utils/tool_util.py +92 -0
  45. lionagi/utils/type_util.py +81 -0
  46. lionagi/version.py +1 -1
  47. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/METADATA +37 -13
  48. lionagi-0.0.113.dist-info/RECORD +84 -0
  49. lionagi/endpoint/chat_completion.py +0 -20
  50. lionagi/endpoint/endpoint_utils.py +0 -0
  51. lionagi/llm_configs.py +0 -21
  52. lionagi/loader/load_utils.py +0 -161
  53. lionagi/schema.py +0 -275
  54. lionagi/service_/__init__.py +0 -6
  55. lionagi/service_/base_service.py +0 -48
  56. lionagi/service_/openrouter.py +0 -1
  57. lionagi/services.py +0 -1
  58. lionagi/tools/tool_utils.py +0 -75
  59. lionagi/utils/sys_utils.py +0 -799
  60. lionagi-0.0.112.dist-info/RECORD +0 -67
  61. /lionagi/{core/responses.py → datastore/chroma.py} +0 -0
  62. /lionagi/{endpoint/assistants.py → datastore/deeplake.py} +0 -0
  63. /lionagi/{endpoint/audio.py → datastore/elasticsearch.py} +0 -0
  64. /lionagi/{endpoint/embeddings.py → datastore/lantern.py} +0 -0
  65. /lionagi/{endpoint/files.py → datastore/pinecone.py} +0 -0
  66. /lionagi/{endpoint/fine_tuning.py → datastore/postgres.py} +0 -0
  67. /lionagi/{endpoint/images.py → datastore/qdrant.py} +0 -0
  68. /lionagi/{endpoint/messages.py → schema/base_condition.py} +0 -0
  69. /lionagi/{service_ → services}/anthropic.py +0 -0
  70. /lionagi/{service_ → services}/anyscale.py +0 -0
  71. /lionagi/{service_ → services}/azure.py +0 -0
  72. /lionagi/{service_ → services}/bedrock.py +0 -0
  73. /lionagi/{service_ → services}/everlyai.py +0 -0
  74. /lionagi/{service_ → services}/gemini.py +0 -0
  75. /lionagi/{service_ → services}/gpt4all.py +0 -0
  76. /lionagi/{service_ → services}/huggingface.py +0 -0
  77. /lionagi/{service_ → services}/litellm.py +0 -0
  78. /lionagi/{service_ → services}/localai.py +0 -0
  79. /lionagi/{service_ → services}/mistralai.py +0 -0
  80. /lionagi/{service_ → services}/ollama.py +0 -0
  81. /lionagi/{service_ → services}/openllm.py +0 -0
  82. /lionagi/{service_ → services}/perplexity.py +0 -0
  83. /lionagi/{service_ → services}/predibase.py +0 -0
  84. /lionagi/{service_ → services}/rungpt.py +0 -0
  85. /lionagi/{service_ → services}/vllm.py +0 -0
  86. /lionagi/{service_ → services}/xinference.py +0 -0
  87. /lionagi/{endpoint → tests}/__init__.py +0 -0
  88. /lionagi/{endpoint/models.py → tools/planner.py} +0 -0
  89. /lionagi/{endpoint/moderations.py → tools/prompter.py} +0 -0
  90. /lionagi/{endpoint/runs.py → tools/sandbox.py} +0 -0
  91. /lionagi/{endpoint/threads.py → tools/summarizer.py} +0 -0
  92. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/LICENSE +0 -0
  93. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/WHEEL +0 -0
  94. {lionagi-0.0.112.dist-info → lionagi-0.0.113.dist-info}/top_level.txt +0 -0
@@ -1,799 +0,0 @@
1
- """
2
- Copyright 2023 HaiyangLi <ocean@lionagi.ai>
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.
15
- """
16
-
17
- import asyncio
18
- import copy
19
- import csv
20
- import json
21
- import os
22
- import re
23
- import tempfile
24
- import time
25
- import hashlib
26
- from datetime import datetime
27
- from collections.abc import Generator, Iterable, MutableMapping
28
- from typing import Any, Callable, Dict, List, Optional, Union
29
-
30
-
31
- def _flatten_dict(input: Dict[str, Any], parent_key: str = '',
32
- sep: str = '_') -> Generator[tuple[str, Any], None, None]:
33
- """
34
- Recursively flattens a nested dictionary into a flat dictionary.
35
-
36
- This generator function traverses a nested dictionary, concatenating nested keys
37
- into a single key using the provided separator. It handles both nested dictionaries
38
- and lists within the dictionary.
39
-
40
- Parameters:
41
- input (Dict[str, Any]): The dictionary to be flattened.
42
-
43
- parent_key (str, optional): Initial key for nested structures. Defaults to ''.
44
-
45
- sep (str, optional): Separator for concatenated key. Defaults to '_'.
46
-
47
- Yields:
48
- Generator[tuple[str, Any], None, None]: Tuples of flattened keys and their values.
49
-
50
- Examples:
51
- >>> example_dict = {"a": 1, "b": {"c": 2, "d": {"e": 3}}}
52
- >>> list(_flatten_dict(example_dict))
53
- [('a', 1), ('b_c', 2), ('b_d_e', 3)]
54
-
55
- >>> example_dict_with_list = {"a": 1, "b": [{"c": 2}, {"d": 3}]}
56
- >>> list(_flatten_dict(example_dict_with_list))
57
- [('a', 1), ('b_0_c', 2), ('b_1_d', 3)]
58
- """
59
- for key, value in input.items():
60
- composed_key = f'{parent_key}{sep}{key}' if parent_key else key
61
- if isinstance(value, MutableMapping):
62
- yield from _flatten_dict(value, composed_key, sep=sep)
63
- elif isinstance(value, list):
64
- for i, item in enumerate(value):
65
- if isinstance(item, MutableMapping):
66
- yield from _flatten_dict(item, f'{composed_key}{sep}{i}', sep=sep)
67
- else:
68
- yield (f'{composed_key}{sep}{i}', item)
69
- else:
70
- yield (composed_key, value)
71
-
72
- def to_flat_dict(input: Dict[str, Any], parent_key: str = '', sep: str = '_') -> Dict[str, Any]:
73
- """
74
- Transforms a nested dictionary into a flat dictionary.
75
-
76
- This function flattens a dictionary with potentially nested structures, concatenating
77
- nested keys using the specified separator. It leverages the '_flatten_dict' generator
78
- function for efficient processing.
79
-
80
- Parameters:
81
- input (Dict[str, Any]): The nested dictionary to flatten.
82
-
83
- parent_key (str, optional): Initial key for nested structures. Defaults to ''.
84
-
85
- sep (str, optional): Separator for concatenated keys. Defaults to '_'.
86
-
87
- Returns:
88
- Dict[str, Any]: The resulting flat dictionary.
89
-
90
- Example:
91
- >>> example_nested_dict = {'a': 1, 'b': {'c': 2}}
92
- >>> to_flat_dict(example_nested_dict)
93
- {'a': 1, 'b_c': 2}
94
- """
95
- return dict(_flatten_dict(input, parent_key, sep))
96
-
97
- def _flatten_list(input: List[Any], dropna: bool = True) -> Generator[Any, None, None]:
98
- """
99
- Recursively flattens a nested list into a single-level list.
100
-
101
- This generator function iterates through a list, flattening nested lists into a single
102
- level. It provides an option to exclude 'None' values from the output, enhancing its utility.
103
-
104
- Parameters:
105
- input (List[Any]): The list to be flattened.
106
-
107
- dropna (bool, optional): Flag to indicate whether 'None' values should be omitted. Defaults to True.
108
-
109
- Yields:
110
- Generator[Any, None, None]: Elements from the flattened list.
111
-
112
- Example:
113
- >>> example_nested_list = [1, [2, 3, None], 4]
114
- >>> list(_flatten_list(example_nested_list))
115
- [1, 2, 3, 4]
116
- """
117
- for element in input:
118
- if isinstance(element, list):
119
- yield from _flatten_list(element, dropna)
120
- elif element is not None or not dropna:
121
- yield element
122
-
123
- def to_list(input: Union[Iterable, Any], flatten_dict: bool = False, flat: bool = True,
124
- dropna: bool = True, parent_key: str = '', sep: str = '_') -> List[Any]:
125
- """
126
- Converts a given input into a list, optionally flattening nested structures.
127
-
128
- This function converts various inputs into a list, with optional handling for
129
- flattening nested lists and dictionaries, and dropping 'None' values. If 'flatten_dict'
130
- is True and the input is a dictionary, it is flattened before conversion.
131
-
132
- Parameters:
133
- input (Union[Iterable, Any]): The input to convert.
134
-
135
- flatten_dict (bool, optional): Flatten dictionary input. Defaults to False.
136
-
137
- flat (bool, optional): Flatten the output list. Defaults to True.
138
-
139
- dropna (bool, optional): Drop 'None' values during flattening. Defaults to True.
140
-
141
- parent_key (str, optional): The parent key for flattening dictionaries. Defaults to an empty string.
142
-
143
- sep (str, optional): The separator for creating flattened dictionary keys. Defaults to '_'.
144
-
145
- Raises:
146
- ValueError: If input is None, callable, or unconvertible to a list.
147
-
148
- Returns:
149
- List[Any]: The resulting list after applying transformations.
150
-
151
- Example:
152
- >>> to_list({'a': 1, 'b': [2, 3]}, flatten_dict=True)
153
- [{'a': 1}, {'b_0': 2}, {'b_1': 3}]
154
- """
155
-
156
- if input is None:
157
- raise ValueError("Unsupported type: None are not convertible to a list.")
158
-
159
- try:
160
- if isinstance(input, dict):
161
- if flatten_dict:
162
- input = dict(_flatten_dict(input, parent_key, sep)) # Flatten and convert to dictionary first
163
- return [{k: v} for k, v in input.items()]
164
- output = [input]
165
- elif isinstance(input, Iterable) and not isinstance(input, str):
166
- output = list(input)
167
- else:
168
- output = [input]
169
-
170
- if flat: # Flatten if necessary
171
- output = list(_flatten_list(output, dropna))
172
- return output
173
-
174
- except TypeError as e:
175
- raise ValueError(f"Unable to convert input to list. Error: {e}")
176
-
177
- def str_to_num(input: str,
178
- upper_bound: Optional[Union[int, float]] = None,
179
- lower_bound: Optional[Union[int, float]] = None,
180
- num_type: type = int,
181
- precision: Optional[int] = None) -> Union[int, float]:
182
- """
183
- Converts found numeric value in a string to a specified type.
184
-
185
- This function searches a string for numeric values and converts it to an integer
186
- or a float, based on the specified type. It also validates the converted value
187
- against optional upper and lower bounds.
188
-
189
- Parameters:
190
- input (str): String to search for numeric values.
191
-
192
- upper_bound (Optional[Union[int, float]]): Upper limit for the numeric value. None for no limit.
193
-
194
- lower_bound (Optional[Union[int, float]]): Lower limit for the numeric value. None for no limit.
195
-
196
- num_type (type): Desired type for the numeric value (int or float).
197
-
198
- precision (Optional[int]): Decimal places for rounding if float. None for no rounding.
199
-
200
- Raises:
201
- ValueError: If no numeric values are found, for conversion errors, or if the number is out of bounds.
202
-
203
- Returns:
204
- Union[int, float]: The converted numeric value, if within specified bounds.
205
-
206
- Example:
207
- >>> str_to_num("Temperature is -5.6 degrees", num_type=float, precision=1)
208
- -5.6
209
- >>> str_to_num("Value is approximately 200", upper_bound=150)
210
- ValueError: Number 200 is greater than the upper bound of 150.
211
- """
212
- numbers = re.findall(r'-?\d+\.?\d*', input)
213
- if not numbers:
214
- raise ValueError(f"No numeric values found in the string: {input}")
215
-
216
- try:
217
- numbers = numbers[0]
218
-
219
- if num_type is int:
220
- numbers = int(float(numbers))
221
- elif num_type is float:
222
- numbers = round(float(numbers), precision) if precision is not None else float(numbers)
223
- else:
224
- raise ValueError(f"Invalid number type: {num_type}")
225
-
226
- if upper_bound is not None and numbers > upper_bound:
227
- raise ValueError(f"Number {numbers} is greater than the upper bound of {upper_bound}.")
228
- if lower_bound is not None and numbers < lower_bound:
229
- raise ValueError(f"Number {numbers} is less than the lower bound of {lower_bound}.")
230
-
231
- return numbers
232
- except ValueError as e:
233
- raise ValueError(f"Error converting string to number: {e}")
234
-
235
- def create_copy(input: Any, n: int) -> Any:
236
- """
237
- Creates a deep copy of the input object a specified number of times.
238
-
239
- This function makes deep copies of the provided input. If the number of copies ('n')
240
- is greater than 1, a list of deep copies is returned. For a single copy, it returns
241
- the copy directly.
242
-
243
- Parameters:
244
- input (Any): The object to be copied.
245
-
246
- n (int): The number of deep copies to create.
247
-
248
- Raises:
249
- ValueError: If 'n' is not a positive integer.
250
-
251
- Returns:
252
- Any: A deep copy of 'input' or a list of deep copies if 'n' > 1.
253
-
254
- Example:
255
- >>> sample_dict = {'key': 'value'}
256
- >>> make_copy(sample_dict, 2)
257
- [{'key': 'value'}, {'key': 'value'}]
258
- """
259
- if not isinstance(n, int) or n < 1:
260
- raise ValueError(f"'n' must be a positive integer: {n}")
261
- return copy.deepcopy(input) if n == 1 else [copy.deepcopy(input) for _ in range(n)]
262
-
263
- def to_temp(input: Any,
264
- flatten_dict: bool = False,
265
- flat: bool = False,
266
- dropna: bool = False):
267
- """
268
- Converts input to a list and writes it to a temporary file in JSON format, with flattening options.
269
-
270
- This function serializes data to a temporary JSON file, useful for transient storage or testing.
271
- It includes options to flatten the input if it contains dictionaries or lists.
272
-
273
- Parameters:
274
- input (Any): The data to be converted and written to a file.
275
-
276
- flatten_dict (bool, optional): Flatten dictionaries in the input. Defaults to False.
277
-
278
- flat (bool, optional): Flatten lists in the input. Defaults to False.
279
-
280
- dropna (bool, optional): Exclude 'None' values during flattening. Defaults to False.
281
-
282
- Raises:
283
- TypeError: If the input is not JSON serializable.
284
-
285
- Example:
286
- >>> temp_file = to_temp({'a': 1, 'b': [2, 3]}, flatten_dict=True)
287
- >>> temp_file.name # Doctest: +ELLIPSIS
288
- '/var/folders/.../tmp...'
289
- """
290
- input = to_list(input, flatten_dict, flat, dropna)
291
-
292
- temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
293
- try:
294
- json.dump(input, temp_file)
295
- except TypeError as e:
296
- temp_file.close() # Ensuring file closure before raising error
297
- raise TypeError(f"Data provided is not JSON serializable: {e}")
298
- temp_file.close()
299
- return temp_file
300
-
301
- def to_csv(input: List[Dict[str, Any]]=None,
302
- filepath: str=None,
303
- file_exist_ok: bool = False) -> None:
304
- """
305
- Writes a list of dictionaries to a CSV file, with dictionary keys as headers.
306
-
307
- This function writes a list of dictionaries to a CSV file. It checks if the file exists
308
- and handles file creation based on the 'file_exist_ok' flag.
309
-
310
- Parameters:
311
- input (List[Dict[str, Any]]): Data to write to the CSV file.
312
-
313
- filepath (str): Path of the output CSV file.
314
-
315
- file_exist_ok (bool, optional): Create the file if it doesn't exist. Defaults to False.
316
-
317
- Raises:
318
- FileExistsError: If the file already exists and 'file_exist_ok' is False.
319
-
320
- Example:
321
- >>> data = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
322
- >>> to_csv(data, 'people.csv')
323
- """
324
-
325
- if not os.path.exists(os.path.dirname(filepath)) and os.path.dirname(filepath) != '':
326
- if file_exist_ok:
327
- os.makedirs(os.path.dirname(filepath), exist_ok=True)
328
- else:
329
- raise FileNotFoundError(f"The directory {os.path.dirname(filepath)} does not exist.")
330
-
331
- with open(filepath, 'w', newline='') as csv_file:
332
- writer = csv.DictWriter(csv_file, fieldnames=input[0].keys())
333
- writer.writeheader()
334
- writer.writerows(input)
335
-
336
-
337
- def append_to_jsonl(data: Any, filepath: str) -> None:
338
- """
339
- Appends data to a JSON lines (jsonl) file.
340
-
341
- Serializes given data to a JSON-formatted string and appends it to a jsonl file.
342
- Useful for logging or data collection where entries are added incrementally.
343
-
344
- Parameters:
345
- data (Any): Data to be serialized and appended.
346
-
347
- filepath (str): Path to the jsonl file.
348
-
349
- Example:
350
- >>> append_to_jsonl({"key": "value"}, "data.jsonl")
351
- # Appends {"key": "value"} to 'data.jsonl'
352
- """
353
- json_string = json.dumps(data)
354
- with open(filepath, "a") as f:
355
- f.write(json_string + "\n")
356
-
357
- def hold_call(input: Any,
358
- func: Callable,
359
- sleep: int = 0.1,
360
- message: Optional[str] = None,
361
- ignore_error: bool = False,
362
- **kwargs) -> Any:
363
- """
364
- Executes a function after a specified delay, handling exceptions optionally.
365
-
366
- Waits for 'sleep' seconds before calling 'func' with 'input'. Handles exceptions by
367
- printing a message and optionally re-raising them.
368
-
369
- Parameters:
370
- input (Any): Input to the function.
371
-
372
- func (Callable): Function to execute.
373
-
374
- sleep (int, optional): Time in seconds to wait before calling the function. Defaults to 0.1.
375
-
376
- message (Optional[str], optional): Message to print on exception. Defaults to None.
377
-
378
- ignore_error (bool, optional): If True, ignores exceptions. Defaults to False.
379
-
380
- **kwargs: Additional keyword arguments for the function.
381
-
382
- Returns:
383
- Any: Result of the function call.
384
-
385
- Raises:
386
- Exception: Re-raises the exception unless 'ignore_error' is True.
387
-
388
- Example:
389
- >>> def add_one(x):
390
- ... return x + 1
391
- >>> hold_call(5, add_one, sleep=2)
392
- 6
393
- """
394
- try:
395
- time.sleep(sleep)
396
- return func(input, **kwargs)
397
- except Exception as e:
398
- if message:
399
- print(f"{message} Error: {e}")
400
- else:
401
- print(f"An error occurred: {e}")
402
- if not ignore_error:
403
- raise
404
-
405
- async def ahold_call(input: Any,
406
- func: Callable,
407
- sleep: int = 5,
408
- message: Optional[str] = None,
409
- ignore_error: bool = False,
410
- **kwargs) -> Any:
411
- """
412
- Asynchronously executes a function after a specified delay, handling exceptions optionally.
413
-
414
- Waits for 'sleep' seconds before calling 'func' with 'input'. Handles exceptions by
415
- printing a message and optionally re-raising them.
416
-
417
- Parameters:
418
- input (Any): Input to the function.
419
-
420
- func (Callable): Asynchronous function to execute.
421
-
422
- sleep (int, optional): Time in seconds to wait before calling the function. Defaults to 5.
423
-
424
- message (Optional[str], optional): Message to print on exception. Defaults to None.
425
-
426
- ignore_error (bool, optional): If True, ignores exceptions. Defaults to False.
427
-
428
- **kwargs: Additional keyword arguments for the function.
429
-
430
- Returns:
431
- Any: Result of the asynchronous function call.
432
-
433
- Raises:
434
- Exception: Re-raises the exception unless 'ignore_error' is True.
435
-
436
- Example:
437
- >>> async def async_add_one(x):
438
- ... return x + 1
439
- >>> asyncio.run(ahold_call(5, async_add_one, sleep=2))
440
- 6
441
- """
442
- try:
443
- if not asyncio.iscoroutinefunction(func):
444
- raise TypeError(f"The function {func} must be an asynchronous function.")
445
- await asyncio.sleep(sleep)
446
- return await func(input, **kwargs)
447
- except Exception as e:
448
- if message:
449
- print(f"{message} Error: {e}")
450
- else:
451
- print(f"An error occurred: {e}")
452
- if not ignore_error:
453
- raise
454
-
455
- def l_call(input: Any,
456
- func: Callable,
457
- flatten_dict: bool = False,
458
- flat: bool = False,
459
- dropna: bool = True, **kwags) -> List[Any]:
460
- """
461
- Applies a function to each element of `input`, after converting it to a list.
462
-
463
- This function converts the `input` to a list, with options to flatten structures
464
- and lists, and then applies a given `func` to each element of the list.
465
-
466
- Parameters:
467
- input (Any): The input to be converted to a list and processed.
468
-
469
- func (Callable): The function to apply to each element of the list.
470
-
471
- flatten_dict (bool, optional): If True, flattens dictionaries in the input. Defaults to False.
472
-
473
- flat (bool, optional): If True, flattens nested lists in the input. Defaults to False.
474
-
475
- dropna (bool, optional): If True, drops None values during flattening. Defaults to True.
476
-
477
- Returns:
478
- List[Any]: A list containing the results of applying the `func` to each element.
479
-
480
- Raises:
481
- ValueError: If the `func` cannot be applied to the `input`.
482
-
483
- Example:
484
- >>> def square(x):
485
- ... return x * x
486
- >>> l_call([1, 2, 3], square)
487
- [1, 4, 9]
488
- """
489
- try:
490
- lst = to_list(input, flatten_dict, flat, dropna)
491
- return [func(i, **kwags) for i in lst]
492
- except Exception as e:
493
- raise ValueError(f"Given function cannot be applied to the input. Error: {e}")
494
-
495
- async def al_call(input: Any,
496
- func: Callable,
497
- flatten_dict: bool = False,
498
- flat: bool = False,
499
- dropna: bool = True, **kwags) -> List[Any]:
500
- """
501
- Asynchronously applies a function to each element of `input`, after converting it to a list.
502
-
503
- This function converts the `input` to a list, with options to flatten
504
- dictionaries and lists, and then applies a given asynchronous `func` to
505
- each element of the list asynchronously.
506
-
507
- Parameters:
508
- input (Any): The input to be converted to a list and processed.
509
-
510
- func (Callable): The asynchronous function to apply to each element of the list.
511
-
512
- flatten_dict (bool, optional): If True, flattens dictionaries in the input. Defaults to False.
513
-
514
- flat (bool, optional): If True, flattens nested lists in the input. Defaults to False.
515
-
516
- dropna (bool, optional): If True, drops None values during flattening. Defaults to True.
517
-
518
- Returns:
519
- List[Any]: A list containing the results of applying the `func` to each element.
520
-
521
- Raises:
522
- ValueError: If the `func` cannot be applied to the `input`.
523
-
524
- Example:
525
- >>> async def async_square(x):
526
- ... return x * x
527
- >>> asyncio.run(al_call([1, 2, 3], async_square))
528
- [1, 4, 9]
529
- """
530
- try:
531
- lst = to_list(input, flatten_dict, flat, dropna)
532
- tasks = [func(i, **kwags) for i in lst]
533
- return await asyncio.gather(*tasks)
534
- except Exception as e:
535
- raise ValueError(f"Given function cannot be applied to the input. Error: {e}")
536
-
537
- def m_call(input: Union[Any, List[Any]],
538
- func: Union[Callable, List[Callable]],
539
- flatten_dict: bool = False,
540
- flat: bool = True,
541
- dropna: bool = True, **kwags) -> List[Any]:
542
- """
543
- Maps multiple functions to corresponding elements of the input.
544
-
545
- This function applies a list of functions to a list of inputs, with each function
546
- being applied to its corresponding input element. It asserts that the number of inputs
547
- and functions are the same and raises an error if they are not.
548
-
549
- Parameters:
550
- input (Union[Any, List[Any]]): The input or list of inputs to be processed.
551
-
552
- func (Union[Callable, List[Callable]]): The function or list of functions to apply.
553
-
554
- flatten_dict (bool, optional): Whether to flatten dictionaries in the input. Defaults to False.
555
-
556
- flat (bool, optional): Whether the output list should be flattened. Defaults to True.
557
-
558
- dropna (bool, optional): Whether to drop None values during flattening. Defaults to True.
559
-
560
- Returns:
561
- List[Any]: A list containing the results from applying each function to its corresponding input.
562
-
563
- Raises:
564
- ValueError: If the number of provided inputs and functions are not the same.
565
-
566
- Example:
567
- >>> def add_one(x):
568
- ... return x + 1
569
- >>> m_call([1, 2], [add_one, add_one])
570
- [2, 3]
571
- """
572
- input = to_list(input, flatten_dict, flat, dropna)
573
- func = to_list(func)
574
- assert len(input) == len(func), "The number of inputs and functions must be the same."
575
- return to_list([l_call(inp, f, flatten_dict, flat, dropna, **kwags)
576
- for f, inp in zip(func, input)])
577
-
578
- async def am_call(input: Union[Any, List[Any]],
579
- func: Union[Callable, List[Callable]],
580
- flatten_dict: bool = False,
581
- flat: bool = True,
582
- dropna: bool = True, **kwags) -> List[Any]:
583
- """
584
- Asynchronously applies multiple functions to corresponding elements of the input.
585
-
586
- This asynchronous function maps a list of functions to a list of inputs, with each
587
- function being applied to its corresponding input element asynchronously. It ensures
588
- that the number of inputs and functions are the same, raising a `ValueError` if not.
589
-
590
- Parameters:
591
- input (Union[Any, List[Any]]): The input or list of inputs to be processed.
592
-
593
- func (Union[Callable, List[Callable]]): The function or list of functions to apply.
594
-
595
- flatten_dict (bool, optional): Whether to flatten dictionaries in the input. Defaults to False.
596
-
597
- flat (bool, optional): Whether the output list should be flattened. Defaults to True.
598
-
599
- dropna (bool, optional): Whether to drop None values during flattening. Defaults to True.
600
-
601
- Returns:
602
- List[Any]: A list containing the results from applying each function to its corresponding input.
603
-
604
- Raises:
605
- ValueError: If the number of inputs and functions do not match.
606
-
607
- Example:
608
- >>> async def async_add_one(x):
609
- ... return x + 1
610
- >>> asyncio.run(am_call([1, 2], [async_add_one, async_add_one]))
611
- [2, 3]
612
- """
613
- input = to_list(input, flatten_dict, flat, dropna)
614
- func = to_list(func)
615
- assert len(input) == len(func), "Input and function counts must match."
616
-
617
- tasks = [al_call(inp, f, flatten_dict, flat, dropna, **kwags)
618
- for f, inp in zip(func, input)]
619
- out = await asyncio.gather(*tasks)
620
- return to_list(out, flat=True)
621
-
622
- def e_call(input: Any,
623
- func: Callable,
624
- flatten_dict: bool = False,
625
- flat: bool = False,
626
- dropna: bool = True, **kwags) -> List[Any]:
627
- """
628
- Applies each function in a list of functions to all elements in the input.
629
-
630
- This function expands the input to match the number of functions and then
631
- applies each function to the entire input. It is useful for applying a series
632
- of different transformations to the same input.
633
-
634
- Parameters:
635
- input (Union[Any, List[Any]]): The input or list of inputs to be processed.
636
-
637
- func (Union[Callable, List[Callable]]): The function or list of functions to apply.
638
-
639
- flatten_dict (bool, optional): Whether to flatten dictionaries in the input. Defaults to False.
640
-
641
- flat (bool, optional): Whether the output list should be flattened. Defaults to True.
642
-
643
- dropna (bool, optional): Whether to drop None values during flattening. Defaults to True.
644
-
645
- Returns:
646
- List[Any]: A list of results after applying each function to the input.
647
-
648
- Example:
649
- >>> def square(x):
650
- ... return x**2
651
- >>> e_call([1, 2, 3], [square])
652
- [[1], [4], [9]]
653
- """
654
-
655
- _f = lambda x, y: m_call(create_copy(x, len(to_list(y))), y,
656
- flatten_dict=flatten_dict, flat=flat, dropna=dropna, **kwags)
657
- return to_list([_f(inp, func) for inp in to_list(input)], flat=flat)
658
-
659
- async def ae_call(input_: Any,
660
- func_: Callable,
661
- flatten_dict: bool = False,
662
- flat: bool = False,
663
- dropna: bool = True, **kwags):
664
- """
665
- Asynchronously applies each function in a list of functions to all elements in the input.
666
-
667
- This asynchronous function expands the input to match the number of functions and
668
- then asynchronously applies each function to the entire input. It is useful for applying a series
669
- of different asynchronous transformations to the same input.
670
-
671
- Parameters:
672
- input_ (Union[Any, List[Any]]): The input or list of inputs to be processed.
673
-
674
- func_ (Union[Callable, List[Callable]]): The function or list of functions to apply.
675
-
676
- flatten_dict (bool, optional): Whether to flatten dictionaries in the input. Defaults to False.
677
-
678
- flat (bool, optional): Whether the output list should be flattened. Defaults to True.
679
-
680
- dropna (bool, optional): Whether to drop None values during flattening. Defaults to True.
681
-
682
- Example:
683
- >>> async def async_square(x):
684
- ... return x**2
685
- >>> asyncio.run(ae_call([1, 2, 3], [async_square]))
686
- [[1, 4, 9]]
687
- """
688
- async def _async_f(x, y):
689
- return await am_call(create_copy(x, len(to_list(y))), y, flatten_dict=flatten_dict, flat=flat, dropna=dropna, **kwags)
690
-
691
- tasks = [_async_f(inp, func_) for inp in to_list(input_)]
692
- return await asyncio.gather(*tasks)
693
-
694
- def get_timestamp() -> str:
695
- """
696
- Generates a current timestamp in a file-safe string format.
697
-
698
- This function creates a timestamp from the current time, formatted in ISO 8601 format,
699
- and replaces characters that are typically problematic in filenames (like colons and periods)
700
- with underscores.
701
-
702
- Returns:
703
- str: The current timestamp in a file-safe string format.
704
-
705
- Example:
706
- >>> get_timestamp() # Doctest: +ELLIPSIS
707
- '...'
708
- """
709
- return datetime.now().isoformat().replace(":", "_").replace(".", "_")
710
-
711
- def create_id() -> str:
712
- """
713
- Generates a unique ID based on the current time and random bytes.
714
-
715
- This function combines the current time in ISO 8601 format with 16 random bytes
716
- to create a unique identifier. The result is hashed using SHA-256 and the first
717
- 16 characters of the hexadecimal digest are returned.
718
-
719
- Returns:
720
- str: A 16-character unique identifier.
721
-
722
- Example:
723
- >>> create_id() # Doctest: +ELLIPSIS
724
- '...'
725
- """
726
- current_time = datetime.now().isoformat().encode('utf-8')
727
- random_bytes = os.urandom(2048)
728
- return hashlib.sha256(current_time + random_bytes).hexdigest()
729
-
730
- def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: bool = True, time_prefix=False) -> str:
731
- """
732
- Creates a file path by optionally appending a timestamp to the filename.
733
-
734
- This function constructs a file path by combining a directory, an optional timestamp,
735
- and a filename. It also ensures the existence of the directory.
736
-
737
- Parameters:
738
- dir (str): The directory in which the file is to be located.
739
-
740
- filename (str): The name of the file.
741
-
742
- timestamp (bool, optional): If True, appends a timestamp to the filename. Defaults to True.
743
-
744
- dir_exist_ok (bool, optional): If True, creates the directory if it doesn't exist. Defaults to True.
745
-
746
- time_prefix (bool, optional): If True, the timestamp is added as a prefix; otherwise, it's appended. Defaults to False.
747
-
748
- Returns:
749
- str: The full path to the file.
750
-
751
- Example:
752
- >>> create_path('/tmp/', 'log.txt', timestamp=False)
753
- '/tmp/log.txt'
754
- """
755
-
756
- dir = dir + '/' if str(dir)[-1] != '/' else dir
757
- filename, ext = filename.split('.')
758
- os.makedirs(dir, exist_ok=dir_exist_ok)
759
-
760
- if timestamp:
761
- timestamp = get_timestamp()
762
- return f"{dir}{timestamp}_{filename}.{ext}" if time_prefix else f"{dir}{filename}_{timestamp}.{ext}"
763
- else:
764
- return f"{dir}{filename}"
765
-
766
- def get_bins(input: List[str], upper: int = 7500) -> List[List[int]]:
767
- """
768
- Get index of elements in a list based on their consecutive cumulative sum of length,
769
- according to some upper threshold. Return lists of indices as bins.
770
-
771
- Parameters:
772
- input (List[str]): List of items to be binned.
773
-
774
- upper (int, optional): Upper threshold for the cumulative sum of the length of items in a bin. Default is 7500.
775
-
776
- Returns:
777
- List[List[int]]: List of lists, where each inner list contains the indices of the items that form a bin.
778
-
779
- Example:
780
- >>> items = ['apple', 'a', 'b', 'banana', 'cheery', 'c', 'd', 'e']
781
- >>> upper = 10
782
- >>> get_bins(items, upper)
783
- [[0, 1, 2], [3], [4, 5, 6, 7]]
784
- """
785
- current = 0
786
- bins = []
787
- bin = []
788
- for idx, item in enumerate(input):
789
- if current + len(item) < upper:
790
- bin.append(idx)
791
- current += len(item)
792
- elif current + len(item) >= upper:
793
- bins.append(bin)
794
- bin = [idx]
795
- current = len(item)
796
- if idx == len(input) - 1 and len(bin) > 0:
797
- bins.append(bin)
798
- return bins
799
-