lionagi 0.0.115__py3-none-any.whl → 0.0.204__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. lionagi/__init__.py +1 -2
  2. lionagi/_services/__init__.py +5 -0
  3. lionagi/_services/anthropic.py +79 -0
  4. lionagi/_services/base_service.py +414 -0
  5. lionagi/_services/oai.py +98 -0
  6. lionagi/_services/openrouter.py +44 -0
  7. lionagi/_services/services.py +91 -0
  8. lionagi/_services/transformers.py +46 -0
  9. lionagi/bridge/langchain.py +26 -16
  10. lionagi/bridge/llama_index.py +50 -20
  11. lionagi/configs/oai_configs.py +2 -14
  12. lionagi/configs/openrouter_configs.py +2 -2
  13. lionagi/core/__init__.py +7 -8
  14. lionagi/core/branch/branch.py +589 -0
  15. lionagi/core/branch/branch_manager.py +139 -0
  16. lionagi/core/branch/conversation.py +484 -0
  17. lionagi/core/core_util.py +59 -0
  18. lionagi/core/flow/flow.py +19 -0
  19. lionagi/core/flow/flow_util.py +62 -0
  20. lionagi/core/instruction_set/__init__.py +0 -5
  21. lionagi/core/instruction_set/instruction_set.py +343 -0
  22. lionagi/core/messages/messages.py +176 -0
  23. lionagi/core/sessions/__init__.py +0 -5
  24. lionagi/core/sessions/session.py +428 -0
  25. lionagi/loaders/chunker.py +51 -47
  26. lionagi/loaders/load_util.py +2 -2
  27. lionagi/loaders/reader.py +45 -39
  28. lionagi/models/imodel.py +53 -0
  29. lionagi/schema/async_queue.py +158 -0
  30. lionagi/schema/base_node.py +318 -147
  31. lionagi/schema/base_tool.py +31 -1
  32. lionagi/schema/data_logger.py +74 -38
  33. lionagi/schema/data_node.py +57 -6
  34. lionagi/structures/graph.py +132 -10
  35. lionagi/structures/relationship.py +58 -20
  36. lionagi/structures/structure.py +36 -25
  37. lionagi/tests/test_utils/test_api_util.py +219 -0
  38. lionagi/tests/test_utils/test_call_util.py +785 -0
  39. lionagi/tests/test_utils/test_encrypt_util.py +323 -0
  40. lionagi/tests/test_utils/test_io_util.py +238 -0
  41. lionagi/tests/test_utils/test_nested_util.py +338 -0
  42. lionagi/tests/test_utils/test_sys_util.py +358 -0
  43. lionagi/tools/tool_manager.py +186 -0
  44. lionagi/tools/tool_util.py +266 -3
  45. lionagi/utils/__init__.py +21 -61
  46. lionagi/utils/api_util.py +359 -71
  47. lionagi/utils/call_util.py +839 -264
  48. lionagi/utils/encrypt_util.py +283 -16
  49. lionagi/utils/io_util.py +178 -93
  50. lionagi/utils/nested_util.py +672 -0
  51. lionagi/utils/pd_util.py +57 -0
  52. lionagi/utils/sys_util.py +284 -156
  53. lionagi/utils/url_util.py +55 -0
  54. lionagi/version.py +1 -1
  55. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/METADATA +21 -17
  56. lionagi-0.0.204.dist-info/RECORD +106 -0
  57. lionagi/core/conversations/__init__.py +0 -5
  58. lionagi/core/conversations/conversation.py +0 -107
  59. lionagi/core/flows/__init__.py +0 -8
  60. lionagi/core/flows/flow.py +0 -8
  61. lionagi/core/flows/flow_util.py +0 -62
  62. lionagi/core/instruction_set/instruction_sets.py +0 -7
  63. lionagi/core/sessions/sessions.py +0 -185
  64. lionagi/endpoints/__init__.py +0 -5
  65. lionagi/endpoints/audio.py +0 -17
  66. lionagi/endpoints/chatcompletion.py +0 -54
  67. lionagi/messages/__init__.py +0 -11
  68. lionagi/messages/instruction.py +0 -15
  69. lionagi/messages/message.py +0 -110
  70. lionagi/messages/response.py +0 -33
  71. lionagi/messages/system.py +0 -12
  72. lionagi/objs/__init__.py +0 -11
  73. lionagi/objs/abc_objs.py +0 -39
  74. lionagi/objs/async_queue.py +0 -135
  75. lionagi/objs/messenger.py +0 -85
  76. lionagi/objs/tool_manager.py +0 -253
  77. lionagi/services/__init__.py +0 -11
  78. lionagi/services/base_api_service.py +0 -230
  79. lionagi/services/oai.py +0 -34
  80. lionagi/services/openrouter.py +0 -31
  81. lionagi/tests/test_api_util.py +0 -46
  82. lionagi/tests/test_call_util.py +0 -115
  83. lionagi/tests/test_convert_util.py +0 -202
  84. lionagi/tests/test_encrypt_util.py +0 -33
  85. lionagi/tests/test_flat_util.py +0 -426
  86. lionagi/tests/test_sys_util.py +0 -0
  87. lionagi/utils/convert_util.py +0 -229
  88. lionagi/utils/flat_util.py +0 -599
  89. lionagi-0.0.115.dist-info/RECORD +0 -110
  90. /lionagi/{services → _services}/anyscale.py +0 -0
  91. /lionagi/{services → _services}/azure.py +0 -0
  92. /lionagi/{services → _services}/bedrock.py +0 -0
  93. /lionagi/{services → _services}/everlyai.py +0 -0
  94. /lionagi/{services → _services}/gemini.py +0 -0
  95. /lionagi/{services → _services}/gpt4all.py +0 -0
  96. /lionagi/{services → _services}/huggingface.py +0 -0
  97. /lionagi/{services → _services}/litellm.py +0 -0
  98. /lionagi/{services → _services}/localai.py +0 -0
  99. /lionagi/{services → _services}/mistralai.py +0 -0
  100. /lionagi/{services → _services}/ollama.py +0 -0
  101. /lionagi/{services → _services}/openllm.py +0 -0
  102. /lionagi/{services → _services}/perplexity.py +0 -0
  103. /lionagi/{services → _services}/predibase.py +0 -0
  104. /lionagi/{services → _services}/rungpt.py +0 -0
  105. /lionagi/{services → _services}/vllm.py +0 -0
  106. /lionagi/{services → _services}/xinference.py +0 -0
  107. /lionagi/{endpoints/assistants.py → agents/__init__.py} +0 -0
  108. /lionagi/{tools → agents}/planner.py +0 -0
  109. /lionagi/{tools → agents}/prompter.py +0 -0
  110. /lionagi/{tools → agents}/scorer.py +0 -0
  111. /lionagi/{tools → agents}/summarizer.py +0 -0
  112. /lionagi/{tools → agents}/validator.py +0 -0
  113. /lionagi/{endpoints/embeddings.py → core/branch/__init__.py} +0 -0
  114. /lionagi/{services/anthropic.py → core/branch/cluster.py} +0 -0
  115. /lionagi/{endpoints/finetune.py → core/flow/__init__.py} +0 -0
  116. /lionagi/{endpoints/image.py → core/messages/__init__.py} +0 -0
  117. /lionagi/{endpoints/moderation.py → models/__init__.py} +0 -0
  118. /lionagi/{endpoints/vision.py → models/base_model.py} +0 -0
  119. /lionagi/{objs → schema}/status_tracker.py +0 -0
  120. /lionagi/tests/{test_io_util.py → test_utils/__init__.py} +0 -0
  121. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/LICENSE +0 -0
  122. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/WHEEL +0 -0
  123. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,57 @@
