lionagi 0.0.114__py3-none-any.whl → 0.0.116__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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_