memorisdk 2.0.1__py3-none-any.whl → 2.1.0__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.
Potentially problematic release.
This version of memorisdk might be problematic. Click here for more details.
- memori/__init__.py +3 -3
- memori/agents/conscious_agent.py +289 -77
- memori/agents/memory_agent.py +19 -9
- memori/agents/retrieval_agent.py +59 -51
- memori/config/manager.py +7 -7
- memori/config/memory_manager.py +25 -25
- memori/config/settings.py +13 -6
- memori/core/conversation.py +15 -15
- memori/core/database.py +14 -13
- memori/core/memory.py +376 -105
- memori/core/providers.py +25 -25
- memori/database/__init__.py +11 -0
- memori/database/adapters/__init__.py +11 -0
- memori/database/adapters/mongodb_adapter.py +739 -0
- memori/database/adapters/mysql_adapter.py +8 -8
- memori/database/adapters/postgresql_adapter.py +6 -6
- memori/database/adapters/sqlite_adapter.py +6 -6
- memori/database/auto_creator.py +8 -9
- memori/database/connection_utils.py +5 -5
- memori/database/connectors/__init__.py +11 -0
- memori/database/connectors/base_connector.py +18 -19
- memori/database/connectors/mongodb_connector.py +527 -0
- memori/database/connectors/mysql_connector.py +13 -15
- memori/database/connectors/postgres_connector.py +12 -12
- memori/database/connectors/sqlite_connector.py +11 -11
- memori/database/models.py +2 -2
- memori/database/mongodb_manager.py +1402 -0
- memori/database/queries/base_queries.py +3 -4
- memori/database/queries/chat_queries.py +3 -5
- memori/database/queries/entity_queries.py +3 -5
- memori/database/queries/memory_queries.py +3 -5
- memori/database/query_translator.py +11 -11
- memori/database/schema_generators/__init__.py +11 -0
- memori/database/schema_generators/mongodb_schema_generator.py +666 -0
- memori/database/schema_generators/mysql_schema_generator.py +2 -4
- memori/database/search/__init__.py +11 -0
- memori/database/search/mongodb_search_adapter.py +653 -0
- memori/database/search/mysql_search_adapter.py +8 -8
- memori/database/search/sqlite_search_adapter.py +6 -6
- memori/database/search_service.py +17 -17
- memori/database/sqlalchemy_manager.py +10 -12
- memori/integrations/__init__.py +1 -1
- memori/integrations/anthropic_integration.py +1 -3
- memori/integrations/litellm_integration.py +23 -6
- memori/integrations/openai_integration.py +31 -3
- memori/tools/memory_tool.py +10 -9
- memori/utils/exceptions.py +58 -58
- memori/utils/helpers.py +11 -12
- memori/utils/input_validator.py +10 -12
- memori/utils/logging.py +4 -4
- memori/utils/pydantic_models.py +57 -57
- memori/utils/query_builder.py +20 -20
- memori/utils/security_audit.py +28 -28
- memori/utils/security_integration.py +9 -9
- memori/utils/transaction_manager.py +20 -19
- memori/utils/validators.py +6 -6
- {memorisdk-2.0.1.dist-info → memorisdk-2.1.0.dist-info}/METADATA +22 -12
- memorisdk-2.1.0.dist-info/RECORD +71 -0
- memorisdk-2.0.1.dist-info/RECORD +0 -66
- {memorisdk-2.0.1.dist-info → memorisdk-2.1.0.dist-info}/WHEEL +0 -0
- {memorisdk-2.0.1.dist-info → memorisdk-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {memorisdk-2.0.1.dist-info → memorisdk-2.1.0.dist-info}/top_level.txt +0 -0
memori/utils/exceptions.py
CHANGED
|
@@ -4,7 +4,7 @@ Custom exceptions for Memoriai with comprehensive error handling
|
|
|
4
4
|
|
|
5
5
|
import traceback
|
|
6
6
|
from datetime import datetime
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class MemoriError(Exception):
|
|
@@ -13,9 +13,9 @@ class MemoriError(Exception):
|
|
|
13
13
|
def __init__(
|
|
14
14
|
self,
|
|
15
15
|
message: str,
|
|
16
|
-
error_code:
|
|
17
|
-
context:
|
|
18
|
-
cause:
|
|
16
|
+
error_code: str | None = None,
|
|
17
|
+
context: dict[str, Any] | None = None,
|
|
18
|
+
cause: Exception | None = None,
|
|
19
19
|
):
|
|
20
20
|
super().__init__(message)
|
|
21
21
|
self.message = message
|
|
@@ -25,7 +25,7 @@ class MemoriError(Exception):
|
|
|
25
25
|
self.timestamp = datetime.now()
|
|
26
26
|
self.traceback = traceback.format_exc() if cause else None
|
|
27
27
|
|
|
28
|
-
def to_dict(self) ->
|
|
28
|
+
def to_dict(self) -> dict[str, Any]:
|
|
29
29
|
"""Convert exception to dictionary for logging/serialization"""
|
|
30
30
|
return {
|
|
31
31
|
"error_type": self.__class__.__name__,
|
|
@@ -52,10 +52,10 @@ class DatabaseError(MemoriError):
|
|
|
52
52
|
def __init__(
|
|
53
53
|
self,
|
|
54
54
|
message: str,
|
|
55
|
-
query:
|
|
56
|
-
connection_string:
|
|
57
|
-
error_code:
|
|
58
|
-
cause:
|
|
55
|
+
query: str | None = None,
|
|
56
|
+
connection_string: str | None = None,
|
|
57
|
+
error_code: str | None = None,
|
|
58
|
+
cause: Exception | None = None,
|
|
59
59
|
):
|
|
60
60
|
context = {}
|
|
61
61
|
if query:
|
|
@@ -88,12 +88,12 @@ class AgentError(MemoriError):
|
|
|
88
88
|
def __init__(
|
|
89
89
|
self,
|
|
90
90
|
message: str,
|
|
91
|
-
agent_type:
|
|
92
|
-
model:
|
|
93
|
-
api_endpoint:
|
|
94
|
-
tokens_used:
|
|
95
|
-
error_code:
|
|
96
|
-
cause:
|
|
91
|
+
agent_type: str | None = None,
|
|
92
|
+
model: str | None = None,
|
|
93
|
+
api_endpoint: str | None = None,
|
|
94
|
+
tokens_used: int | None = None,
|
|
95
|
+
error_code: str | None = None,
|
|
96
|
+
cause: Exception | None = None,
|
|
97
97
|
):
|
|
98
98
|
context = {}
|
|
99
99
|
if agent_type:
|
|
@@ -119,10 +119,10 @@ class ConfigurationError(MemoriError):
|
|
|
119
119
|
def __init__(
|
|
120
120
|
self,
|
|
121
121
|
message: str,
|
|
122
|
-
setting_path:
|
|
123
|
-
config_file:
|
|
124
|
-
error_code:
|
|
125
|
-
cause:
|
|
122
|
+
setting_path: str | None = None,
|
|
123
|
+
config_file: str | None = None,
|
|
124
|
+
error_code: str | None = None,
|
|
125
|
+
cause: Exception | None = None,
|
|
126
126
|
):
|
|
127
127
|
context = {}
|
|
128
128
|
if setting_path:
|
|
@@ -144,11 +144,11 @@ class ValidationError(MemoriError):
|
|
|
144
144
|
def __init__(
|
|
145
145
|
self,
|
|
146
146
|
message: str,
|
|
147
|
-
field_name:
|
|
148
|
-
field_value:
|
|
149
|
-
expected_type:
|
|
150
|
-
error_code:
|
|
151
|
-
cause:
|
|
147
|
+
field_name: str | None = None,
|
|
148
|
+
field_value: Any | None = None,
|
|
149
|
+
expected_type: str | None = None,
|
|
150
|
+
error_code: str | None = None,
|
|
151
|
+
cause: Exception | None = None,
|
|
152
152
|
):
|
|
153
153
|
context = {}
|
|
154
154
|
if field_name:
|
|
@@ -172,10 +172,10 @@ class IntegrationError(MemoriError):
|
|
|
172
172
|
def __init__(
|
|
173
173
|
self,
|
|
174
174
|
message: str,
|
|
175
|
-
provider:
|
|
176
|
-
integration_type:
|
|
177
|
-
error_code:
|
|
178
|
-
cause:
|
|
175
|
+
provider: str | None = None,
|
|
176
|
+
integration_type: str | None = None,
|
|
177
|
+
error_code: str | None = None,
|
|
178
|
+
cause: Exception | None = None,
|
|
179
179
|
):
|
|
180
180
|
context = {}
|
|
181
181
|
if provider:
|
|
@@ -197,10 +197,10 @@ class AuthenticationError(MemoriError):
|
|
|
197
197
|
def __init__(
|
|
198
198
|
self,
|
|
199
199
|
message: str,
|
|
200
|
-
auth_type:
|
|
201
|
-
endpoint:
|
|
202
|
-
error_code:
|
|
203
|
-
cause:
|
|
200
|
+
auth_type: str | None = None,
|
|
201
|
+
endpoint: str | None = None,
|
|
202
|
+
error_code: str | None = None,
|
|
203
|
+
cause: Exception | None = None,
|
|
204
204
|
):
|
|
205
205
|
context = {}
|
|
206
206
|
if auth_type:
|
|
@@ -222,11 +222,11 @@ class RateLimitError(MemoriError):
|
|
|
222
222
|
def __init__(
|
|
223
223
|
self,
|
|
224
224
|
message: str,
|
|
225
|
-
provider:
|
|
226
|
-
limit_type:
|
|
227
|
-
retry_after:
|
|
228
|
-
error_code:
|
|
229
|
-
cause:
|
|
225
|
+
provider: str | None = None,
|
|
226
|
+
limit_type: str | None = None,
|
|
227
|
+
retry_after: int | None = None,
|
|
228
|
+
error_code: str | None = None,
|
|
229
|
+
cause: Exception | None = None,
|
|
230
230
|
):
|
|
231
231
|
context = {}
|
|
232
232
|
if provider:
|
|
@@ -250,11 +250,11 @@ class MemoryNotFoundError(MemoriError):
|
|
|
250
250
|
def __init__(
|
|
251
251
|
self,
|
|
252
252
|
message: str,
|
|
253
|
-
memory_id:
|
|
254
|
-
namespace:
|
|
255
|
-
search_criteria:
|
|
256
|
-
error_code:
|
|
257
|
-
cause:
|
|
253
|
+
memory_id: str | None = None,
|
|
254
|
+
namespace: str | None = None,
|
|
255
|
+
search_criteria: dict[str, Any] | None = None,
|
|
256
|
+
error_code: str | None = None,
|
|
257
|
+
cause: Exception | None = None,
|
|
258
258
|
):
|
|
259
259
|
context = {}
|
|
260
260
|
if memory_id:
|
|
@@ -278,10 +278,10 @@ class ProcessingError(MemoriError):
|
|
|
278
278
|
def __init__(
|
|
279
279
|
self,
|
|
280
280
|
message: str,
|
|
281
|
-
processing_stage:
|
|
282
|
-
input_data:
|
|
283
|
-
error_code:
|
|
284
|
-
cause:
|
|
281
|
+
processing_stage: str | None = None,
|
|
282
|
+
input_data: dict[str, Any] | None = None,
|
|
283
|
+
error_code: str | None = None,
|
|
284
|
+
cause: Exception | None = None,
|
|
285
285
|
):
|
|
286
286
|
context = {}
|
|
287
287
|
if processing_stage:
|
|
@@ -308,10 +308,10 @@ class TimeoutError(MemoriError):
|
|
|
308
308
|
def __init__(
|
|
309
309
|
self,
|
|
310
310
|
message: str,
|
|
311
|
-
operation:
|
|
312
|
-
timeout_seconds:
|
|
313
|
-
error_code:
|
|
314
|
-
cause:
|
|
311
|
+
operation: str | None = None,
|
|
312
|
+
timeout_seconds: float | None = None,
|
|
313
|
+
error_code: str | None = None,
|
|
314
|
+
cause: Exception | None = None,
|
|
315
315
|
):
|
|
316
316
|
context = {}
|
|
317
317
|
if operation:
|
|
@@ -333,11 +333,11 @@ class ResourceExhaustedError(MemoriError):
|
|
|
333
333
|
def __init__(
|
|
334
334
|
self,
|
|
335
335
|
message: str,
|
|
336
|
-
resource_type:
|
|
337
|
-
current_usage:
|
|
338
|
-
limit:
|
|
339
|
-
error_code:
|
|
340
|
-
cause:
|
|
336
|
+
resource_type: str | None = None,
|
|
337
|
+
current_usage: int | float | None = None,
|
|
338
|
+
limit: int | float | None = None,
|
|
339
|
+
error_code: str | None = None,
|
|
340
|
+
cause: Exception | None = None,
|
|
341
341
|
):
|
|
342
342
|
context = {}
|
|
343
343
|
if resource_type:
|
|
@@ -361,7 +361,7 @@ class ExceptionHandler:
|
|
|
361
361
|
|
|
362
362
|
@staticmethod
|
|
363
363
|
def handle_database_exception(
|
|
364
|
-
e: Exception, query:
|
|
364
|
+
e: Exception, query: str | None = None
|
|
365
365
|
) -> DatabaseError:
|
|
366
366
|
"""Convert generic exception to DatabaseError with context"""
|
|
367
367
|
if isinstance(e, DatabaseError):
|
|
@@ -375,7 +375,7 @@ class ExceptionHandler:
|
|
|
375
375
|
|
|
376
376
|
@staticmethod
|
|
377
377
|
def handle_agent_exception(
|
|
378
|
-
e: Exception, agent_type:
|
|
378
|
+
e: Exception, agent_type: str | None = None
|
|
379
379
|
) -> AgentError:
|
|
380
380
|
"""Convert generic exception to AgentError with context"""
|
|
381
381
|
if isinstance(e, AgentError):
|
|
@@ -389,7 +389,7 @@ class ExceptionHandler:
|
|
|
389
389
|
|
|
390
390
|
@staticmethod
|
|
391
391
|
def handle_validation_exception(
|
|
392
|
-
e: Exception, field_name:
|
|
392
|
+
e: Exception, field_name: str | None = None
|
|
393
393
|
) -> ValidationError:
|
|
394
394
|
"""Convert generic exception to ValidationError with context"""
|
|
395
395
|
if isinstance(e, ValidationError):
|
memori/utils/helpers.py
CHANGED
|
@@ -7,9 +7,10 @@ import functools
|
|
|
7
7
|
import hashlib
|
|
8
8
|
import json
|
|
9
9
|
import uuid
|
|
10
|
+
from collections.abc import Awaitable, Callable
|
|
10
11
|
from datetime import datetime, timedelta
|
|
11
12
|
from pathlib import Path
|
|
12
|
-
from typing import Any,
|
|
13
|
+
from typing import Any, TypeVar
|
|
13
14
|
|
|
14
15
|
from .exceptions import MemoriError
|
|
15
16
|
|
|
@@ -52,7 +53,7 @@ class StringUtils:
|
|
|
52
53
|
return hash_obj.hexdigest()
|
|
53
54
|
|
|
54
55
|
@staticmethod
|
|
55
|
-
def extract_keywords(text: str, max_keywords: int = 10) ->
|
|
56
|
+
def extract_keywords(text: str, max_keywords: int = 10) -> list[str]:
|
|
56
57
|
"""Extract keywords from text (simple implementation)"""
|
|
57
58
|
import re
|
|
58
59
|
|
|
@@ -201,7 +202,7 @@ class JsonUtils:
|
|
|
201
202
|
return json.dumps(default or {}, indent=indent)
|
|
202
203
|
|
|
203
204
|
@staticmethod
|
|
204
|
-
def merge_dicts(base:
|
|
205
|
+
def merge_dicts(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
|
|
205
206
|
"""Deep merge two dictionaries"""
|
|
206
207
|
result = base.copy()
|
|
207
208
|
|
|
@@ -222,16 +223,14 @@ class FileUtils:
|
|
|
222
223
|
"""File handling utilities"""
|
|
223
224
|
|
|
224
225
|
@staticmethod
|
|
225
|
-
def ensure_directory(path:
|
|
226
|
+
def ensure_directory(path: str | Path) -> Path:
|
|
226
227
|
"""Ensure directory exists, create if not"""
|
|
227
228
|
path = Path(path)
|
|
228
229
|
path.mkdir(parents=True, exist_ok=True)
|
|
229
230
|
return path
|
|
230
231
|
|
|
231
232
|
@staticmethod
|
|
232
|
-
def safe_read_text(
|
|
233
|
-
file_path: Union[str, Path], encoding: str = "utf-8"
|
|
234
|
-
) -> Optional[str]:
|
|
233
|
+
def safe_read_text(file_path: str | Path, encoding: str = "utf-8") -> str | None:
|
|
235
234
|
"""Safely read text file"""
|
|
236
235
|
try:
|
|
237
236
|
return Path(file_path).read_text(encoding=encoding)
|
|
@@ -240,7 +239,7 @@ class FileUtils:
|
|
|
240
239
|
|
|
241
240
|
@staticmethod
|
|
242
241
|
def safe_write_text(
|
|
243
|
-
file_path:
|
|
242
|
+
file_path: str | Path, content: str, encoding: str = "utf-8"
|
|
244
243
|
) -> bool:
|
|
245
244
|
"""Safely write text file"""
|
|
246
245
|
try:
|
|
@@ -252,7 +251,7 @@ class FileUtils:
|
|
|
252
251
|
return False
|
|
253
252
|
|
|
254
253
|
@staticmethod
|
|
255
|
-
def get_file_size(file_path:
|
|
254
|
+
def get_file_size(file_path: str | Path) -> int:
|
|
256
255
|
"""Get file size in bytes"""
|
|
257
256
|
try:
|
|
258
257
|
return Path(file_path).stat().st_size
|
|
@@ -260,7 +259,7 @@ class FileUtils:
|
|
|
260
259
|
return 0
|
|
261
260
|
|
|
262
261
|
@staticmethod
|
|
263
|
-
def is_file_recent(file_path:
|
|
262
|
+
def is_file_recent(file_path: str | Path, hours: int = 24) -> bool:
|
|
264
263
|
"""Check if file was modified recently"""
|
|
265
264
|
try:
|
|
266
265
|
mtime = datetime.fromtimestamp(Path(file_path).stat().st_mtime)
|
|
@@ -386,7 +385,7 @@ class PerformanceUtils:
|
|
|
386
385
|
return wrapper
|
|
387
386
|
|
|
388
387
|
@staticmethod
|
|
389
|
-
def memory_usage() ->
|
|
388
|
+
def memory_usage() -> dict[str, float]:
|
|
390
389
|
"""Get current memory usage"""
|
|
391
390
|
try:
|
|
392
391
|
import psutil
|
|
@@ -417,7 +416,7 @@ class AsyncUtils:
|
|
|
417
416
|
)
|
|
418
417
|
|
|
419
418
|
@staticmethod
|
|
420
|
-
async def gather_with_concurrency(limit: int, *tasks) ->
|
|
419
|
+
async def gather_with_concurrency(limit: int, *tasks) -> list[Any]:
|
|
421
420
|
"""Run tasks with concurrency limit"""
|
|
422
421
|
semaphore = asyncio.Semaphore(limit)
|
|
423
422
|
|
memori/utils/input_validator.py
CHANGED
|
@@ -7,7 +7,7 @@ import html
|
|
|
7
7
|
import json
|
|
8
8
|
import re
|
|
9
9
|
from datetime import datetime
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
from loguru import logger
|
|
13
13
|
|
|
@@ -40,7 +40,7 @@ class InputValidator:
|
|
|
40
40
|
@classmethod
|
|
41
41
|
def validate_and_sanitize_query(cls, query: str, max_length: int = 10000) -> str:
|
|
42
42
|
"""Validate and sanitize search query input"""
|
|
43
|
-
if not isinstance(query,
|
|
43
|
+
if not isinstance(query, str | type(None)):
|
|
44
44
|
raise ValidationError("Query must be a string or None")
|
|
45
45
|
|
|
46
46
|
if query is None:
|
|
@@ -95,9 +95,7 @@ class InputValidator:
|
|
|
95
95
|
return sanitized_namespace
|
|
96
96
|
|
|
97
97
|
@classmethod
|
|
98
|
-
def validate_category_filter(
|
|
99
|
-
cls, category_filter: Optional[List[str]]
|
|
100
|
-
) -> List[str]:
|
|
98
|
+
def validate_category_filter(cls, category_filter: list[str] | None) -> list[str]:
|
|
101
99
|
"""Validate and sanitize category filter list"""
|
|
102
100
|
if category_filter is None:
|
|
103
101
|
return []
|
|
@@ -130,7 +128,7 @@ class InputValidator:
|
|
|
130
128
|
return sanitized_categories
|
|
131
129
|
|
|
132
130
|
@classmethod
|
|
133
|
-
def validate_limit(cls, limit:
|
|
131
|
+
def validate_limit(cls, limit: int | str) -> int:
|
|
134
132
|
"""Validate and sanitize limit parameter"""
|
|
135
133
|
try:
|
|
136
134
|
int_limit = int(limit)
|
|
@@ -223,7 +221,7 @@ class InputValidator:
|
|
|
223
221
|
return sanitized_content.strip()
|
|
224
222
|
|
|
225
223
|
@classmethod
|
|
226
|
-
def validate_timestamp(cls, timestamp:
|
|
224
|
+
def validate_timestamp(cls, timestamp: datetime | str | None) -> datetime:
|
|
227
225
|
"""Validate and normalize timestamp"""
|
|
228
226
|
if timestamp is None:
|
|
229
227
|
return datetime.now()
|
|
@@ -246,7 +244,7 @@ class InputValidator:
|
|
|
246
244
|
|
|
247
245
|
@classmethod
|
|
248
246
|
def validate_score(
|
|
249
|
-
cls, score:
|
|
247
|
+
cls, score: float | int | str, field_name: str = "score"
|
|
250
248
|
) -> float:
|
|
251
249
|
"""Validate and normalize score values (0.0 to 1.0)"""
|
|
252
250
|
try:
|
|
@@ -328,8 +326,8 @@ class DatabaseInputValidator:
|
|
|
328
326
|
|
|
329
327
|
@classmethod
|
|
330
328
|
def validate_insert_params(
|
|
331
|
-
cls, table: str, params:
|
|
332
|
-
) ->
|
|
329
|
+
cls, table: str, params: dict[str, Any]
|
|
330
|
+
) -> dict[str, Any]:
|
|
333
331
|
"""Validate parameters for database insert operations"""
|
|
334
332
|
sanitized_params = {}
|
|
335
333
|
|
|
@@ -383,9 +381,9 @@ class DatabaseInputValidator:
|
|
|
383
381
|
cls,
|
|
384
382
|
query: str,
|
|
385
383
|
namespace: str,
|
|
386
|
-
category_filter:
|
|
384
|
+
category_filter: list[str] | None,
|
|
387
385
|
limit: int,
|
|
388
|
-
) ->
|
|
386
|
+
) -> dict[str, Any]:
|
|
389
387
|
"""Validate all search parameters together"""
|
|
390
388
|
return {
|
|
391
389
|
"query": InputValidator.validate_and_sanitize_query(query),
|
memori/utils/logging.py
CHANGED
|
@@ -4,7 +4,7 @@ Centralized logging configuration for Memoriai
|
|
|
4
4
|
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
from loguru import logger
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ class LoggingManager:
|
|
|
16
16
|
"""Centralized logging management"""
|
|
17
17
|
|
|
18
18
|
_initialized = False
|
|
19
|
-
_current_config:
|
|
19
|
+
_current_config: LoggingSettings | None = None
|
|
20
20
|
|
|
21
21
|
@classmethod
|
|
22
22
|
def setup_logging(cls, settings: LoggingSettings, verbose: bool = False) -> None:
|
|
@@ -107,7 +107,7 @@ class LoggingManager:
|
|
|
107
107
|
logger.error(f"Failed to update log level: {e}")
|
|
108
108
|
|
|
109
109
|
@classmethod
|
|
110
|
-
def add_custom_handler(cls, handler_config:
|
|
110
|
+
def add_custom_handler(cls, handler_config: dict[str, Any]) -> None:
|
|
111
111
|
"""Add a custom logging handler"""
|
|
112
112
|
try:
|
|
113
113
|
logger.add(**handler_config)
|
|
@@ -121,7 +121,7 @@ class LoggingManager:
|
|
|
121
121
|
return cls._initialized
|
|
122
122
|
|
|
123
123
|
@classmethod
|
|
124
|
-
def get_current_config(cls) ->
|
|
124
|
+
def get_current_config(cls) -> LoggingSettings | None:
|
|
125
125
|
"""Get current logging configuration"""
|
|
126
126
|
return cls._current_config
|
|
127
127
|
|