1
+ from typing import List, Dict
2
+ import pandas as pd
3
+ from ..schema.data_node import DataNode
4
+ from .sys_util import timestamp_to_datetime
5
+
6
+ def to_pd_df(items: List[Dict], how: str = 'all') -> pd.DataFrame:
7
+ """
8
+ Converts a list of dictionaries to a pandas DataFrame, dropping NA values.
9
+
10
+ Args:
11
+ items (List[Dict]): A list of dictionaries to be converted to a DataFrame.
12
+ how (str): How to handle NA values. Options are 'all' to drop rows with all NA values,
13
+ 'any' to drop rows with any NA values, or 'none' to keep all rows.
14
+
15
+ Returns:
16
+ pd.DataFrame: A pandas DataFrame containing the data from the input list of dictionaries.
17
+ """
18
+ df = pd.DataFrame(items).dropna(how=how)
19
+ df.reset_index(drop=True, inplace=True)
20
+ return df
21
+
22
+ def pd_row_to_node(row: pd.Series):
23
+ """
24
+ Converts a pandas Series row to a DataNode object with structured data.
25
+
26
+ Args:
27
+ row (pd.Series): A pandas Series containing row data to be converted.
28
+
29
+ Returns:
30
+ DataNode: A DataNode object containing structured data from the input pandas Series.
31
+ """
32
+ dict_ = row.to_dict()
33
+ dict_['datetime'] = str(timestamp_to_datetime(dict_['datetime']))
34
+ dict_['content'] = {'headline': dict_.pop('headline'), 'summary': dict_.pop('summary')}
35
+ dict_['metadata'] = {'datetime': dict_.pop('datetime'), 'url': dict_.pop('url'), 'id': dict_.pop('id')}
36
+ return DataNode.from_dict(dict_)
37
+
38
+ def expand_df_datetime(df: pd.DataFrame) -> pd.DataFrame:
39
+ """
40
+ Expands a pandas DataFrame with a datetime column into separate year, month, and day columns.
41
+
42
+ Args:
43
+ df (pd.DataFrame): The pandas DataFrame containing a 'datetime' column to be expanded.
44
+
45
+ Returns:
46
+ pd.DataFrame: A pandas DataFrame with 'year', 'month', and 'day' columns.
47
+ """
48
+ df_expanded = df.copy()
49
+ if df_expanded['datetime'].dtype == int:
50
+ df_expanded['datetime'] = df_expanded['datetime'].apply(lambda x: timestamp_to_datetime(x))
51
+
52
+ df_expanded.insert(0, 'year', df_expanded['datetime'].dt.year)
53
+ df_expanded.insert(1, 'month', df_expanded['datetime'].dt.month)
54
+ df_expanded.insert(2, 'day', df_expanded['datetime'].dt.day)
55
+ df_expanded.drop('datetime', axis=1, inplace=True)
56
+
57
+ return df_expanded
lionagi/utils/sys_util.py CHANGED
@@ -1,226 +1,354 @@
1
- """
2
- Copyright 2023 HaiyangLi <ocean@lionagi.ai>
1
+ import os
2
+ import copy
3
+ from datetime import datetime
4
+ import hashlib
5
+ import re
3
6
 
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
+ import json
8
+ import logging
7
9
 
8
- http://www.apache.org/licenses/LICENSE-2.0
10
+ from typing import Any, List, Dict, Union
9
11
 
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
12
 
17
- # this module has no internal dependency
18
- import os
19
- import copy
20
- import hashlib
21
- from pathlib import Path
22
- from datetime import datetime
23
- from typing import Any, Generator, List, Dict
13
+ def get_timestamp() -> str:
14
+ """
15
+ Generates a current timestamp in ISO format with colons and periods replaced by
16
+ underscores.
17
+
18
+ Returns:
19
+ str: The current timestamp.
24
20
 
