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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. lionagi/__init__.py +7 -2
  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 +15 -0
  9. lionagi/{session/conversation.py → core/conversations.py} +10 -17
  10. lionagi/core/flows.py +1 -0
  11. lionagi/core/instruction_sets.py +1 -0
  12. lionagi/{session/message.py → core/messages.py} +5 -5
  13. lionagi/core/sessions.py +262 -0
  14. lionagi/datastore/__init__.py +1 -0
  15. lionagi/datastore/chroma.py +1 -0
  16. lionagi/datastore/deeplake.py +1 -0
  17. lionagi/datastore/elasticsearch.py +1 -0
  18. lionagi/datastore/lantern.py +1 -0
  19. lionagi/datastore/pinecone.py +1 -0
  20. lionagi/datastore/postgres.py +1 -0
  21. lionagi/datastore/qdrant.py +1 -0
  22. lionagi/loader/__init__.py +12 -0
  23. lionagi/loader/chunker.py +157 -0
  24. lionagi/loader/reader.py +124 -0
  25. lionagi/objs/__init__.py +7 -0
  26. lionagi/objs/messenger.py +163 -0
  27. lionagi/objs/tool_registry.py +247 -0
  28. lionagi/schema/__init__.py +11 -0
  29. lionagi/schema/base_condition.py +1 -0
  30. lionagi/schema/base_schema.py +239 -0
  31. lionagi/schema/base_tool.py +9 -0
  32. lionagi/schema/data_logger.py +94 -0
  33. lionagi/services/__init__.py +14 -0
  34. lionagi/services/anthropic.py +1 -0
  35. lionagi/services/anyscale.py +0 -0
  36. lionagi/services/azure.py +1 -0
  37. lionagi/{api/oai_service.py → services/base_api_service.py} +74 -148
  38. lionagi/services/bedrock.py +0 -0
  39. lionagi/services/chatcompletion.py +48 -0
  40. lionagi/services/everlyai.py +0 -0
  41. lionagi/services/gemini.py +0 -0
  42. lionagi/services/gpt4all.py +0 -0
  43. lionagi/services/huggingface.py +0 -0
  44. lionagi/services/litellm.py +1 -0
  45. lionagi/services/localai.py +0 -0
  46. lionagi/services/mistralai.py +0 -0
  47. lionagi/services/oai.py +34 -0
  48. lionagi/services/ollama.py +1 -0
  49. lionagi/services/openllm.py +0 -0
  50. lionagi/services/openrouter.py +32 -0
  51. lionagi/services/perplexity.py +0 -0
  52. lionagi/services/predibase.py +0 -0
  53. lionagi/services/rungpt.py +0 -0
  54. lionagi/services/service_objs.py +282 -0
  55. lionagi/services/vllm.py +0 -0
  56. lionagi/services/xinference.py +0 -0
  57. lionagi/structure/__init__.py +7 -0
  58. lionagi/structure/relationship.py +128 -0
  59. lionagi/structure/structure.py +160 -0
  60. lionagi/tests/__init__.py +0 -0
  61. lionagi/tests/test_flatten_util.py +426 -0
  62. lionagi/tools/__init__.py +0 -0
  63. lionagi/tools/coder.py +1 -0
  64. lionagi/tools/planner.py +1 -0
  65. lionagi/tools/prompter.py +1 -0
  66. lionagi/tools/sandbox.py +1 -0
  67. lionagi/tools/scorer.py +1 -0
  68. lionagi/tools/summarizer.py +1 -0
  69. lionagi/tools/validator.py +1 -0
  70. lionagi/utils/__init__.py +46 -8
  71. lionagi/utils/api_util.py +63 -416
  72. lionagi/utils/call_util.py +347 -0
  73. lionagi/utils/flat_util.py +540 -0
  74. lionagi/utils/io_util.py +102 -0
  75. lionagi/utils/load_utils.py +190 -0
  76. lionagi/utils/sys_util.py +85 -660
  77. lionagi/utils/tool_util.py +82 -199
  78. lionagi/utils/type_util.py +81 -0
  79. lionagi/version.py +1 -1
  80. {lionagi-0.0.111.dist-info → lionagi-0.0.113.dist-info}/METADATA +44 -15
  81. lionagi-0.0.113.dist-info/RECORD +84 -0
  82. lionagi/api/__init__.py +0 -8
  83. lionagi/api/oai_config.py +0 -16
  84. lionagi/session/__init__.py +0 -7
  85. lionagi/session/session.py +0 -380
  86. lionagi/utils/doc_util.py +0 -331
  87. lionagi/utils/log_util.py +0 -86
  88. lionagi-0.0.111.dist-info/RECORD +0 -20
  89. {lionagi-0.0.111.dist-info → lionagi-0.0.113.dist-info}/LICENSE +0 -0
  90. {lionagi-0.0.111.dist-info → lionagi-0.0.113.dist-info}/WHEEL +0 -0
  91. {lionagi-0.0.111.dist-info → lionagi-0.0.113.dist-info}/top_level.txt +0 -0
lionagi/utils/sys_util.py CHANGED
@@ -13,226 +13,14 @@ Copyright 2023 HaiyangLi <ocean@lionagi.ai>
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
-
17
- import asyncio
18
- import copy
19
- import csv
20
- import json
21
16
  import os
22
- import re
23
- import tempfile
24
- import time
17
+ import copy
25
18
  import hashlib
19
+ from pathlib import Path
26
20
  from datetime import datetime
