lionagi 0.0.114__py3-none-any.whl → 0.0.116__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 (99) hide show
  1. lionagi/__init__.py +7 -4
  2. lionagi/bridge/__init__.py +19 -4
  3. lionagi/bridge/langchain.py +23 -3
  4. lionagi/bridge/llama_index.py +5 -3
  5. lionagi/configs/__init__.py +1 -1
  6. lionagi/configs/oai_configs.py +88 -1
  7. lionagi/core/__init__.py +6 -9
  8. lionagi/core/conversations/__init__.py +5 -0
  9. lionagi/core/conversations/conversation.py +107 -0
  10. lionagi/core/flows/__init__.py +8 -0
  11. lionagi/core/flows/flow.py +8 -0
  12. lionagi/core/flows/flow_util.py +62 -0
  13. lionagi/core/instruction_set/__init__.py +5 -0
  14. lionagi/core/instruction_set/instruction_sets.py +7 -0
  15. lionagi/core/sessions/__init__.py +5 -0
  16. lionagi/core/sessions/sessions.py +187 -0
  17. lionagi/endpoints/__init__.py +5 -0
  18. lionagi/endpoints/assistants.py +0 -0
  19. lionagi/endpoints/audio.py +17 -0
  20. lionagi/endpoints/chatcompletion.py +54 -0
  21. lionagi/endpoints/embeddings.py +0 -0
  22. lionagi/endpoints/finetune.py +0 -0
  23. lionagi/endpoints/image.py +0 -0
  24. lionagi/endpoints/moderation.py +0 -0
  25. lionagi/endpoints/vision.py +0 -0
  26. lionagi/{loader → loaders}/__init__.py +7 -1
  27. lionagi/{loader → loaders}/chunker.py +6 -12
  28. lionagi/{utils/load_utils.py → loaders/load_util.py} +47 -6
  29. lionagi/{loader → loaders}/reader.py +4 -12
  30. lionagi/messages/__init__.py +11 -0
  31. lionagi/messages/instruction.py +15 -0
  32. lionagi/messages/message.py +110 -0
  33. lionagi/messages/response.py +33 -0
  34. lionagi/messages/system.py +12 -0
  35. lionagi/objs/__init__.py +10 -6
  36. lionagi/objs/abc_objs.py +39 -0
  37. lionagi/objs/async_queue.py +135 -0
  38. lionagi/objs/messenger.py +70 -148
  39. lionagi/objs/status_tracker.py +37 -0
  40. lionagi/objs/{tool_registry.py → tool_manager.py} +8 -6
  41. lionagi/schema/__init__.py +3 -3
  42. lionagi/schema/base_node.py +251 -0
  43. lionagi/schema/base_tool.py +8 -3
  44. lionagi/schema/data_logger.py +2 -3
  45. lionagi/schema/data_node.py +37 -0
  46. lionagi/services/__init__.py +1 -4
  47. lionagi/services/base_api_service.py +15 -5
  48. lionagi/services/oai.py +2 -2
  49. lionagi/services/openrouter.py +2 -3
  50. lionagi/structures/graph.py +96 -0
  51. lionagi/{structure → structures}/relationship.py +10 -2
  52. lionagi/structures/structure.py +102 -0
  53. lionagi/tests/test_api_util.py +46 -0
  54. lionagi/tests/test_call_util.py +115 -0
  55. lionagi/tests/test_convert_util.py +202 -0
  56. lionagi/tests/test_encrypt_util.py +33 -0
  57. lionagi/tests/{test_flatten_util.py → test_flat_util.py} +1 -1
  58. lionagi/tests/test_io_util.py +0 -0
  59. lionagi/tests/test_sys_util.py +0 -0
  60. lionagi/tools/__init__.py +5 -0
  61. lionagi/tools/tool_util.py +7 -0
  62. lionagi/utils/__init__.py +55 -35
  63. lionagi/utils/api_util.py +19 -17
  64. lionagi/utils/call_util.py +2 -1
  65. lionagi/utils/convert_util.py +229 -0
  66. lionagi/utils/encrypt_util.py +16 -0
  67. lionagi/utils/flat_util.py +38 -0
  68. lionagi/utils/io_util.py +2 -2
  69. lionagi/utils/sys_util.py +45 -10
  70. lionagi/version.py +1 -1
  71. {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/METADATA +2 -2
  72. lionagi-0.0.116.dist-info/RECORD +110 -0
  73. lionagi/core/conversations.py +0 -108
  74. lionagi/core/flows.py +0 -1
  75. lionagi/core/instruction_sets.py +0 -1
  76. lionagi/core/messages.py +0 -166
  77. lionagi/core/sessions.py +0 -297
  78. lionagi/schema/base_schema.py +0 -252
  79. lionagi/services/chatcompletion.py +0 -48
  80. lionagi/services/service_objs.py +0 -282
  81. lionagi/structure/structure.py +0 -160
  82. lionagi/tools/coder.py +0 -1
  83. lionagi/tools/sandbox.py +0 -1
  84. lionagi/utils/tool_util.py +0 -92
  85. lionagi/utils/type_util.py +0 -81
  86. lionagi-0.0.114.dist-info/RECORD +0 -84
  87. /lionagi/configs/{openrouter_config.py → openrouter_configs.py} +0 -0
  88. /lionagi/{datastore → datastores}/__init__.py +0 -0
  89. /lionagi/{datastore → datastores}/chroma.py +0 -0
  90. /lionagi/{datastore → datastores}/deeplake.py +0 -0
  91. /lionagi/{datastore → datastores}/elasticsearch.py +0 -0
  92. /lionagi/{datastore → datastores}/lantern.py +0 -0
  93. /lionagi/{datastore → datastores}/pinecone.py +0 -0
  94. /lionagi/{datastore → datastores}/postgres.py +0 -0
  95. /lionagi/{datastore → datastores}/qdrant.py +0 -0
  96. /lionagi/{structure → structures}/__init__.py +0 -0
  97. {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/LICENSE +0 -0
  98. {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/WHEEL +0 -0
  99. {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/top_level.txt +0 -0
@@ -1,282 +0,0 @@
1
- import asyncio
2
- from abc import ABC, abstractmethod
3
- from dataclasses import dataclass
4
- from typing import Any, Callable, Dict, NoReturn
5
-
6
-
7
- # should be fine ------------------------------------------------------------------
8
- @dataclass
9
- class StatusTracker:
10
- """
11
- Class for keeping track of various task statuses.
12
-
13
- This class serves as a simple way to monitor different types of task
14
- outcomes and errors within a system. It uses dataclasses for easy
15
- creation and management of state.
16
-
17
- Attributes:
18
- num_tasks_started:
19
- The number of tasks that have been initiated.
20
- num_tasks_in_progress:
21
- The number of tasks currently being processed.
22
- num_tasks_succeeded:
23
- The number of tasks that have completed successfully.
24
- num_tasks_failed:
25
- The number of tasks that have failed.
26
- num_rate_limit_errors:
27
- The number of tasks that failed due to rate limiting.
28
- num_api_errors:
29
- The number of tasks that failed due to API errors.
30
- num_other_errors:
31
- The number of tasks that failed due to other errors.
32
- """
33
- num_tasks_started: int = 0
34
- num_tasks_in_progress: int = 0
35
- num_tasks_succeeded: int = 0
36
- num_tasks_failed: int = 0
37
- num_rate_limit_errors: int = 0
38
- num_api_errors: int = 0
39
- num_other_errors: int = 0
40
-
41
-
42
- class AsyncQueue:
43
- """
44
- A queue class that handles asynchronous operations using asyncio.
45
-
46
- This class provides an asynchronous queue that can enqueue items, process them
47
- asynchronously, and support graceful shutdowns. It is designed to facilitate
48
- concurrent task processing in an orderly and controlled manner.
49
-
50
- Attributes:
51
- queue (asyncio.Queue):
52
- A queue to hold items for asynchronous processing.
53
- _stop_event (asyncio.Event):
54
- An event to signal when the queue should stop processing.
55
-
56
- Methods:
57
- enqueue(item):
58
- Add an item to the queue for processing.
59
- dequeue():
60
- Remove and return an item from the queue.
61
- join():
62
- Wait until all items in the queue have been processed.
63
- stop():
64
- Signal to stop processing new items in the queue.
65
- stopped():
66
- Check if the queue has been signaled to stop.
67
- process_requests(func):
68
- Process items using a provided function.
69
- """
70
-
71
- def __init__(self) -> None:
72
- """
73
- Initializes an AsyncQueue object with an empty asyncio Queue and a stop event.
74
- """
75
- self.queue = asyncio.Queue()
76
- self._stop_event = asyncio.Event()
77
-
78
- async def enqueue(self, item: Any) -> None:
79
- """
80
- Asynchronously add an item to the queue for processing.
81
-
82
- Parameters:
83
- item (Any): The item to be added to the queue.
84
-
85
- Example:
86
- >>> async_queue = AsyncQueue()
87
- >>> asyncio.run(async_queue.enqueue('Task 1'))
88
- """
89
- await self.queue.put(item)
90
-
91
- async def dequeue(self) -> Any:
92
- """
93
- Asynchronously remove and return an item from the queue.
94
-
95
- If the queue is empty, this method will wait until an item is available.
96
-
97
- Returns:
98
- Any: The next item from the queue.
99
-
100
- Example:
101
- >>> async_queue = AsyncQueue()
102
- >>> asyncio.run(async_queue.enqueue('Task 1'))
103
- >>> asyncio.run(async_queue.dequeue())
104
- 'Task 1'
105
- """
106
- return await self.queue.get()
107
-
108
- async def join(self) -> None:
109
- """
110
- Asynchronously wait until all items in the queue have been processed.
111
-
112
- This method blocks until every item that has been enqueued is processed,
113
- ensuring that all tasks are completed.
114
-
115
- Example:
116
- >>> async_queue = AsyncQueue()
117
- >>> asyncio.run(async_queue.enqueue('Task 1'))
118
- >>> asyncio.run(async_queue.join()) # This will block until 'Task 1' is processed.
119
- """
120
- await self.queue.join()
121
-
122
- async def stop(self) -> None:
123
- """
124
- Signal the queue to stop processing new items.
125
-
126
- Once called, the queue will not process any new items after the current ones
127
- are completed, allowing for a graceful shutdown.
128
-
129
- Example:
130
- >>> async_queue = AsyncQueue()
131
- >>> asyncio.run(async_queue.stop()) # This signals the queue to stop processing.
132
- """
133
- self._stop_event.set()
134
-
135
- def stopped(self) -> bool:
136
- """
137
- Check if the queue has been signaled to stop processing.
138
-
139
- Returns:
140
- bool: True if a stop has been signaled, False otherwise.
141
-
142
- Example:
143
- >>> async_queue = AsyncQueue()
144
- >>> asyncio.run(async_queue.stop())
145
- >>> async_queue.stopped()
146
- True
147
- """
148
- return self._stop_event.is_set()
149
-
150
- async def process_requests(self, func: Callable[[Any], Any]) -> None:
151
- """
152
- Asynchronously process items from the queue using the provided function.
153
-
154
- Continuously dequeues items and applies the given function to each.
155
- The processing stops when the queue is signaled to stop or a sentinel value (`None`) is dequeued.
156
-
157
- Parameters:
158
- func (Callable[[Any], Any]): A coroutine function to process items from the queue.
159
-
160
- Example:
161
- >>> async def sample_processing(task):
162
- ... print("Processing:", task)
163
- >>> async_queue = AsyncQueue()
164
- >>> asyncio.run(async_queue.enqueue('Task 1'))
165
- >>> asyncio.run(async_queue.process_requests(sample_processing))
166
- Processing: Task 1
167
- """
168
- while not self.stopped():
169
- item = await self.dequeue()
170
- if item is None: # Using `None` as a sentinel value to cease processing.
171
- await self.stop()
172
- break
173
- await func(item)
174
-
175
-
176
- class BaseService(ABC):
177
-
178
- @abstractmethod
179
- def __init__(self) -> None:
180
- ...
181
-
182
- @abstractmethod
183
- async def serve(self) -> Any:
184
- ...
185
-
186
-
187
-
188
- class RateLimiter(ABC):
189
- """
190
- An abstract base class for rate limiting mechanisms.
191
-
192
- This class defines a structure for rate limiters, which are used to control the frequency
193
- of requests sent to or received from a network interface controller or an API.
194
-
195
- Attributes:
196
- max_requests_per_minute (int):
197
- Maximum number of requests permitted per minute.
198
- max_tokens_per_minute (int):
199
- Maximum number of tokens that can accumulate per minute.
200
- available_request_capacity (int):
201
- Current number of available request slots.
202
- available_token_capacity (int):
203
- Current number of available tokens.
204
-
205
- Methods:
206
- rate_limit_replenisher:
207
- Coroutine to replenish rate limits over time.
208
- calculate_num_token:
209
- Method to calculate required tokens for a request.
210
- """
211
-
212
- def __init__(self, max_requests_per_minute: int, max_tokens_per_minute: int) -> None:
213
- """
214
- Initializes the RateLimiter with specified maximum request and token limits.
215
-
216
- Parameters:
217
- max_requests_per_minute (int): Maximum requests allowed per minute.
218
-
219
- max_tokens_per_minute (int): Maximum tokens allowed to accumulate per minute.
220
-
221
- Example:
222
- >>> class MyRateLimiter(RateLimiter):
223
- ... async def rate_limit_replenisher(self) -> NoReturn:
224
- ... # Implementation for rate replenishment.
225
- ... def calculate_num_token(self, payload: Dict[str, Any], api_endpoint: str) -> int:
226
- ... # Implementation for token calculation.
227
- ...
228
- >>> limiter = MyRateLimiter(100, 200)
229
- """
230
- self.max_requests_per_minute = max_requests_per_minute
231
- self.max_tokens_per_minute = max_tokens_per_minute
232
- self.available_request_capacity = max_requests_per_minute
233
- self.available_token_capacity = max_tokens_per_minute
234
-
235
- @abstractmethod
236
- async def rate_limit_replenisher(self) -> NoReturn:
237
- """
238
- Asynchronously replenishes rate limit capacities.
239
-
240
- This coroutine should be implemented to periodically restore `available_request_capacity`
241
- and `available_token_capacity` according to specific rules defined in subclasses.
242
-
243
- Example:
244
- >>> class MyRateLimiter(RateLimiter):
245
- ... async def rate_limit_replenisher(self) -> NoReturn:
246
- ... while True:
247
- ... # Replenishment logic here
248
- ...
249
- >>> limiter = MyRateLimiter(100, 200)
250
- """
251
-
252
- ...
253
-
254
- @abstractmethod
255
- def calculate_num_token(self, payload: Dict[str, Any], api_endpoint: str) -> int:
256
- """
257
- Calculates required tokens for a request.
258
-
259
- Subclasses should implement this method to determine the number of tokens needed based
260
- on the request payload and target endpoint.
261
-
262
- Parameters:
263
- payload (Dict[str, Any]): Payload of the request.
264
-
265
- api_endpoint (str): Target API endpoint for the request.
266
-
267
- Returns:
268
- int: Calculated number of tokens required for the request.
269
-
270
- Example:
271
- >>> class MyRateLimiter(RateLimiter):
272
- ... def calculate_num_token(self, payload: Dict[str, Any], api_endpoint: str) -> int:
273
- ... return len(payload.get('data', '')) // 10
274
- ...
275
- >>> limiter = MyRateLimiter(100, 200)
276
- >>> limiter.calculate_num_token({'data': '12345'}, 'api/send')
277
- 0
278
- """
279
-
280
- ...
281
-
282
-
@@ -1,160 +0,0 @@
1
- from typing import TypeVar, Dict, Optional, Any, Type, Union, List
2
- from pydantic import Field
3
- from ..schema.base_schema import BaseNode
4
- from .relationship import Relationship
5
-
6
- T = TypeVar('T', bound='BaseNode')
7
- R = TypeVar('R', bound='Relationship')
8
-
9
-
10
- class Structure(BaseNode):
11
- """
12
- Represents the structure of a graph consisting of nodes and relationships.
13
- """
14
- nodes: Dict[str, T] = Field(default_factory=dict)
15
- relationships: Dict[str, R] = Field(default_factory=dict)
16
- node_relationships: Dict[str, Dict[str, Dict[str, str]]] = Field(default_factory=dict)
17
-
18
- def add_node(self, node: T) -> None:
19
- """
20
- Adds a node to the structure.
21
-
22
- Args:
23
- node (T): The node instance to be added.
24
- """
25
- self.nodes[node.id_] = node
26
- self.node_relationships[node.id_] = {'in': {}, 'out': {}}
27
-
28
- def add_relationship(self, relationship: R) -> None:
29
- """
30
- Adds a relationship to the structure.
31
-
32
- Args:
33
- relationship (R): The relationship instance to be added.
34
- """
35
- id_, source_, target_ = (
36
- relationship.id_, relationship.source_node_id, relationship.target_node_id
37
- )
38
-
39
- self.relationships.update({id_ : relationship})
40
- self.node_relationships[source_]['out'].update({id_ : target_})
41
- self.node_relationships[target_]['in'].update({id_ : source_})
42
-
43
- # type can be dict or list
44
- @staticmethod
45
- def _typed_return(type: Type[Union[Dict, List]],
46
- obj: Optional[Dict[str, Any]] = None
47
- ) -> Union[Dict[str, Any], List[Any]]:
48
- """
49
- Returns the object in the specified type format.
50
-
51
- Args:
52
- type (Type[Union[Dict, List]]): The type to return the object as (dict or list).
53
-
54
- obj (Optional[Dict[str, Any]]): The object to be converted.
55
-
56
- Returns:
57
- Union[Dict[str, Any], List[Any]]: The object in the specified type format.
58
- """
59
- if type is list:
60
- return list(obj.values())
61
- return obj
62
-
63
- def get_relationships(self, type: Type = dict) -> Union[Dict[str, R], List[R]]:
64
- """
65
- Returns the relationships in the specified type format.
66
-
67
- Args:
68
- type (Type): The type to return the relationships as (dict or list).
69
-
70
- Returns:
71
- Union[Dict[str, R], List[R]]: The relationships in the specified type format.
72
- """
73
- return self._typed_return(self.relationships, type=type)
74
-
75
- def get_node_relationships(self, id_: str, in_out: str, type: Type = dict
76
- ) -> Union[Dict[str, str], List[str]]:
77
- """
78
- Returns the relationships of a node in the specified type format.
79
-
80
- Args:
81
- id_ (str): The ID of the node.
82
-
83
- in_out (str): 'in' for incoming relationships, 'out' for outgoing relationships.
84
-
85
- type (Type): The type to return the relationships as (dict or list).
86
-
87
- Returns:
88
- Union[Dict[str, str], List[str]]: The relationships of the node in the specified type format.
89
- """
90
- node_relationships = self.node_relationships[id_][in_out]
91
- return self._typed_return(node_relationships, type=type)
92
-
93
- def node_exist(self, node: Union[T, str]) -> bool:
94
- """
95
- Checks if a node exists in the structure.
96
-
97
- Args:
98
- node (Union[T, str]): The node instance or node ID to check for existence.
99
-
100
- Returns:
101
- bool: True if the node exists, False otherwise.
102
- """
103
-
104
- return node.id_ in self.nodes.keys()
105
-
106
- def relationship_exist(self, relationship: R) -> bool:
107
- """
108
- Checks if a relationship exists in the structure.
109
-
110
- Args:
111
- relationship (R): The relationship instance to check for existence.
112
-
113
- Returns:
114
- bool: True if the relationship exists, False otherwise.
115
- """
116
- return relationship.id_ in self.relationships.keys()
117
-
118
- def remove_node_relationships(self, relationship_dict: Dict[str, str], in_out: str) -> None:
119
- """
120
- Removes relationships of a node from the structure.
121
-
122
- Args:
123
- relationship_dict (Dict[str, str]): A dictionary of relationship IDs to node IDs.
124
-
125
- in_out (str): 'in' to remove incoming relationships, 'out' to remove outgoing relationships.
126
- """
127
- for relationship_id, node_id in relationship_dict.items():
128
- self.node_relationships[node_id][in_out].pop(relationship_id)
129
- self.relationships.pop(relationship_id)
130
-
131
- def remove_node(self, node: Union[T, str]) -> None:
132
- """
133
- Removes a node and its associated relationships from the structure.
134
-
135
- Args:
136
- node (Union[T, str]): The node instance or node ID to be removed.
137
- """
138
- node_id = node if isinstance(node, str) else node.id_
139
- out_ = self.get_node_relationships(node_id, 'out')
140
- in_ = self.get_node_relationships(node_id, 'in')
141
-
142
- self.remove_node_relationships(out_, 'in')
143
- self.remove_node_relationships(in_, 'out')
144
- self.node_relationships.pop(node_id)
145
-
146
- def remove_relationship(self, relationship: R) -> None:
147
- """
148
- Removes a relationship from the structure.
149
-
150
- Args:
151
- relationship (R): The relationship instance to be removed.
152
- """
153
- id_, source_, target_ = (relationship.id_,
154
- relationship.source_node_id,
155
- relationship.target_node_id)
156
-
157
- self.node_relationships[source_]['out'].pop(id_)
158
- self.node_relationships[target_]['in'].pop(id_)
159
- self.relationships.pop(id_)
160
-
lionagi/tools/coder.py DELETED
@@ -1 +0,0 @@
1
- # TODO
lionagi/tools/sandbox.py DELETED
@@ -1 +0,0 @@
1
- # TODO
@@ -1,92 +0,0 @@
1
- import inspect
2
- from ..schema.base_tool import Tool
3
-
4
-
5
- def extract_docstring_details(func):
6
- """
7
- Extracts detailed descriptions for each parameter and the function from the docstring.
8
-
9
- Parameters:
10
- func (function): The function to extract details from.
11
-
12
- Returns:
13
- Tuple[str, dict]: Function description and a dictionary of parameter descriptions.
14
- """
15
- docstring = inspect.getdoc(func)
16
- if not docstring:
17
- return "No description available.", {}
18
-
19
- # Splitting the docstring into lines
20
- lines = docstring.split('\n')
21
-
22
- # Extracting the function description
23
- func_description = lines[0].strip()
24
-
25
- # Extracting parameter descriptions
26
- param_descriptions = {}
27
- current_param = None
28
- for line in lines[1:]:
29
- line = line.strip()
30
- if line.startswith(':param'):
31
- _, param, desc = line.split(' ', 2)
32
- current_param = param.strip(':')
33
- param_descriptions[current_param] = desc
34
- elif current_param and line:
35
- # Continue the description of the current parameter
36
- param_descriptions[current_param] += ' ' + line
37
-
38
- return func_description, param_descriptions
39
-
40
- def func_to_schema(func):
41
- """
42
- Generates a schema description for a given function, using typing hints and docstrings.
43
- The schema includes the function's name, description, and parameters.
44
-
45
- Parameters:
46
- func (function): The function to generate a schema for.
47
-
48
- Returns:
49
- dict: A schema describing the function.
50
- """
51
- # Extracting function name and docstring details
52
- func_name = func.__name__
53
- func_description, param_descriptions = extract_docstring_details(func)
54
-
55
- # Extracting parameters with typing hints
56
- sig = inspect.signature(func)
57
- parameters = {
58
- "type": "object",
59
- "properties": {},
60
- "required": [],
61
- }
62
-
63
- for name, param in sig.parameters.items():
64
- # Default type to string and update if type hint is available
65
- param_type = "string"
66
- if param.annotation is not inspect.Parameter.empty:
67
- param_type = param.annotation.__name__
68
-
69
- # Extract parameter description from docstring, if available
70
- param_description = param_descriptions.get(name, "No description available.")
71
-
72
- # Assuming all parameters are required for simplicity
73
- parameters["required"].append(name)
74
- parameters["properties"][name] = {
75
- "type": param_type,
76
- "description": param_description,
77
- }
78
-
79
- # Constructing the schema
80
- schema = {
81
- "type": "function",
82
- "function": {
83
- "name": func_name,
84
- "description": func_description,
85
- "parameters": parameters,
86
- }
87
- }
88
- return schema
89
-
90
- def func_to_tool(func_, schema, parser=None):
91
- # schema = func_to_schema(func_)
92
- return Tool(func=func_, parser=parser, schema_=schema)
@@ -1,81 +0,0 @@
1
- import re
2
- from typing import Optional, Union, Iterable, List, Any, Type
3
-
4
- from .flat_util import flatten_list
5
-
6
-
7
- def str_to_num(input_: str,
8
- upper_bound: Optional[Union[int, float]] = None,
9
- lower_bound: Optional[Union[int, float]] = None,
10
- num_type: Type[Union[int, float]] = int,
11
- precision: Optional[int] = None) -> Union[int, float]:
12
- """
13
- Converts the first number in the input string to the specified numeric type.
14
-
15
- Parameters:
16
- input_ (str): The input string to extract the number from.
17
-
18
- upper_bound (Optional[Union[int, float]]): The upper bound for the number. Defaults to None.
19
-
20
- lower_bound (Optional[Union[int, float]]): The lower bound for the number. Defaults to None.
21
-
22
- num_type (Type[Union[int, float]]): The type of the number to return (int or float). Defaults to int.
23
-
24
- precision (Optional[int]): The precision for the floating-point number. Defaults to None.
25
-
26
- Returns:
27
- Union[int, float]: The converted number.
28
-
29
- Raises:
30
- ValueError: If no numeric values are found in the string or if there are conversion errors.
31
- """
32
- numbers = re.findall(r'-?\d+\.?\d*', input_)
33
- if not numbers:
34
- raise ValueError(f"No numeric values found in the string: {input_}")
35
-
36
- try:
37
- numbers = numbers[0]
38
- if num_type is int:
39
- numbers = int(float(numbers))
40
- elif num_type is float:
41
- numbers = round(float(numbers), precision) if precision is not None else float(numbers)
42
- else:
43
- raise ValueError(f"Invalid number type: {num_type}")
44
- if upper_bound is not None and numbers > upper_bound:
45
- raise ValueError(f"Number {numbers} is greater than the upper bound of {upper_bound}.")
46
- if lower_bound is not None and numbers < lower_bound:
47
- raise ValueError(f"Number {numbers} is less than the lower bound of {lower_bound}.")
48
- return numbers
49
-
50
- except ValueError as e:
51
- raise ValueError(f"Error converting string to number: {e}")
52
-
53
- def to_list(input_: Any, flatten: bool = True, dropna: bool = False) -> List[Any]:
54
- """
55
- Converts the input to a list, optionally flattening it and dropping None values.
56
-
57
- Parameters:
58
- input_ (Any): The input to convert to a list.
59
-
60
- flatten (bool): Whether to flatten the input if it is a nested list. Defaults to True.
61
-
62
- dropna (bool): Whether to drop None values from the list. Defaults to False.
63
-
64
- Returns:
65
- List[Any]: The input converted to a list.
66
-
67
- Raises:
68
- ValueError: If the input cannot be converted to a list.
69
- """
70
- if isinstance(input_, list) and flatten:
71
- input_ = flatten_list(input_)
72
- if dropna:
73
- input_ = [i for i in input_ if i is not None]
74
- elif isinstance(input_, Iterable) and not isinstance(input_, (str, dict)):
75
- try:
76
- input_ = list(input_)
77
- except:
78
- raise ValueError("Input cannot be converted to a list.")
79
- else:
80
- input_ = [input_]
81
- return input_