25
- def create_copy(input: Any, n: int) -> Any:
21
+ Examples:
22
+ >>> isinstance(get_timestamp(), str)
23
+ True
26
24
  """
27
- Creates a deep copy of the input object a specified number of times.
25
+ return datetime.now().isoformat().replace(":", "_").replace(".", "_")
28
26
 
29
- This function makes deep copies of the provided input. If the number of copies ('n')
30
- is greater than 1, a list of deep copies is returned. For a single copy, it returns
31
- the copy directly.
27
+ def create_copy(input: Any, n: int = 1) -> Any:
28
+ """
29
+ Creates a deep copy of the given input. If 'n' is greater than 1, returns a
30
+ list of deep copies.
32
31
 
33
- Parameters:
32
+ Args:
34
33
  input (Any): The object to be copied.
34
+ n (int, optional): The number of copies to create. Defaults to 1.
35
35
 
36
- n (int): The number of deep copies to create.
36
+ Returns:
37
+ Any: A deep copy of the input, or a list of deep copies.
37
38
 
38
39
  Raises:
39
40
  ValueError: If 'n' is not a positive integer.
40
-
41
- Returns:
42
- Any: A deep copy of 'input' or a list of deep copies if 'n' > 1.
43
-
44
- Example:
45
- >>> sample_dict = {'key': 'value'}
46
- >>> make_copy(sample_dict, 2)
47
- [{'key': 'value'}, {'key': 'value'}]
41
+
42
+ Examples:
43
+ >>> create_copy([1, 2, 3], 2)
44
+ [[1, 2, 3], [1, 2, 3]]
45
+ >>> create_copy("Hello")
46
+ 'Hello'
48
47
  """
49
48
  if not isinstance(n, int) or n < 1:
50
49
  raise ValueError(f"'n' must be a positive integer: {n}")
51
50
  return copy.deepcopy(input) if n == 1 else [copy.deepcopy(input) for _ in range(n)]
52
51
 
53
- def create_id(n=32) -> str:
52
+ def create_path(
53
+ dir: str, filename: str, timestamp: bool = True, dir_exist_ok: bool = True,
54
+ time_prefix: bool = False
55
+ ) -> str:
54
56
  """