27
- from collections.abc import Generator, Iterable, MutableMapping
28
- from typing import Any, Callable, Dict, List, Optional, Tuple, 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.
21
+ from typing import Any, Generator, List
42
22
 
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 make_copy(input: Any, n: int) -> Any:
23
+ def create_copy(input: Any, n: int) -> Any:
236
24
  """
237
25
  Creates a deep copy of the input object a specified number of times.
238
26
 
@@ -260,436 +48,24 @@ def make_copy(input: Any, n: int) -> Any:
260
48
  raise ValueError(f"'n' must be a positive integer: {n}")
261
49
  return copy.deepcopy(input) if n == 1 else [copy.deepcopy(input) for _ in range(n)]
262
50
 
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]],
302
- filepath: str,
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) -> List[Any]:
51
+ def create_id(n=32) -> str:
460
52
  """
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) 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) -> 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) 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) -> 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)
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) -> 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)
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) -> 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.
53
+ Generates a unique ID based on the current time and random bytes.
642
54
 
643
- dropna (bool, optional): Whether to drop None values during flattening. Defaults to True.
55
+ This function combines the current time in ISO 8601 format with 16 random bytes
56
+ to create a unique identifier. The result is hashed using SHA-256 and the first
57
+ 16 characters of the hexadecimal digest are returned.
644
58
 
645
59
  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(make_copy(x, len(to_list(y))), y,
656
- flatten_dict=flatten_dict, flat=flat, dropna=dropna)
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):
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.
60
+ str: A 16-character unique identifier.
681
61
 
682
62
  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]]
63
+ >>> create_id() # Doctest: +ELLIPSIS
64
+ '...'
687
65
  """
688
- async def _async_f(x, y):
689
- return await am_call(make_copy(x, len(to_list(y))), y, flatten_dict=flatten_dict, flat=flat, dropna=dropna)
690
-
691
- tasks = [_async_f(inp, func_) for inp in to_list(input_)]
692
- return await asyncio.gather(*tasks)
66
+ current_time = datetime.now().isoformat().encode('utf-8')
67
+ random_bytes = os.urandom(2048)
68
+ return hashlib.sha256(current_time + random_bytes).hexdigest()[:n]
693
69
 
694
70
  def get_timestamp() -> str:
695
71
  """
@@ -708,26 +84,6 @@ def get_timestamp() -> str:
708
84
  """
709
85
  return datetime.now().isoformat().replace(":", "_").replace(".", "_")
710
86
 
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(16)
728
- return hashlib.sha256(current_time + random_bytes).hexdigest()[:16]
729
-
730
-
731
87
  def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: bool = True, time_prefix=False) -> str:
732
88
  """
733
89
  Creates a file path by optionally appending a timestamp to the filename.
@@ -763,4 +119,73 @@ def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: b
763
119
  return f"{dir}{timestamp}_{filename}.{ext}" if time_prefix else f"{dir}{filename}_{timestamp}.{ext}"
764
120
  else:
765
121
  return f"{dir}{filename}"
766
-
122
+
123
+ def split_path(path: Path) -> tuple:
124
+ folder_name = path.parent.name
125
+ file_name = path.name
126
+ return (folder_name, file_name)
127
+
128
+ def get_bins(input: List[str], upper: int = 7500) -> List[List[int]]:
129
+ """
130
+ Get index of elements in a list based on their consecutive cumulative sum of length,
131
+ according to some upper threshold. Return lists of indices as bins.
132
+
133
+ Parameters:
134
+ input (List[str]): List of items to be binned.
135
+
136
+ upper (int, optional): Upper threshold for the cumulative sum of the length of items in a bin. Default is 7500.
137
+
138
+ Returns:
139
+ List[List[int]]: List of lists, where each inner list contains the indices of the items that form a bin.
140
+
141
+ Example:
142
+ >>> items = ['apple', 'a', 'b', 'banana', 'cheery', 'c', 'd', 'e']
143
+ >>> upper = 10
144
+ >>> get_bins(items, upper)
145
+ [[0, 1, 2], [3], [4, 5, 6, 7]]
146
+ """
147
+ current = 0
148
+ bins = []
149
+ bin = []
150
+
151
+ for idx, item in enumerate(input):
152
+
153
+ if current + len(item) < upper:
154
+ bin.append(idx)
155
+ current += len(item)
156
+
157
+ elif current + len(item) >= upper:
158
+ bins.append(bin)
159
+ bin = [idx]
160
+ current = len(item)
161
+
162
+ if idx == len(input) - 1 and len(bin) > 0:
163
+ bins.append(bin)
164
+
165
+ return bins
166
+
167
+ def task_id_generator() -> Generator[int, None, None]:
168
+ """
169
+ A generator function that yields a sequential series of task IDs.
170
+
171
+ Yields:
172
+ int: The next task ID in the sequence, starting from 0.
173
+
174
+ Examples:
175
+ task_id_gen = task_id_generator()
176
+ next(task_id_gen) # Yields 0
177
+ next(task_id_gen) # Yields 1
178
+ """
179
+ task_id = 0
180
+ while True:
181
+ yield task_id
182
+ task_id += 1
183
+
184
+ def change_dict_key(dict_, old_key, new_key):
185
+ dict_[new_key] = dict_.pop(old_key)
186
+
187
+ # def parse_function_call(response: str) -> Tuple[str, Dict]:
188
+ # out = json.loads(response)
189
+ # func = out.get('function', '').lstrip('call_')
190
+ # args = json.loads(out.get('arguments', '{}'))
191
+ # return func, args