55
- Generates a unique ID based on the current time and random bytes.
57
+ Creates a file path with an optional timestamp, ensuring the directory exists.
56
58
 
57
- This function combines the current time in ISO 8601 format with 16 random bytes
58
- to create a unique identifier. The result is hashed using SHA-256 and the first
59
- 16 characters of the hexadecimal digest are returned.
59
+ Args:
60
+ dir (str): The directory in which the file will be placed.
61
+ filename (str): The name of the file.
62
+ timestamp (bool, optional): Flag to include a timestamp in the filename. Defaults to True.
63
+ dir_exist_ok (bool, optional): Flag to create the directory if it doesn't exist. Defaults to True.
64
+ time_prefix (bool, optional): Flag to place the timestamp as a prefix. Defaults to False.
60
65
 
61
66
  Returns:
62
- str: A 16-character unique identifier.
67
+ str: The full path of the file with the optional timestamp.
63
68
 
64
- Example:
65
- >>> create_id() # Doctest: +ELLIPSIS
66
- '...'
69
+ Examples:
70
+ >>> create_path("/tmp", "log.txt", timestamp=False)
71
+ '/tmp/log.txt'
72
+ >>> isinstance(create_path("/tmp", "report", time_prefix=True), str)
73
+ True
67
74
  """
68
- current_time = datetime.now().isoformat().encode('utf-8')
69
- random_bytes = os.urandom(2048)
70
- return hashlib.sha256(current_time + random_bytes).hexdigest()[:n]
75
+ dir = dir + '/' if not dir.endswith('/') else dir
76
+ if '.' in filename:
77
+ name, ext = filename.rsplit('.', 1)
78
+ else:
79
+ name, ext = filename, ''
80
+ os.makedirs(dir, exist_ok=dir_exist_ok)
81
+ timestamp_str = get_timestamp() if timestamp else ''
82
+ filename = f"{timestamp_str}_{name}" if time_prefix else f"{name}_{timestamp_str}"
83
+ return f"{dir}{filename}.{ext}" if ext else f"{dir}{filename}"
71
84
 
72
- def get_timestamp() -> str:
85
+ def split_path(path: str) -> tuple:
73
86
  """
74
- Generates a current timestamp in a file-safe string format.
87
+ Splits a file path into its directory name and file name.
75
88
 
76
- This function creates a timestamp from the current time, formatted in ISO 8601 format,
77
- and replaces characters that are typically problematic in filenames (like colons and periods)
78
- with underscores.
89
+ Args:
90
+ path (str): The file path to split.
79
91
 
80
92
  Returns:
81
- str: The current timestamp in a file-safe string format.
93
+ tuple: A tuple containing the directory name and file name.
82
94
 
83
- Example:
84
- >>> get_timestamp() # Doctest: +ELLIPSIS
85
- '...'
95
+ Examples:
96
+ >>> split_path("/home/user/document.txt")
97
+ ('/home/user', 'document.txt')
98
+ >>> split_path("document.txt")
99
+ ('', 'document.txt')
86
100
  """
87
- return datetime.now().isoformat().replace(":", "_").replace(".", "_")
101
+ folder_name = os.path.dirname(path)
102
+ file_name = os.path.basename(path)
103
+ return (folder_name, file_name)
88
104
 
89
- def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: bool = True, time_prefix=False) -> str:
105
+ def str_to_num(
106
+ input: str, upper_bound: float = None, lower_bound: float = None,
107
+ num_type: type = int, precision: int = None
108
+ ) -> Any:
90
109
  """
91
- Creates a file path by optionally appending a timestamp to the filename.
92
-
93
- This function constructs a file path by combining a directory, an optional timestamp,
94
- and a filename. It also ensures the existence of the directory.
95
-
96
- Parameters:
97
- dir (str): The directory in which the file is to be located.
98
-
99
- filename (str): The name of the file.
100
-
101
- timestamp (bool, optional): If True, appends a timestamp to the filename. Defaults to True.
110
+ Converts the first number in a string to a specified numeric type and checks if it
111
+ falls within specified bounds.
102
112
 
103
- dir_exist_ok (bool, optional): If True, creates the directory if it doesn't exist. Defaults to True.
104
-
105
- time_prefix (bool, optional): If True, the timestamp is added as a prefix; otherwise, it's appended. Defaults to False.
113
+ Args:
114
+ input (str): The string containing the number.
115
+ upper_bound (float, optional): The upper limit for the number. Defaults to None.
116
+ lower_bound (float, optional): The lower limit for the number. Defaults to None.
117
+ num_type (type): The numeric type to which the number will be converted. Default is int.
118
+ precision (int, optional): The precision for floating point numbers. Defaults to None.
106
119
 
107
120
  Returns:
108
- str: The full path to the file.
121
+ Any: The converted number in the specified type.
109
122
 
110
- Example:
111
- >>> create_path('/tmp/', 'log.txt', timestamp=False)
112
- '/tmp/log.txt'
123
+ Raises:
124
+ ValueError: If no numeric value is found, or the number is outside the specified bounds.
125
+
126
+ Examples:
127
+ >>> str_to_num("Value is 20.5", upper_bound=30, num_type=float)
128
+ 20.5
129
+ >>> str_to_num("Temperature -5 degrees", lower_bound=0)
130
+ ValueError: Number -5 is less than the lower bound of 0.
113
131
  """
132
+ number_str = _extract_first_number(input)
133
+ if number_str is None:
134
+ raise ValueError(f"No numeric values found in the string: {input}")
114
135
 
115
- dir = dir + '/' if str(dir)[-1] != '/' else dir
116
- filename, ext = filename.split('.')
117
- os.makedirs(dir, exist_ok=dir_exist_ok)
118
-
119
- if timestamp:
120
- timestamp = get_timestamp()
121
- return f"{dir}{timestamp}_{filename}.{ext}" if time_prefix else f"{dir}{filename}_{timestamp}.{ext}"
122
- else:
123
- return f"{dir}{filename}"
136
+ number = _convert_to_num(number_str, num_type, precision)
124
137
 
125
- def split_path(path: Path) -> tuple:
126
- folder_name = path.parent.name
127
- file_name = path.name
128
- return (folder_name, file_name)
138
+ if upper_bound is not None and number > upper_bound:
139
+ raise ValueError(f"Number {number} is greater than the upper bound of {upper_bound}.")
140
+ if lower_bound is not None and number < lower_bound:
141
+ raise ValueError(f"Number {number} is less than the lower bound of {lower_bound}.")
142
+
143
+ return number
129
144
 
130
- def get_bins(input: List[str], upper: int = 7500) -> List[List[int]]:
145
+ def get_bins(input: List[str], upper: int) -> List[List[int]]:
131
146
  """
132
- Get index of elements in a list based on their consecutive cumulative sum of length,
133
- according to some upper threshold. Return lists of indices as bins.
134
-
135
- Parameters:
136
- input (List[str]): List of items to be binned.
147
+ Distributes a list of strings into bins where the total length of strings in each
148
+ bin is less than a specified upper limit.
149
+
150
+ Args:
151
+ input (List[str]): The list of strings to be distributed.
152
+ upper (int): The upper limit for the total length of strings in each bin.
137
153
 
138
- upper (int, optional): Upper threshold for the cumulative sum of the length of items in a bin. Default is 7500.
139
-
140
154
  Returns:
141
- List[List[int]]: List of lists, where each inner list contains the indices of the items that form a bin.
142
-
143
- Example:
144
- >>> items = ['apple', 'a', 'b', 'banana', 'cheery', 'c', 'd', 'e']
145
- >>> upper = 10
146
- >>> get_bins(items, upper)
147
- [[0, 1, 2], [3], [4, 5, 6, 7]]
155
+ List[List[int]]: A list of lists, where each inner list contains indices of the
156
+ input list that make up a bin.
157
+
158
+ Examples:
159
+ >>> get_bins(["one", "two", "three", "four", "five"], 10)
160
+ [[0, 1], [2], [3, 4]]
148
161
  """
149
162
  current = 0
150
163
  bins = []
151
- bin = []
152
-
164
+ current_bin = []
153
165
  for idx, item in enumerate(input):
154
166
 
155
167
  if current + len(item) < upper:
156
- bin.append(idx)
168
+ current_bin.append(idx)
157
169
  current += len(item)
158
-
159
- elif current + len(item) >= upper:
160
- bins.append(bin)
161
- bin = [idx]
170
+
171
+ else:
172
+ bins.append(current_bin)
173
+ current_bin = [idx]
162
174
  current = len(item)
163
-
164
- if idx == len(input) - 1 and len(bin) > 0:
165
- bins.append(bin)
166
-
175
+
176
+ if current_bin:
177
+ bins.append(current_bin)
178
+
167
179
  return bins
168
180
 
169
- def change_dict_key(dict_, old_key, new_key):
170
- dict_[new_key] = dict_.pop(old_key)
181
+ def create_id(n: int = 32) -> str:
182
+ """
183
+ Creates a unique identifier using a combination of the current time and random bytes.
171
184
 
172
- def timestamp_to_datetime(timestamp: int) -> str:
173
- if isinstance(timestamp, str):
174
- try:
175
- timestamp = int(timestamp)
176
- except:
177
- return timestamp
178
- return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
185
+ Args:
186
+ n (int, optional): The length of the identifier. Defaults to 32.
179
187
 
180
- def is_schema(dict_: Dict, schema: Dict):
181
- for key, expected_type in schema.items():
182
- if not isinstance(dict_[key], expected_type):
183
- return False
184
- return True
188
+ Returns:
189
+ str: The generated unique identifier.
185
190
 
186
- def create_hash(data: str, algorithm: str = 'sha256') -> str:
191
+ Examples:
192
+ >>> len(create_id())
193
+ 32
194
+ >>> len(create_id(16))
195
+ 16
196
+ """
197
+ current_time = datetime.now().isoformat().encode('utf-8')
198
+ random_bytes = os.urandom(32)
199
+ return hashlib.sha256(current_time + random_bytes).hexdigest()[:n]
200
+
201
+ def directory_cleaner(directory: str) -> None:
202
+ """
203
+ Deletes all files within a given directory.
204
+
205
+ Args:
206
+ directory (str): The path of the directory to clean.
207
+
208
+ Raises:
209
+ FileNotFoundError: If the specified directory does not exist.
210
+ Exception: If a file in the directory cannot be deleted.
211
+
212
+ Examples:
213
+ >>> directory_cleaner("/path/to/nonexistent/directory")
214
+ FileNotFoundError: The specified directory does not exist.
187
215
  """
188
- Generates a hash for the given data using the specified algorithm.
216
+ if not os.path.exists(directory):
217
+ raise FileNotFoundError("The specified directory does not exist.")
218
+
219
+ for filename in os.listdir(directory):
220
+ file_path = os.path.join(directory, filename)
221
+ try:
222
+ if os.path.isfile(file_path) or os.path.islink(file_path):
223
+ os.unlink(file_path)
224
+ logging.info(f'Successfully deleted {file_path}')
225
+ except Exception as e:
226
+ logging.error(f'Failed to delete {file_path}. Reason: {e}')
227
+ raise
228
+
229
+ def strip_lower(input: Any) -> str:
230
+ """
231
+ Strips and converts a given input to lower case.
189
232
 
190
- Parameters:
191
- data (str): The data to be hashed.
192
- algorithm (str): The hashing algorithm to use (e.g., 'sha256', 'md5').
233
+ Args:
234
+ input (Any): The input to be processed.
193
235
 
194
236
  Returns:
195
- str: The generated hash string.
237
+ str: The processed string in lower case.
238
+
239
+ Raises:
240
+ Exception: If the input cannot be converted to a string.
241
+
242
+ Examples:
243
+ >>> strip_lower(" Hello WORLD ")
244
+ 'hello world'
245
+ >>> strip_lower(123)
246
+ '123'
196
247
  """
197
- hasher = hashlib.new(algorithm)
198
- hasher.update(data.encode())
199
- return hasher.hexdigest()
248
+ try:
249
+ return str(input).strip().lower()
250
+ except:
251
+ return False
200
252
 
253
+ def _extract_first_number(inputstr: str) -> str:
254
+ """
255
+ Extracts the first number from a given string.
201
256
 
257
+ Args:
258
+ inputstr (str): The string from which the number will be extracted.
202
259
 
203
- # def parse_function_call(response: str) -> Tuple[str, Dict]:
204
- # out = json.loads(response)
205
- # func = out.get('function', '').lstrip('call_')
206
- # args = json.loads(out.get('arguments', '{}'))
207
- # return func, args
260
+ Returns:
261
+ str: The first number found in the string, returned as a string.
262
+ Returns None if no number is found.
263
+
264
+ Examples:
265
+ >>> extract_first_number("The 2 little pigs")
266
+ '2'
267
+ >>> extract_first_number("No numbers")
268
+ None
269
+ """
270
+ numbers = re.findall(r'-?\d+\.?\d*', inputstr)
271
+ return numbers[0] if numbers else None
208
272
 
209
- # ------------------------------------------------------------------------
210
- # credit to OpenAI for the following functions
211
- def task_id_generator() -> Generator[int, None, None]:
273
+ def _convert_to_num(number_str: str, num_type: type = int, precision: int = None) -> Any:
212
274
  """
213
- A generator function that yields a sequential series of task IDs.
275
+ Converts a string representation of a number to a specified numeric type.
276
+
277
+ Args:
278
+ number_str (str): The string representation of the number.
279
+ num_type (type): The type to which the number will be converted. Default is int.
280
+ precision (int, optional): The precision for floating point numbers. Defaults to None.
281
+
282
+ Returns:
283
+ Any: The converted number in the specified type.
214
284
 
215
- Yields:
216
- int: The next task ID in the sequence, starting from 0.
285
+ Raises:
286
+ ValueError: If an invalid number type is provided.
217
287
 
218
288
  Examples:
219
- task_id_gen = task_id_generator()
220
- next(task_id_gen) # Yields 0
221
- next(task_id_gen) # Yields 1
222
- """
223
- task_id = 0
224
- while True:
225
- yield task_id
226
- task_id += 1
289
+ >>> convert_to_num("3.142", float, 2)
290
+ 3.14
291
+ >>> convert_to_num("100", int)
292
+ 100
293
+ """
294
+ if num_type is int:
295
+ return int(float(number_str))
296
+ elif num_type is float:
297
+ return round(float(number_str), precision) if precision is not None else float(number_str)
298
+ else:
299
+ raise ValueError(f"Invalid number type: {num_type}")
300
+
301
+ def change_dict_key(dict_: Dict[Any, Any], old_key: str, new_key: str) -> None:
302
+ """
303
+ Changes a key in a dictionary to a new key.
304
+
305
+ Args:
306
+ dict_ (Dict[Any, Any]): The dictionary to change the key in.
307
+ old_key (str): The old key to be changed.
308
+ new_key (str): The new key to change to.
309
+ """
310
+ if old_key in dict_:
311
+ dict_[new_key] = dict_.pop(old_key)
312
+
313
+ def as_dict(input_: Any) -> Dict[Any, Any]:
314
+ """
315
+ Converts a JSON string or a dictionary to a dictionary.
316
+
317
+ Args:
318
+ input_: A JSON string or dictionary to convert.
319
+
320
+ Returns:
321
+ The input converted to a dictionary.
322
+
323
+ Raises:
324
+ ValueError: If the input cannot be converted to a dictionary.
325
+ """
326
+ if isinstance(input_, str):
327
+ try:
328
+ return json.loads(input_)
329
+ except Exception as e:
330
+ raise f"Could not convert input to dict: {e}"
331
+ elif isinstance(input_, dict):
332
+ return input_
333
+ else:
334
+ raise f"Could not convert input to dict: {input_}"
335
+
336
+ def is_schema(dict_: Dict, schema: Dict):
337
+ """
338
+ Checks if a dictionary matches a given schema.
339
+
340
+ Args:
341
+ dict_: The dictionary to check.
342
+ schema: The schema to validate against.
343
+
344
+ Returns:
345
+ True if the dictionary matches the schema, False otherwise.
346
+ """
347
+ for key, expected_type in schema.items():
348
+ if not isinstance(dict_[key], expected_type):
349
+ return False
350
+ return True
351
+
352
+ def timestamp_to_datetime(timestamp):
353
+ return datetime.fromtimestamp(timestamp)
354
+
@@ -0,0 +1,55 @@
1
+ from bs4 import BeautifulSoup
2
+ import requests
3
+
4
+ def get_url_response(url: str, timeout: tuple = (1, 1), **kwargs) -> requests.Response:
5
+ """
6
+ Sends a GET request to a URL and returns the response.
7
+
8
+ Args:
9
+ url (str): The URL to send the GET request to.
10
+ timeout (tuple): A tuple specifying the connection and read timeouts in seconds.
11
+ Defaults to (1, 1).
12
+ **kwargs: Additional keyword arguments to be passed to the requests.get() function.
13
+
14
+ Returns:
15
+ requests.Response: A Response object containing the server's response to the GET request.
16
+
17
+ Raises:
18
+ TimeoutError: If a timeout occurs while requesting or reading the response.
19
+ Exception: If an error other than a timeout occurs during the request.
20
+ """
21
+ try:
22
+ response = requests.get(url, timeout=timeout, **kwargs)
23
+ response.raise_for_status()
24
+ return response
25
+ except requests.exceptions.ConnectTimeout:
26
+ raise TimeoutError(f"Timeout: requesting >{timeout[0]} seconds.")
27
+ except requests.exceptions.ReadTimeout:
28
+ raise TimeoutError(f"Timeout: reading >{timeout[1]} seconds.")
29
+ except Exception as e:
30
+ raise e
31
+
32
+ def get_url_content(url: str) -> str:
33
+ """
34
+ Retrieve and parse the content from a given URL.
35
+
36
+ Args:
37
+ url (str): The URL to fetch and parse.
38
+
39
+ Returns:
40
+ str: The text content extracted from the URL.
41
+
42
+ Raises:
43
+ ValueError: If there is an issue during content retrieval or parsing.
44
+ """
45
+ try:
46
+ response = requests.get(url)
47
+ response.raise_for_status()
48
+
49
+ soup = BeautifulSoup(response.text, 'html.parser')
50
+
51
+ text_content = ' '.join([p.get_text() for p in soup.find_all('p')])
52
+ return text_content
53
+ except Exception as e:
54
+ raise f"Error fetching content for {url}: {e}"
55
+
lionagi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.115"
1
+ __version__ = "0.0.204"